From c25bf8fe0c2e563b34eb1a3176ca05be980cca8b Mon Sep 17 00:00:00 2001 From: Ahmed Date: Thu, 26 Mar 2026 21:14:15 +0200 Subject: [PATCH 01/11] feat: add security principal and auth seam --- .../io/eventlens/api/EventLensServer.java | 75 +++----- .../api/http/RequestContextMdcFilter.java | 8 +- .../eventlens/api/http/SecurityContext.java | 56 ++++++ .../eventlens/api/routes/AggregateRoutes.java | 33 +--- .../eventlens/api/routes/AnomalyRoutes.java | 40 +--- .../api/routes/AsyncExportRoutes.java | 44 +---- .../io/eventlens/api/routes/ExportRoutes.java | 33 +--- .../eventlens/api/routes/TimelineRoutes.java | 35 +--- .../api/security/AuthenticationResult.java | 23 +++ .../api/security/BasicAuthenticator.java | 48 +++++ .../api/security/RequestAuthenticator.java | 11 ++ .../api/AuthenticationIntegrationTest.java | 174 ++++++++++++++++++ .../io/eventlens/core/security/Principal.java | 34 ++++ 13 files changed, 393 insertions(+), 221 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/AuthenticationResult.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/BasicAuthenticator.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/RequestAuthenticator.java create mode 100644 eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/Principal.java diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index b5f9e67..91c13b3 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -5,10 +5,12 @@ import io.eventlens.api.source.SourceRegistry; import io.eventlens.api.websocket.LiveTailWebSocket; import io.eventlens.api.export.ExportService; +import io.eventlens.api.http.SecurityContext; import io.eventlens.api.shutdown.GracefulShutdown; import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.routes.MetricsRoutes; import io.eventlens.api.http.RequestContextMdcFilter; +import io.eventlens.api.security.BasicAuthenticator; import io.eventlens.core.EventLensConfig; import io.eventlens.core.aggregator.ReducerRegistry; import io.eventlens.core.RateLimiter; @@ -18,6 +20,7 @@ import io.eventlens.core.exception.QueryTimeoutException; import io.eventlens.core.pii.PiiMasker; import io.eventlens.core.plugin.PluginManager; +import io.eventlens.core.security.Principal; import io.eventlens.core.spi.EventStoreReader; import io.javalin.Javalin; import io.javalin.compression.CompressionStrategy; @@ -179,6 +182,7 @@ public EventLensServer( } ctx.header("X-Request-Id", requestId); ctx.attribute("requestId", requestId); + SecurityContext.setPrincipal(ctx, Principal.anonymous()); // 4.2 Structured logging context (MDC) new RequestContextMdcFilter().handle(ctx); @@ -231,7 +235,7 @@ public EventLensServer( if (rateLimiter != null) { cfg.routes.before("/api/*", ctx -> { - String clientIp = extractClientIp(ctx); + String clientIp = SecurityContext.clientIp(ctx); var result = rateLimiter.tryConsume(clientIp); ctx.header("X-RateLimit-Limit", String.valueOf(result.limitPerMinute())); @@ -251,64 +255,51 @@ public EventLensServer( } if (authConfig.isEnabled()) { + var basicAuthenticator = new BasicAuthenticator( + authConfig.getUsername(), + authConfig.getPassword(), + "EventLens"); if ("changeme".equals(authConfig.getPassword())) { log.warn("Basic auth is enabled with default password 'changeme'. Change server.auth.password in production."); } - String expectedAuth = authConfig.getUsername() + ":" + authConfig.getPassword(); cfg.routes.before("/api/*", ctx -> { - String clientIp = extractClientIp(ctx); - String requestId = ctx.attribute("requestId"); - String userAgent = ctx.userAgent(); - String suppliedUser = ctx.basicAuthCredentials() != null - ? ctx.basicAuthCredentials().getUsername() : null; - String auth = ctx.basicAuthCredentials() != null - ? suppliedUser + ":" + ctx.basicAuthCredentials().getPassword() - : null; - - if (!expectedAuth.equals(auth)) { - // 1.8 — emit LOGIN_FAILED - auditLogger.log(AuditEvent.builder() + var authResult = basicAuthenticator.authenticate(ctx); + if (!authResult.success()) { + // 1.8 - emit LOGIN_FAILED + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_LOGIN_FAILED) .resourceType(AuditEvent.RT_AUTH) - .userId(suppliedUser != null ? suppliedUser : "anonymous") + .userId(authResult.attemptedUserId() != null ? authResult.attemptedUserId() : "anonymous") .authMethod("basic") - .clientIp(clientIp) - .requestId(requestId != null ? requestId : "unknown") - .userAgent(userAgent) - .details(Map.of("reason", "invalid_credentials", "path", ctx.path())) + .details(Map.of("reason", authResult.failureReason(), "path", ctx.path())) .build()); ctx.status(401) - .header("WWW-Authenticate", "Basic realm=\"EventLens\"") + .header("WWW-Authenticate", authResult.challengeHeader()) .json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { - // 1.8 — emit LOGIN success (once per request, not once per session) - auditLogger.log(AuditEvent.builder() + SecurityContext.setPrincipal(ctx, authResult.principal()); + new RequestContextMdcFilter().handle(ctx); + // 1.8 - emit LOGIN success (once per request, not once per session) + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_LOGIN) .resourceType(AuditEvent.RT_AUTH) - .userId(suppliedUser) - .authMethod("basic") - .clientIp(clientIp) - .requestId(requestId != null ? requestId : "unknown") - .userAgent(userAgent) .details(Map.of("path", ctx.path())) .build()); - // Store principal for downstream audit events - ctx.attribute("auditUserId", suppliedUser); - ctx.attribute("auditAuthMethod", "basic"); } }); cfg.routes.before("/ws/*", ctx -> { - String auth = ctx.basicAuthCredentials() != null - ? ctx.basicAuthCredentials().getUsername() + ":" + ctx.basicAuthCredentials().getPassword() - : null; - if (!expectedAuth.equals(auth)) { + var authResult = basicAuthenticator.authenticate(ctx); + if (!authResult.success()) { ctx.status(401) - .header("WWW-Authenticate", "Basic realm=\"EventLens\"") + .header("WWW-Authenticate", authResult.challengeHeader()) .json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); + } else { + SecurityContext.setPrincipal(ctx, authResult.principal()); + new RequestContextMdcFilter().handle(ctx); } }); log.info("Basic auth ENABLED for /api/* and /ws/*"); @@ -486,19 +477,5 @@ private static void markDeprecated(io.javalin.http.Context ctx, String successor ctx.header("Sunset", "2026-01-01"); ctx.header("Link", "<" + successor + ">; rel=\"successor-version\""); } - - private static String extractClientIp(io.javalin.http.Context ctx) { - String xff = ctx.header("X-Forwarded-For"); - if (xff != null && !xff.isBlank()) { - int comma = xff.indexOf(','); - String first = (comma >= 0 ? xff.substring(0, comma) : xff).trim(); - if (!first.isBlank()) return first; - } - String xri = ctx.header("X-Real-IP"); - if (xri != null && !xri.isBlank()) return xri.trim(); - return ctx.ip(); - } } - - diff --git a/eventlens-api/src/main/java/io/eventlens/api/http/RequestContextMdcFilter.java b/eventlens-api/src/main/java/io/eventlens/api/http/RequestContextMdcFilter.java index 4a55402..c763059 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/http/RequestContextMdcFilter.java +++ b/eventlens-api/src/main/java/io/eventlens/api/http/RequestContextMdcFilter.java @@ -15,12 +15,9 @@ public void handle(@NotNull Context ctx) { String requestId = ctx.attribute("requestId"); if (requestId != null) MDC.put("requestId", requestId); - String userId = ctx.attribute("auditUserId"); - MDC.put("userId", userId != null ? userId : "anonymous"); + MDC.put("userId", SecurityContext.principal(ctx).userId()); - String clientIp = ctx.header("X-Real-IP"); - if (clientIp == null || clientIp.isBlank()) clientIp = ctx.ip(); - MDC.put("clientIp", clientIp); + MDC.put("clientIp", SecurityContext.clientIp(ctx)); MDC.put("method", ctx.method().name()); MDC.put("path", ctx.path()); @@ -30,4 +27,3 @@ public static void clear() { MDC.clear(); } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java b/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java new file mode 100644 index 0000000..5a8d385 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java @@ -0,0 +1,56 @@ +package io.eventlens.api.http; + +import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.security.Principal; +import io.javalin.http.Context; + +/** + * Shared request-security and audit-context helper. + */ +public final class SecurityContext { + + public static final String ATTR_PRINCIPAL = "eventlensPrincipal"; + private static final String LEGACY_AUDIT_USER_ID = "auditUserId"; + private static final String LEGACY_AUDIT_AUTH_METHOD = "auditAuthMethod"; + + private SecurityContext() { + } + + public static void setPrincipal(Context ctx, Principal principal) { + Principal effective = principal == null ? Principal.anonymous() : principal; + ctx.attribute(ATTR_PRINCIPAL, effective); + // Preserve legacy attributes while the rest of the codebase is still migrating. + ctx.attribute(LEGACY_AUDIT_USER_ID, effective.userId()); + ctx.attribute(LEGACY_AUDIT_AUTH_METHOD, effective.authMethod()); + } + + public static Principal principal(Context ctx) { + Principal principal = ctx.attribute(ATTR_PRINCIPAL); + return principal != null ? principal : Principal.anonymous(); + } + + public static AuditEvent.Builder audit(Context ctx) { + Principal principal = principal(ctx); + return AuditEvent.builder() + .userId(principal.userId()) + .authMethod(principal.authMethod()) + .clientIp(clientIp(ctx)) + .requestId(requestId(ctx)) + .userAgent(ctx.userAgent()); + } + + public static String clientIp(Context ctx) { + String xff = ctx.header("X-Forwarded-For"); + if (xff != null && !xff.isBlank()) { + int c = xff.indexOf(','); + return (c >= 0 ? xff.substring(0, c) : xff).trim(); + } + String xri = ctx.header("X-Real-IP"); + return xri != null && !xri.isBlank() ? xri.trim() : ctx.ip(); + } + + public static String requestId(Context ctx) { + String requestId = ctx.attribute("requestId"); + return requestId != null ? requestId : "unknown"; + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java index 22e6ecd..cfb2576 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java @@ -2,6 +2,7 @@ import io.eventlens.api.cache.QueryResultCache; import io.eventlens.api.http.ConditionalGet; +import io.eventlens.api.http.SecurityContext; import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; import io.eventlens.core.audit.AuditEvent; @@ -56,14 +57,9 @@ public void search(Context ctx) { searchTtl, () -> source.reader().searchAggregates(query, limit)); - auditLogger.log(AuditEvent.builder() + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_SEARCH) .resourceType(AuditEvent.RT_AGGREGATE) - .userId(userId(ctx)) - .authMethod(authMethod(ctx)) - .clientIp(clientIp(ctx)) - .requestId(requestId(ctx)) - .userAgent(ctx.userAgent()) .details(Map.of( "q", query, "limit", limit, @@ -86,29 +82,4 @@ public void recentEvents(Context ctx) { EventStoreReader reader = sourceRegistry.resolve(ctx.queryParam("source")).reader(); ConditionalGet.json(ctx, reader.getRecentEvents(limit)); } - - private static String userId(Context ctx) { - String v = ctx.attribute("auditUserId"); - return v != null ? v : "anonymous"; - } - - private static String authMethod(Context ctx) { - String v = ctx.attribute("auditAuthMethod"); - return v != null ? v : "anonymous"; - } - - private static String clientIp(Context ctx) { - String xff = ctx.header("X-Forwarded-For"); - if (xff != null && !xff.isBlank()) { - int c = xff.indexOf(','); - return (c >= 0 ? xff.substring(0, c) : xff).trim(); - } - String xri = ctx.header("X-Real-IP"); - return xri != null && !xri.isBlank() ? xri.trim() : ctx.ip(); - } - - private static String requestId(Context ctx) { - String v = ctx.attribute("requestId"); - return v != null ? v : "unknown"; - } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java index 01cbac0..4a72a7e 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.api.source.SourceRegistry; +import io.eventlens.api.http.SecurityContext; import io.eventlens.core.EventLensConfig; import io.eventlens.core.InputValidator; import io.eventlens.core.audit.AuditEvent; @@ -40,15 +41,10 @@ public void scanAggregate(Context ctx) { var source = sourceRegistry.resolve(ctx.queryParam("source")); var result = detectorFor(source.id(), source).scan(id); - auditLogger.log(AuditEvent.builder() + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_VIEW_ANOMALIES) .resourceType(AuditEvent.RT_ANOMALY) .resourceId(id) - .userId(userId(ctx)) - .authMethod(authMethod(ctx)) - .clientIp(clientIp(ctx)) - .requestId(requestId(ctx)) - .userAgent(ctx.userAgent()) .details(Map.of( "anomalyCount", result.size(), "source", source.id())) @@ -65,14 +61,9 @@ public void scanRecent(Context ctx) { var source = sourceRegistry.resolve(ctx.queryParam("source")); var result = detectorFor(source.id(), source).scanRecent(limit); - auditLogger.log(AuditEvent.builder() + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_VIEW_ANOMALIES) .resourceType(AuditEvent.RT_ANOMALY) - .userId(userId(ctx)) - .authMethod(authMethod(ctx)) - .clientIp(clientIp(ctx)) - .requestId(requestId(ctx)) - .userAgent(ctx.userAgent()) .details(Map.of( "limit", limit, "anomalyCount", result.size(), @@ -87,29 +78,4 @@ private AnomalyDetector detectorFor(String sourceId, SourceRegistry.ResolvedSour sourceId, ignored -> new AnomalyDetector(source.reader(), source.replayEngine(), anomalyConfig)); } - - private static String userId(Context ctx) { - String v = ctx.attribute("auditUserId"); - return v != null ? v : "anonymous"; - } - - private static String authMethod(Context ctx) { - String v = ctx.attribute("auditAuthMethod"); - return v != null ? v : "anonymous"; - } - - private static String clientIp(Context ctx) { - String xff = ctx.header("X-Forwarded-For"); - if (xff != null && !xff.isBlank()) { - int c = xff.indexOf(','); - return (c >= 0 ? xff.substring(0, c) : xff).trim(); - } - String xri = ctx.header("X-Real-IP"); - return xri != null && !xri.isBlank() ? xri.trim() : ctx.ip(); - } - - private static String requestId(Context ctx) { - String v = ctx.attribute("requestId"); - return v != null ? v : "unknown"; - } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java index b2d9358..fbe0948 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java @@ -2,6 +2,7 @@ import io.eventlens.api.export.ExportJob; import io.eventlens.api.export.ExportService; +import io.eventlens.api.http.SecurityContext; import io.eventlens.core.InputValidator; import io.javalin.http.Context; @@ -36,10 +37,10 @@ public void start(Context ctx) { req.format, limit, Map.of( - "userId", userId(ctx), - "authMethod", authMethod(ctx), - "clientIp", clientIp(ctx), - "requestId", requestId(ctx), + "userId", SecurityContext.principal(ctx).userId(), + "authMethod", SecurityContext.principal(ctx).authMethod(), + "clientIp", SecurityContext.clientIp(ctx), + "requestId", SecurityContext.requestId(ctx), "userAgent", ctx.userAgent() != null ? ctx.userAgent() : "unknown" )); @@ -63,8 +64,11 @@ public void status(Context ctx) { long fileSize = 0L; try { var f = job.file(); - if (f != null && Files.exists(f)) fileSize = Files.size(f); - } catch (Exception ignored) {} + if (f != null && Files.exists(f)) { + fileSize = Files.size(f); + } + } catch (Exception ignored) { + } ctx.json(Map.of( "exportId", job.exportId(), @@ -118,32 +122,4 @@ public void download(Context ctx) { ctx.status(500).json(Map.of("error", "download_failed", "message", e.getMessage())); } } - - // ── Helpers ────────────────────────────────────────────────────────────── - - private static String userId(Context ctx) { - String v = ctx.attribute("auditUserId"); - return v != null ? v : "anonymous"; - } - - private static String authMethod(Context ctx) { - String v = ctx.attribute("auditAuthMethod"); - return v != null ? v : "anonymous"; - } - - private static String clientIp(Context ctx) { - String xff = ctx.header("X-Forwarded-For"); - if (xff != null && !xff.isBlank()) { - int c = xff.indexOf(','); - return (c >= 0 ? xff.substring(0, c) : xff).trim(); - } - String xri = ctx.header("X-Real-IP"); - return xri != null && !xri.isBlank() ? xri.trim() : ctx.ip(); - } - - private static String requestId(Context ctx) { - String v = ctx.attribute("requestId"); - return v != null ? v : "unknown"; - } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java index 7c7cb27..5ba384b 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.core.InputValidator; +import io.eventlens.api.http.SecurityContext; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.engine.ExportEngine; @@ -46,15 +47,10 @@ public void export(Context ctx) { }; // 1.8 — audit - auditLogger.log(AuditEvent.builder() + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_EXPORT) .resourceType(AuditEvent.RT_EXPORT) .resourceId(id) - .userId(userId(ctx)) - .authMethod(authMethod(ctx)) - .clientIp(clientIp(ctx)) - .requestId(requestId(ctx)) - .userAgent(ctx.userAgent()) .details(Map.of("format", formatStr, "byteCount", content.length())) .build()); @@ -62,29 +58,4 @@ public void export(Context ctx) { } // ── Helpers ────────────────────────────────────────────────────────────── - - private static String userId(Context ctx) { - String v = ctx.attribute("auditUserId"); - return v != null ? v : "anonymous"; - } - - private static String authMethod(Context ctx) { - String v = ctx.attribute("auditAuthMethod"); - return v != null ? v : "anonymous"; - } - - private static String clientIp(Context ctx) { - String xff = ctx.header("X-Forwarded-For"); - if (xff != null && !xff.isBlank()) { - int c = xff.indexOf(','); - return (c >= 0 ? xff.substring(0, c) : xff).trim(); - } - String xri = ctx.header("X-Real-IP"); - return xri != null && !xri.isBlank() ? xri.trim() : ctx.ip(); - } - - private static String requestId(Context ctx) { - String v = ctx.attribute("requestId"); - return v != null ? v : "unknown"; - } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java index b61afc2..d061e26 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import io.eventlens.api.cache.QueryResultCache; import io.eventlens.api.http.ConditionalGet; +import io.eventlens.api.http.SecurityContext; import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; import io.eventlens.core.audit.AuditEvent; @@ -65,15 +66,10 @@ public void getTimeline(Context ctx) { timelineTtl, () -> buildTimelineEnvelope(source, id, limit, offset, cursorParam, fields)); - auditLogger.log(AuditEvent.builder() + auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_VIEW_TIMELINE) .resourceType(AuditEvent.RT_AGGREGATE) .resourceId(id) - .userId(userId(ctx)) - .authMethod(authMethod(ctx)) - .clientIp(clientIp(ctx)) - .requestId(requestId(ctx)) - .userAgent(ctx.userAgent()) .details(Map.of( "limit", limit, "offset", offset, @@ -221,33 +217,6 @@ private StoredEvent maskEvent(StoredEvent event) { return event; } } - - private static String userId(Context ctx) { - String v = ctx.attribute("auditUserId"); - return v != null ? v : "anonymous"; - } - - private static String authMethod(Context ctx) { - String v = ctx.attribute("auditAuthMethod"); - return v != null ? v : "anonymous"; - } - - private static String clientIp(Context ctx) { - String xff = ctx.header("X-Forwarded-For"); - if (xff != null && !xff.isBlank()) { - int c = xff.indexOf(','); - return (c >= 0 ? xff.substring(0, c) : xff).trim(); - } - String xri = ctx.header("X-Real-IP"); - return xri != null && !xri.isBlank() ? xri.trim() : ctx.ip(); - } - - private static String requestId(Context ctx) { - String v = ctx.attribute("requestId"); - return v != null ? v : "unknown"; - } - private record TimelineEnvelope(AggregateTimeline timeline, boolean hasMore, String nextCursor) { } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/AuthenticationResult.java b/eventlens-api/src/main/java/io/eventlens/api/security/AuthenticationResult.java new file mode 100644 index 0000000..df03fef --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/AuthenticationResult.java @@ -0,0 +1,23 @@ +package io.eventlens.api.security; + +import io.eventlens.core.security.Principal; + +/** + * Result of a single authentication attempt. + */ +public record AuthenticationResult( + boolean success, + Principal principal, + String attemptedUserId, + String failureReason, + String challengeHeader +) { + + public static AuthenticationResult success(Principal principal) { + return new AuthenticationResult(true, principal, principal.userId(), null, null); + } + + public static AuthenticationResult failure(String attemptedUserId, String failureReason, String challengeHeader) { + return new AuthenticationResult(false, Principal.anonymous(), attemptedUserId, failureReason, challengeHeader); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/BasicAuthenticator.java b/eventlens-api/src/main/java/io/eventlens/api/security/BasicAuthenticator.java new file mode 100644 index 0000000..b7b6120 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/BasicAuthenticator.java @@ -0,0 +1,48 @@ +package io.eventlens.api.security; + +import io.eventlens.core.security.Principal; +import io.javalin.http.Context; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +/** + * Basic-auth implementation for the v5 auth seam. + */ +public final class BasicAuthenticator implements RequestAuthenticator { + + private final String expectedUsername; + private final String expectedPassword; + private final String challengeHeader; + + public BasicAuthenticator(String expectedUsername, String expectedPassword, String realm) { + this.expectedUsername = expectedUsername == null ? "" : expectedUsername; + this.expectedPassword = expectedPassword == null ? "" : expectedPassword; + this.challengeHeader = "Basic realm=\"" + (realm == null || realm.isBlank() ? "EventLens" : realm) + "\""; + } + + @Override + public AuthenticationResult authenticate(Context ctx) { + var credentials = ctx.basicAuthCredentials(); + if (credentials == null) { + return AuthenticationResult.failure(null, "missing_credentials", challengeHeader); + } + + String suppliedUser = credentials.getUsername(); + String suppliedPassword = credentials.getPassword(); + + boolean userMatches = constantTimeEquals(expectedUsername, suppliedUser); + boolean passwordMatches = constantTimeEquals(expectedPassword, suppliedPassword); + if (userMatches && passwordMatches) { + return AuthenticationResult.success(Principal.basic(suppliedUser)); + } + + return AuthenticationResult.failure(suppliedUser, "invalid_credentials", challengeHeader); + } + + private static boolean constantTimeEquals(String expected, String actual) { + byte[] left = (expected == null ? "" : expected).getBytes(StandardCharsets.UTF_8); + byte[] right = (actual == null ? "" : actual).getBytes(StandardCharsets.UTF_8); + return MessageDigest.isEqual(left, right); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/RequestAuthenticator.java b/eventlens-api/src/main/java/io/eventlens/api/security/RequestAuthenticator.java new file mode 100644 index 0000000..012f73b --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/RequestAuthenticator.java @@ -0,0 +1,11 @@ +package io.eventlens.api.security; + +import io.javalin.http.Context; + +/** + * Authenticates an incoming request without coupling the caller to a specific + * auth mechanism. + */ +public interface RequestAuthenticator { + AuthenticationResult authenticate(Context ctx); +} diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java new file mode 100644 index 0000000..185fbfb --- /dev/null +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -0,0 +1,174 @@ +package io.eventlens.api; + +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.aggregator.ReducerRegistry; +import io.eventlens.core.engine.AnomalyDetector; +import io.eventlens.core.engine.BisectEngine; +import io.eventlens.core.engine.DiffEngine; +import io.eventlens.core.engine.ExportEngine; +import io.eventlens.core.engine.ReplayEngine; +import io.eventlens.core.model.StoredEvent; +import io.eventlens.core.plugin.PluginManager; +import io.eventlens.core.spi.EventStoreReader; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.net.ServerSocket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Base64; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class AuthenticationIntegrationTest { + + private EventLensServer server; + + @AfterEach + void tearDown() { + if (server != null) { + server.stop(); + } + } + + @Test + void protectedApiRejectsMissingBasicCredentials() throws Exception { + int port = freePort(); + server = startServer(port, true); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(401); + assertThat(response.headers().firstValue("WWW-Authenticate")).hasValue("Basic realm=\"EventLens\""); + } + + @Test + void protectedApiAcceptsValidBasicCredentials() throws Exception { + int port = freePort(); + server = startServer(port, true); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).contains("ORD-1001"); + } + + @Test + void authDisabledStillAllowsExistingReadFlow() throws Exception { + int port = freePort(); + server = startServer(port, false); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).contains("ORD-1001"); + } + + private EventLensServer startServer(int port, boolean authEnabled) { + EventStoreReader reader = new EventStoreReader() { + @Override + public List getEvents(String aggregateId) { + return List.of(); + } + + @Override + public List getEventsUpTo(String aggregateId, long maxSequence) { + return List.of(); + } + + @Override + public List findAggregateIds(String aggregateType, int limit, int offset) { + return List.of("ORD-1001"); + } + + @Override + public List getRecentEvents(int limit) { + return List.of(new StoredEvent( + "evt-1", "ORD-1001", "Order", 1, "ORDER_CREATED", + "{}", "{}", Instant.now(), 1L)); + } + + @Override + public List getEventsAfter(long globalPosition, int limit) { + return List.of(); + } + + @Override + public long countEvents(String aggregateId) { + return 0; + } + + @Override + public List getAggregateTypes() { + return List.of("Order"); + } + + @Override + public List searchAggregates(String query, int limit) { + return List.of("ORD-1001"); + } + }; + + var cfg = new EventLensConfig(); + cfg.getServer().setPort(port); + cfg.getServer().getAuth().setEnabled(authEnabled); + cfg.getServer().getAuth().setUsername("admin"); + cfg.getServer().getAuth().setPassword("correct horse battery"); + cfg.getServer().getSecurity().getRateLimit().setEnabled(false); + + var replayEngine = new ReplayEngine(reader, new ReducerRegistry()); + var bisectEngine = new BisectEngine(replayEngine, reader); + var anomalyDetector = new AnomalyDetector(reader, replayEngine, cfg.getAnomaly()); + var exportEngine = new ExportEngine(reader, replayEngine); + var diffEngine = new DiffEngine(replayEngine); + + EventLensServer eventLensServer = new EventLensServer( + cfg, + reader, + replayEngine, + new ReducerRegistry(), + new PluginManager(30), + "default", + bisectEngine, + anomalyDetector, + exportEngine, + diffEngine); + eventLensServer.start(); + return eventLensServer; + } + + private static String basicAuth(String username, String password) { + String raw = username + ":" + password; + return "Basic " + Base64.getEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8)); + } + + private static int freePort() throws Exception { + try (ServerSocket socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/Principal.java b/eventlens-core/src/main/java/io/eventlens/core/security/Principal.java new file mode 100644 index 0000000..50a51c5 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/Principal.java @@ -0,0 +1,34 @@ +package io.eventlens.core.security; + +import java.util.Set; + +/** + * Request-scoped authenticated identity used by the API layer. + * + *

v5 starts with basic-auth and anonymous principals, but the shape is + * intentionally ready for future OIDC/session/API-key expansion without + * changing every call site again. + */ +public record Principal( + String userId, + String displayName, + String authMethod, + Set roles, + boolean authenticated +) { + + public Principal { + userId = userId == null || userId.isBlank() ? "anonymous" : userId; + displayName = displayName == null || displayName.isBlank() ? userId : displayName; + authMethod = authMethod == null || authMethod.isBlank() ? "anonymous" : authMethod; + roles = roles == null ? Set.of() : Set.copyOf(roles); + } + + public static Principal anonymous() { + return new Principal("anonymous", "anonymous", "anonymous", Set.of(), false); + } + + public static Principal basic(String username) { + return new Principal(username, username, "basic", Set.of(), true); + } +} From da112149aaa0e4307a3e969955467dec7f2c2fb3 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Thu, 26 Mar 2026 21:38:33 +0200 Subject: [PATCH 02/11] feat: add metadata persistence foundation --- .../java/io/eventlens/cli/ServeCommand.java | 9 +- eventlens-core/build.gradle.kts | 2 + .../io/eventlens/core/ConfigValidator.java | 28 ++++ .../io/eventlens/core/EventLensConfig.java | 42 +++++- .../eventlens/core/metadata/ApiKeyRecord.java | 17 +++ .../core/metadata/ApiKeyRepository.java | 110 +++++++++++++++ .../core/metadata/AuditLogRecord.java | 17 +++ .../core/metadata/AuditLogRepository.java | 80 +++++++++++ .../core/metadata/MetadataDatabase.java | 132 ++++++++++++++++++ .../core/metadata/MetadataJsonCodec.java | 56 ++++++++ .../metadata/MetadataMigrationRunner.java | 93 ++++++++++++ .../core/metadata/MetadataRepositories.java | 31 ++++ .../core/metadata/SessionRecord.java | 18 +++ .../core/metadata/SessionRepository.java | 117 ++++++++++++++++ .../metadata/V1__create_metadata_schema.sql | 63 +++++++++ .../eventlens/core/ConfigValidatorTest.java | 11 ++ .../core/metadata/MetadataDatabaseTest.java | 108 ++++++++++++++ 17 files changed, 932 insertions(+), 2 deletions(-) create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRecord.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRepository.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRecord.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataJsonCodec.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataMigrationRunner.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataRepositories.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRecord.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java create mode 100644 eventlens-core/src/main/resources/db/metadata/V1__create_metadata_schema.sql create mode 100644 eventlens-core/src/test/java/io/eventlens/core/metadata/MetadataDatabaseTest.java diff --git a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java index f321446..5b134ab 100644 --- a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java +++ b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java @@ -12,6 +12,7 @@ import io.eventlens.core.engine.DiffEngine; import io.eventlens.core.engine.ExportEngine; import io.eventlens.core.engine.ReplayEngine; +import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.plugin.PluginDiscovery; import io.eventlens.core.plugin.PluginManager; import io.eventlens.core.spi.EventStoreReader; @@ -73,6 +74,8 @@ public void run() { if (tableName != null) config.getDatasource().setTable(tableName); ConfigValidator.validateOrThrow(config); + MetadataDatabase metadataDatabase = MetadataDatabase.open( + config.getSecurity() != null ? config.getSecurity().getMetadata() : null); PluginManager pluginManager = new PluginManager(config.getPlugins().getHealthCheckIntervalSeconds()); PluginDiscovery.DiscoveryResult discovered = new PluginDiscovery().discoverFromClasspath() @@ -110,6 +113,11 @@ public void run() { datasourceStreamBindings(config)); Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + metadataDatabase.close(); + } catch (Exception e) { + log.warn("Failed to close metadata database cleanly", e); + } try { pluginManager.close(); } catch (Exception e) { @@ -228,4 +236,3 @@ private StreamAdapterPlugin createBuiltinStream(String type) { }; } } - diff --git a/eventlens-core/build.gradle.kts b/eventlens-core/build.gradle.kts index ffc5872..95a6731 100644 --- a/eventlens-core/build.gradle.kts +++ b/eventlens-core/build.gradle.kts @@ -4,4 +4,6 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.2") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.2") implementation("com.github.ben-manes.caffeine:caffeine:3.2.3") + implementation("com.zaxxer:HikariCP:5.1.0") + implementation("org.xerial:sqlite-jdbc:3.50.3.0") } diff --git a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java index 1b81a5d..e445ff9 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java +++ b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java @@ -87,6 +87,7 @@ public static List validate(EventLensConfig config) { } } + validateMetadata(config, issues); validateLegacyDatasource(config, issues); validateDatasourceInstances(config.getDatasourcesOrLegacy(), issues); validateStreamInstances(config.getStreamsOrLegacy(), issues); @@ -214,6 +215,33 @@ private static void validateStreamInstances(List issues) { + var security = config.getSecurity(); + if (security == null || security.getMetadata() == null) { + return; + } + + var metadata = security.getMetadata(); + if (!metadata.isEnabled()) { + return; + } + + if (isBlank(metadata.getJdbcUrl())) { + issues.add(error("security.metadata.jdbc-url", "Required when metadata storage is enabled")); + } else if (!metadata.getJdbcUrl().startsWith("jdbc:sqlite:")) { + issues.add(error("security.metadata.jdbc-url", "v5 metadata storage currently supports SQLite only")); + } else if ("jdbc:sqlite::memory:".equalsIgnoreCase(metadata.getJdbcUrl())) { + issues.add(warning("security.metadata.jdbc-url", + "In-memory metadata loses sessions, API keys, and DB-backed audit data on restart")); + } + + if (metadata.getBusyTimeoutMs() < 0 || metadata.getBusyTimeoutMs() > 600_000) { + issues.add(error("security.metadata.busy-timeout-ms", "Must be between 0 and 600000")); + } + + validatePool("security.metadata.pool", metadata.getPool(), issues); + } + private static void validatePool(String base, EventLensConfig.PoolConfig pool, List issues) { if (pool == null) { return; diff --git a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java index 4f04cdf..963c079 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java +++ b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java @@ -18,6 +18,7 @@ public class EventLensConfig { private AnomalyConfig anomaly = new AnomalyConfig(); private UiConfig ui = new UiConfig(); private AuditConfig audit = new AuditConfig(); + private SecurityFeaturesConfig security = new SecurityFeaturesConfig(); private DataProtectionConfig dataProtection = new DataProtectionConfig(); private ExportConfig export = new ExportConfig(); private PluginsConfig plugins = new PluginsConfig(); @@ -51,6 +52,9 @@ public class EventLensConfig { public AuditConfig getAudit() { return audit; } public void setAudit(AuditConfig audit) { this.audit = audit; } + public SecurityFeaturesConfig getSecurity() { return security; } + public void setSecurity(SecurityFeaturesConfig security) { this.security = security; } + public DataProtectionConfig getDataProtection() { return dataProtection; } public void setDataProtection(DataProtectionConfig dataProtection) { this.dataProtection = dataProtection; } @@ -123,6 +127,43 @@ public static class SecurityConfig { public void setRateLimit(RateLimitConfig rateLimit) { this.rateLimit = rateLimit; } } + public static class SecurityFeaturesConfig { + private MetadataConfig metadata = new MetadataConfig(); + + public MetadataConfig getMetadata() { return metadata; } + public void setMetadata(MetadataConfig metadata) { this.metadata = metadata; } + } + + public static class MetadataConfig { + private boolean enabled = false; + private String jdbcUrl = "jdbc:sqlite:./data/eventlens-metadata.db"; + private PoolConfig pool = defaultPool(); + private boolean walEnabled = true; + private int busyTimeoutMs = 5_000; + private boolean foreignKeysEnabled = true; + + public boolean isEnabled() { return enabled; } + public void setEnabled(boolean enabled) { this.enabled = enabled; } + public String getJdbcUrl() { return jdbcUrl; } + public void setJdbcUrl(String jdbcUrl) { this.jdbcUrl = jdbcUrl; } + public PoolConfig getPool() { return pool; } + public void setPool(PoolConfig pool) { this.pool = pool; } + public boolean isWalEnabled() { return walEnabled; } + public void setWalEnabled(boolean walEnabled) { this.walEnabled = walEnabled; } + public int getBusyTimeoutMs() { return busyTimeoutMs; } + public void setBusyTimeoutMs(int busyTimeoutMs) { this.busyTimeoutMs = busyTimeoutMs; } + public boolean isForeignKeysEnabled() { return foreignKeysEnabled; } + public void setForeignKeysEnabled(boolean foreignKeysEnabled) { this.foreignKeysEnabled = foreignKeysEnabled; } + + private static PoolConfig defaultPool() { + PoolConfig pool = new PoolConfig(); + pool.setMaximumPoolSize(4); + pool.setMinimumIdle(1); + pool.setLeakDetectionThresholdMs(0); + return pool; + } + } + public static class RateLimitConfig { private boolean enabled = false; private int requestsPerMinute = 120; @@ -402,4 +443,3 @@ public PiiPatternConfig(String name, String regex, String mask) { public void setMask(String mask) { this.mask = mask; } } } - diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRecord.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRecord.java new file mode 100644 index 0000000..9b931e6 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRecord.java @@ -0,0 +1,17 @@ +package io.eventlens.core.metadata; + +import java.time.Instant; +import java.util.List; + +public record ApiKeyRecord( + String apiKeyId, + String keyPrefix, + String keyHash, + String description, + String principalUserId, + List scopes, + Instant createdAt, + Instant expiresAt, + Instant revokedAt, + Instant lastUsedAt) { +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRepository.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRepository.java new file mode 100644 index 0000000..6a9c604 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/ApiKeyRepository.java @@ -0,0 +1,110 @@ +package io.eventlens.core.metadata; + +import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class ApiKeyRepository { + + private final DataSource dataSource; + + public ApiKeyRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + public void insert(ApiKeyRecord record) { + String sql = """ + INSERT INTO api_keys( + api_key_id, key_prefix, key_hash, description, principal_user_id, scopes_json, + created_at, expires_at, revoked_at, last_used_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement(sql)) { + bind(ps, record); + ps.executeUpdate(); + } catch (Exception e) { + throw new IllegalStateException("Failed to insert api key " + record.apiKeyId(), e); + } + } + + public Optional findByPrefix(String keyPrefix) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement("SELECT * FROM api_keys WHERE key_prefix = ?")) { + ps.setString(1, keyPrefix); + try (ResultSet rs = ps.executeQuery()) { + return rs.next() ? Optional.of(map(rs)) : Optional.empty(); + } + } catch (Exception e) { + throw new IllegalStateException("Failed to load API key prefix " + keyPrefix, e); + } + } + + public List list() { + List results = new ArrayList<>(); + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement("SELECT * FROM api_keys ORDER BY created_at DESC"); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + results.add(map(rs)); + } + return results; + } catch (Exception e) { + throw new IllegalStateException("Failed to list API keys", e); + } + } + + public void markUsed(String apiKeyId, Instant lastUsedAt) { + updateTimestamp("UPDATE api_keys SET last_used_at = ? WHERE api_key_id = ?", apiKeyId, lastUsedAt); + } + + public void revoke(String apiKeyId, Instant revokedAt) { + updateTimestamp("UPDATE api_keys SET revoked_at = ? WHERE api_key_id = ?", apiKeyId, revokedAt); + } + + private void updateTimestamp(String sql, String apiKeyId, Instant timestamp) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setString(1, timestamp != null ? timestamp.toString() : null); + ps.setString(2, apiKeyId); + ps.executeUpdate(); + } catch (Exception e) { + throw new IllegalStateException("Failed to update API key " + apiKeyId, e); + } + } + + private static void bind(PreparedStatement ps, ApiKeyRecord record) throws Exception { + ps.setString(1, record.apiKeyId()); + ps.setString(2, record.keyPrefix()); + ps.setString(3, record.keyHash()); + ps.setString(4, record.description()); + ps.setString(5, record.principalUserId()); + ps.setString(6, MetadataJsonCodec.writeList(record.scopes())); + ps.setString(7, record.createdAt().toString()); + ps.setString(8, record.expiresAt() != null ? record.expiresAt().toString() : null); + ps.setString(9, record.revokedAt() != null ? record.revokedAt().toString() : null); + ps.setString(10, record.lastUsedAt() != null ? record.lastUsedAt().toString() : null); + } + + private static ApiKeyRecord map(ResultSet rs) throws Exception { + return new ApiKeyRecord( + rs.getString("api_key_id"), + rs.getString("key_prefix"), + rs.getString("key_hash"), + rs.getString("description"), + rs.getString("principal_user_id"), + MetadataJsonCodec.readList(rs.getString("scopes_json")), + Instant.parse(rs.getString("created_at")), + parseInstant(rs.getString("expires_at")), + parseInstant(rs.getString("revoked_at")), + parseInstant(rs.getString("last_used_at"))); + } + + private static Instant parseInstant(String value) { + return value == null || value.isBlank() ? null : Instant.parse(value); + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRecord.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRecord.java new file mode 100644 index 0000000..6301cfd --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRecord.java @@ -0,0 +1,17 @@ +package io.eventlens.core.metadata; + +import java.time.Instant; + +public record AuditLogRecord( + long auditId, + String action, + String resourceType, + String resourceId, + String userId, + String authMethod, + String clientIp, + String requestId, + String userAgent, + String detailsJson, + Instant createdAt) { +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java new file mode 100644 index 0000000..359bba6 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java @@ -0,0 +1,80 @@ +package io.eventlens.core.metadata; + +import io.eventlens.core.audit.AuditEvent; + +import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public final class AuditLogRepository { + + private final DataSource dataSource; + + public AuditLogRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + public long append(AuditEvent event, Instant createdAt) { + String sql = """ + INSERT INTO audit_log( + action, resource_type, resource_id, user_id, auth_method, + client_ip, request_id, user_agent, details_json, created_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + ps.setString(1, event.action()); + ps.setString(2, event.resourceType()); + ps.setString(3, event.resourceId()); + ps.setString(4, event.userId()); + ps.setString(5, event.authMethod()); + ps.setString(6, event.clientIp()); + ps.setString(7, event.requestId()); + ps.setString(8, event.userAgent()); + ps.setString(9, MetadataJsonCodec.writeObjectMap(event.details())); + ps.setString(10, createdAt.toString()); + ps.executeUpdate(); + try (ResultSet keys = ps.getGeneratedKeys()) { + return keys.next() ? keys.getLong(1) : -1L; + } + } catch (Exception e) { + throw new IllegalStateException("Failed to append audit log entry", e); + } + } + + public List findRecent(int limit) { + List results = new ArrayList<>(); + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement( + "SELECT * FROM audit_log ORDER BY created_at DESC LIMIT ?")) { + ps.setInt(1, limit); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + results.add(map(rs)); + } + } + return results; + } catch (Exception e) { + throw new IllegalStateException("Failed to query audit log", e); + } + } + + private static AuditLogRecord map(ResultSet rs) throws Exception { + return new AuditLogRecord( + rs.getLong("audit_id"), + rs.getString("action"), + rs.getString("resource_type"), + rs.getString("resource_id"), + rs.getString("user_id"), + rs.getString("auth_method"), + rs.getString("client_ip"), + rs.getString("request_id"), + rs.getString("user_agent"), + rs.getString("details_json"), + Instant.parse(rs.getString("created_at"))); + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java new file mode 100644 index 0000000..1f9b6f9 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java @@ -0,0 +1,132 @@ +package io.eventlens.core.metadata; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.exception.ConfigurationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * SQLite-backed metadata bootstrap for v5 security features. + * + *

The scope is intentionally narrow: sessions, API keys, and audit storage. + * Feature-specific repositories land in later epics on top of this seam.

+ */ +public final class MetadataDatabase implements AutoCloseable { + + private static final Logger log = LoggerFactory.getLogger(MetadataDatabase.class); + + private final boolean enabled; + private final HikariDataSource dataSource; + + private MetadataDatabase(boolean enabled, HikariDataSource dataSource) { + this.enabled = enabled; + this.dataSource = dataSource; + } + + public static MetadataDatabase disabled() { + return new MetadataDatabase(false, null); + } + + public static MetadataDatabase open(EventLensConfig.MetadataConfig config) { + if (config == null || !config.isEnabled()) { + return disabled(); + } + if (config.getJdbcUrl() == null || !config.getJdbcUrl().startsWith("jdbc:sqlite:")) { + throw new ConfigurationException("security.metadata.jdbc-url must be a SQLite JDBC URL when metadata is enabled"); + } + + ensureSqliteParentDirectory(config.getJdbcUrl()); + + HikariConfig hikari = new HikariConfig(); + hikari.setJdbcUrl(config.getJdbcUrl()); + hikari.setPoolName("eventlens-metadata"); + hikari.setAutoCommit(true); + hikari.setReadOnly(false); + + var pool = config.getPool(); + if (pool != null) { + hikari.setMaximumPoolSize(pool.getMaximumPoolSize()); + hikari.setMinimumIdle(pool.getMinimumIdle()); + hikari.setConnectionTimeout(pool.getConnectionTimeoutMs()); + hikari.setIdleTimeout(pool.getIdleTimeoutMs()); + hikari.setMaxLifetime(pool.getMaxLifetimeMs()); + hikari.setLeakDetectionThreshold(pool.getLeakDetectionThresholdMs()); + } + + HikariDataSource dataSource = new HikariDataSource(hikari); + try { + configurePragmas(dataSource, config); + MetadataMigrationRunner.migrate(dataSource); + log.info("Metadata database ready: {}", config.getJdbcUrl()); + return new MetadataDatabase(true, dataSource); + } catch (Exception e) { + dataSource.close(); + throw new ConfigurationException("Failed to initialize metadata database: " + e.getMessage(), e); + } + } + + public boolean isEnabled() { + return enabled; + } + + public DataSource dataSource() { + if (!enabled || dataSource == null) { + throw new IllegalStateException("Metadata database is disabled"); + } + return dataSource; + } + + public MetadataRepositories repositories() { + return new MetadataRepositories(dataSource()); + } + + @Override + public void close() { + if (dataSource != null) { + dataSource.close(); + } + } + + private static void configurePragmas(HikariDataSource dataSource, EventLensConfig.MetadataConfig config) throws SQLException { + try (Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement()) { + if (config.isWalEnabled()) { + try (ResultSet ignored = statement.executeQuery("PRAGMA journal_mode=WAL")) { + // Execute and discard result; later tests assert the effective mode. + } + } + statement.execute("PRAGMA busy_timeout=" + Math.max(0, config.getBusyTimeoutMs())); + statement.execute("PRAGMA synchronous=NORMAL"); + statement.execute("PRAGMA temp_store=MEMORY"); + statement.execute("PRAGMA foreign_keys=" + (config.isForeignKeysEnabled() ? "ON" : "OFF")); + } + } + + private static void ensureSqliteParentDirectory(String jdbcUrl) { + String raw = jdbcUrl.substring("jdbc:sqlite:".length()); + if (raw.isBlank() || raw.equals(":memory:")) { + return; + } + + Path dbPath = Path.of(raw); + Path parent = dbPath.isAbsolute() ? dbPath.getParent() : Path.of("").resolve(dbPath).normalize().getParent(); + if (parent == null) { + return; + } + try { + Files.createDirectories(parent); + } catch (Exception e) { + throw new ConfigurationException("Unable to create metadata database directory: " + parent, e); + } + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataJsonCodec.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataJsonCodec.java new file mode 100644 index 0000000..5c418fb --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataJsonCodec.java @@ -0,0 +1,56 @@ +package io.eventlens.core.metadata; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.List; +import java.util.Map; + +final class MetadataJsonCodec { + + private static final ObjectMapper MAPPER = new ObjectMapper().findAndRegisterModules(); + private static final TypeReference> STRING_LIST = new TypeReference<>() { }; + private static final TypeReference> STRING_MAP = new TypeReference<>() { }; + + private MetadataJsonCodec() { + } + + static String writeList(List values) { + return write(values == null ? List.of() : values); + } + + static String writeMap(Map values) { + return write(values == null ? Map.of() : values); + } + + static String writeObjectMap(Map values) { + return write(values == null ? Map.of() : values); + } + + static List readList(String raw) { + return read(raw, STRING_LIST, List.of()); + } + + static Map readMap(String raw) { + return read(raw, STRING_MAP, Map.of()); + } + + private static String write(Object value) { + try { + return MAPPER.writeValueAsString(value); + } catch (Exception e) { + throw new IllegalStateException("Failed to encode metadata JSON", e); + } + } + + private static T read(String raw, TypeReference type, T fallback) { + if (raw == null || raw.isBlank()) { + return fallback; + } + try { + return MAPPER.readValue(raw, type); + } catch (Exception e) { + throw new IllegalStateException("Failed to decode metadata JSON", e); + } + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataMigrationRunner.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataMigrationRunner.java new file mode 100644 index 0000000..60f4389 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataMigrationRunner.java @@ -0,0 +1,93 @@ +package io.eventlens.core.metadata; + +import javax.sql.DataSource; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.time.Instant; +import java.util.List; + +/** + * Lightweight SQL migration runner for the embedded metadata database. + * + *

We keep this intentionally small instead of introducing a full migration + * framework in the first metadata epic. If metadata portability expands later, + * this seam can be swapped without changing repository code.

+ */ +final class MetadataMigrationRunner { + + private static final List MIGRATIONS = List.of( + new Migration("1", "create_metadata_schema", "/db/metadata/V1__create_metadata_schema.sql") + ); + + private MetadataMigrationRunner() { + } + + static void migrate(DataSource dataSource) throws Exception { + try (Connection connection = dataSource.getConnection()) { + connection.setAutoCommit(false); + ensureSchemaHistoryTable(connection); + + for (Migration migration : MIGRATIONS) { + if (isApplied(connection, migration.version())) { + continue; + } + applyMigration(connection, migration); + } + + connection.commit(); + } + } + + private static void ensureSchemaHistoryTable(Connection connection) throws Exception { + try (Statement statement = connection.createStatement()) { + statement.execute(""" + CREATE TABLE IF NOT EXISTS metadata_schema_history ( + version TEXT PRIMARY KEY, + description TEXT NOT NULL, + installed_at TEXT NOT NULL + ) + """); + } + } + + private static boolean isApplied(Connection connection, String version) throws Exception { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT 1 FROM metadata_schema_history WHERE version = ?")) { + ps.setString(1, version); + try (ResultSet rs = ps.executeQuery()) { + return rs.next(); + } + } + } + + private static void applyMigration(Connection connection, Migration migration) throws Exception { + String sql = loadSql(migration.resourcePath()); + try (Statement statement = connection.createStatement()) { + statement.executeUpdate(sql); + } + + try (PreparedStatement ps = connection.prepareStatement( + "INSERT INTO metadata_schema_history(version, description, installed_at) VALUES (?, ?, ?)")) { + ps.setString(1, migration.version()); + ps.setString(2, migration.description()); + ps.setString(3, Instant.now().toString()); + ps.executeUpdate(); + } + } + + private static String loadSql(String resourcePath) throws Exception { + try (InputStream input = MetadataMigrationRunner.class.getResourceAsStream(resourcePath)) { + if (input == null) { + throw new IllegalStateException("Missing migration resource: " + resourcePath); + } + return new String(input.readAllBytes(), StandardCharsets.UTF_8); + } + } + + private record Migration(String version, String description, String resourcePath) { + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataRepositories.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataRepositories.java new file mode 100644 index 0000000..084e87e --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataRepositories.java @@ -0,0 +1,31 @@ +package io.eventlens.core.metadata; + +import javax.sql.DataSource; + +/** + * Typed access point for v5 metadata-backed repositories. + */ +public final class MetadataRepositories { + + private final SessionRepository sessions; + private final ApiKeyRepository apiKeys; + private final AuditLogRepository auditLogs; + + public MetadataRepositories(DataSource dataSource) { + this.sessions = new SessionRepository(dataSource); + this.apiKeys = new ApiKeyRepository(dataSource); + this.auditLogs = new AuditLogRepository(dataSource); + } + + public SessionRepository sessions() { + return sessions; + } + + public ApiKeyRepository apiKeys() { + return apiKeys; + } + + public AuditLogRepository auditLogs() { + return auditLogs; + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRecord.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRecord.java new file mode 100644 index 0000000..dab251d --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRecord.java @@ -0,0 +1,18 @@ +package io.eventlens.core.metadata; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +public record SessionRecord( + String sessionId, + String principalUserId, + String displayName, + String authMethod, + List roles, + Map attributes, + Instant createdAt, + Instant lastSeenAt, + Instant idleExpiresAt, + Instant absoluteExpiresAt) { +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java new file mode 100644 index 0000000..f209b54 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java @@ -0,0 +1,117 @@ +package io.eventlens.core.metadata; + +import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.time.Instant; +import java.util.Optional; + +public final class SessionRepository { + + private final DataSource dataSource; + + public SessionRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + public void upsert(SessionRecord record) { + String sql = """ + INSERT INTO sessions( + session_id, principal_user_id, display_name, auth_method, roles_json, attributes_json, + created_at, last_seen_at, idle_expires_at, absolute_expires_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(session_id) DO UPDATE SET + principal_user_id = excluded.principal_user_id, + display_name = excluded.display_name, + auth_method = excluded.auth_method, + roles_json = excluded.roles_json, + attributes_json = excluded.attributes_json, + created_at = excluded.created_at, + last_seen_at = excluded.last_seen_at, + idle_expires_at = excluded.idle_expires_at, + absolute_expires_at = excluded.absolute_expires_at + """; + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement(sql)) { + bind(ps, record); + ps.executeUpdate(); + } catch (Exception e) { + throw new IllegalStateException("Failed to upsert session " + record.sessionId(), e); + } + } + + public Optional findById(String sessionId) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement("SELECT * FROM sessions WHERE session_id = ?")) { + ps.setString(1, sessionId); + try (ResultSet rs = ps.executeQuery()) { + return rs.next() ? Optional.of(map(rs)) : Optional.empty(); + } + } catch (Exception e) { + throw new IllegalStateException("Failed to load session " + sessionId, e); + } + } + + public void touch(String sessionId, Instant lastSeenAt, Instant idleExpiresAt) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement( + "UPDATE sessions SET last_seen_at = ?, idle_expires_at = ? WHERE session_id = ?")) { + ps.setString(1, lastSeenAt.toString()); + ps.setString(2, idleExpiresAt.toString()); + ps.setString(3, sessionId); + ps.executeUpdate(); + } catch (Exception e) { + throw new IllegalStateException("Failed to touch session " + sessionId, e); + } + } + + public int deleteExpired(Instant now) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement( + "DELETE FROM sessions WHERE idle_expires_at <= ? OR absolute_expires_at <= ?")) { + String value = now.toString(); + ps.setString(1, value); + ps.setString(2, value); + return ps.executeUpdate(); + } catch (Exception e) { + throw new IllegalStateException("Failed to delete expired sessions", e); + } + } + + public void deleteById(String sessionId) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement("DELETE FROM sessions WHERE session_id = ?")) { + ps.setString(1, sessionId); + ps.executeUpdate(); + } catch (Exception e) { + throw new IllegalStateException("Failed to delete session " + sessionId, e); + } + } + + private static void bind(PreparedStatement ps, SessionRecord record) throws Exception { + ps.setString(1, record.sessionId()); + ps.setString(2, record.principalUserId()); + ps.setString(3, record.displayName()); + ps.setString(4, record.authMethod()); + ps.setString(5, MetadataJsonCodec.writeList(record.roles())); + ps.setString(6, MetadataJsonCodec.writeMap(record.attributes())); + ps.setString(7, record.createdAt().toString()); + ps.setString(8, record.lastSeenAt().toString()); + ps.setString(9, record.idleExpiresAt().toString()); + ps.setString(10, record.absoluteExpiresAt().toString()); + } + + private static SessionRecord map(ResultSet rs) throws Exception { + return new SessionRecord( + rs.getString("session_id"), + rs.getString("principal_user_id"), + rs.getString("display_name"), + rs.getString("auth_method"), + MetadataJsonCodec.readList(rs.getString("roles_json")), + MetadataJsonCodec.readMap(rs.getString("attributes_json")), + Instant.parse(rs.getString("created_at")), + Instant.parse(rs.getString("last_seen_at")), + Instant.parse(rs.getString("idle_expires_at")), + Instant.parse(rs.getString("absolute_expires_at"))); + } +} diff --git a/eventlens-core/src/main/resources/db/metadata/V1__create_metadata_schema.sql b/eventlens-core/src/main/resources/db/metadata/V1__create_metadata_schema.sql new file mode 100644 index 0000000..c01698d --- /dev/null +++ b/eventlens-core/src/main/resources/db/metadata/V1__create_metadata_schema.sql @@ -0,0 +1,63 @@ +CREATE TABLE IF NOT EXISTS sessions ( + session_id TEXT PRIMARY KEY, + principal_user_id TEXT NOT NULL, + display_name TEXT, + auth_method TEXT NOT NULL, + roles_json TEXT NOT NULL DEFAULT '[]', + attributes_json TEXT NOT NULL DEFAULT '{}', + created_at TEXT NOT NULL, + last_seen_at TEXT NOT NULL, + idle_expires_at TEXT NOT NULL, + absolute_expires_at TEXT NOT NULL +); + +CREATE INDEX IF NOT EXISTS idx_sessions_idle_expires_at + ON sessions(idle_expires_at); + +CREATE TABLE IF NOT EXISTS api_keys ( + api_key_id TEXT PRIMARY KEY, + key_prefix TEXT NOT NULL UNIQUE, + key_hash TEXT NOT NULL, + description TEXT, + principal_user_id TEXT NOT NULL, + scopes_json TEXT NOT NULL DEFAULT '[]', + created_at TEXT NOT NULL, + expires_at TEXT, + revoked_at TEXT, + last_used_at TEXT +); + +CREATE INDEX IF NOT EXISTS idx_api_keys_revoked_at + ON api_keys(revoked_at); + +CREATE TABLE IF NOT EXISTS audit_log ( + audit_id INTEGER PRIMARY KEY AUTOINCREMENT, + action TEXT NOT NULL, + resource_type TEXT NOT NULL, + resource_id TEXT, + user_id TEXT NOT NULL, + auth_method TEXT NOT NULL, + client_ip TEXT, + request_id TEXT, + user_agent TEXT, + details_json TEXT NOT NULL DEFAULT '{}', + created_at TEXT NOT NULL +); + +CREATE INDEX IF NOT EXISTS idx_audit_log_created_at + ON audit_log(created_at); + +CREATE INDEX IF NOT EXISTS idx_audit_log_user_id + ON audit_log(user_id); + +CREATE TRIGGER IF NOT EXISTS audit_log_prevent_update +BEFORE UPDATE ON audit_log +BEGIN + SELECT RAISE(ABORT, 'Updates are prohibited on audit_log'); +END; + +CREATE TRIGGER IF NOT EXISTS audit_log_prevent_delete +BEFORE DELETE ON audit_log +BEGIN + SELECT RAISE(ABORT, 'Deletes are prohibited on audit_log'); +END; diff --git a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java index 2d53c37..54e501a 100644 --- a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java +++ b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java @@ -51,4 +51,15 @@ void pluralMysqlDatasourceIsValid() { var issues = ConfigValidator.validate(cfg); assertThat(issues.stream().filter(i -> i.severity() == ConfigValidator.ValidationError.Severity.ERROR && i.path().startsWith("datasources[0]")).toList()).isEmpty(); } + + @Test + void metadataStorageMustUseSqliteWhenEnabled() { + var cfg = new EventLensConfig(); + cfg.getSecurity().getMetadata().setEnabled(true); + cfg.getSecurity().getMetadata().setJdbcUrl("jdbc:postgresql://localhost/eventlens_metadata"); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.metadata.jdbc-url") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } } diff --git a/eventlens-core/src/test/java/io/eventlens/core/metadata/MetadataDatabaseTest.java b/eventlens-core/src/test/java/io/eventlens/core/metadata/MetadataDatabaseTest.java new file mode 100644 index 0000000..205eb34 --- /dev/null +++ b/eventlens-core/src/test/java/io/eventlens/core/metadata/MetadataDatabaseTest.java @@ -0,0 +1,108 @@ +package io.eventlens.core.metadata; + +import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.EventLensConfig; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class MetadataDatabaseTest { + + @TempDir + Path tempDir; + + @Test + void openInitializesSqliteMetadataSchemaAndPragmas() throws Exception { + EventLensConfig.MetadataConfig config = new EventLensConfig.MetadataConfig(); + config.setEnabled(true); + config.setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("metadata.db")); + + try (MetadataDatabase database = MetadataDatabase.open(config); + Connection connection = database.dataSource().getConnection(); + Statement statement = connection.createStatement()) { + + assertThat(database.isEnabled()).isTrue(); + assertThat(tableExists(statement, "metadata_schema_history")).isTrue(); + assertThat(tableExists(statement, "sessions")).isTrue(); + assertThat(tableExists(statement, "api_keys")).isTrue(); + assertThat(tableExists(statement, "audit_log")).isTrue(); + assertThat(singleValue(statement, "PRAGMA journal_mode")).isEqualToIgnoringCase("wal"); + } + } + + @Test + void repositoriesPersistAndReloadMetadataRecords() { + EventLensConfig.MetadataConfig config = new EventLensConfig.MetadataConfig(); + config.setEnabled(true); + config.setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("metadata-repos.db")); + + Instant now = Instant.parse("2026-03-26T19:00:00Z"); + try (MetadataDatabase database = MetadataDatabase.open(config)) { + var repositories = database.repositories(); + + repositories.sessions().upsert(new SessionRecord( + "sess-1", + "alice", + "Alice", + "oidc", + List.of("admin"), + Map.of("source", "entra"), + now, + now, + now.plusSeconds(300), + now.plusSeconds(3600))); + + repositories.apiKeys().insert(new ApiKeyRecord( + "key-1", + "el_live_demo", + "argon2$hash", + "demo key", + "alice", + List.of("EXPORT_READ"), + now, + null, + null, + null)); + + long auditId = repositories.auditLogs().append(AuditEvent.builder() + .action(AuditEvent.ACTION_EXPORT) + .resourceType(AuditEvent.RT_EXPORT) + .resourceId("export-1") + .userId("alice") + .authMethod("oidc") + .clientIp("127.0.0.1") + .requestId("req-1") + .userAgent("JUnit") + .details(Map.of("format", "json")) + .build(), now); + + assertThat(repositories.sessions().findById("sess-1")).isPresent(); + assertThat(repositories.apiKeys().findByPrefix("el_live_demo")).isPresent(); + assertThat(repositories.auditLogs().findRecent(10)) + .extracting(AuditLogRecord::auditId) + .contains(auditId); + } + } + + private static boolean tableExists(Statement statement, String tableName) throws Exception { + try (ResultSet rs = statement.executeQuery( + "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tableName + "'")) { + return rs.next(); + } + } + + private static String singleValue(Statement statement, String sql) throws Exception { + try (ResultSet rs = statement.executeQuery(sql)) { + return rs.next() ? rs.getString(1) : null; + } + } +} From e1f9905f75f4d8c757ae3247606c59de337351dc Mon Sep 17 00:00:00 2001 From: Ahmed Date: Thu, 26 Mar 2026 22:03:11 +0200 Subject: [PATCH 03/11] build: speed up gradle checks and ci caching --- .github/workflows/build.yml | 10 ++++++++-- .github/workflows/codeql.yml | 6 ++++++ .github/workflows/release.yml | 6 +++++- eventlens-api/build.gradle.kts | 3 +++ eventlens-ui/build.gradle.kts | 4 +++- gradle.properties | 7 +++++++ 6 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 gradle.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64bf42d..d7c305b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,12 +17,16 @@ jobs: with: java-version: '21' distribution: 'temurin' - cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 - name: Set up Node.js 22 uses: actions/setup-node@v6 with: node-version: '22' + cache: 'npm' + cache-dependency-path: eventlens-ui/package-lock.json - name: Give execute permission for gradlew run: chmod +x gradlew @@ -48,7 +52,9 @@ jobs: with: java-version: '21' distribution: 'temurin' - cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 - name: Give execute permission for gradlew run: chmod +x gradlew diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0946dfc..e2bd57f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -38,12 +38,18 @@ jobs: with: java-version: '21' distribution: 'temurin' + + - name: Set up Gradle + if: matrix.language == 'java' + uses: gradle/actions/setup-gradle@v4 - name: Set up Node.js 22 if: matrix.language == 'java' uses: actions/setup-node@v6 with: node-version: '22' + cache: 'npm' + cache-dependency-path: eventlens-ui/package-lock.json - name: Build Java if: matrix.language == 'java' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 629f545..df05a45 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,12 +20,16 @@ jobs: with: java-version: '21' distribution: 'temurin' - cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 - name: Set up Node.js 22 uses: actions/setup-node@v6 with: node-version: '22' + cache: 'npm' + cache-dependency-path: eventlens-ui/package-lock.json - name: Give execute permission for gradlew run: chmod +x gradlew diff --git a/eventlens-api/build.gradle.kts b/eventlens-api/build.gradle.kts index d6d6b38..da9bd32 100644 --- a/eventlens-api/build.gradle.kts +++ b/eventlens-api/build.gradle.kts @@ -14,3 +14,6 @@ dependencies { testImplementation("org.testcontainers:mysql:1.20.1") } +tasks.named("processResources") { + dependsOn(":eventlens-ui:npmBuild") +} diff --git a/eventlens-ui/build.gradle.kts b/eventlens-ui/build.gradle.kts index e629799..317f607 100644 --- a/eventlens-ui/build.gradle.kts +++ b/eventlens-ui/build.gradle.kts @@ -17,7 +17,9 @@ val npmInstall by tasks.registering(Exec::class) { commandLine( if (System.getProperty("os.name").lowercase().contains("win")) "npm.cmd" else "npm", - "install" + "ci", + "--no-audit", + "--no-fund" ) } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8200d3e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,7 @@ +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn +org.gradle.vfs.watch=true +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 From 973b309f9f896b4b37ddb68d5f18404d6f98275e Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 16:05:02 +0200 Subject: [PATCH 04/11] feat: add oidc browser sessions --- eventlens-api/build.gradle.kts | 1 + .../io/eventlens/api/EventLensServer.java | 190 +++++++++-- .../eventlens/api/http/SecurityContext.java | 15 + .../io/eventlens/api/routes/AuthRoutes.java | 171 ++++++++++ .../eventlens/api/routes/OidcAuthRoutes.java | 203 ++++++++++++ .../io/eventlens/api/security/CsrfTokens.java | 18 + .../api/security/SessionAuthenticator.java | 47 +++ .../security/oidc/OidcIdTokenValidator.java | 156 +++++++++ .../security/oidc/OidcLoginStateService.java | 105 ++++++ .../api/security/oidc/OidcProviderClient.java | 132 ++++++++ .../security/oidc/OidcProviderMetadata.java | 9 + .../api/security/oidc/OidcTokenResponse.java | 9 + .../api/security/oidc/PendingOidcLogin.java | 9 + .../security/oidc/ValidatedOidcIdentity.java | 8 + .../resources/web/assets/index-2dMaJUyt.css | 1 - .../resources/web/assets/index-BU7xN9MK.css | 1 + .../resources/web/assets/index-kO-MJOqr.js | 14 + .../resources/web/assets/index-ri1P99Z8.js | 14 - .../src/main/resources/web/index.html | 4 +- .../api/AuthenticationIntegrationTest.java | 313 +++++++++++++++++- .../java/io/eventlens/cli/ServeCommand.java | 3 +- .../io/eventlens/core/ConfigValidator.java | 88 +++++ .../io/eventlens/core/EventLensConfig.java | 57 ++++ .../core/security/SessionService.java | 107 ++++++ .../eventlens/core/ConfigValidatorTest.java | 27 ++ .../core/security/SessionServiceTest.java | 78 +++++ eventlens-ui/src/App.tsx | 198 ++++++++++- eventlens-ui/src/api/client.ts | 83 ++++- eventlens-ui/src/hooks/useReplay.ts | 4 +- eventlens-ui/src/index.css | 194 +++++++++++ 30 files changed, 2207 insertions(+), 52 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/CsrfTokens.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcIdTokenValidator.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcLoginStateService.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderClient.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderMetadata.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcTokenResponse.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/PendingOidcLogin.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/oidc/ValidatedOidcIdentity.java delete mode 100644 eventlens-api/src/main/resources/web/assets/index-2dMaJUyt.css create mode 100644 eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css create mode 100644 eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js delete mode 100644 eventlens-api/src/main/resources/web/assets/index-ri1P99Z8.js create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java create mode 100644 eventlens-core/src/test/java/io/eventlens/core/security/SessionServiceTest.java diff --git a/eventlens-api/build.gradle.kts b/eventlens-api/build.gradle.kts index da9bd32..f2f92e6 100644 --- a/eventlens-api/build.gradle.kts +++ b/eventlens-api/build.gradle.kts @@ -3,6 +3,7 @@ dependencies { implementation(project(":eventlens-spi")) implementation("io.javalin:javalin:7.1.0") implementation("com.fasterxml.jackson.core:jackson-databind:2.21.2") + implementation("com.nimbusds:nimbus-jose-jwt:10.8") implementation("ch.qos.logback:logback-classic:1.5.32") implementation("io.micrometer:micrometer-core:1.16.4") implementation("io.micrometer:micrometer-registry-prometheus:1.16.4") diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index 91c13b3..8bd185b 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -11,6 +11,10 @@ import io.eventlens.api.routes.MetricsRoutes; import io.eventlens.api.http.RequestContextMdcFilter; import io.eventlens.api.security.BasicAuthenticator; +import io.eventlens.api.security.SessionAuthenticator; +import io.eventlens.api.security.oidc.OidcIdTokenValidator; +import io.eventlens.api.security.oidc.OidcLoginStateService; +import io.eventlens.api.security.oidc.OidcProviderClient; import io.eventlens.core.EventLensConfig; import io.eventlens.core.aggregator.ReducerRegistry; import io.eventlens.core.RateLimiter; @@ -18,9 +22,11 @@ import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.engine.*; import io.eventlens.core.exception.QueryTimeoutException; +import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.pii.PiiMasker; import io.eventlens.core.plugin.PluginManager; import io.eventlens.core.security.Principal; +import io.eventlens.core.security.SessionService; import io.eventlens.core.spi.EventStoreReader; import io.javalin.Javalin; import io.javalin.compression.CompressionStrategy; @@ -59,6 +65,7 @@ public class EventLensServer { private final int port; private final ExportService exportService; private final EventStoreReader reader; + private final MetadataDatabase metadataDatabase; public EventLensServer( EventLensConfig config, @@ -71,7 +78,7 @@ public EventLensServer( AnomalyDetector anomalyDetector, ExportEngine exportEngine, DiffEngine diffEngine) { - this(config, reader, replayEngine, reducerRegistry, pluginManager, defaultSourceId, bisectEngine, anomalyDetector, exportEngine, diffEngine, Map.of()); + this(config, reader, replayEngine, reducerRegistry, pluginManager, defaultSourceId, bisectEngine, anomalyDetector, exportEngine, diffEngine, Map.of(), MetadataDatabase.disabled()); } public EventLensServer( @@ -86,8 +93,25 @@ public EventLensServer( ExportEngine exportEngine, DiffEngine diffEngine, Map sourceStreamBindings) { + this(config, reader, replayEngine, reducerRegistry, pluginManager, defaultSourceId, bisectEngine, anomalyDetector, exportEngine, diffEngine, sourceStreamBindings, MetadataDatabase.disabled()); + } + + public EventLensServer( + EventLensConfig config, + EventStoreReader reader, + ReplayEngine replayEngine, + ReducerRegistry reducerRegistry, + PluginManager pluginManager, + String defaultSourceId, + BisectEngine bisectEngine, + AnomalyDetector anomalyDetector, + ExportEngine exportEngine, + DiffEngine diffEngine, + Map sourceStreamBindings, + MetadataDatabase metadataDatabase) { this.port = config.getServer().getPort(); this.reader = reader; + this.metadataDatabase = metadataDatabase == null ? MetadataDatabase.disabled() : metadataDatabase; // ── 1.8 Audit Logger ────────────────────────────────────────────── final AuditLogger auditLogger = new AuditLogger( @@ -134,6 +158,38 @@ public EventLensServer( var liveTailWs = new LiveTailWebSocket(sourceRegistry, pluginManager, auditLogger, defaultSourceId, sourceStreamBindings); var authConfig = config.getServer().getAuth(); + var v5AuthConfig = config.getSecurity() != null ? config.getSecurity().getAuth() : null; + var authProvider = v5AuthConfig != null ? v5AuthConfig.getProvider() : "disabled"; + boolean sessionProviderEnabled = "basic".equalsIgnoreCase(authProvider) || "oidc".equalsIgnoreCase(authProvider); + boolean oidcEnabled = "oidc".equalsIgnoreCase(authProvider); + boolean basicCompatibilityEnabled = authConfig.isEnabled(); + boolean authEnabled = basicCompatibilityEnabled || sessionProviderEnabled; + var sessionConfig = v5AuthConfig != null ? v5AuthConfig.getSession() : new EventLensConfig.SessionConfig(); + var oidcCallbackPath = oidcEnabled ? v5AuthConfig.getOidc().getRedirectPath() : "/api/v1/auth/callback"; + var basicAuthenticator = new BasicAuthenticator( + authConfig.getUsername(), + authConfig.getPassword(), + "EventLens"); + var sessionService = this.metadataDatabase.isEnabled() + ? new SessionService(this.metadataDatabase.repositories().sessions(), sessionConfig) + : null; + var sessionAuthenticator = sessionService != null + ? new SessionAuthenticator(sessionService, sessionConfig.getCookieName()) + : null; + var authRoutes = sessionService != null + ? new AuthRoutes(sessionService, sessionConfig, basicAuthenticator, auditLogger, authProvider, basicCompatibilityEnabled) + : null; + var oidcProviderClient = new OidcProviderClient(); + var oidcRoutes = oidcEnabled && sessionService != null + ? new OidcAuthRoutes( + sessionService, + sessionConfig, + v5AuthConfig.getOidc(), + oidcProviderClient, + new OidcIdTokenValidator(oidcProviderClient), + new OidcLoginStateService(this.metadataDatabase.repositories().sessions()), + auditLogger) + : null; // ── Javalin 7: all routes + handlers inside cfg.routes ──────────── this.app = Javalin.create(cfg -> { @@ -254,55 +310,77 @@ public EventLensServer( }); } - if (authConfig.isEnabled()) { - var basicAuthenticator = new BasicAuthenticator( - authConfig.getUsername(), - authConfig.getPassword(), - "EventLens"); + if (authEnabled) { if ("changeme".equals(authConfig.getPassword())) { log.warn("Basic auth is enabled with default password 'changeme'. Change server.auth.password in production."); } cfg.routes.before("/api/*", ctx -> { - var authResult = basicAuthenticator.authenticate(ctx); + if (isPublicAuthPath(ctx.path(), oidcCallbackPath)) { + if (sessionAuthenticator != null && hasSessionCookie(ctx, sessionConfig.getCookieName())) { + var sessionResult = sessionAuthenticator.authenticate(ctx); + if (sessionResult.success()) { + SecurityContext.setPrincipal(ctx, sessionResult.principal()); + new RequestContextMdcFilter().handle(ctx); + } + } + return; + } + + var authHolder = resolveAuthentication(ctx, sessionAuthenticator, basicCompatibilityEnabled ? basicAuthenticator : null, sessionConfig.getCookieName()); + var authResult = authHolder.result(); if (!authResult.success()) { // 1.8 - emit LOGIN_FAILED auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_LOGIN_FAILED) .resourceType(AuditEvent.RT_AUTH) .userId(authResult.attemptedUserId() != null ? authResult.attemptedUserId() : "anonymous") - .authMethod("basic") + .authMethod(authHolder.method()) .details(Map.of("reason", authResult.failureReason(), "path", ctx.path())) .build()); - ctx.status(401) - .header("WWW-Authenticate", authResult.challengeHeader()) - .json(Map.of("error", "Unauthorized")); + ctx.status(401); + if (authResult.challengeHeader() != null) { + ctx.header("WWW-Authenticate", authResult.challengeHeader()); + } + ctx.json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { SecurityContext.setPrincipal(ctx, authResult.principal()); new RequestContextMdcFilter().handle(ctx); - // 1.8 - emit LOGIN success (once per request, not once per session) - auditLogger.log(SecurityContext.audit(ctx) - .action(AuditEvent.ACTION_LOGIN) - .resourceType(AuditEvent.RT_AUTH) - .details(Map.of("path", ctx.path())) - .build()); + if (isMutatingMethod(ctx.method().name()) && SecurityContext.session(ctx) != null) { + String expectedCsrf = SecurityContext.csrfToken(ctx); + String suppliedCsrf = ctx.header("X-CSRF-Token"); + if (expectedCsrf == null || !expectedCsrf.equals(suppliedCsrf)) { + ctx.status(403).json(Map.of("error", "csrf_required")); + ctx.skipRemainingHandlers(); + return; + } + } + if ("basic".equals(authHolder.method())) { + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_LOGIN) + .resourceType(AuditEvent.RT_AUTH) + .details(Map.of("path", ctx.path())) + .build()); + } } }); cfg.routes.before("/ws/*", ctx -> { - var authResult = basicAuthenticator.authenticate(ctx); + var authResult = resolveAuthentication(ctx, sessionAuthenticator, basicCompatibilityEnabled ? basicAuthenticator : null, sessionConfig.getCookieName()).result(); if (!authResult.success()) { - ctx.status(401) - .header("WWW-Authenticate", authResult.challengeHeader()) - .json(Map.of("error", "Unauthorized")); + ctx.status(401); + if (authResult.challengeHeader() != null) { + ctx.header("WWW-Authenticate", authResult.challengeHeader()); + } + ctx.json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { SecurityContext.setPrincipal(ctx, authResult.principal()); new RequestContextMdcFilter().handle(ctx); } }); - log.info("Basic auth ENABLED for /api/* and /ws/*"); + log.info("Auth enabled for /api/* and /ws/* with provider={} basicCompatibility={}", authProvider, basicCompatibilityEnabled); } // ── Routes ──────────────────────────────────────────────────── @@ -319,6 +397,16 @@ public EventLensServer( // OpenAPI (5.2) cfg.routes.get("/api/v1/openapi.json", openApiRoutes::spec); + if (authRoutes != null) { + cfg.routes.get("/api/v1/auth/session", authRoutes::session); + cfg.routes.post("/api/v1/auth/logout", authRoutes::logout); + cfg.routes.post("/api/v1/auth/login/basic", authRoutes::createBasicSession); + } + if (oidcRoutes != null) { + cfg.routes.get("/api/v1/auth/login/oidc", oidcRoutes::start); + cfg.routes.get(v5AuthConfig.getOidc().getRedirectPath(), oidcRoutes::callback); + } + // Aggregates (v1) cfg.routes.get("/api/v1/aggregates/search", aggregateRoutes::search); cfg.routes.get("/api/v1/meta/types", aggregateRoutes::types); @@ -455,6 +543,12 @@ public void start() { } catch (Exception ignored) { } }, + () -> { + try { + metadataDatabase.close(); + } catch (Exception ignored) { + } + }, () -> { if (reader instanceof AutoCloseable closeable) { closeable.close(); @@ -466,6 +560,20 @@ public void start() { public void stop() { app.stop(); + try { + exportService.close(); + } catch (Exception ignored) { + } + try { + metadataDatabase.close(); + } catch (Exception ignored) { + } + try { + if (reader instanceof AutoCloseable closeable) { + closeable.close(); + } + } catch (Exception ignored) { + } } public Javalin getApp() { @@ -477,5 +585,41 @@ private static void markDeprecated(io.javalin.http.Context ctx, String successor ctx.header("Sunset", "2026-01-01"); ctx.header("Link", "<" + successor + ">; rel=\"successor-version\""); } -} + private static boolean isPublicAuthPath(String path, String oidcCallbackPath) { + return "/api/v1/auth/session".equals(path) + || "/api/v1/auth/logout".equals(path) + || "/api/v1/auth/login/basic".equals(path) + || "/api/v1/auth/login/oidc".equals(path) + || oidcCallbackPath.equals(path); + } + + private static boolean hasSessionCookie(io.javalin.http.Context ctx, String cookieName) { + String value = ctx.cookie(cookieName); + return value != null && !value.isBlank(); + } + + private static boolean isMutatingMethod(String method) { + return "POST".equalsIgnoreCase(method) + || "PUT".equalsIgnoreCase(method) + || "PATCH".equalsIgnoreCase(method) + || "DELETE".equalsIgnoreCase(method); + } + + private static AuthenticationResultHolder resolveAuthentication( + io.javalin.http.Context ctx, + SessionAuthenticator sessionAuthenticator, + BasicAuthenticator basicAuthenticator, + String sessionCookieName) { + if (sessionAuthenticator != null && hasSessionCookie(ctx, sessionCookieName)) { + return new AuthenticationResultHolder("session", sessionAuthenticator.authenticate(ctx)); + } + if (basicAuthenticator != null) { + return new AuthenticationResultHolder("basic", basicAuthenticator.authenticate(ctx)); + } + return new AuthenticationResultHolder("session", io.eventlens.api.security.AuthenticationResult.failure(null, "missing_session", null)); + } + + private record AuthenticationResultHolder(String method, io.eventlens.api.security.AuthenticationResult result) { + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java b/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java index 5a8d385..d91f81d 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java +++ b/eventlens-api/src/main/java/io/eventlens/api/http/SecurityContext.java @@ -1,6 +1,7 @@ package io.eventlens.api.http; import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.metadata.SessionRecord; import io.eventlens.core.security.Principal; import io.javalin.http.Context; @@ -10,6 +11,7 @@ public final class SecurityContext { public static final String ATTR_PRINCIPAL = "eventlensPrincipal"; + public static final String ATTR_SESSION = "eventlensSession"; private static final String LEGACY_AUDIT_USER_ID = "auditUserId"; private static final String LEGACY_AUDIT_AUTH_METHOD = "auditAuthMethod"; @@ -29,6 +31,19 @@ public static Principal principal(Context ctx) { return principal != null ? principal : Principal.anonymous(); } + public static void setSession(Context ctx, SessionRecord session) { + ctx.attribute(ATTR_SESSION, session); + } + + public static SessionRecord session(Context ctx) { + return ctx.attribute(ATTR_SESSION); + } + + public static String csrfToken(Context ctx) { + SessionRecord session = session(ctx); + return session != null ? session.attributes().get("csrfToken") : null; + } + public static AuditEvent.Builder audit(Context ctx) { Principal principal = principal(ctx); return AuditEvent.builder() diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java new file mode 100644 index 0000000..83127f2 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java @@ -0,0 +1,171 @@ +package io.eventlens.api.routes; + +import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.BasicAuthenticator; +import io.eventlens.api.security.CsrfTokens; +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.audit.AuditLogger; +import io.eventlens.core.security.Principal; +import io.eventlens.core.security.SessionService; +import io.javalin.http.Context; + +import java.time.Duration; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Minimal auth/session endpoints for the v5 auth transition. + */ +public final class AuthRoutes { + + private final SessionService sessionService; + private final EventLensConfig.SessionConfig sessionConfig; + private final BasicAuthenticator basicAuthenticator; + private final AuditLogger auditLogger; + private final String authProvider; + private final boolean basicLoginEnabled; + + public AuthRoutes( + SessionService sessionService, + EventLensConfig.SessionConfig sessionConfig, + BasicAuthenticator basicAuthenticator, + AuditLogger auditLogger, + String authProvider, + boolean basicLoginEnabled) { + this.sessionService = sessionService; + this.sessionConfig = sessionConfig; + this.basicAuthenticator = basicAuthenticator; + this.auditLogger = auditLogger; + this.authProvider = authProvider; + this.basicLoginEnabled = basicLoginEnabled; + } + + public void session(Context ctx) { + Principal principal = SecurityContext.principal(ctx); + if (!principal.authenticated()) { + ctx.json(Map.of( + "authenticated", false, + "provider", authProvider, + "basicLoginEnabled", basicLoginEnabled + )); + return; + } + + ctx.json(Map.of( + "authenticated", true, + "provider", authProvider, + "basicLoginEnabled", basicLoginEnabled, + "csrfToken", SecurityContext.csrfToken(ctx), + "principal", principalPayload(principal) + )); + } + + public void logout(Context ctx) { + String expectedCsrf = SecurityContext.csrfToken(ctx); + String suppliedCsrf = ctx.header("X-CSRF-Token"); + if (expectedCsrf != null && !expectedCsrf.equals(suppliedCsrf)) { + ctx.status(403).json(Map.of("error", "csrf_required")); + return; + } + + String sessionId = ctx.cookie(sessionConfig.getCookieName()); + if (sessionId != null && !sessionId.isBlank()) { + sessionService.invalidate(sessionId); + } + expireCookie(ctx); + + auditLogger.log(SecurityContext.audit(ctx) + .action("LOGOUT") + .resourceType(AuditEvent.RT_AUTH) + .details(Map.of("path", ctx.path())) + .build()); + + ctx.json(Map.of("authenticated", false)); + } + + public void createBasicSession(Context ctx) { + var authResult = basicAuthenticator.authenticate(ctx); + if (!authResult.success()) { + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_LOGIN_FAILED) + .resourceType(AuditEvent.RT_AUTH) + .userId(authResult.attemptedUserId() != null ? authResult.attemptedUserId() : "anonymous") + .authMethod("basic") + .details(Map.of("reason", authResult.failureReason(), "path", ctx.path())) + .build()); + + ctx.status(401) + .header("WWW-Authenticate", authResult.challengeHeader()) + .json(Map.of("error", "Unauthorized")); + return; + } + + Principal principal = authResult.principal(); + String returnHash = sanitizeReturnHash(ctx.bodyAsClass(BasicSessionRequest.class).returnHash()); + String csrfToken = CsrfTokens.generate(); + var session = sessionService.create(principal, Map.of( + "returnHash", returnHash, + "csrfToken", csrfToken + )); + setSessionCookie(ctx, session.sessionId()); + SecurityContext.setPrincipal(ctx, principal); + SecurityContext.setSession(ctx, session); + + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_LOGIN) + .resourceType(AuditEvent.RT_AUTH) + .details(Map.of("path", ctx.path(), "session", true)) + .build()); + + ctx.json(Map.of( + "authenticated", true, + "principal", principalPayload(principal), + "csrfToken", csrfToken, + "returnHash", returnHash + )); + } + + private void setSessionCookie(Context ctx, String sessionId) { + String sameSite = sessionConfig.getSameSite(); + long maxAge = Duration.ofSeconds(sessionConfig.getAbsoluteTimeoutSeconds()).toSeconds(); + StringBuilder cookie = new StringBuilder() + .append(sessionConfig.getCookieName()).append('=').append(sessionId) + .append("; Path=/; HttpOnly; SameSite=").append(sameSite) + .append("; Max-Age=").append(maxAge); + if (sessionConfig.isSecureCookie()) { + cookie.append("; Secure"); + } + ctx.header("Set-Cookie", cookie.toString()); + } + + private void expireCookie(Context ctx) { + StringBuilder cookie = new StringBuilder() + .append(sessionConfig.getCookieName()).append("=; Path=/; HttpOnly; SameSite=") + .append(sessionConfig.getSameSite()) + .append("; Max-Age=0"); + if (sessionConfig.isSecureCookie()) { + cookie.append("; Secure"); + } + ctx.header("Set-Cookie", cookie.toString()); + } + + private static Map principalPayload(Principal principal) { + Map payload = new LinkedHashMap<>(); + payload.put("userId", principal.userId()); + payload.put("displayName", principal.displayName()); + payload.put("authMethod", principal.authMethod()); + payload.put("roles", principal.roles()); + return payload; + } + + private static String sanitizeReturnHash(String value) { + if (value == null || value.isBlank()) { + return "#/timeline"; + } + return value.startsWith("#/") ? value : "#/timeline"; + } + + public record BasicSessionRequest(String returnHash) { + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java new file mode 100644 index 0000000..06504f4 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java @@ -0,0 +1,203 @@ +package io.eventlens.api.routes; + +import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.CsrfTokens; +import io.eventlens.api.security.oidc.OidcIdTokenValidator; +import io.eventlens.api.security.oidc.OidcLoginStateService; +import io.eventlens.api.security.oidc.OidcProviderClient; +import io.eventlens.api.security.oidc.PendingOidcLogin; +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.audit.AuditLogger; +import io.eventlens.core.security.Principal; +import io.eventlens.core.security.SessionService; +import io.javalin.http.Context; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public final class OidcAuthRoutes { + + private static final String STATE_COOKIE = "__Host-eventlens_oidc_state"; + + private final SessionService sessionService; + private final EventLensConfig.SessionConfig sessionConfig; + private final EventLensConfig.OidcConfig oidcConfig; + private final OidcProviderClient providerClient; + private final OidcIdTokenValidator tokenValidator; + private final OidcLoginStateService loginStateService; + private final AuditLogger auditLogger; + + public OidcAuthRoutes( + SessionService sessionService, + EventLensConfig.SessionConfig sessionConfig, + EventLensConfig.OidcConfig oidcConfig, + OidcProviderClient providerClient, + OidcIdTokenValidator tokenValidator, + OidcLoginStateService loginStateService, + AuditLogger auditLogger) { + this.sessionService = sessionService; + this.sessionConfig = sessionConfig; + this.oidcConfig = oidcConfig; + this.providerClient = providerClient; + this.tokenValidator = tokenValidator; + this.loginStateService = loginStateService; + this.auditLogger = auditLogger; + } + + public void start(Context ctx) { + String returnHash = sanitizeReturnHash(ctx.queryParam("returnHash")); + PendingOidcLogin login = loginStateService.create(returnHash); + var metadata = providerClient.discover(oidcConfig.getIssuer()); + + setStateCookie(ctx, login.stateId()); + String redirectUri = externalBaseUrl(ctx) + oidcConfig.getRedirectPath(); + String authorizationUrl = metadata.authorizationEndpoint() + + "?response_type=code" + + "&client_id=" + encode(oidcConfig.getClientId()) + + "&redirect_uri=" + encode(redirectUri) + + "&scope=" + encode(String.join(" ", oidcConfig.getScopes())) + + "&state=" + encode(login.stateId()) + + "&nonce=" + encode(login.nonce()) + + "&code_challenge=" + encode(OidcLoginStateService.codeChallenge(login.codeVerifier())) + + "&code_challenge_method=S256"; + ctx.redirect(authorizationUrl); + } + + public void callback(Context ctx) { + String state = ctx.queryParam("state"); + String cookieState = ctx.cookie(STATE_COOKIE); + expireStateCookie(ctx); + + if (!safeEquals(state, cookieState)) { + failCallback(ctx, "state_mismatch"); + return; + } + + String providerError = ctx.queryParam("error"); + if (providerError != null && !providerError.isBlank()) { + failCallback(ctx, providerError); + return; + } + + Optional pending = loginStateService.consume(state); + if (pending.isEmpty()) { + failCallback(ctx, "missing_state"); + return; + } + + String code = ctx.queryParam("code"); + if (code == null || code.isBlank()) { + failCallback(ctx, "missing_code"); + return; + } + + PendingOidcLogin login = pending.get(); + try { + String redirectUri = externalBaseUrl(ctx) + oidcConfig.getRedirectPath(); + var metadata = providerClient.discover(oidcConfig.getIssuer()); + var tokenResponse = providerClient.exchangeCode(metadata, oidcConfig, redirectUri, code, login.codeVerifier()); + var identity = tokenValidator.validate(tokenResponse.idToken(), metadata, oidcConfig, login.nonce()); + + Principal principal = new Principal( + identity.subject(), + identity.displayName(), + "oidc", + java.util.Set.of(), + true + ); + + Map attributes = new java.util.LinkedHashMap<>(); + attributes.put("issuer", metadata.issuer()); + attributes.put("csrfToken", CsrfTokens.generate()); + if (identity.email() != null && !identity.email().isBlank()) { + attributes.put("email", identity.email()); + } + + var session = sessionService.create(principal, attributes); + setSessionCookie(ctx, session.sessionId()); + + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_LOGIN) + .resourceType(AuditEvent.RT_AUTH) + .userId(principal.userId()) + .authMethod("oidc") + .details(Map.of("path", ctx.path(), "provider", metadata.issuer())) + .build()); + + ctx.redirect("/" + login.returnHash()); + } catch (RuntimeException e) { + failCallback(ctx, "token_validation_failed"); + } + } + + private void failCallback(Context ctx, String reason) { + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_LOGIN_FAILED) + .resourceType(AuditEvent.RT_AUTH) + .authMethod("oidc") + .details(Map.of("reason", reason, "path", ctx.path())) + .build()); + ctx.redirect("/?authError=" + encode(reason) + "#/timeline"); + } + + private void setSessionCookie(Context ctx, String sessionId) { + StringBuilder cookie = new StringBuilder() + .append(sessionConfig.getCookieName()).append('=').append(sessionId) + .append("; Path=/; HttpOnly; SameSite=").append(sessionConfig.getSameSite()) + .append("; Max-Age=").append(sessionConfig.getAbsoluteTimeoutSeconds()); + if (sessionConfig.isSecureCookie()) { + cookie.append("; Secure"); + } + ctx.res().addHeader("Set-Cookie", cookie.toString()); + } + + private void setStateCookie(Context ctx, String stateId) { + StringBuilder cookie = new StringBuilder() + .append(STATE_COOKIE).append('=').append(stateId) + .append("; Path=/; HttpOnly; SameSite=Lax; Max-Age=300"); + if (sessionConfig.isSecureCookie()) { + cookie.append("; Secure"); + } + ctx.res().addHeader("Set-Cookie", cookie.toString()); + } + + private void expireStateCookie(Context ctx) { + StringBuilder cookie = new StringBuilder() + .append(STATE_COOKIE) + .append("=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0"); + if (sessionConfig.isSecureCookie()) { + cookie.append("; Secure"); + } + ctx.res().addHeader("Set-Cookie", cookie.toString()); + } + + private static String externalBaseUrl(Context ctx) { + String scheme = headerOrDefault(ctx, "X-Forwarded-Proto", ctx.scheme()); + String host = headerOrDefault(ctx, "X-Forwarded-Host", ctx.host()); + return scheme + "://" + host; + } + + private static String headerOrDefault(Context ctx, String header, String fallback) { + String value = ctx.header(header); + return value == null || value.isBlank() ? fallback : value; + } + + private static String sanitizeReturnHash(String value) { + if (value == null || value.isBlank()) { + return "#/timeline"; + } + return value.startsWith("#/") ? value : "#/timeline"; + } + + private static boolean safeEquals(String left, String right) { + return left != null && left.equals(right); + } + + private static String encode(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/CsrfTokens.java b/eventlens-api/src/main/java/io/eventlens/api/security/CsrfTokens.java new file mode 100644 index 0000000..6b9d61d --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/CsrfTokens.java @@ -0,0 +1,18 @@ +package io.eventlens.api.security; + +import java.security.SecureRandom; +import java.util.Base64; + +public final class CsrfTokens { + + private static final SecureRandom RANDOM = new SecureRandom(); + + private CsrfTokens() { + } + + public static String generate() { + byte[] bytes = new byte[24]; + RANDOM.nextBytes(bytes); + return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java b/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java new file mode 100644 index 0000000..a4f7dd0 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java @@ -0,0 +1,47 @@ +package io.eventlens.api.security; + +import io.eventlens.api.http.SecurityContext; +import io.eventlens.core.metadata.SessionRecord; +import io.eventlens.core.security.Principal; +import io.eventlens.core.security.SessionService; +import io.javalin.http.Context; + +import java.util.Optional; +import java.util.Set; + +/** + * Cookie-backed session authenticator for browser/UI requests. + */ +public final class SessionAuthenticator implements RequestAuthenticator { + + private final SessionService sessionService; + private final String cookieName; + + public SessionAuthenticator(SessionService sessionService, String cookieName) { + this.sessionService = sessionService; + this.cookieName = cookieName; + } + + @Override + public AuthenticationResult authenticate(Context ctx) { + String sessionId = ctx.cookie(cookieName); + if (sessionId == null || sessionId.isBlank()) { + return AuthenticationResult.failure(null, "missing_session", null); + } + + Optional record = sessionService.touch(sessionId); + if (record.isEmpty()) { + return AuthenticationResult.failure(null, "invalid_session", null); + } + + SessionRecord session = record.get(); + SecurityContext.setSession(ctx, session); + Principal principal = new Principal( + session.principalUserId(), + session.displayName(), + session.authMethod(), + Set.copyOf(session.roles()), + true); + return AuthenticationResult.success(principal); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcIdTokenValidator.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcIdTokenValidator.java new file mode 100644 index 0000000..79e79cf --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcIdTokenValidator.java @@ -0,0 +1,156 @@ +package io.eventlens.api.security.oidc; + +import com.fasterxml.jackson.databind.JsonNode; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.ECDSAVerifier; +import com.nimbusds.jose.crypto.MACVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.OctetSequenceKey; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import io.eventlens.core.EventLensConfig; + +import java.text.ParseException; +import java.time.Clock; +import java.time.Instant; +import java.util.List; +import java.util.Objects; + +public final class OidcIdTokenValidator { + + private final OidcProviderClient providerClient; + private final Clock clock; + + public OidcIdTokenValidator(OidcProviderClient providerClient) { + this(providerClient, Clock.systemUTC()); + } + + OidcIdTokenValidator(OidcProviderClient providerClient, Clock clock) { + this.providerClient = providerClient; + this.clock = clock; + } + + public ValidatedOidcIdentity validate( + String idToken, + OidcProviderMetadata metadata, + EventLensConfig.OidcConfig config, + String expectedNonce) { + try { + SignedJWT jwt = SignedJWT.parse(idToken); + JWKSet jwkSet = JWKSet.parse(providerClient.fetchJwks(metadata.jwksUri()).toString()); + verifySignature(jwt, jwkSet); + + JWTClaimsSet claims = jwt.getJWTClaimsSet(); + validateClaims(claims, metadata, config, expectedNonce); + + String subject = required(claims.getSubject(), "sub"); + String displayName = firstNonBlank( + claims.getStringClaim("name"), + claims.getStringClaim("preferred_username"), + claims.getStringClaim("email"), + subject + ); + String email = claims.getStringClaim("email"); + return new ValidatedOidcIdentity(subject, displayName, email); + } catch (ParseException e) { + throw new IllegalStateException("Invalid OIDC ID token", e); + } + } + + private void validateClaims( + JWTClaimsSet claims, + OidcProviderMetadata metadata, + EventLensConfig.OidcConfig config, + String expectedNonce) throws ParseException { + String issuer = required(claims.getIssuer(), "iss"); + if (!Objects.equals(metadata.issuer(), issuer) && !Objects.equals(config.getIssuer(), issuer)) { + throw new IllegalStateException("OIDC issuer mismatch"); + } + + List audience = claims.getAudience(); + if (audience == null || !audience.contains(config.getClientId())) { + throw new IllegalStateException("OIDC audience mismatch"); + } + + Instant now = clock.instant(); + if (claims.getExpirationTime() == null || !claims.getExpirationTime().toInstant().isAfter(now.minusSeconds(30))) { + throw new IllegalStateException("OIDC ID token is expired"); + } + if (claims.getNotBeforeTime() != null && claims.getNotBeforeTime().toInstant().isAfter(now.plusSeconds(30))) { + throw new IllegalStateException("OIDC ID token is not yet valid"); + } + + String nonce = claims.getStringClaim("nonce"); + if (!Objects.equals(expectedNonce, nonce)) { + throw new IllegalStateException("OIDC nonce mismatch"); + } + + if (audience.size() > 1) { + String authorizedParty = claims.getStringClaim("azp"); + if (!Objects.equals(config.getClientId(), authorizedParty)) { + throw new IllegalStateException("OIDC azp mismatch"); + } + } + } + + private static void verifySignature(SignedJWT jwt, JWKSet jwkSet) { + try { + JWK key = selectKey(jwt, jwkSet); + if (key == null) { + throw new IllegalStateException("No matching OIDC signing key found"); + } + JWSVerifier verifier = verifierFor(key); + if (!jwt.verify(verifier)) { + throw new IllegalStateException("OIDC ID token signature validation failed"); + } + } catch (JOSEException e) { + throw new IllegalStateException("OIDC signature validation failed", e); + } + } + + private static JWK selectKey(SignedJWT jwt, JWKSet jwkSet) { + String keyId = jwt.getHeader().getKeyID(); + if (keyId != null) { + return jwkSet.getKeys().stream() + .filter(key -> keyId.equals(key.getKeyID())) + .findFirst() + .orElse(null); + } + return jwkSet.getKeys().stream().findFirst().orElse(null); + } + + private static JWSVerifier verifierFor(JWK key) throws JOSEException { + if (key instanceof RSAKey rsaKey) { + return new RSASSAVerifier(rsaKey.toRSAPublicKey()); + } + if (key instanceof ECKey ecKey) { + return new ECDSAVerifier(ecKey.toECPublicKey()); + } + if (key instanceof OctetSequenceKey octKey) { + return new MACVerifier(octKey.toByteArray()); + } + throw new IllegalStateException("Unsupported OIDC signing key type: " + key.getKeyType()); + } + + private static String required(String value, String field) { + if (value == null || value.isBlank()) { + throw new IllegalStateException("OIDC claim is missing: " + field); + } + return value; + } + + private static String firstNonBlank(String... values) { + for (String value : values) { + if (value != null && !value.isBlank()) { + return value; + } + } + return ""; + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcLoginStateService.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcLoginStateService.java new file mode 100644 index 0000000..a0491da --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcLoginStateService.java @@ -0,0 +1,105 @@ +package io.eventlens.api.security.oidc; + +import io.eventlens.core.metadata.SessionRecord; +import io.eventlens.core.metadata.SessionRepository; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.time.Clock; +import java.time.Instant; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +public final class OidcLoginStateService { + + private static final SecureRandom RANDOM = new SecureRandom(); + private static final int TTL_SECONDS = 300; + private static final String STATE_AUTH_METHOD = "oidc_state"; + + private final SessionRepository sessionRepository; + private final Clock clock; + + public OidcLoginStateService(SessionRepository sessionRepository) { + this(sessionRepository, Clock.systemUTC()); + } + + OidcLoginStateService(SessionRepository sessionRepository, Clock clock) { + this.sessionRepository = sessionRepository; + this.clock = clock; + } + + public PendingOidcLogin create(String returnHash) { + Instant now = clock.instant(); + String stateId = newOpaqueToken(); + String nonce = newOpaqueToken(); + String codeVerifier = newOpaqueToken(); + SessionRecord record = new SessionRecord( + stateId, + "oidc-state", + "oidc-state", + STATE_AUTH_METHOD, + List.of(), + Map.of( + "nonce", nonce, + "codeVerifier", codeVerifier, + "returnHash", returnHash + ), + now, + now, + now.plusSeconds(TTL_SECONDS), + now.plusSeconds(TTL_SECONDS) + ); + sessionRepository.upsert(record); + return new PendingOidcLogin(stateId, nonce, codeVerifier, returnHash); + } + + public Optional consume(String stateId) { + if (stateId == null || stateId.isBlank()) { + return Optional.empty(); + } + Optional record = sessionRepository.findById(stateId); + sessionRepository.deleteById(stateId); + if (record.isEmpty()) { + return Optional.empty(); + } + SessionRecord session = record.get(); + if (!STATE_AUTH_METHOD.equals(session.authMethod()) || isExpired(session, clock.instant())) { + return Optional.empty(); + } + String nonce = session.attributes().get("nonce"); + String codeVerifier = session.attributes().get("codeVerifier"); + String returnHash = session.attributes().getOrDefault("returnHash", "#/timeline"); + if (isBlank(nonce) || isBlank(codeVerifier)) { + return Optional.empty(); + } + return Optional.of(new PendingOidcLogin(session.sessionId(), nonce, codeVerifier, returnHash)); + } + + public static String codeChallenge(String codeVerifier) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hashed = digest.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII)); + return Base64.getUrlEncoder().withoutPadding().encodeToString(hashed); + } catch (Exception e) { + throw new IllegalStateException("Failed to derive PKCE code challenge", e); + } + } + + private static boolean isExpired(SessionRecord session, Instant now) { + return !session.idleExpiresAt().isAfter(now) || !session.absoluteExpiresAt().isAfter(now); + } + + private static String newOpaqueToken() { + byte[] bytes = new byte[32]; + RANDOM.nextBytes(bytes); + return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes) + UUID.randomUUID().toString().replace("-", ""); + } + + private static boolean isBlank(String value) { + return value == null || value.isBlank(); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderClient.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderClient.java new file mode 100644 index 0000000..a8e0ff3 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderClient.java @@ -0,0 +1,132 @@ +package io.eventlens.api.security.oidc; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.eventlens.core.EventLensConfig; + +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class OidcProviderClient { + + private final HttpClient httpClient; + private final ObjectMapper objectMapper; + + public OidcProviderClient() { + this(HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).followRedirects(HttpClient.Redirect.NEVER).build(), + new ObjectMapper()); + } + + OidcProviderClient(HttpClient httpClient, ObjectMapper objectMapper) { + this.httpClient = httpClient; + this.objectMapper = objectMapper; + } + + public OidcProviderMetadata discover(String issuer) { + String normalizedIssuer = trimTrailingSlash(issuer); + String wellKnown = normalizedIssuer + "/.well-known/openid-configuration"; + JsonNode json = getJson(wellKnown); + return new OidcProviderMetadata( + readRequiredText(json, "issuer"), + readRequiredText(json, "authorization_endpoint"), + readRequiredText(json, "token_endpoint"), + readRequiredText(json, "jwks_uri")); + } + + public OidcTokenResponse exchangeCode( + OidcProviderMetadata metadata, + EventLensConfig.OidcConfig config, + String redirectUri, + String code, + String codeVerifier) { + Map form = new LinkedHashMap<>(); + form.put("grant_type", "authorization_code"); + form.put("code", code); + form.put("redirect_uri", redirectUri); + form.put("code_verifier", codeVerifier); + String body = toFormBody(form); + + String credentials = config.getClientId() + ":" + config.getClientSecret(); + String basicAuth = java.util.Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); + + HttpRequest request = HttpRequest.newBuilder(URI.create(metadata.tokenEndpoint())) + .timeout(Duration.ofSeconds(15)) + .header("Authorization", "Basic " + basicAuth) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + JsonNode json = sendJson(request, "OIDC token exchange"); + return new OidcTokenResponse( + readOptionalText(json, "access_token"), + readOptionalText(json, "token_type"), + json.path("expires_in").asLong(0), + readRequiredText(json, "id_token")); + } + + public JsonNode fetchJwks(String jwksUri) { + return getJson(jwksUri); + } + + private JsonNode getJson(String uri) { + HttpRequest request = HttpRequest.newBuilder(URI.create(uri)) + .timeout(Duration.ofSeconds(10)) + .GET() + .build(); + return sendJson(request, "OIDC metadata fetch"); + } + + private JsonNode sendJson(HttpRequest request, String operation) { + try { + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() < 200 || response.statusCode() >= 300) { + throw new IllegalStateException(operation + " failed with status " + response.statusCode()); + } + return objectMapper.readTree(response.body()); + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(operation + " failed", e); + } + } + + private static String readRequiredText(JsonNode node, String field) { + String value = readOptionalText(node, field); + if (value == null || value.isBlank()) { + throw new IllegalStateException("OIDC metadata field is missing: " + field); + } + return value; + } + + private static String readOptionalText(JsonNode node, String field) { + JsonNode value = node.get(field); + return value == null || value.isNull() ? null : value.asText(); + } + + private static String toFormBody(Map form) { + StringBuilder body = new StringBuilder(); + for (Map.Entry entry : form.entrySet()) { + if (body.length() > 0) { + body.append('&'); + } + body.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8)); + body.append('='); + body.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)); + } + return body.toString(); + } + + private static String trimTrailingSlash(String value) { + if (value == null) { + return ""; + } + return value.endsWith("/") ? value.substring(0, value.length() - 1) : value; + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderMetadata.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderMetadata.java new file mode 100644 index 0000000..d205854 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcProviderMetadata.java @@ -0,0 +1,9 @@ +package io.eventlens.api.security.oidc; + +public record OidcProviderMetadata( + String issuer, + String authorizationEndpoint, + String tokenEndpoint, + String jwksUri +) { +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcTokenResponse.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcTokenResponse.java new file mode 100644 index 0000000..65511dd --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/OidcTokenResponse.java @@ -0,0 +1,9 @@ +package io.eventlens.api.security.oidc; + +public record OidcTokenResponse( + String accessToken, + String tokenType, + long expiresIn, + String idToken +) { +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/PendingOidcLogin.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/PendingOidcLogin.java new file mode 100644 index 0000000..d5aa8a8 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/PendingOidcLogin.java @@ -0,0 +1,9 @@ +package io.eventlens.api.security.oidc; + +public record PendingOidcLogin( + String stateId, + String nonce, + String codeVerifier, + String returnHash +) { +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/oidc/ValidatedOidcIdentity.java b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/ValidatedOidcIdentity.java new file mode 100644 index 0000000..acb3cd1 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/oidc/ValidatedOidcIdentity.java @@ -0,0 +1,8 @@ +package io.eventlens.api.security.oidc; + +public record ValidatedOidcIdentity( + String subject, + String displayName, + String email +) { +} diff --git a/eventlens-api/src/main/resources/web/assets/index-2dMaJUyt.css b/eventlens-api/src/main/resources/web/assets/index-2dMaJUyt.css deleted file mode 100644 index 7b112cc..0000000 --- a/eventlens-api/src/main/resources/web/assets/index-2dMaJUyt.css +++ /dev/null @@ -1 +0,0 @@ -:root{--bg-base:#050508;--bg-surface:#0a0c14;--bg-raised:#0f1220;--bg-elevated:#161b2e;--bg-panel:linear-gradient(145deg, #0c0f1a 0%, #0a0d18 50%, #080b14 100%);--border:#1a2040;--border-muted:#121830;--border-glow:#00f0ff26;--text-primary:#e8eef8;--text-secondary:#94a3c0;--text-muted:#5a6a8a;--neon-cyan:#00f0ff;--neon-cyan-dim:#00f0ff14;--neon-cyan-mid:#00f0ff40;--neon-magenta:#ff00e5;--neon-magenta-dim:#ff00e514;--neon-green:#0f8;--neon-green-dim:#00ff881a;--neon-amber:#fa0;--neon-amber-dim:#ffaa001a;--neon-red:#f35;--neon-red-dim:#ff33551a;--neon-purple:#a855f7;--accent-blue:#4f9cf9;--accent-blue-dim:#4f9cf926;--accent-green:var(--neon-green);--accent-red:var(--neon-red);--accent-yellow:var(--neon-amber);--accent-purple:var(--neon-purple);--font-sans:"Inter", system-ui, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", monospace;--font-display:"Orbitron", var(--font-sans);--radius:6px;--radius-lg:10px;--shadow:0 4px 24px #0009;--shadow-neon:0 0 20px #00f0ff14, 0 0 60px #00f0ff08;--transition:.2s ease;--bottom-panel-scroll-height:280px}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{font-size:15px}body{font-family:var(--font-sans);background:var(--bg-base);color:var(--text-primary);-webkit-font-smoothing:antialiased;min-height:100vh;line-height:1.65}body:after{content:"";pointer-events:none;z-index:9999;background:repeating-linear-gradient(0deg,#0000,#0000 2px,#00f0ff04 2px 4px);position:fixed;inset:0}.app{flex-direction:column;min-height:100vh;display:flex}.app-header{--header-pad-x:24px;--header-pad-right:48px;--header-control-h:34px;padding:0 var(--header-pad-right) 0 var(--header-pad-x);border-bottom:1px solid var(--border);z-index:100;background:linear-gradient(#0d1020 0%,#080b14 100%);grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;column-gap:clamp(12px,2vw,20px);height:64px;display:grid;position:sticky;top:0;box-shadow:0 2px 20px #00000080,inset 0 -1px #00f0ff0f}.brand{justify-self:start;align-items:center;gap:12px;min-width:0;display:flex}.brand-logo{justify-content:center;align-items:center;width:36px;height:36px;display:flex;position:relative}.brand-logo svg{width:36px;height:36px;filter:drop-shadow(0 0 6px var(--neon-cyan)) drop-shadow(0 0 12px #00f0ff4d)}.brand-name{font-family:var(--font-display);letter-spacing:1.5px;color:var(--text-primary);text-transform:uppercase;font-size:15px;font-weight:700}.brand-sub{color:var(--text-muted);letter-spacing:.5px;font-size:10px}.header-title{font-family:var(--font-display);letter-spacing:3px;text-transform:uppercase;background:linear-gradient(135deg, var(--neon-cyan), #4facfe, var(--neon-magenta));-webkit-text-fill-color:transparent;filter:drop-shadow(0 0 8px #00f0ff66);text-align:center;-webkit-background-clip:text;background-clip:text;flex-shrink:0;margin:0;font-size:20px;font-weight:800;line-height:1}.header-center{flex-flow:row;justify-content:center;justify-self:center;align-items:center;gap:clamp(10px,1.5vw,16px);min-width:0;display:flex}.header-demo-pill{box-sizing:border-box;height:var(--header-control-h);background:var(--neon-amber-dim);color:var(--neon-amber);font-family:var(--font-mono);white-space:nowrap;border:1px solid #ffaa0059;border-radius:999px;flex-shrink:0;justify-content:center;align-items:center;padding:0 12px;font-size:11px;line-height:1;display:inline-flex}.header-actions{flex-direction:row;justify-content:flex-end;justify-self:end;align-items:center;gap:clamp(10px,1.2vw,16px);min-width:0;display:flex}.header-actions .conn-stats,.header-actions .header-status{flex-shrink:0}.workspace-datasource{flex-direction:column;gap:6px;min-width:0;margin:0;display:flex}.workspace-datasource-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:10px;line-height:1}.workspace-datasource-select{box-sizing:border-box;appearance:none;width:100%;height:34px;color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition);background-color:#0c1020f2;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3c0' d='M3 4.5 6 8l3-3.5'/%3E%3C/svg%3E");background-position:right 10px center;background-repeat:no-repeat;border:1px solid #ffffff24;border-radius:8px;padding:0 32px 0 12px;font-size:12px;line-height:1;box-shadow:inset 0 1px #00f0ff0f}.workspace-datasource-select:hover{border-color:#00f0ff40}.workspace-datasource-select:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.header-status{font-size:12px;font-family:var(--font-sans);letter-spacing:.3px;align-items:center;gap:8px;display:flex}.header-status .status-text{color:var(--neon-green);text-shadow:0 0 8px #00ff8880}.header-status .status-text.offline{color:var(--neon-red);text-shadow:0 0 8px #ff335580}.dot{border-radius:50%;width:8px;height:8px}.dot-green{background:var(--neon-green);box-shadow:0 0 6px var(--neon-green), 0 0 12px #0f86;animation:2s infinite pulse-neon}.dot-red{background:var(--neon-red);box-shadow:0 0 6px var(--neon-red)}.dot-yellow{background:var(--neon-amber);box-shadow:0 0 6px var(--neon-amber);animation:1.5s infinite pulse-neon}@keyframes pulse-neon{0%,to{opacity:1}50%{opacity:.4}}.app-main{flex-direction:column;flex:1;gap:16px;width:100%;max-width:1440px;margin:0 auto;padding:20px;display:flex}.card{background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow), var(--shadow-neon);padding:20px;position:relative;overflow:hidden}.card.card--dropdown-host{z-index:30;overflow:visible}.card:before{content:"";background:linear-gradient(90deg, transparent, var(--neon-cyan-mid), transparent);height:1px;position:absolute;top:0;left:0;right:0}.card:after{content:"";background:linear-gradient(90deg,#0000,#ff00e51a,#0000);height:1px;position:absolute;bottom:0;left:0;right:0}.card-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;text-shadow:0 0 6px #00f0ff33;align-items:center;gap:8px;margin-bottom:16px;font-size:13px;font-weight:600;display:flex}.control-ribbon{padding:14px 18px}.control-ribbon-top{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:14px;display:flex}.control-ribbon-title{margin-bottom:4px}.control-ribbon-subtitle{color:var(--text-muted);font-size:12px}.control-ribbon-nav{align-items:center;gap:12px;font-size:13px;display:inline-flex}.control-ribbon-nav a{text-decoration:none}.control-ribbon-nav a[aria-current=page]{text-underline-offset:2px;text-decoration:underline}.control-panel{gap:12px;padding:16px 18px;display:grid}.control-panel-grid{grid-template-columns:minmax(220px,360px) minmax(0,1fr);align-items:end;gap:12px;display:grid}.control-field,.control-field--search{min-width:0}.control-field-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-size:11px;display:block}.control-select{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0d1123eb;border:1px solid #ffffff24;border-radius:10px;outline:none;padding:10px 12px;font-size:12px}.control-select:focus{border-color:var(--neon-cyan-mid);box-shadow:0 0 14px #00f0ff26}.datasource-pills{flex-wrap:wrap;gap:8px;display:flex}.datasource-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:4px 8px}.selection-summary{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);margin-top:2px}.selection-clear-btn{color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);background:0 0;border:none;margin-left:12px}.selection-clear-btn:hover{color:var(--neon-cyan)}.event-agg-button,.anomaly-aggregate-link{cursor:pointer;background:0 0;border:none;padding:0}.event-agg-button{color:var(--text-secondary);font:inherit}.event-agg-button:hover,.event-agg-button:focus-visible{color:var(--neon-cyan);text-shadow:0 0 8px #00f0ff47}.anomaly-aggregate-link .anomaly-meta-value{color:var(--neon-cyan)}.anomaly-aggregate-link:hover .anomaly-meta-value,.anomaly-aggregate-link:focus-visible .anomaly-meta-value{text-shadow:0 0 8px #00f0ff4d}.workspace-dock{z-index:110;border:1px solid var(--border-muted);pointer-events:auto;border-right:none;border-radius:10px 0 0 10px;flex-direction:row;align-items:center;width:auto;height:auto;max-height:min(72vh,100vh - 112px);transition:box-shadow .2s;display:flex;position:fixed;inset:50% 0 auto auto;overflow:hidden;transform:translateY(-50%);box-shadow:-6px 4px 22px #0000006b}.workspace-dock--open{box-shadow:-8px 6px 28px #0000007a,0 0 0 1px #00f0ff14}.workspace-dock-handle{border:none;border-left:1px solid var(--border-muted);width:36px;height:36px;color:var(--neon-cyan);font-family:var(--font-mono);cursor:pointer;transition:background var(--transition), color var(--transition);background:linear-gradient(#0f1324 0%,#0a0e18 100%);flex-direction:row;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:flex}.workspace-dock-handle:hover{color:var(--text-primary);background:linear-gradient(#141a30 0%,#0d1220 100%)}.workspace-dock-handle:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:-2px}.workspace-dock-chevron{font-size:15px;font-weight:700;line-height:1}.workspace-dock-panel{border-left:1px solid var(--border);background:linear-gradient(145deg,#0c101c 0%,#080c14 100%);flex-direction:column;flex:0 auto;gap:8px;width:min(252px,100vw - 48px);min-width:0;max-height:min(72vh,100vh - 112px);padding:10px 12px 12px 14px;display:flex;overflow:hidden auto}.workspace-dock-panel[hidden]{display:none!important}.workspace-dock-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:12px;font-weight:600}.workspace-dock-scrim{z-index:109;cursor:pointer;background:#03050c73;border:none;margin:0;padding:0;position:fixed;inset:64px 0 36px}.workspace-sidebar-kpis{border:1px solid var(--border-muted);border-radius:var(--radius);background:#080b148c;gap:6px;padding:8px 10px;display:grid}.workspace-kpi-row{color:var(--text-muted);font-size:11px;font-family:var(--font-mono);justify-content:space-between;gap:8px;display:flex}.workspace-kpi-row strong{color:var(--text-primary)}.workspace-sidebar-links{color:var(--text-secondary);gap:6px;font-size:12px;display:grid}.workspace-content{gap:16px;display:grid}.search-panel{width:100%;min-width:0}.selection-clear-btn:focus-visible,.control-ribbon-nav a:focus-visible,.control-select:focus-visible,.workspace-datasource-select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.plugin-dashboard{gap:14px;display:grid}.plugin-cards-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.plugin-cards-grid--dense{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}.plugin-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(140deg,#101527f2,#0a0e1cf2);padding:12px 14px;box-shadow:inset 0 1px #ffffff05}.plugin-card--interactive{transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition)}.plugin-card--interactive:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 16px #00f0ff1a}.plugin-card--interactive:focus-within{border-color:var(--neon-cyan-mid);box-shadow:0 0 0 2px #00f0ff26}.plugin-card-head{justify-content:space-between;align-items:center;gap:10px;display:flex}.plugin-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:2px 8px}.plugin-card-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);overflow-wrap:anywhere;margin-top:8px}.plugin-card-detail{color:var(--text-secondary);margin-top:8px;font-size:12px;line-height:1.55}.search-wrapper{position:relative}.search-input{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);outline:none;padding:14px 16px 14px 44px;font-size:13px}.search-input:focus{border-color:var(--neon-cyan);box-shadow:0 0 14px #00f0ff2e}.search-input::placeholder{color:var(--text-muted)}.search-icon{color:var(--neon-cyan);pointer-events:none;filter:drop-shadow(0 0 4px #00f0ff80);font-size:16px;position:absolute;top:50%;left:14px;transform:translateY(-50%)}.search-results{background:var(--bg-raised);border:1px solid var(--neon-cyan-mid);border-radius:var(--radius);z-index:500;max-height:min(55vh,420px);position:absolute;top:calc(100% + 6px);left:0;right:0;overflow:hidden auto;box-shadow:0 12px 40px #000000a6,0 0 24px #00f0ff14}.search-result-item{cursor:pointer;transition:background var(--transition);font-family:var(--font-mono);color:var(--text-primary);text-align:left;background:0 0;border:none;align-items:center;gap:10px;width:100%;padding:10px 16px;font-size:13px;display:flex}.search-result-item:hover{background:var(--bg-elevated);box-shadow:inset 3px 0 0 var(--neon-cyan)}.search-result-item+.search-result-item{border-top:1px solid var(--border-muted)}.search-result-chevron{color:var(--text-muted);flex-shrink:0;padding-right:4px}.search-result-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:0 6px;min-width:0;display:flex}.search-result-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0;font-size:10px}.search-result-colon{color:var(--text-muted);flex-shrink:0;margin-right:2px}.search-result-value{overflow-wrap:anywhere;word-break:break-word;flex:1;min-width:0}.conn-stats{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:16px;font-size:11px;display:flex}.conn-stat{flex-direction:column;align-items:flex-end;gap:1px;display:flex}.conn-stat-label{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);font-size:9px}.conn-stat-value{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66;font-size:12px}.conn-stat-value.green{color:var(--neon-green);text-shadow:0 0 6px #0f86}.conn-stat-value.amber{color:var(--neon-amber);text-shadow:0 0 6px #fa06}.conn-stat--metric .conn-stat-value,.conn-stat-value--uptime{text-align:right;font-variant-numeric:tabular-nums;min-width:6.5ch;display:inline-block}.mini-wave{align-items:flex-end;gap:1px;height:24px;display:flex}.mini-wave-bar{background:var(--neon-cyan);border-radius:1px;width:3px;animation:1.2s ease-in-out infinite wave-pulse;box-shadow:0 0 4px #00f0ff4d}@keyframes wave-pulse{0%,to{transform:scaleY(.3)}50%{transform:scaleY(1)}}.timeline-count-pill{color:var(--accent-blue);font-family:var(--font-mono);background:var(--accent-blue-dim);border:1px solid #4f9cf940;border-radius:999px;padding:2px 10px;font-size:11px}.timeline-hint{color:var(--text-muted);max-width:52rem;margin:-8px 0 12px;font-size:12px;line-height:1.5}.timeline-rail{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-base);position:relative;box-shadow:inset 0 1px #00f0ff0a}.timeline-stepper{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);padding:12px 14px 14px;overflow:auto hidden;-webkit-mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000);mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}.timeline-stepper-track{flex-wrap:nowrap;align-items:center;gap:4px;width:max-content;min-height:88px;display:flex}.timeline-step-arrow{color:var(--text-muted);opacity:.7;flex-shrink:0;align-self:center;padding:0 2px;font-size:11px}.timeline-step-arrow-compact{padding:0 1px;font-size:9px}.timeline-step{border-radius:var(--radius);border:1px solid var(--border-muted);background:var(--bg-raised);cursor:pointer;text-align:left;min-width:118px;max-width:200px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-step-compact{gap:2px;min-width:96px;max-width:140px;padding:8px 10px}.timeline-step-compact .timeline-step-badge{font-size:8px}.timeline-step-compact .timeline-step-type{-webkit-line-clamp:1;font-size:10px}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-step-badge{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:10px;font-weight:600}.timeline-step-seq{color:var(--text-muted);font-size:10px;font-family:var(--font-mono)}.timeline-step-type{color:var(--text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}.timeline-step-created{border-left:3px solid var(--neon-green)}.timeline-step-deleted{border-left:3px solid var(--neon-red)}.timeline-step-completed{border-left:3px solid var(--neon-green)}.timeline-step-failed{border-left:3px solid var(--neon-red)}.timeline-step-transfer{border-left:3px solid var(--neon-amber)}.timeline-step-item{border-left:3px solid var(--neon-purple)}.timeline-step-progress{border-left:3px solid #38bdf8}.timeline-step-default{border-left:3px solid var(--neon-cyan)}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);cursor:pointer;text-align:left;min-width:140px;max-width:240px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-group-chip.expanded{border-color:#00f0ff59}.timeline-group-count{font-family:var(--font-display);letter-spacing:.5px;color:var(--neon-cyan);font-size:16px;font-weight:800;line-height:1}.timeline-group-chevron{color:var(--text-muted);font-size:10px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.timeline-group-range{color:var(--text-muted);font-size:10px;font-family:var(--font-mono);line-height:1.4}.timeline-expanded-deck{border-top:1px solid var(--border-muted);background:#00000040;padding:10px 14px 14px}.timeline-expanded-head{flex-wrap:wrap;align-items:center;gap:10px 16px;margin-bottom:10px;display:flex}.timeline-expanded-title{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:12px;font-weight:700}.timeline-expanded-meta{font-family:var(--font-mono);color:var(--text-muted);flex:1;font-size:11px}.timeline-expanded-close{border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-raised);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition);margin-left:auto;padding:4px 12px;font-size:11px}.timeline-expanded-close:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan)}.timeline-expanded-strip{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex-wrap:nowrap;align-items:center;gap:4px;width:100%;padding-bottom:6px;display:flex;overflow-x:auto}.timeline-slider{width:100%;accent-color:var(--neon-cyan);cursor:pointer;margin-top:4px}.timeline-info{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);grid-template-columns:minmax(0,1fr) minmax(0,2.2fr) minmax(0,1fr);align-items:start;gap:12px;margin-top:12px;display:grid}.timeline-info-edge{color:var(--text-muted);font-size:11px}.timeline-info-center{text-align:center;color:var(--text-secondary);line-height:1.55}.timeline-info-center strong{color:var(--neon-cyan);font-weight:600}.timeline-info-muted{color:var(--text-muted);font-weight:400}.timeline-info-type{color:var(--text-primary);font-size:11px}.state-grid{grid-template-columns:1fr 1fr;gap:16px;display:grid}.state-panel h4{font-family:var(--font-display);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:8px;font-size:10px;font-weight:600}.state-panel-before h4{color:var(--text-muted)}.state-panel-after h4{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.json-block{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);font-family:var(--font-mono);max-height:260px;color:var(--text-secondary);white-space:pre;padding:12px;font-size:12px;line-height:1.7;overflow:auto}.json-tree-root{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);max-height:320px;font-family:var(--font-mono);color:var(--text-secondary);padding:10px 12px;font-size:13px;line-height:1.75;overflow:auto}.json-tree-line{flex-wrap:wrap;align-items:baseline;gap:2px 0;min-height:1.5em;display:flex}.json-tree-toggle{width:22px;height:22px;color:var(--text-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;margin-right:4px;padding:0;font-size:10px;line-height:1}.json-tree-toggle:hover{color:var(--neon-cyan);background:var(--neon-cyan-dim)}.json-key{color:#7dd3fc}.json-string{color:#86efac}.json-number{color:#fcd34d}.json-boolean{color:#c4b5fd}.json-null{color:var(--text-muted);font-style:italic}.json-punct{color:var(--text-muted)}.json-ellipsis{color:var(--text-muted);font-size:11px;font-style:italic}.json-unknown{color:var(--neon-amber)}.diff-panel{border-top:1px solid var(--border-muted);margin-top:20px;padding-top:16px}.diff-toolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.diff-toolbar-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:13px;font-weight:600}.diff-view-toggle{border-radius:var(--radius);border:1px solid var(--border);font-family:var(--font-mono);font-size:11px;display:flex;overflow:hidden}.diff-view-toggle button{background:var(--bg-raised);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:6px 14px}.diff-view-toggle button+button{border-left:1px solid var(--border)}.diff-view-toggle button:hover{color:var(--text-secondary)}.diff-view-toggle button.active{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.diff-body{align-items:stretch;gap:10px;display:flex}.diff-minimap{border:1px solid var(--border-muted);background:var(--bg-base);border-radius:4px;flex-direction:column;flex-shrink:0;width:10px;max-height:280px;display:flex;overflow:hidden}.diff-minimap-chunk{background:var(--border);cursor:pointer;min-height:8px;transition:background var(--transition);border:none;flex:1;margin:0;padding:0}.diff-minimap-chunk:hover,.diff-minimap-chunk:focus-visible{background:var(--neon-cyan-mid);outline:none}.diff-scroll{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex:1;min-width:0;max-height:280px;overflow:auto}.diff-list{flex-direction:column;gap:0;display:flex}.diff-row{font-family:var(--font-mono);background:var(--bg-raised);border-left:3px solid var(--neon-cyan);border-bottom:1px solid var(--border-muted);border-radius:0;align-items:stretch;gap:0;padding:0;font-size:12px;line-height:1.65;display:flex}.diff-row:last-child{border-radius:0 0 var(--radius) var(--radius);border-bottom:none}.diff-row:first-child{border-radius:var(--radius) var(--radius) 0 0}.diff-line-no{text-align:right;width:36px;color:var(--text-muted);background:var(--bg-base);border-right:1px solid var(--border-muted);-webkit-user-select:none;user-select:none;flex-shrink:0;padding:10px 6px;font-size:10px}.diff-row-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:8px 12px;padding:10px 12px;display:flex}.diff-field{color:var(--text-primary);min-width:6rem;font-weight:700}.diff-values-inline{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.diff-old{color:var(--neon-red);font-weight:400;text-decoration:line-through}.diff-arrow{color:var(--text-muted);flex-shrink:0}.diff-new{color:var(--neon-green);text-shadow:0 0 4px #00ff8840;font-weight:400}.diff-split-head{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);border-bottom:1px solid var(--border);background:var(--bg-raised);z-index:1;grid-template-columns:1fr 1fr;gap:8px;padding:8px 12px 8px 48px;font-size:11px;display:grid;position:sticky;top:0}.diff-split-old-label{color:var(--neon-red)}.diff-split-new-label{color:var(--neon-green)}.diff-split-row{border-bottom:1px solid var(--border-muted);align-items:stretch;gap:0;display:flex}.diff-split-row:last-child{border-bottom:none}.diff-split-cells{flex:1;grid-template-columns:1fr 1fr;gap:8px;min-width:0;padding:8px 12px 8px 0;display:grid}.diff-split-cell{border-radius:var(--radius);background:var(--bg-base);border:1px solid var(--border-muted);min-width:0;padding:8px 10px}.diff-split-cell .diff-field{margin-bottom:6px;font-size:11px;display:block}.diff-cell-value{overflow-wrap:anywhere;word-break:break-word;font-size:12px;font-weight:400;line-height:1.6}.diff-split-old .diff-cell-value{color:var(--neon-red);opacity:.9}.diff-split-new .diff-cell-value{color:var(--neon-green)}.event-meta{color:var(--text-muted);grid-template-columns:repeat(2,1fr);gap:10px 16px;margin-top:20px;font-size:12px;display:grid}.event-meta-bar{background:var(--bg-raised);border:1px solid var(--border-muted);border-radius:var(--radius);padding:14px 16px;box-shadow:inset 0 1px #00f0ff0a}.event-meta-time{font-family:var(--font-mono);color:var(--text-secondary);font-size:12px;font-weight:500}.event-meta-id{font-family:var(--font-mono);color:var(--text-primary);overflow-wrap:anywhere;word-break:break-word;grid-column:1/-1;font-size:11px;line-height:1.5}.event-meta-extra{font-family:var(--font-mono);color:var(--text-secondary);font-size:11px}@media (width<=900px){.diff-split-cells{grid-template-columns:1fr}.diff-split-head{display:none}}.copy-btn{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-secondary);cursor:pointer;transition:all var(--transition);font-size:12px;font-family:var(--font-mono);margin-top:12px;padding:6px 14px}.copy-btn:hover{background:var(--bg-raised);color:var(--neon-cyan);border-color:var(--neon-cyan);box-shadow:0 0 10px #00f0ff1a}.live-header{justify-content:space-between;align-items:center;margin-bottom:12px;display:flex}.live-indicator{font-family:var(--font-sans);letter-spacing:.2px;align-items:center;gap:6px;font-size:11px;display:flex}.pause-btn{background:var(--bg-elevated);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);border-radius:4px;padding:5px 12px;font-size:11px}.pause-btn:hover{background:var(--bg-raised);border-color:var(--neon-cyan);color:var(--neon-cyan);box-shadow:0 0 8px #00f0ff1a}.event-stream{height:var(--bottom-panel-scroll-height);min-height:0;font-family:var(--font-mono);flex-direction:column;gap:3px;padding-right:4px;font-size:12px;display:flex;overflow-y:auto}.event-stream,.anomaly-scroll-region,.search-results{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base)}.event-stream::-webkit-scrollbar{width:6px}.anomaly-scroll-region::-webkit-scrollbar{width:6px}.search-results::-webkit-scrollbar{width:6px}.event-stream::-webkit-scrollbar-track{background:var(--bg-base)}.anomaly-scroll-region::-webkit-scrollbar-track{background:var(--bg-base)}.search-results::-webkit-scrollbar-track{background:var(--bg-base)}.event-stream::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.anomaly-scroll-region::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.search-results::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.event-stream::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.anomaly-scroll-region::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.search-results::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.event-row{transition:all var(--transition);cursor:default;background:#ffffff03;border-left:3px solid #0000;border-radius:4px;align-items:center;gap:12px;padding:6px 10px;display:flex}.event-row:hover{background:var(--bg-elevated)}.event-row.type-withdrawn{border-left-color:var(--neon-magenta)}.event-row.type-deposited{border-left-color:var(--neon-cyan)}.event-row.type-created{border-left-color:var(--neon-green)}.event-row.type-transfer{border-left-color:var(--neon-amber)}.event-row.type-deleted{border-left-color:var(--neon-red)}.event-row.type-completed{border-left-color:var(--neon-green)}.event-row.type-failed{border-left-color:var(--neon-red)}.event-row.type-default{border-left-color:var(--neon-purple)}.event-time{color:var(--text-muted);flex-shrink:0;width:75px;font-size:11px}.event-type{flex-shrink:0;width:180px;font-weight:500}.event-agg{color:var(--text-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.event-icon{text-align:center;flex-shrink:0;width:20px;font-size:14px}.type-created{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-deleted{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-completed{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-failed{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-transfer{color:var(--neon-amber);text-shadow:0 0 6px #ffaa004d}.type-default{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.type-withdrawn{color:var(--neon-magenta);text-shadow:0 0 6px #ff00e54d}.type-deposited{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.anomaly-panel-inner{position:relative}.anomaly-shield{background:radial-gradient(#00ff880f 0%,#0000 70%);border:1px solid #00ff8826;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;margin-bottom:20px;padding:30px 20px;display:flex}.shield-icon{justify-content:center;align-items:center;width:64px;height:64px;margin-bottom:12px;display:flex;position:relative}.shield-icon svg{filter:drop-shadow(0 0 12px #00ff8880)drop-shadow(0 0 24px #0f83);width:64px;height:64px}.shield-icon:after{content:"";border:1px solid #00ff8826;border-radius:50%;animation:3s ease-in-out infinite shield-pulse;position:absolute;inset:-8px}@keyframes shield-pulse{0%,to{opacity:.3;transform:scale(1)}50%{opacity:.8;transform:scale(1.15)}}.shield-text{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-green);text-shadow:0 0 8px #0f86;font-size:13px;font-weight:600}.gauge-row{grid-template-columns:1fr 1fr 1fr;gap:12px;display:grid}.gauge-card{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);text-align:center;padding:12px;position:relative;overflow:hidden}.gauge-card:before{content:"";height:2px;position:absolute;bottom:0;left:0;right:0}.gauge-card.optimal:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-card.baseline:before{background:var(--neon-cyan);box-shadow:0 0 8px #00f0ff66}.gauge-card.zero:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);margin-bottom:6px;font-size:10px;font-weight:500}.gauge-value{font-family:var(--font-display);letter-spacing:1px;font-size:13px;font-weight:700}.gauge-value.optimal{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-value.baseline{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66}.gauge-value.zero{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-wave{justify-content:center;align-items:flex-end;gap:2px;height:20px;margin-top:6px;display:flex}.gauge-wave-bar{border-radius:1px;width:2px;animation:1.5s ease-in-out infinite gauge-wave-anim}.gauge-wave-bar.green{background:var(--neon-green);box-shadow:0 0 3px #0f86}.gauge-wave-bar.cyan{background:var(--neon-cyan);box-shadow:0 0 3px #00f0ff66}@keyframes gauge-wave-anim{0%,to{height:4px}50%{height:16px}}.anomaly-scroll-region{box-sizing:border-box;height:var(--bottom-panel-scroll-height);max-height:var(--bottom-panel-scroll-height);z-index:1;min-height:0;padding-right:4px;position:relative;overflow:hidden auto}.anomaly-list-inner{flex-direction:column;gap:10px;display:flex}.anomaly-card-title-row{flex-wrap:wrap;width:100%}.anomaly-title-text{flex:1}.anomaly-header-count{min-width:28px;height:28px;font-family:var(--font-display);color:var(--bg-base);background:var(--neon-amber);border-radius:999px;justify-content:center;align-items:center;margin-left:auto;padding:0 10px;font-size:11px;font-weight:700;display:inline-flex;box-shadow:0 0 12px #ffaa0059}.anomaly-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-raised);transition:border-color var(--transition), box-shadow var(--transition);overflow:hidden}.anomaly-card.CRITICAL{border-left:4px solid #f44}.anomaly-card.HIGH{border-left:4px solid var(--neon-red)}.anomaly-card.MEDIUM{border-left:4px solid var(--neon-amber)}.anomaly-card.LOW{border-left:4px solid var(--neon-cyan)}.anomaly-card-summary{cursor:pointer;font-family:var(--font-sans);flex-wrap:wrap;align-items:center;gap:10px 12px;padding:14px 16px;list-style:none;display:flex}.anomaly-card-summary::-webkit-details-marker{display:none}.anomaly-severity-badge{font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:4px 10px;font-size:9px;font-weight:700}.anomaly-severity-badge.sev-critical{color:#f66;background:#f443;border:1px solid #ff444459}.anomaly-severity-badge.sev-high{background:var(--neon-red-dim);color:var(--neon-red);border:1px solid #ff335559}.anomaly-severity-badge.sev-medium{background:var(--neon-amber-dim);color:var(--neon-amber);border:1px solid #ffaa0059}.anomaly-severity-badge.sev-low{background:var(--neon-cyan-dim);color:var(--neon-cyan);border:1px solid #00f0ff40}.anomaly-card-title{min-width:0;color:var(--text-primary);flex:1;font-size:15px;font-weight:600;line-height:1.45}.anomaly-card-chevron{color:var(--text-muted);transition:transform var(--transition);flex-shrink:0;font-size:10px}.anomaly-card[open] .anomaly-card-chevron{transform:rotate(-180deg)}.anomaly-card-body{border-top:1px solid var(--border-muted);background:#0003;padding:0 16px 16px}.anomaly-card-meta{flex-wrap:wrap;align-items:baseline;gap:8px 12px;margin-top:12px;font-size:13px;line-height:1.5;display:flex}.anomaly-card-meta:first-child{margin-top:12px}.anomaly-meta-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);min-width:72px;font-size:10px}.anomaly-meta-value{font-family:var(--font-mono);color:var(--text-secondary);overflow-wrap:anywhere}.no-anomalies{color:var(--neon-green);align-items:center;gap:8px;padding:20px 0;font-size:13px;display:flex}.bottom-grid{grid-template-columns:1fr 1fr;align-items:start;gap:16px;display:grid}.bottom-grid>.card{flex-direction:column;align-items:stretch;min-width:0;min-height:0;display:flex}.bottom-grid>.card>.card-title,.bottom-grid>.card>.live-header,.bottom-grid .anomaly-scroll-region,.bottom-grid .event-stream{flex-shrink:0}.skeleton{background:linear-gradient(90deg, var(--bg-raised) 25%, var(--bg-elevated) 50%, var(--bg-raised) 75%);border-radius:var(--radius);background-size:200% 100%;animation:1.4s infinite shimmer}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.toast-container{z-index:999;flex-direction:column;gap:8px;display:flex;position:fixed;bottom:24px;right:24px}.toast{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);box-shadow:var(--shadow);border-left:3px solid var(--neon-amber);padding:10px 16px;font-size:13px;animation:.2s slideIn}.toast.error{border-left-color:var(--neon-red)}.toast.success{border-left-color:var(--neon-green)}@keyframes slideIn{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:var(--bg-base)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.demo-banner{font-size:13px;font-family:var(--font-mono);color:var(--neon-amber);background:var(--neon-amber-dim);border-radius:var(--radius);border:1px solid #ffaa0059;margin:0 0 16px;padding:10px 16px}.demo-banner code{color:var(--neon-cyan)}@media (width<=900px){.state-grid,.bottom-grid{grid-template-columns:1fr}.header-center .header-demo-pill{display:none}.workspace-dock-panel{width:min(240px,88vw)}.gauge-row,.control-panel-grid{grid-template-columns:1fr}.timeline-info{text-align:center;grid-template-columns:1fr}.timeline-info-edge{display:none}}.state-tabs{border-bottom:1px solid var(--border);gap:2px;margin-bottom:16px;padding-bottom:0;display:flex}.state-tab{color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:color var(--transition), border-color var(--transition);border-radius:var(--radius) var(--radius) 0 0;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-1px;padding:8px 16px;font-size:13px;font-weight:500;display:flex}.state-tab:hover{color:var(--text-secondary)}.state-tab.active{color:var(--neon-cyan);border-bottom-color:var(--neon-cyan);background:var(--neon-cyan-dim);text-shadow:0 0 8px #00f0ff33}.state-tab-emoji{font-size:14px}.state-tab-content{min-height:80px}.summary-tab{padding-top:4px}.summary-changes{flex-direction:column;gap:8px;display:flex}.summary-changes-header{color:var(--text-muted);border-bottom:1px solid var(--border-muted);margin-bottom:4px;padding-bottom:6px;font-size:12px;font-weight:600}.summary-change-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-amber);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.summary-change-row:hover{background:var(--bg-elevated)}.summary-change-field{color:var(--text-primary);min-width:5rem;font-weight:700;font-family:var(--font-sans)}.summary-change-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.summary-change-arrow{color:var(--text-muted);flex-shrink:0}.summary-change-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.summary-no-changes{color:var(--neon-green);align-items:center;gap:12px;padding:20px 0;font-size:14px;display:flex}.event-summary-bar{background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);border:1px solid var(--border);border-left:3px solid var(--neon-cyan);border-radius:var(--radius);justify-content:space-between;align-items:center;gap:12px;margin-bottom:4px;padding:10px 20px;display:flex;box-shadow:0 0 16px #00f0ff0d}.event-summary-left{flex-wrap:wrap;align-items:baseline;gap:6px 12px;display:flex}.event-summary-type{font-family:var(--font-sans);color:var(--text-primary);font-size:14px;font-weight:600}.event-summary-meta{font-family:var(--font-mono);color:var(--text-muted);font-size:12px}.event-summary-changes{color:var(--neon-amber);font-size:12px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;flex-shrink:0;padding:3px 10px;font-weight:600}.diff-count-badge{color:var(--neon-amber);font-size:11px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;align-items:center;margin-left:6px;padding:2px 8px;font-weight:500;display:inline-flex}.diff-summary-view{flex-direction:column;gap:6px;display:flex}.diff-summary-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-cyan);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.diff-summary-row:hover{background:var(--bg-elevated)}.diff-summary-field{color:var(--text-primary);min-width:6rem;font-weight:700;font-family:var(--font-sans)}.diff-summary-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.diff-summary-arrow{color:var(--text-muted);flex-shrink:0}.diff-summary-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.diff-jump-next{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;margin-left:auto;padding:2px 8px;font-size:10px}.diff-jump-next:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.json-tree-changed{border-radius:3px;background:#ffaa001a!important}.timeline-header-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.timeline-jump-group{border:1px solid var(--border);border-radius:var(--radius);align-items:center;gap:0;display:flex;overflow:hidden}.timeline-jump-input{background:var(--bg-raised);border:none;border-right:1px solid var(--border);width:100px;color:var(--text-primary);font-family:var(--font-mono);outline:none;padding:5px 8px;font-size:12px}.timeline-jump-input:focus{border-right-color:var(--neon-cyan);background:var(--bg-elevated)}.timeline-jump-input::placeholder{color:var(--text-muted)}.timeline-jump-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input[type=number]{-moz-appearance:textfield}.timeline-jump-btn{background:var(--bg-elevated);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:5px 10px;font-size:14px}.timeline-jump-btn:hover{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.timeline-filter-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.filter-chip{border:1px solid var(--border);background:var(--bg-raised);color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);white-space:nowrap;text-overflow:ellipsis;border-radius:999px;max-width:160px;padding:3px 10px;font-size:11px;overflow:hidden}.filter-chip:hover{border-color:var(--neon-cyan-mid);color:var(--text-secondary)}.filter-chip.active{background:var(--neon-cyan-dim);color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff33;border-color:#00f0ff66}.timeline-anomaly-marker{color:var(--neon-amber);text-shadow:0 0 4px #fa09;align-self:flex-start;font-size:7px;line-height:1}.keyboard-hints{z-index:200;border-top:1px solid var(--border);-webkit-backdrop-filter:blur(12px);background:#080b14eb;position:fixed;bottom:0;left:0;right:0}.keyboard-hints-bar{font-family:var(--font-mono);color:var(--text-muted);flex-wrap:wrap;justify-content:center;align-items:center;gap:8px;padding:6px 24px;font-size:11px;display:flex}.keyboard-hints-item{align-items:center;gap:5px;display:flex}.keyboard-hints-sep{color:var(--border);font-size:14px;line-height:1}.keyboard-hints-grid{flex-wrap:wrap;justify-content:center;gap:10px 24px;padding:14px 24px;display:flex}.keyboard-hint-row{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:10px;font-size:12px;display:flex}.keyboard-hint-desc{color:var(--text-muted)}.keyboard-key{border:1px solid var(--border);border-bottom:2px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:4px;align-items:center;padding:2px 8px;font-size:11px;display:inline-flex}.keyboard-key-mini{border:1px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:3px;align-items:center;padding:1px 5px;font-size:10px;display:inline-flex}.keyboard-hints-close{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;padding:4px 12px;font-size:11px}.keyboard-hints-close:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.app-main{padding-bottom:40px}.command-palette-backdrop{z-index:1000;background:#050508b3;justify-content:center;align-items:flex-start;padding-top:12vh;display:flex;position:fixed;inset:0}.command-palette{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius-lg);width:min(640px,100vw - 32px);box-shadow:var(--shadow);overflow:hidden}.command-palette-input{border:0;border-bottom:1px solid var(--border);width:100%;color:var(--text-primary);font:inherit;background:0 0;padding:16px}.command-palette-list{max-height:320px;list-style:none;overflow:auto}.command-palette-item{cursor:pointer;color:var(--text-primary);padding:12px 16px}.command-palette-item.active,.command-palette-item:hover{background:var(--neon-cyan-dim)}.command-palette-item.muted{color:var(--text-muted)}.statistics-toolbar,.stats-kpis,.stats-distribution{flex-wrap:wrap;gap:12px;display:flex}.statistics-panel-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px;display:flex}.statistics-back-btn{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff1a,#4f9cf912);border:1px solid #00f0ff33;padding:8px 14px;font-size:12px}.statistics-back-btn:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.statistics-back-btn:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.stats-kpis{margin:12px 0 16px}.stat-card{border:1px solid var(--border);border-radius:var(--radius);background:#ffffff05;flex-direction:column;gap:6px;min-width:140px;padding:14px;display:flex}.stat-card strong{font-size:24px}.stats-chart{gap:8px;margin-bottom:16px;display:grid}.stats-bar-row,.stats-list-row,.replay-toolbar{align-items:center;gap:10px;display:flex}.stats-bar-track{background:#ffffff0f;border-radius:999px;flex:1;height:10px;overflow:hidden}.stats-bar-fill{background:linear-gradient(90deg, var(--neon-cyan), var(--accent-blue));height:100%}.stats-distribution>div,.replay-state{flex:1;min-width:260px}.replay-debugger{border-color:#00f0ff29}.replay-toolbar{border:1px solid var(--border-muted);border-radius:var(--radius);background:linear-gradient(145deg,#0c0f1cf0,#080a12f5);justify-content:space-between;margin-bottom:12px;padding:12px 14px}.replay-toolbar button{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border:1px solid #00f0ff38;padding:8px 14px;font-size:12px}.replay-toolbar button:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.replay-toolbar button:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-toolbar button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;transform:none}.replay-controls-center{flex-direction:column;align-items:center;gap:10px;display:flex}.replay-auto-controls{flex-wrap:wrap;justify-content:center;align-items:center;gap:10px;display:flex}.replay-auto-toggle{min-width:160px}.replay-auto-toggle.active{background:linear-gradient(145deg,#00ff8826,#00cc6614);border-color:#00ff884d;box-shadow:0 0 14px #00ff8829}.replay-interval-picker{color:var(--text-secondary);font-family:var(--font-mono);align-items:center;gap:8px;font-size:12px;display:flex}.replay-interval-picker select{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);background:#080c14f2;border:1px solid #00f0ff33;padding:7px 10px;font-size:12px}.replay-interval-picker select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-interval-picker select:disabled{opacity:.6;cursor:not-allowed}.replay-compare-warning{border-radius:var(--radius);color:#ffe7b2;background:linear-gradient(145deg,#2a1d08db,#1c1204f0);border:1px solid #ffd1663d;justify-content:space-between;align-items:center;gap:14px;margin-bottom:12px;padding:12px 14px;display:flex}.replay-warning-hint{color:#ffe7b2c7;font-family:var(--font-mono);font-size:11px}.replay-compare-actions{flex-shrink:0;align-items:center;gap:10px;display:flex}.replay-warning-btn{border-radius:var(--radius);color:#ffe7b2;font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), background var(--transition), transform var(--transition);background:#ffd1660f;border:1px solid #ffd16638;padding:8px 12px;font-size:12px}.replay-warning-btn:hover{background:#ffd1661f;border-color:#ffd16675;transform:translateY(-1px)}.replay-warning-btn--primary{color:var(--text-primary);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border-color:#00f0ff47}.replay-warning-btn--primary:hover{border-color:var(--neon-cyan-mid);background:linear-gradient(145deg,#00f0ff2e,#4f9cf91a)}.replay-position{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;font-size:13px}.timeline-virtual-container{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(#080a12f5,#06080efa),radial-gradient(circle at top,#00f0ff0d,#0000 55%);margin-top:12px;box-shadow:inset 0 1px #00f0ff0d}.timeline-step{border:1px solid var(--border-muted);border-radius:var(--radius);min-width:136px;max-width:168px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#101422f5,#0a0c16fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;margin:0;padding:12px 14px;display:flex}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-step.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-step-badge{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:.8px;text-transform:uppercase;font-size:11px}.timeline-step-seq{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-type{color:var(--text-primary);letter-spacing:.2px;font-size:12px;font-weight:600}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);min-width:190px;max-width:220px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#141828f7,#0b0e18fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:6px;margin:0;padding:12px 16px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-group-chip.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-group-chip.expanded{background:linear-gradient(145deg,#181e30f7,#0c101cfc);border-color:#00f0ff59}.timeline-group-chip-top{justify-content:flex-start;align-items:center;gap:8px;width:100%;display:flex}.timeline-group-count{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;font-size:22px;line-height:1}.timeline-group-chevron{color:var(--text-muted);margin-left:auto;font-size:11px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);font-size:12px;font-weight:700;line-height:1.35}.timeline-group-range{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-compact{min-width:132px;max-width:156px;padding:10px 12px}.timeline-step-compact .timeline-step-type{font-size:11px}.timeline-step-compare{box-shadow:inset 0 0 0 1px var(--neon-magenta)}.timeline-track-item{align-items:center;gap:12px;display:flex}.timeline-track-item--group{align-items:flex-start}.timeline-expanded-head{justify-content:space-between}.timeline-collapse-btn{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;margin-left:auto;padding:6px 12px;font-size:12px}.timeline-collapse-btn:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan);background:#00f0ff0f}.diff-split-row--added .diff-split-new,.diff-split-row--changed .diff-split-new{background:#00ff880f}.diff-split-row--removed .diff-split-old,.diff-split-row--changed .diff-split-old{background:#ff33550f}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden} diff --git a/eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css b/eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css new file mode 100644 index 0000000..26852b3 --- /dev/null +++ b/eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css @@ -0,0 +1 @@ +:root{--bg-base:#050508;--bg-surface:#0a0c14;--bg-raised:#0f1220;--bg-elevated:#161b2e;--bg-panel:linear-gradient(145deg, #0c0f1a 0%, #0a0d18 50%, #080b14 100%);--border:#1a2040;--border-muted:#121830;--border-glow:#00f0ff26;--text-primary:#e8eef8;--text-secondary:#94a3c0;--text-muted:#5a6a8a;--neon-cyan:#00f0ff;--neon-cyan-dim:#00f0ff14;--neon-cyan-mid:#00f0ff40;--neon-magenta:#ff00e5;--neon-magenta-dim:#ff00e514;--neon-green:#0f8;--neon-green-dim:#00ff881a;--neon-amber:#fa0;--neon-amber-dim:#ffaa001a;--neon-red:#f35;--neon-red-dim:#ff33551a;--neon-purple:#a855f7;--accent-blue:#4f9cf9;--accent-blue-dim:#4f9cf926;--accent-green:var(--neon-green);--accent-red:var(--neon-red);--accent-yellow:var(--neon-amber);--accent-purple:var(--neon-purple);--font-sans:"Inter", system-ui, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", monospace;--font-display:"Orbitron", var(--font-sans);--radius:6px;--radius-lg:10px;--shadow:0 4px 24px #0009;--shadow-neon:0 0 20px #00f0ff14, 0 0 60px #00f0ff08;--transition:.2s ease;--bottom-panel-scroll-height:280px}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{font-size:15px}body{font-family:var(--font-sans);background:var(--bg-base);color:var(--text-primary);-webkit-font-smoothing:antialiased;min-height:100vh;line-height:1.65}body:after{content:"";pointer-events:none;z-index:9999;background:repeating-linear-gradient(0deg,#0000,#0000 2px,#00f0ff04 2px 4px);position:fixed;inset:0}.app{flex-direction:column;min-height:100vh;display:flex}.auth-shell{background:radial-gradient(circle at 0 0,#00f0ff14,#0000 28%),radial-gradient(circle at 100% 0,#ff00e514,#0000 26%),linear-gradient(#060811 0%,#050508 100%)}.auth-screen{justify-content:center;align-items:center;min-height:100vh;padding:32px 20px;display:flex}.auth-card{background:linear-gradient(160deg,#0e1222f5,#080b14fa),radial-gradient(circle at top,#00f0ff0f,#0000 56%);border:1px solid #00f0ff24;border-radius:18px;width:min(460px,100%);padding:28px;box-shadow:0 18px 60px #0000008c,0 0 40px #00f0ff14}.auth-eyebrow{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-cyan);margin-bottom:10px;font-size:11px}.auth-title{font-family:var(--font-display);letter-spacing:1px;margin-bottom:10px;font-size:26px;line-height:1.2}.auth-copy{color:var(--text-secondary);margin-bottom:20px}.auth-form{gap:14px;display:grid}.auth-field{gap:6px;display:grid}.auth-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:11px}.auth-input{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0a0e1aeb;border:1px solid #ffffff24;border-radius:10px;padding:12px 14px;font-size:13px}.auth-input:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.auth-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);justify-content:space-between;gap:10px;display:flex}.auth-route{overflow-wrap:anywhere}.auth-error{color:#ff9caf;background:#ff335514;border:1px solid #ff33553d;border-radius:10px;padding:10px 12px;font-size:12px}.auth-submit{color:var(--text-primary);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff24,#4f9cf914);border:1px solid #00f0ff3d;border-radius:12px;padding:12px 16px;font-size:12px}.auth-submit:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff24}.auth-submit--secondary{width:100%;margin-bottom:14px}.auth-submit:disabled{opacity:.6;cursor:not-allowed;box-shadow:none;transform:none}.app-header{--header-pad-x:24px;--header-pad-right:48px;--header-control-h:34px;padding:0 var(--header-pad-right) 0 var(--header-pad-x);border-bottom:1px solid var(--border);z-index:100;background:linear-gradient(#0d1020 0%,#080b14 100%);grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;column-gap:clamp(12px,2vw,20px);height:64px;display:grid;position:sticky;top:0;box-shadow:0 2px 20px #00000080,inset 0 -1px #00f0ff0f}.brand{justify-self:start;align-items:center;gap:12px;min-width:0;display:flex}.brand-logo{justify-content:center;align-items:center;width:36px;height:36px;display:flex;position:relative}.brand-logo svg{width:36px;height:36px;filter:drop-shadow(0 0 6px var(--neon-cyan)) drop-shadow(0 0 12px #00f0ff4d)}.brand-name{font-family:var(--font-display);letter-spacing:1.5px;color:var(--text-primary);text-transform:uppercase;font-size:15px;font-weight:700}.brand-sub{color:var(--text-muted);letter-spacing:.5px;font-size:10px}.header-title{font-family:var(--font-display);letter-spacing:3px;text-transform:uppercase;background:linear-gradient(135deg, var(--neon-cyan), #4facfe, var(--neon-magenta));-webkit-text-fill-color:transparent;filter:drop-shadow(0 0 8px #00f0ff66);text-align:center;-webkit-background-clip:text;background-clip:text;flex-shrink:0;margin:0;font-size:20px;font-weight:800;line-height:1}.header-center{flex-flow:row;justify-content:center;justify-self:center;align-items:center;gap:clamp(10px,1.5vw,16px);min-width:0;display:flex}.header-demo-pill{box-sizing:border-box;height:var(--header-control-h);background:var(--neon-amber-dim);color:var(--neon-amber);font-family:var(--font-mono);white-space:nowrap;border:1px solid #ffaa0059;border-radius:999px;flex-shrink:0;justify-content:center;align-items:center;padding:0 12px;font-size:11px;line-height:1;display:inline-flex}.header-actions{flex-direction:row;justify-content:flex-end;justify-self:end;align-items:center;gap:clamp(10px,1.2vw,16px);min-width:0;display:flex}.header-user{background:#090d18e0;border:1px solid #00f0ff29;border-radius:999px;align-items:center;gap:12px;padding:6px 10px 6px 12px;display:inline-flex}.header-user-meta{flex-direction:column;align-items:flex-end;gap:2px;display:flex}.header-user-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:9px}.header-user-name{color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;max-width:180px;font-size:12px;font-weight:600;overflow:hidden}.header-user-action{color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;border:1px solid #ffffff1f;border-radius:999px;padding:6px 10px;font-size:11px}.header-user-action:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid);background:#00f0ff0f}.header-user-action:disabled{opacity:.6;cursor:not-allowed}.header-actions .conn-stats,.header-actions .header-status{flex-shrink:0}.workspace-datasource{flex-direction:column;gap:6px;min-width:0;margin:0;display:flex}.workspace-datasource-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:10px;line-height:1}.workspace-datasource-select{box-sizing:border-box;appearance:none;width:100%;height:34px;color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition);background-color:#0c1020f2;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3c0' d='M3 4.5 6 8l3-3.5'/%3E%3C/svg%3E");background-position:right 10px center;background-repeat:no-repeat;border:1px solid #ffffff24;border-radius:8px;padding:0 32px 0 12px;font-size:12px;line-height:1;box-shadow:inset 0 1px #00f0ff0f}.workspace-datasource-select:hover{border-color:#00f0ff40}.workspace-datasource-select:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.header-status{font-size:12px;font-family:var(--font-sans);letter-spacing:.3px;align-items:center;gap:8px;display:flex}.header-status .status-text{color:var(--neon-green);text-shadow:0 0 8px #00ff8880}.header-status .status-text.offline{color:var(--neon-red);text-shadow:0 0 8px #ff335580}.dot{border-radius:50%;width:8px;height:8px}.dot-green{background:var(--neon-green);box-shadow:0 0 6px var(--neon-green), 0 0 12px #0f86;animation:2s infinite pulse-neon}.dot-red{background:var(--neon-red);box-shadow:0 0 6px var(--neon-red)}.dot-yellow{background:var(--neon-amber);box-shadow:0 0 6px var(--neon-amber);animation:1.5s infinite pulse-neon}@keyframes pulse-neon{0%,to{opacity:1}50%{opacity:.4}}.app-main{flex-direction:column;flex:1;gap:16px;width:100%;max-width:1440px;margin:0 auto;padding:20px;display:flex}.card{background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow), var(--shadow-neon);padding:20px;position:relative;overflow:hidden}.card.card--dropdown-host{z-index:30;overflow:visible}.card:before{content:"";background:linear-gradient(90deg, transparent, var(--neon-cyan-mid), transparent);height:1px;position:absolute;top:0;left:0;right:0}.card:after{content:"";background:linear-gradient(90deg,#0000,#ff00e51a,#0000);height:1px;position:absolute;bottom:0;left:0;right:0}.card-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;text-shadow:0 0 6px #00f0ff33;align-items:center;gap:8px;margin-bottom:16px;font-size:13px;font-weight:600;display:flex}.control-ribbon{padding:14px 18px}.control-ribbon-top{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:14px;display:flex}.control-ribbon-title{margin-bottom:4px}.control-ribbon-subtitle{color:var(--text-muted);font-size:12px}.control-ribbon-nav{align-items:center;gap:12px;font-size:13px;display:inline-flex}.control-ribbon-nav a{text-decoration:none}.control-ribbon-nav a[aria-current=page]{text-underline-offset:2px;text-decoration:underline}.control-panel{gap:12px;padding:16px 18px;display:grid}.control-panel-grid{grid-template-columns:minmax(220px,360px) minmax(0,1fr);align-items:end;gap:12px;display:grid}.control-field,.control-field--search{min-width:0}.control-field-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-size:11px;display:block}.control-select{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0d1123eb;border:1px solid #ffffff24;border-radius:10px;outline:none;padding:10px 12px;font-size:12px}.control-select:focus{border-color:var(--neon-cyan-mid);box-shadow:0 0 14px #00f0ff26}.datasource-pills{flex-wrap:wrap;gap:8px;display:flex}.datasource-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:4px 8px}.selection-summary{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);margin-top:2px}.selection-clear-btn{color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);background:0 0;border:none;margin-left:12px}.selection-clear-btn:hover{color:var(--neon-cyan)}.event-agg-button,.anomaly-aggregate-link{cursor:pointer;background:0 0;border:none;padding:0}.event-agg-button{color:var(--text-secondary);font:inherit}.event-agg-button:hover,.event-agg-button:focus-visible{color:var(--neon-cyan);text-shadow:0 0 8px #00f0ff47}.anomaly-aggregate-link .anomaly-meta-value{color:var(--neon-cyan)}.anomaly-aggregate-link:hover .anomaly-meta-value,.anomaly-aggregate-link:focus-visible .anomaly-meta-value{text-shadow:0 0 8px #00f0ff4d}.workspace-dock{z-index:110;border:1px solid var(--border-muted);pointer-events:auto;border-right:none;border-radius:10px 0 0 10px;flex-direction:row;align-items:center;width:auto;height:auto;max-height:min(72vh,100vh - 112px);transition:box-shadow .2s;display:flex;position:fixed;inset:50% 0 auto auto;overflow:hidden;transform:translateY(-50%);box-shadow:-6px 4px 22px #0000006b}.workspace-dock--open{box-shadow:-8px 6px 28px #0000007a,0 0 0 1px #00f0ff14}.workspace-dock-handle{border:none;border-left:1px solid var(--border-muted);width:36px;height:36px;color:var(--neon-cyan);font-family:var(--font-mono);cursor:pointer;transition:background var(--transition), color var(--transition);background:linear-gradient(#0f1324 0%,#0a0e18 100%);flex-direction:row;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:flex}.workspace-dock-handle:hover{color:var(--text-primary);background:linear-gradient(#141a30 0%,#0d1220 100%)}.workspace-dock-handle:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:-2px}.workspace-dock-chevron{font-size:15px;font-weight:700;line-height:1}.workspace-dock-panel{border-left:1px solid var(--border);background:linear-gradient(145deg,#0c101c 0%,#080c14 100%);flex-direction:column;flex:0 auto;gap:8px;width:min(252px,100vw - 48px);min-width:0;max-height:min(72vh,100vh - 112px);padding:10px 12px 12px 14px;display:flex;overflow:hidden auto}.workspace-dock-panel[hidden]{display:none!important}.workspace-dock-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:12px;font-weight:600}.workspace-dock-scrim{z-index:109;cursor:pointer;background:#03050c73;border:none;margin:0;padding:0;position:fixed;inset:64px 0 36px}.workspace-sidebar-kpis{border:1px solid var(--border-muted);border-radius:var(--radius);background:#080b148c;gap:6px;padding:8px 10px;display:grid}.workspace-kpi-row{color:var(--text-muted);font-size:11px;font-family:var(--font-mono);justify-content:space-between;gap:8px;display:flex}.workspace-kpi-row strong{color:var(--text-primary)}.workspace-sidebar-links{color:var(--text-secondary);gap:6px;font-size:12px;display:grid}.workspace-content{gap:16px;display:grid}.search-panel{width:100%;min-width:0}.selection-clear-btn:focus-visible,.control-ribbon-nav a:focus-visible,.control-select:focus-visible,.workspace-datasource-select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.plugin-dashboard{gap:14px;display:grid}.plugin-cards-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.plugin-cards-grid--dense{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}.plugin-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(140deg,#101527f2,#0a0e1cf2);padding:12px 14px;box-shadow:inset 0 1px #ffffff05}.plugin-card--interactive{transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition)}.plugin-card--interactive:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 16px #00f0ff1a}.plugin-card--interactive:focus-within{border-color:var(--neon-cyan-mid);box-shadow:0 0 0 2px #00f0ff26}.plugin-card-head{justify-content:space-between;align-items:center;gap:10px;display:flex}.plugin-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:2px 8px}.plugin-card-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);overflow-wrap:anywhere;margin-top:8px}.plugin-card-detail{color:var(--text-secondary);margin-top:8px;font-size:12px;line-height:1.55}.search-wrapper{position:relative}.search-input{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);outline:none;padding:14px 16px 14px 44px;font-size:13px}.search-input:focus{border-color:var(--neon-cyan);box-shadow:0 0 14px #00f0ff2e}.search-input::placeholder{color:var(--text-muted)}.search-icon{color:var(--neon-cyan);pointer-events:none;filter:drop-shadow(0 0 4px #00f0ff80);font-size:16px;position:absolute;top:50%;left:14px;transform:translateY(-50%)}.search-results{background:var(--bg-raised);border:1px solid var(--neon-cyan-mid);border-radius:var(--radius);z-index:500;max-height:min(55vh,420px);position:absolute;top:calc(100% + 6px);left:0;right:0;overflow:hidden auto;box-shadow:0 12px 40px #000000a6,0 0 24px #00f0ff14}.search-result-item{cursor:pointer;transition:background var(--transition);font-family:var(--font-mono);color:var(--text-primary);text-align:left;background:0 0;border:none;align-items:center;gap:10px;width:100%;padding:10px 16px;font-size:13px;display:flex}.search-result-item:hover{background:var(--bg-elevated);box-shadow:inset 3px 0 0 var(--neon-cyan)}.search-result-item+.search-result-item{border-top:1px solid var(--border-muted)}.search-result-chevron{color:var(--text-muted);flex-shrink:0;padding-right:4px}.search-result-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:0 6px;min-width:0;display:flex}.search-result-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0;font-size:10px}.search-result-colon{color:var(--text-muted);flex-shrink:0;margin-right:2px}.search-result-value{overflow-wrap:anywhere;word-break:break-word;flex:1;min-width:0}.conn-stats{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:16px;font-size:11px;display:flex}.conn-stat{flex-direction:column;align-items:flex-end;gap:1px;display:flex}.conn-stat-label{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);font-size:9px}.conn-stat-value{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66;font-size:12px}.conn-stat-value.green{color:var(--neon-green);text-shadow:0 0 6px #0f86}.conn-stat-value.amber{color:var(--neon-amber);text-shadow:0 0 6px #fa06}.conn-stat--metric .conn-stat-value,.conn-stat-value--uptime{text-align:right;font-variant-numeric:tabular-nums;min-width:6.5ch;display:inline-block}.mini-wave{align-items:flex-end;gap:1px;height:24px;display:flex}.mini-wave-bar{background:var(--neon-cyan);border-radius:1px;width:3px;animation:1.2s ease-in-out infinite wave-pulse;box-shadow:0 0 4px #00f0ff4d}@keyframes wave-pulse{0%,to{transform:scaleY(.3)}50%{transform:scaleY(1)}}.timeline-count-pill{color:var(--accent-blue);font-family:var(--font-mono);background:var(--accent-blue-dim);border:1px solid #4f9cf940;border-radius:999px;padding:2px 10px;font-size:11px}.timeline-hint{color:var(--text-muted);max-width:52rem;margin:-8px 0 12px;font-size:12px;line-height:1.5}.timeline-rail{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-base);position:relative;box-shadow:inset 0 1px #00f0ff0a}.timeline-stepper{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);padding:12px 14px 14px;overflow:auto hidden;-webkit-mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000);mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}.timeline-stepper-track{flex-wrap:nowrap;align-items:center;gap:4px;width:max-content;min-height:88px;display:flex}.timeline-step-arrow{color:var(--text-muted);opacity:.7;flex-shrink:0;align-self:center;padding:0 2px;font-size:11px}.timeline-step-arrow-compact{padding:0 1px;font-size:9px}.timeline-step{border-radius:var(--radius);border:1px solid var(--border-muted);background:var(--bg-raised);cursor:pointer;text-align:left;min-width:118px;max-width:200px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-step-compact{gap:2px;min-width:96px;max-width:140px;padding:8px 10px}.timeline-step-compact .timeline-step-badge{font-size:8px}.timeline-step-compact .timeline-step-type{-webkit-line-clamp:1;font-size:10px}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-step-badge{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:10px;font-weight:600}.timeline-step-seq{color:var(--text-muted);font-size:10px;font-family:var(--font-mono)}.timeline-step-type{color:var(--text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}.timeline-step-created{border-left:3px solid var(--neon-green)}.timeline-step-deleted{border-left:3px solid var(--neon-red)}.timeline-step-completed{border-left:3px solid var(--neon-green)}.timeline-step-failed{border-left:3px solid var(--neon-red)}.timeline-step-transfer{border-left:3px solid var(--neon-amber)}.timeline-step-item{border-left:3px solid var(--neon-purple)}.timeline-step-progress{border-left:3px solid #38bdf8}.timeline-step-default{border-left:3px solid var(--neon-cyan)}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);cursor:pointer;text-align:left;min-width:140px;max-width:240px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-group-chip.expanded{border-color:#00f0ff59}.timeline-group-count{font-family:var(--font-display);letter-spacing:.5px;color:var(--neon-cyan);font-size:16px;font-weight:800;line-height:1}.timeline-group-chevron{color:var(--text-muted);font-size:10px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.timeline-group-range{color:var(--text-muted);font-size:10px;font-family:var(--font-mono);line-height:1.4}.timeline-expanded-deck{border-top:1px solid var(--border-muted);background:#00000040;padding:10px 14px 14px}.timeline-expanded-head{flex-wrap:wrap;align-items:center;gap:10px 16px;margin-bottom:10px;display:flex}.timeline-expanded-title{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:12px;font-weight:700}.timeline-expanded-meta{font-family:var(--font-mono);color:var(--text-muted);flex:1;font-size:11px}.timeline-expanded-close{border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-raised);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition);margin-left:auto;padding:4px 12px;font-size:11px}.timeline-expanded-close:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan)}.timeline-expanded-strip{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex-wrap:nowrap;align-items:center;gap:4px;width:100%;padding-bottom:6px;display:flex;overflow-x:auto}.timeline-slider{width:100%;accent-color:var(--neon-cyan);cursor:pointer;margin-top:4px}.timeline-info{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);grid-template-columns:minmax(0,1fr) minmax(0,2.2fr) minmax(0,1fr);align-items:start;gap:12px;margin-top:12px;display:grid}.timeline-info-edge{color:var(--text-muted);font-size:11px}.timeline-info-center{text-align:center;color:var(--text-secondary);line-height:1.55}.timeline-info-center strong{color:var(--neon-cyan);font-weight:600}.timeline-info-muted{color:var(--text-muted);font-weight:400}.timeline-info-type{color:var(--text-primary);font-size:11px}.state-grid{grid-template-columns:1fr 1fr;gap:16px;display:grid}.state-panel h4{font-family:var(--font-display);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:8px;font-size:10px;font-weight:600}.state-panel-before h4{color:var(--text-muted)}.state-panel-after h4{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.json-block{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);font-family:var(--font-mono);max-height:260px;color:var(--text-secondary);white-space:pre;padding:12px;font-size:12px;line-height:1.7;overflow:auto}.json-tree-root{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);max-height:320px;font-family:var(--font-mono);color:var(--text-secondary);padding:10px 12px;font-size:13px;line-height:1.75;overflow:auto}.json-tree-line{flex-wrap:wrap;align-items:baseline;gap:2px 0;min-height:1.5em;display:flex}.json-tree-toggle{width:22px;height:22px;color:var(--text-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;margin-right:4px;padding:0;font-size:10px;line-height:1}.json-tree-toggle:hover{color:var(--neon-cyan);background:var(--neon-cyan-dim)}.json-key{color:#7dd3fc}.json-string{color:#86efac}.json-number{color:#fcd34d}.json-boolean{color:#c4b5fd}.json-null{color:var(--text-muted);font-style:italic}.json-punct{color:var(--text-muted)}.json-ellipsis{color:var(--text-muted);font-size:11px;font-style:italic}.json-unknown{color:var(--neon-amber)}.diff-panel{border-top:1px solid var(--border-muted);margin-top:20px;padding-top:16px}.diff-toolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.diff-toolbar-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:13px;font-weight:600}.diff-view-toggle{border-radius:var(--radius);border:1px solid var(--border);font-family:var(--font-mono);font-size:11px;display:flex;overflow:hidden}.diff-view-toggle button{background:var(--bg-raised);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:6px 14px}.diff-view-toggle button+button{border-left:1px solid var(--border)}.diff-view-toggle button:hover{color:var(--text-secondary)}.diff-view-toggle button.active{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.diff-body{align-items:stretch;gap:10px;display:flex}.diff-minimap{border:1px solid var(--border-muted);background:var(--bg-base);border-radius:4px;flex-direction:column;flex-shrink:0;width:10px;max-height:280px;display:flex;overflow:hidden}.diff-minimap-chunk{background:var(--border);cursor:pointer;min-height:8px;transition:background var(--transition);border:none;flex:1;margin:0;padding:0}.diff-minimap-chunk:hover,.diff-minimap-chunk:focus-visible{background:var(--neon-cyan-mid);outline:none}.diff-scroll{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex:1;min-width:0;max-height:280px;overflow:auto}.diff-list{flex-direction:column;gap:0;display:flex}.diff-row{font-family:var(--font-mono);background:var(--bg-raised);border-left:3px solid var(--neon-cyan);border-bottom:1px solid var(--border-muted);border-radius:0;align-items:stretch;gap:0;padding:0;font-size:12px;line-height:1.65;display:flex}.diff-row:last-child{border-radius:0 0 var(--radius) var(--radius);border-bottom:none}.diff-row:first-child{border-radius:var(--radius) var(--radius) 0 0}.diff-line-no{text-align:right;width:36px;color:var(--text-muted);background:var(--bg-base);border-right:1px solid var(--border-muted);-webkit-user-select:none;user-select:none;flex-shrink:0;padding:10px 6px;font-size:10px}.diff-row-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:8px 12px;padding:10px 12px;display:flex}.diff-field{color:var(--text-primary);min-width:6rem;font-weight:700}.diff-values-inline{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.diff-old{color:var(--neon-red);font-weight:400;text-decoration:line-through}.diff-arrow{color:var(--text-muted);flex-shrink:0}.diff-new{color:var(--neon-green);text-shadow:0 0 4px #00ff8840;font-weight:400}.diff-split-head{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);border-bottom:1px solid var(--border);background:var(--bg-raised);z-index:1;grid-template-columns:1fr 1fr;gap:8px;padding:8px 12px 8px 48px;font-size:11px;display:grid;position:sticky;top:0}.diff-split-old-label{color:var(--neon-red)}.diff-split-new-label{color:var(--neon-green)}.diff-split-row{border-bottom:1px solid var(--border-muted);align-items:stretch;gap:0;display:flex}.diff-split-row:last-child{border-bottom:none}.diff-split-cells{flex:1;grid-template-columns:1fr 1fr;gap:8px;min-width:0;padding:8px 12px 8px 0;display:grid}.diff-split-cell{border-radius:var(--radius);background:var(--bg-base);border:1px solid var(--border-muted);min-width:0;padding:8px 10px}.diff-split-cell .diff-field{margin-bottom:6px;font-size:11px;display:block}.diff-cell-value{overflow-wrap:anywhere;word-break:break-word;font-size:12px;font-weight:400;line-height:1.6}.diff-split-old .diff-cell-value{color:var(--neon-red);opacity:.9}.diff-split-new .diff-cell-value{color:var(--neon-green)}.event-meta{color:var(--text-muted);grid-template-columns:repeat(2,1fr);gap:10px 16px;margin-top:20px;font-size:12px;display:grid}.event-meta-bar{background:var(--bg-raised);border:1px solid var(--border-muted);border-radius:var(--radius);padding:14px 16px;box-shadow:inset 0 1px #00f0ff0a}.event-meta-time{font-family:var(--font-mono);color:var(--text-secondary);font-size:12px;font-weight:500}.event-meta-id{font-family:var(--font-mono);color:var(--text-primary);overflow-wrap:anywhere;word-break:break-word;grid-column:1/-1;font-size:11px;line-height:1.5}.event-meta-extra{font-family:var(--font-mono);color:var(--text-secondary);font-size:11px}@media (width<=900px){.diff-split-cells{grid-template-columns:1fr}.diff-split-head{display:none}}.copy-btn{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-secondary);cursor:pointer;transition:all var(--transition);font-size:12px;font-family:var(--font-mono);margin-top:12px;padding:6px 14px}.copy-btn:hover{background:var(--bg-raised);color:var(--neon-cyan);border-color:var(--neon-cyan);box-shadow:0 0 10px #00f0ff1a}.live-header{justify-content:space-between;align-items:center;margin-bottom:12px;display:flex}.live-indicator{font-family:var(--font-sans);letter-spacing:.2px;align-items:center;gap:6px;font-size:11px;display:flex}.pause-btn{background:var(--bg-elevated);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);border-radius:4px;padding:5px 12px;font-size:11px}.pause-btn:hover{background:var(--bg-raised);border-color:var(--neon-cyan);color:var(--neon-cyan);box-shadow:0 0 8px #00f0ff1a}.event-stream{height:var(--bottom-panel-scroll-height);min-height:0;font-family:var(--font-mono);flex-direction:column;gap:3px;padding-right:4px;font-size:12px;display:flex;overflow-y:auto}.event-stream,.anomaly-scroll-region,.search-results{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base)}.event-stream::-webkit-scrollbar{width:6px}.anomaly-scroll-region::-webkit-scrollbar{width:6px}.search-results::-webkit-scrollbar{width:6px}.event-stream::-webkit-scrollbar-track{background:var(--bg-base)}.anomaly-scroll-region::-webkit-scrollbar-track{background:var(--bg-base)}.search-results::-webkit-scrollbar-track{background:var(--bg-base)}.event-stream::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.anomaly-scroll-region::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.search-results::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.event-stream::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.anomaly-scroll-region::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.search-results::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.event-row{transition:all var(--transition);cursor:default;background:#ffffff03;border-left:3px solid #0000;border-radius:4px;align-items:center;gap:12px;padding:6px 10px;display:flex}.event-row:hover{background:var(--bg-elevated)}.event-row.type-withdrawn{border-left-color:var(--neon-magenta)}.event-row.type-deposited{border-left-color:var(--neon-cyan)}.event-row.type-created{border-left-color:var(--neon-green)}.event-row.type-transfer{border-left-color:var(--neon-amber)}.event-row.type-deleted{border-left-color:var(--neon-red)}.event-row.type-completed{border-left-color:var(--neon-green)}.event-row.type-failed{border-left-color:var(--neon-red)}.event-row.type-default{border-left-color:var(--neon-purple)}.event-time{color:var(--text-muted);flex-shrink:0;width:75px;font-size:11px}.event-type{flex-shrink:0;width:180px;font-weight:500}.event-agg{color:var(--text-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.event-icon{text-align:center;flex-shrink:0;width:20px;font-size:14px}.type-created{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-deleted{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-completed{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-failed{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-transfer{color:var(--neon-amber);text-shadow:0 0 6px #ffaa004d}.type-default{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.type-withdrawn{color:var(--neon-magenta);text-shadow:0 0 6px #ff00e54d}.type-deposited{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.anomaly-panel-inner{position:relative}.anomaly-shield{background:radial-gradient(#00ff880f 0%,#0000 70%);border:1px solid #00ff8826;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;margin-bottom:20px;padding:30px 20px;display:flex}.shield-icon{justify-content:center;align-items:center;width:64px;height:64px;margin-bottom:12px;display:flex;position:relative}.shield-icon svg{filter:drop-shadow(0 0 12px #00ff8880)drop-shadow(0 0 24px #0f83);width:64px;height:64px}.shield-icon:after{content:"";border:1px solid #00ff8826;border-radius:50%;animation:3s ease-in-out infinite shield-pulse;position:absolute;inset:-8px}@keyframes shield-pulse{0%,to{opacity:.3;transform:scale(1)}50%{opacity:.8;transform:scale(1.15)}}.shield-text{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-green);text-shadow:0 0 8px #0f86;font-size:13px;font-weight:600}.gauge-row{grid-template-columns:1fr 1fr 1fr;gap:12px;display:grid}.gauge-card{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);text-align:center;padding:12px;position:relative;overflow:hidden}.gauge-card:before{content:"";height:2px;position:absolute;bottom:0;left:0;right:0}.gauge-card.optimal:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-card.baseline:before{background:var(--neon-cyan);box-shadow:0 0 8px #00f0ff66}.gauge-card.zero:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);margin-bottom:6px;font-size:10px;font-weight:500}.gauge-value{font-family:var(--font-display);letter-spacing:1px;font-size:13px;font-weight:700}.gauge-value.optimal{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-value.baseline{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66}.gauge-value.zero{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-wave{justify-content:center;align-items:flex-end;gap:2px;height:20px;margin-top:6px;display:flex}.gauge-wave-bar{border-radius:1px;width:2px;animation:1.5s ease-in-out infinite gauge-wave-anim}.gauge-wave-bar.green{background:var(--neon-green);box-shadow:0 0 3px #0f86}.gauge-wave-bar.cyan{background:var(--neon-cyan);box-shadow:0 0 3px #00f0ff66}@keyframes gauge-wave-anim{0%,to{height:4px}50%{height:16px}}.anomaly-scroll-region{box-sizing:border-box;height:var(--bottom-panel-scroll-height);max-height:var(--bottom-panel-scroll-height);z-index:1;min-height:0;padding-right:4px;position:relative;overflow:hidden auto}.anomaly-list-inner{flex-direction:column;gap:10px;display:flex}.anomaly-card-title-row{flex-wrap:wrap;width:100%}.anomaly-title-text{flex:1}.anomaly-header-count{min-width:28px;height:28px;font-family:var(--font-display);color:var(--bg-base);background:var(--neon-amber);border-radius:999px;justify-content:center;align-items:center;margin-left:auto;padding:0 10px;font-size:11px;font-weight:700;display:inline-flex;box-shadow:0 0 12px #ffaa0059}.anomaly-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-raised);transition:border-color var(--transition), box-shadow var(--transition);overflow:hidden}.anomaly-card.CRITICAL{border-left:4px solid #f44}.anomaly-card.HIGH{border-left:4px solid var(--neon-red)}.anomaly-card.MEDIUM{border-left:4px solid var(--neon-amber)}.anomaly-card.LOW{border-left:4px solid var(--neon-cyan)}.anomaly-card-summary{cursor:pointer;font-family:var(--font-sans);flex-wrap:wrap;align-items:center;gap:10px 12px;padding:14px 16px;list-style:none;display:flex}.anomaly-card-summary::-webkit-details-marker{display:none}.anomaly-severity-badge{font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:4px 10px;font-size:9px;font-weight:700}.anomaly-severity-badge.sev-critical{color:#f66;background:#f443;border:1px solid #ff444459}.anomaly-severity-badge.sev-high{background:var(--neon-red-dim);color:var(--neon-red);border:1px solid #ff335559}.anomaly-severity-badge.sev-medium{background:var(--neon-amber-dim);color:var(--neon-amber);border:1px solid #ffaa0059}.anomaly-severity-badge.sev-low{background:var(--neon-cyan-dim);color:var(--neon-cyan);border:1px solid #00f0ff40}.anomaly-card-title{min-width:0;color:var(--text-primary);flex:1;font-size:15px;font-weight:600;line-height:1.45}.anomaly-card-chevron{color:var(--text-muted);transition:transform var(--transition);flex-shrink:0;font-size:10px}.anomaly-card[open] .anomaly-card-chevron{transform:rotate(-180deg)}.anomaly-card-body{border-top:1px solid var(--border-muted);background:#0003;padding:0 16px 16px}.anomaly-card-meta{flex-wrap:wrap;align-items:baseline;gap:8px 12px;margin-top:12px;font-size:13px;line-height:1.5;display:flex}.anomaly-card-meta:first-child{margin-top:12px}.anomaly-meta-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);min-width:72px;font-size:10px}.anomaly-meta-value{font-family:var(--font-mono);color:var(--text-secondary);overflow-wrap:anywhere}.no-anomalies{color:var(--neon-green);align-items:center;gap:8px;padding:20px 0;font-size:13px;display:flex}.bottom-grid{grid-template-columns:1fr 1fr;align-items:start;gap:16px;display:grid}.bottom-grid>.card{flex-direction:column;align-items:stretch;min-width:0;min-height:0;display:flex}.bottom-grid>.card>.card-title,.bottom-grid>.card>.live-header,.bottom-grid .anomaly-scroll-region,.bottom-grid .event-stream{flex-shrink:0}.skeleton{background:linear-gradient(90deg, var(--bg-raised) 25%, var(--bg-elevated) 50%, var(--bg-raised) 75%);border-radius:var(--radius);background-size:200% 100%;animation:1.4s infinite shimmer}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.toast-container{z-index:999;flex-direction:column;gap:8px;display:flex;position:fixed;bottom:24px;right:24px}.toast{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);box-shadow:var(--shadow);border-left:3px solid var(--neon-amber);padding:10px 16px;font-size:13px;animation:.2s slideIn}.toast.error{border-left-color:var(--neon-red)}.toast.success{border-left-color:var(--neon-green)}@keyframes slideIn{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:var(--bg-base)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.demo-banner{font-size:13px;font-family:var(--font-mono);color:var(--neon-amber);background:var(--neon-amber-dim);border-radius:var(--radius);border:1px solid #ffaa0059;margin:0 0 16px;padding:10px 16px}.demo-banner code{color:var(--neon-cyan)}@media (width<=900px){.state-grid,.bottom-grid{grid-template-columns:1fr}.header-center .header-demo-pill{display:none}.workspace-dock-panel{width:min(240px,88vw)}.gauge-row,.control-panel-grid{grid-template-columns:1fr}.timeline-info{text-align:center;grid-template-columns:1fr}.timeline-info-edge{display:none}}.state-tabs{border-bottom:1px solid var(--border);gap:2px;margin-bottom:16px;padding-bottom:0;display:flex}.state-tab{color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:color var(--transition), border-color var(--transition);border-radius:var(--radius) var(--radius) 0 0;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-1px;padding:8px 16px;font-size:13px;font-weight:500;display:flex}.state-tab:hover{color:var(--text-secondary)}.state-tab.active{color:var(--neon-cyan);border-bottom-color:var(--neon-cyan);background:var(--neon-cyan-dim);text-shadow:0 0 8px #00f0ff33}.state-tab-emoji{font-size:14px}.state-tab-content{min-height:80px}.summary-tab{padding-top:4px}.summary-changes{flex-direction:column;gap:8px;display:flex}.summary-changes-header{color:var(--text-muted);border-bottom:1px solid var(--border-muted);margin-bottom:4px;padding-bottom:6px;font-size:12px;font-weight:600}.summary-change-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-amber);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.summary-change-row:hover{background:var(--bg-elevated)}.summary-change-field{color:var(--text-primary);min-width:5rem;font-weight:700;font-family:var(--font-sans)}.summary-change-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.summary-change-arrow{color:var(--text-muted);flex-shrink:0}.summary-change-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.summary-no-changes{color:var(--neon-green);align-items:center;gap:12px;padding:20px 0;font-size:14px;display:flex}.event-summary-bar{background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);border:1px solid var(--border);border-left:3px solid var(--neon-cyan);border-radius:var(--radius);justify-content:space-between;align-items:center;gap:12px;margin-bottom:4px;padding:10px 20px;display:flex;box-shadow:0 0 16px #00f0ff0d}.event-summary-left{flex-wrap:wrap;align-items:baseline;gap:6px 12px;display:flex}.event-summary-type{font-family:var(--font-sans);color:var(--text-primary);font-size:14px;font-weight:600}.event-summary-meta{font-family:var(--font-mono);color:var(--text-muted);font-size:12px}.event-summary-changes{color:var(--neon-amber);font-size:12px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;flex-shrink:0;padding:3px 10px;font-weight:600}.diff-count-badge{color:var(--neon-amber);font-size:11px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;align-items:center;margin-left:6px;padding:2px 8px;font-weight:500;display:inline-flex}.diff-summary-view{flex-direction:column;gap:6px;display:flex}.diff-summary-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-cyan);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.diff-summary-row:hover{background:var(--bg-elevated)}.diff-summary-field{color:var(--text-primary);min-width:6rem;font-weight:700;font-family:var(--font-sans)}.diff-summary-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.diff-summary-arrow{color:var(--text-muted);flex-shrink:0}.diff-summary-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.diff-jump-next{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;margin-left:auto;padding:2px 8px;font-size:10px}.diff-jump-next:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.json-tree-changed{border-radius:3px;background:#ffaa001a!important}.timeline-header-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.timeline-jump-group{border:1px solid var(--border);border-radius:var(--radius);align-items:center;gap:0;display:flex;overflow:hidden}.timeline-jump-input{background:var(--bg-raised);border:none;border-right:1px solid var(--border);width:100px;color:var(--text-primary);font-family:var(--font-mono);outline:none;padding:5px 8px;font-size:12px}.timeline-jump-input:focus{border-right-color:var(--neon-cyan);background:var(--bg-elevated)}.timeline-jump-input::placeholder{color:var(--text-muted)}.timeline-jump-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input[type=number]{-moz-appearance:textfield}.timeline-jump-btn{background:var(--bg-elevated);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:5px 10px;font-size:14px}.timeline-jump-btn:hover{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.timeline-filter-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.filter-chip{border:1px solid var(--border);background:var(--bg-raised);color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);white-space:nowrap;text-overflow:ellipsis;border-radius:999px;max-width:160px;padding:3px 10px;font-size:11px;overflow:hidden}.filter-chip:hover{border-color:var(--neon-cyan-mid);color:var(--text-secondary)}.filter-chip.active{background:var(--neon-cyan-dim);color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff33;border-color:#00f0ff66}.timeline-anomaly-marker{color:var(--neon-amber);text-shadow:0 0 4px #fa09;align-self:flex-start;font-size:7px;line-height:1}.keyboard-hints{z-index:200;border-top:1px solid var(--border);-webkit-backdrop-filter:blur(12px);background:#080b14eb;position:fixed;bottom:0;left:0;right:0}.keyboard-hints-bar{font-family:var(--font-mono);color:var(--text-muted);flex-wrap:wrap;justify-content:center;align-items:center;gap:8px;padding:6px 24px;font-size:11px;display:flex}.keyboard-hints-item{align-items:center;gap:5px;display:flex}.keyboard-hints-sep{color:var(--border);font-size:14px;line-height:1}.keyboard-hints-grid{flex-wrap:wrap;justify-content:center;gap:10px 24px;padding:14px 24px;display:flex}.keyboard-hint-row{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:10px;font-size:12px;display:flex}.keyboard-hint-desc{color:var(--text-muted)}.keyboard-key{border:1px solid var(--border);border-bottom:2px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:4px;align-items:center;padding:2px 8px;font-size:11px;display:inline-flex}.keyboard-key-mini{border:1px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:3px;align-items:center;padding:1px 5px;font-size:10px;display:inline-flex}.keyboard-hints-close{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;padding:4px 12px;font-size:11px}.keyboard-hints-close:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.app-main{padding-bottom:40px}.command-palette-backdrop{z-index:1000;background:#050508b3;justify-content:center;align-items:flex-start;padding-top:12vh;display:flex;position:fixed;inset:0}.command-palette{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius-lg);width:min(640px,100vw - 32px);box-shadow:var(--shadow);overflow:hidden}.command-palette-input{border:0;border-bottom:1px solid var(--border);width:100%;color:var(--text-primary);font:inherit;background:0 0;padding:16px}.command-palette-list{max-height:320px;list-style:none;overflow:auto}.command-palette-item{cursor:pointer;color:var(--text-primary);padding:12px 16px}.command-palette-item.active,.command-palette-item:hover{background:var(--neon-cyan-dim)}.command-palette-item.muted{color:var(--text-muted)}.statistics-toolbar,.stats-kpis,.stats-distribution{flex-wrap:wrap;gap:12px;display:flex}.statistics-panel-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px;display:flex}.statistics-back-btn{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff1a,#4f9cf912);border:1px solid #00f0ff33;padding:8px 14px;font-size:12px}.statistics-back-btn:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.statistics-back-btn:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.stats-kpis{margin:12px 0 16px}.stat-card{border:1px solid var(--border);border-radius:var(--radius);background:#ffffff05;flex-direction:column;gap:6px;min-width:140px;padding:14px;display:flex}.stat-card strong{font-size:24px}.stats-chart{gap:8px;margin-bottom:16px;display:grid}.stats-bar-row,.stats-list-row,.replay-toolbar{align-items:center;gap:10px;display:flex}.stats-bar-track{background:#ffffff0f;border-radius:999px;flex:1;height:10px;overflow:hidden}.stats-bar-fill{background:linear-gradient(90deg, var(--neon-cyan), var(--accent-blue));height:100%}.stats-distribution>div,.replay-state{flex:1;min-width:260px}.replay-debugger{border-color:#00f0ff29}.replay-toolbar{border:1px solid var(--border-muted);border-radius:var(--radius);background:linear-gradient(145deg,#0c0f1cf0,#080a12f5);justify-content:space-between;margin-bottom:12px;padding:12px 14px}.replay-toolbar button{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border:1px solid #00f0ff38;padding:8px 14px;font-size:12px}.replay-toolbar button:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.replay-toolbar button:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-toolbar button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;transform:none}.replay-controls-center{flex-direction:column;align-items:center;gap:10px;display:flex}.replay-auto-controls{flex-wrap:wrap;justify-content:center;align-items:center;gap:10px;display:flex}.replay-auto-toggle{min-width:160px}.replay-auto-toggle.active{background:linear-gradient(145deg,#00ff8826,#00cc6614);border-color:#00ff884d;box-shadow:0 0 14px #00ff8829}.replay-interval-picker{color:var(--text-secondary);font-family:var(--font-mono);align-items:center;gap:8px;font-size:12px;display:flex}.replay-interval-picker select{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);background:#080c14f2;border:1px solid #00f0ff33;padding:7px 10px;font-size:12px}.replay-interval-picker select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-interval-picker select:disabled{opacity:.6;cursor:not-allowed}.replay-compare-warning{border-radius:var(--radius);color:#ffe7b2;background:linear-gradient(145deg,#2a1d08db,#1c1204f0);border:1px solid #ffd1663d;justify-content:space-between;align-items:center;gap:14px;margin-bottom:12px;padding:12px 14px;display:flex}.replay-warning-hint{color:#ffe7b2c7;font-family:var(--font-mono);font-size:11px}.replay-compare-actions{flex-shrink:0;align-items:center;gap:10px;display:flex}.replay-warning-btn{border-radius:var(--radius);color:#ffe7b2;font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), background var(--transition), transform var(--transition);background:#ffd1660f;border:1px solid #ffd16638;padding:8px 12px;font-size:12px}.replay-warning-btn:hover{background:#ffd1661f;border-color:#ffd16675;transform:translateY(-1px)}.replay-warning-btn--primary{color:var(--text-primary);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border-color:#00f0ff47}.replay-warning-btn--primary:hover{border-color:var(--neon-cyan-mid);background:linear-gradient(145deg,#00f0ff2e,#4f9cf91a)}.replay-position{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;font-size:13px}.timeline-virtual-container{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(#080a12f5,#06080efa),radial-gradient(circle at top,#00f0ff0d,#0000 55%);margin-top:12px;box-shadow:inset 0 1px #00f0ff0d}.timeline-step{border:1px solid var(--border-muted);border-radius:var(--radius);min-width:136px;max-width:168px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#101422f5,#0a0c16fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;margin:0;padding:12px 14px;display:flex}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-step.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-step-badge{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:.8px;text-transform:uppercase;font-size:11px}.timeline-step-seq{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-type{color:var(--text-primary);letter-spacing:.2px;font-size:12px;font-weight:600}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);min-width:190px;max-width:220px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#141828f7,#0b0e18fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:6px;margin:0;padding:12px 16px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-group-chip.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-group-chip.expanded{background:linear-gradient(145deg,#181e30f7,#0c101cfc);border-color:#00f0ff59}.timeline-group-chip-top{justify-content:flex-start;align-items:center;gap:8px;width:100%;display:flex}.timeline-group-count{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;font-size:22px;line-height:1}.timeline-group-chevron{color:var(--text-muted);margin-left:auto;font-size:11px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);font-size:12px;font-weight:700;line-height:1.35}.timeline-group-range{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-compact{min-width:132px;max-width:156px;padding:10px 12px}.timeline-step-compact .timeline-step-type{font-size:11px}.timeline-step-compare{box-shadow:inset 0 0 0 1px var(--neon-magenta)}.timeline-track-item{align-items:center;gap:12px;display:flex}.timeline-track-item--group{align-items:flex-start}.timeline-expanded-head{justify-content:space-between}.timeline-collapse-btn{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;margin-left:auto;padding:6px 12px;font-size:12px}.timeline-collapse-btn:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan);background:#00f0ff0f}.diff-split-row--added .diff-split-new,.diff-split-row--changed .diff-split-new{background:#00ff880f}.diff-split-row--removed .diff-split-old,.diff-split-row--changed .diff-split-old{background:#ff33550f}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden} diff --git a/eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js b/eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js new file mode 100644 index 0000000..5dd2a5b --- /dev/null +++ b/eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js @@ -0,0 +1,14 @@ +var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},ee=Object.prototype.hasOwnProperty;function te(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ne(e,t){return te(e.type,t,e.props)}function T(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function re(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var ie=/\/+/g;function E(e,t){return typeof e==`object`&&e&&e.key!=null?re(``+e.key):t.toString(36)}function ae(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function oe(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,oe(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+E(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(ie,`$&/`)+`/`),oe(o,r,i,``,function(e){return e})):o!=null&&(T(o)&&(o=ne(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(ie,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,T());else{var t=n(l);t!==null&&E(x,t.startTime-e)}}var S=!1,C=-1,w=5,ee=-1;function te(){return g?!0:!(e.unstable_now()-eet&&te());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&E(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?T():S=!1}}}var T;if(typeof y==`function`)T=function(){y(ne)};else if(typeof MessageChannel<`u`){var re=new MessageChannel,ie=re.port2;re.port1.onmessage=ne,T=function(){ie.postMessage(null)}}else T=function(){_(ne,0)};function E(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,E(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,T()))),r},e.unstable_shouldYield=te,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=m()})),g=o((e=>{var t=p(),n=d(),r=h();function i(e){var t=`https://react.dev/errors/`+e;if(1de||(e.current=ue[de],ue[de]=null,de--)}function A(e,t){de++,ue[de]=e.current,e.current=t}var pe=fe(null),me=fe(null),he=fe(null),ge=fe(null);function _e(e,t){switch(A(he,t),A(me,e),A(pe,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Vd(t),e=Hd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}k(pe),A(pe,e)}function ve(){k(pe),k(me),k(he)}function ye(e){e.memoizedState!==null&&A(ge,e);var t=pe.current,n=Hd(t,e.type);t!==n&&(A(me,e),A(pe,n))}function be(e){me.current===e&&(k(pe),k(me)),ge.current===e&&(k(ge),Qf._currentValue=le)}var xe,Se;function Ce(e){if(xe===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);xe=t&&t[1]||``,Se=-1)`:-1i||c[r]!==l[i]){var u=` +`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{we=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?Ce(n):``}function Ee(e,t){switch(e.tag){case 26:case 27:case 5:return Ce(e.type);case 16:return Ce(`Lazy`);case 13:return e.child!==t&&t!==null?Ce(`Suspense Fallback`):Ce(`Suspense`);case 19:return Ce(`SuspenseList`);case 0:case 15:return Te(e.type,!1);case 11:return Te(e.type.render,!1);case 1:return Te(e.type,!0);case 31:return Ce(`Activity`);default:return``}}function De(e){try{var t=``,n=null;do t+=Ee(e,n),n=e,e=e.return;while(e);return t}catch(e){return` +Error generating stack: `+e.message+` +`+e.stack}}var Oe=Object.prototype.hasOwnProperty,ke=t.unstable_scheduleCallback,Ae=t.unstable_cancelCallback,je=t.unstable_shouldYield,Me=t.unstable_requestPaint,Ne=t.unstable_now,Pe=t.unstable_getCurrentPriorityLevel,Fe=t.unstable_ImmediatePriority,Ie=t.unstable_UserBlockingPriority,Le=t.unstable_NormalPriority,Re=t.unstable_LowPriority,ze=t.unstable_IdlePriority,Be=t.log,Ve=t.unstable_setDisableYieldValue,He=null,Ue=null;function We(e){if(typeof Be==`function`&&Ve(e),Ue&&typeof Ue.setStrictMode==`function`)try{Ue.setStrictMode(He,e)}catch{}}var Ge=Math.clz32?Math.clz32:Je,Ke=Math.log,qe=Math.LN2;function Je(e){return e>>>=0,e===0?32:31-(Ke(e)/qe|0)|0}var Ye=256,Xe=262144,j=4194304;function M(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ze(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=M(n))):i=M(o):i=M(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=M(n))):i=M(o)):i=M(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function Qe(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function $e(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function et(){var e=j;return j<<=1,!(j&62914560)&&(j=4194304),e}function tt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function nt(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function rt(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),hn=!1;if(mn)try{var gn={};Object.defineProperty(gn,`passive`,{get:function(){hn=!0}}),window.addEventListener(`test`,gn,gn),window.removeEventListener(`test`,gn,gn)}catch{hn=!1}var _n=null,vn=null,yn=null;function bn(){if(yn)return yn;var e,t=vn,n=t.length,r,i=`value`in _n?_n.value:_n.textContent,a=i.length;for(e=0;e=Xn),$n=` `,er=!1;function tr(e,t){switch(e){case`keyup`:return Jn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function nr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var rr=!1;function ir(e,t){switch(e){case`compositionend`:return nr(t);case`keypress`:return t.which===32?(er=!0,$n):null;case`textInput`:return e=t.data,e===$n&&er?null:e;default:return null}}function ar(e,t){if(rr)return e===`compositionend`||!Yn&&tr(e,t)?(e=bn(),yn=vn=_n=null,rr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Dr(n)}}function kr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?kr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Ar(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Vt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Vt(e.document)}return t}function jr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Mr=mn&&`documentMode`in document&&11>=document.documentMode,Nr=null,Pr=null,Fr=null,Ir=!1;function Lr(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ir||Nr==null||Nr!==Vt(r)||(r=Nr,`selectionStart`in r&&jr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Fr&&Er(Fr,r)||(Fr=r,r=Ed(Pr,`onSelect`),0>=o,i-=o,z=1<<32-Ge(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),V&&Di(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),V&&Di(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return V&&Di(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),V&&Di(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===T&&Ea(l)===r.type){n(e,r.sibling),c=a(r,o.props),Na(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=mi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=pi(o.type,o.key,o.props,null,e.mode,c),Na(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=_i(o,e.mode,c),c.return=e,e=c}return s(e);case T:return o=Ea(o),b(e,r,o,c)}if(ce(o))return h(e,r,o,c);if(ae(o)){if(l=ae(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Ma(o),c);if(o.$$typeof===C)return b(e,r,ea(e,o),c);Pa(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=hi(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{ja=0;var i=b(e,t,n,r);return Aa=null,i}catch(t){if(t===ba||t===Sa)throw t;var a=li(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Ia=Fa(!0),La=Fa(!1),Ra=!1;function za(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ba(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Va(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Ha(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,K&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=oi(e),L(e,null,n),t}return ri(e,r,t,n),oi(e)}function Ua(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,at(e,n)}}function Wa(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Ga=!1;function Ka(){if(Ga){var e=da;if(e!==null)throw e}}function qa(e,t,n,r){Ga=!1;var i=e.updateQueue;Ra=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(Y&f)===f:(r&f)===f){f!==0&&f===ua&&(Ga=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=m({},d,f);break a;case 2:Ra=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Gl|=o,e.lanes=o,e.memoizedState=d}}function Ja(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Ya(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=D.T,s={};D.T=s,Ps(e,!1,t,n);try{var c=i(),l=D.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ns(e,t,ma(c,r),pu(e)):Ns(e,t,r,pu(e))}catch(n){Ns(e,t,{then:function(){},status:`rejected`,reason:n},pu())}finally{O.p=a,o!==null&&s.types!==null&&(o.types=s.types),D.T=o}}function Cs(){}function ws(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Ts(e).queue;Ss(e,a,t,le,n===null?Cs:function(){return Es(e),n(r)})}function Ts(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:le,baseState:le,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Fo,lastRenderedState:le},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Fo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Es(e){var t=Ts(e);t.next===null&&(t=e.alternate.memoizedState),Ns(e,t.next.queue,{},pu())}function Ds(){return $i(Qf)}function Os(){return Ao().memoizedState}function ks(){return Ao().memoizedState}function As(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=pu();e=Va(n);var r=Ha(t,e,n);r!==null&&(hu(r,t,n),Ua(r,t,n)),t={cache:oa()},e.payload=t;return}t=t.return}}function js(e,t,n){var r=pu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Fs(e)?Is(t,n):(n=ii(e,t,n,r),n!==null&&(hu(n,e,r),Ls(n,t,r)))}function Ms(e,t,n){Ns(e,t,n,pu())}function Ns(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Fs(e))Is(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,Tr(s,o))return ri(e,t,i,0),q===null&&ni(),!1}catch{}if(n=ii(e,t,i,r),n!==null)return hu(n,e,r),Ls(n,t,r),!0}return!1}function Ps(e,t,n,r){if(r={lane:2,revertLane:dd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Fs(e)){if(t)throw Error(i(479))}else t=ii(e,n,r,2),t!==null&&hu(t,e,2)}function Fs(e){var t=e.alternate;return e===H||t!==null&&t===H}function Is(e,t){mo=po=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ls(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,at(e,n)}}var Rs={readContext:$i,use:No,useCallback:bo,useContext:bo,useEffect:bo,useImperativeHandle:bo,useLayoutEffect:bo,useInsertionEffect:bo,useMemo:bo,useReducer:bo,useRef:bo,useState:bo,useDebugValue:bo,useDeferredValue:bo,useTransition:bo,useSyncExternalStore:bo,useId:bo,useHostTransitionStatus:bo,useFormState:bo,useActionState:bo,useOptimistic:bo,useMemoCache:bo,useCacheRefresh:bo};Rs.useEffectEvent=bo;var zs={readContext:$i,use:No,useCallback:function(e,t){return ko().memoizedState=[e,t===void 0?null:t],e},useContext:$i,useEffect:ls,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),ss(4194308,4,hs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ss(4194308,4,e,t)},useInsertionEffect:function(e,t){ss(4,2,e,t)},useMemo:function(e,t){var n=ko();t=t===void 0?null:t;var r=e();if(ho){We(!0);try{e()}finally{We(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=ko();if(n!==void 0){var i=n(t);if(ho){We(!0);try{n(t)}finally{We(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=js.bind(null,H,e),[r.memoizedState,e]},useRef:function(e){var t=ko();return e={current:e},t.memoizedState=e},useState:function(e){e=Go(e);var t=e.queue,n=Ms.bind(null,H,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:_s,useDeferredValue:function(e,t){return bs(ko(),e,t)},useTransition:function(){var e=Go(!1);return e=Ss.bind(null,H,e.queue,!0,!1),ko().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=H,a=ko();if(V){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),q===null)throw Error(i(349));Y&127||Bo(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,ls(Ho.bind(null,r,o,e),[e]),r.flags|=2048,as(9,{destroy:void 0},Vo.bind(null,r,o,n,t),null),n},useId:function(){var e=ko(),t=q.identifierPrefix;if(V){var n=Ei,r=z;n=(r&~(1<<32-Ge(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=go++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[ft]=t,o[N]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Pd(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Nc(t)}}return W(t),Pc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Nc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=he.current,zi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Mi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[ft]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Md(e.nodeValue,n)),e||Ii(t,!0)}else e=Bd(e).createTextNode(r),e[ft]=t,t.stateNode=e}return W(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=zi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[ft]=t}else Bi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;W(t),e=!1}else n=Vi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(so(t),t):(so(t),null);if(t.flags&128)throw Error(i(558))}return W(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=zi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[ft]=t}else Bi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;W(t),a=!1}else a=Vi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(so(t),t):(so(t),null)}return so(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Ic(t,t.updateQueue),W(t),null);case 4:return ve(),e===null&&Sd(t.stateNode.containerInfo),W(t),null;case 10:return qi(t.type),W(t),null;case 19:if(k(co),r=t.memoizedState,r===null)return W(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Lc(r,!1);else{if(Wl!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=lo(e),o!==null){for(t.flags|=128,Lc(r,!1),e=o.updateQueue,t.updateQueue=e,Ic(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)fi(n,e),n=n.sibling;return A(co,co.current&1|2),V&&Di(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&Ne()>tu&&(t.flags|=128,a=!0,Lc(r,!1),t.lanes=4194304)}else{if(!a)if(e=lo(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Ic(t,e),Lc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!V)return W(t),null}else 2*Ne()-r.renderingStartTime>tu&&n!==536870912&&(t.flags|=128,a=!0,Lc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(W(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=Ne(),e.sibling=null,n=co.current,A(co,a?n&1|2:n&1),V&&Di(t,r.treeForkCount),e);case 22:case 23:return so(t),eo(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(W(t),t.subtreeFlags&6&&(t.flags|=8192)):W(t),n=t.updateQueue,n!==null&&Ic(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&k(ga),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),qi(aa),W(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function zc(e,t){switch(Ai(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return qi(aa),ve(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return be(t),null;case 31:if(t.memoizedState!==null){if(so(t),t.alternate===null)throw Error(i(340));Bi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(so(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Bi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return k(co),null;case 4:return ve(),null;case 10:return qi(t.type),null;case 22:case 23:return so(t),eo(),e!==null&&k(ga),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return qi(aa),null;case 25:return null;default:return null}}function Bc(e,t){switch(Ai(t),t.tag){case 3:qi(aa),ve();break;case 26:case 27:case 5:be(t);break;case 4:ve();break;case 31:t.memoizedState!==null&&so(t);break;case 13:so(t);break;case 19:k(co);break;case 10:qi(t.type);break;case 22:case 23:so(t),eo(),e!==null&&k(ga);break;case 24:qi(aa)}}function Vc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Z(t,t.return,e)}}function Hc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Z(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Z(t,t.return,e)}}function Uc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Ya(t,n)}catch(t){Z(e,e.return,t)}}}function Wc(e,t,n){n.props=Ks(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Z(e,t,n)}}function Gc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Z(e,t,n)}}function Kc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){Z(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Z(e,t,n)}else n.current=null}function qc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Z(e,e.return,t)}}function Jc(e,t,n){try{var r=e.stateNode;Fd(r,e.type,n,t),r[N]=t}catch(t){Z(e,e.return,t)}}function Yc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Zd(e.type)||e.tag===4}function Xc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Yc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Zd(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Zc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=an));else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Zc(e,t,n),e=e.sibling;e!==null;)Zc(e,t,n),e=e.sibling}function Qc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(Qc(e,t,n),e=e.sibling;e!==null;)Qc(e,t,n),e=e.sibling}function $c(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Pd(t,r,n),t[ft]=e,t[N]=n}catch(t){Z(e,e.return,t)}}var el=!1,tl=!1,nl=!1,rl=typeof WeakSet==`function`?WeakSet:Set,il=null;function al(e,t){if(e=e.containerInfo,Rd=sp,e=Ar(e),jr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(zd={focusedElem:e,selectionRange:n},sp=!1,il=t;il!==null;)if(t=il,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,il=e;else for(;il!==null;){switch(t=il,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Pd(o,r,n),o[ft]=e,wt(o),r=o;break a;case`link`:var s=Vf(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Or(s,h),v=Or(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,D.T=null,n=lu,lu=null;var o=au,s=su;if(iu=0,ou=au=null,su=0,K&6)throw Error(i(331));var c=K;if(K|=4,Pl(o.current),El(o,o.current,s,n),K=c,id(0,!1),Ue&&typeof Ue.onPostCommitFiberRoot==`function`)try{Ue.onPostCommitFiberRoot(He,o)}catch{}return!0}finally{O.p=a,D.T=r,Vu(e,t)}}function Wu(e,t,n){t=yi(n,t),t=Qs(e.stateNode,t,2),e=Ha(e,t,2),e!==null&&(nt(e,2),rd(e))}function Z(e,t,n){if(e.tag===3)Wu(e,e,n);else for(;t!==null;){if(t.tag===3){Wu(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(ru===null||!ru.has(r))){e=yi(n,e),n=$s(2),r=Ha(t,n,2),r!==null&&(ec(n,r,t,e),nt(r,2),rd(r));break}}t=t.return}}function Gu(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Rl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Hl=!0,i.add(n),e=Ku.bind(null,e,t,n),t.then(e,e))}function Ku(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,q===e&&(Y&n)===n&&(Wl===4||Wl===3&&(Y&62914560)===Y&&300>Ne()-$l?!(K&2)&&Su(e,0):ql|=n,Yl===Y&&(Yl=0)),rd(e)}function qu(e,t){t===0&&(t=et()),e=ai(e,t),e!==null&&(nt(e,t),rd(e))}function Ju(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),qu(e,n)}function Yu(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),qu(e,n)}function Xu(e,t){return ke(e,t)}var Zu=null,Qu=null,$u=!1,ed=!1,td=!1,nd=0;function rd(e){e!==Qu&&e.next===null&&(Qu===null?Zu=Qu=e:Qu=Qu.next=e),ed=!0,$u||($u=!0,ud())}function id(e,t){if(!td&&ed){td=!0;do for(var n=!1,r=Zu;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ge(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,ld(r,a))}else a=Y,a=Ze(r,r===q?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||Qe(r,a)||(n=!0,ld(r,a));r=r.next}while(n);td=!1}}function ad(){od()}function od(){ed=$u=!1;var e=0;nd!==0&&Gd()&&(e=nd);for(var t=Ne(),n=null,r=Zu;r!==null;){var i=r.next,a=sd(r,t);a===0?(r.next=null,n===null?Zu=i:n.next=i,i===null&&(Qu=n)):(n=r,(e!==0||a&3)&&(ed=!0)),r=i}iu!==0&&iu!==5||id(e,!1),nd!==0&&(nd=0)}function sd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Id(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function xf(e,t,n){var r=bf;if(r&&typeof t==`string`&&t){var i=Ut(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),hf.has(i)||(hf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Pd(t,`link`,e),wt(t),r.head.appendChild(t)))}}function Sf(e){_f.D(e),xf(`dns-prefetch`,e,null)}function Cf(e,t){_f.C(e,t),xf(`preconnect`,e,t)}function wf(e,t,n){_f.L(e,t,n);var r=bf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Ut(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Ut(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Ut(n.imageSizes)+`"]`)):i+=`[href="`+Ut(e)+`"]`;var a=i;switch(t){case`style`:a=Af(e);break;case`script`:a=Pf(e)}mf.has(a)||(e=m({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),mf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(jf(a))||t===`script`&&r.querySelector(Ff(a))||(t=r.createElement(`link`),Pd(t,`link`,e),wt(t),r.head.appendChild(t)))}}function Tf(e,t){_f.m(e,t);var n=bf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Ut(r)+`"][href="`+Ut(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Pf(e)}if(!mf.has(a)&&(e=m({rel:`modulepreload`,href:e},t),mf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Ff(a)))return}r=n.createElement(`link`),Pd(r,`link`,e),wt(r),n.head.appendChild(r)}}}function Ef(e,t,n){_f.S(e,t,n);var r=bf;if(r&&e){var i=Ct(r).hoistableStyles,a=Af(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(jf(a)))s.loading=5;else{e=m({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=mf.get(a))&&Rf(e,n);var c=o=r.createElement(`link`);wt(c),Pd(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Lf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Df(e,t){_f.X(e,t);var n=bf;if(n&&e){var r=Ct(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),wt(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Of(e,t){_f.M(e,t);var n=bf;if(n&&e){var r=Ct(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0,type:`module`},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),wt(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function kf(e,t,n,r){var a=(a=he.current)?gf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Af(n.href),n=Ct(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Af(n.href);var o=Ct(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(jf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),mf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},mf.set(e,n),o||Nf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Pf(n),n=Ct(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Af(e){return`href="`+Ut(e)+`"`}function jf(e){return`link[rel="stylesheet"][`+e+`]`}function Mf(e){return m({},e,{"data-precedence":e.precedence,precedence:null})}function Nf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Pd(t,`link`,n),wt(t),e.head.appendChild(t))}function Pf(e){return`[src="`+Ut(e)+`"]`}function Ff(e){return`script[async]`+e}function If(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Ut(n.href)+`"]`);if(r)return t.instance=r,wt(r),r;var a=m({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),wt(r),Pd(r,`style`,a),Lf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Af(n.href);var o=e.querySelector(jf(a));if(o)return t.state.loading|=4,t.instance=o,wt(o),o;r=Mf(n),(a=mf.get(a))&&Rf(r,a),o=(e.ownerDocument||e).createElement(`link`),wt(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Pd(o,`link`,r),t.state.loading|=4,Lf(o,n.precedence,e),t.instance=o;case`script`:return o=Pf(n.src),(a=e.querySelector(Ff(o)))?(t.instance=a,wt(a),a):(r=n,(a=mf.get(o))&&(r=m({},n),zf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),wt(a),Pd(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Lf(r,n.precedence,e));return t.instance}function Lf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Uf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Wf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Gf(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Af(r.href),a=t.querySelector(jf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Jf.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,wt(a);return}a=t.ownerDocument||t,r=Mf(r),(i=mf.get(i))&&Rf(r,i),a=a.createElement(`link`),wt(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Pd(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Jf.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var Kf=0;function qf(e,t){return e.stylesheets&&e.count===0&&Xf(e,e.stylesheets),0Kf?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Jf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Xf(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Yf=null;function Xf(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Yf=new Map,t.forEach(Zf,e),Yf=null,Jf.call(e))}function Zf(e,t){if(!(t.state.loading&4)){var n=Yf.get(e);if(n)var r=n.get(null);else{n=new Map,Yf.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=g()})),v=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},y=new class extends v{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e();return window.addEventListener(`visibilitychange`,t,!1),()=>{window.removeEventListener(`visibilitychange`,t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(e=>{typeof e==`boolean`?this.setFocused(e):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){let e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e==`boolean`?this.#e:globalThis.document?.visibilityState!==`hidden`}},b={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},x=new class{#e=b;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}};function S(e){setTimeout(e,0)}var C=typeof window>`u`||`Deno`in globalThis;function w(){}function ee(e,t){return typeof e==`function`?e(t):e}function te(e){return typeof e==`number`&&e>=0&&e!==1/0}function ne(e,t){return Math.max(e+(t||0)-Date.now(),0)}function T(e,t){return typeof e==`function`?e(t):e}function re(e,t){return typeof e==`function`?e(t):e}function ie(e,t){let{type:n=`all`,exact:r,fetchStatus:i,predicate:a,queryKey:o,stale:s}=e;if(o){if(r){if(t.queryHash!==ae(o,t.options))return!1}else if(!se(t.queryKey,o))return!1}if(n!==`all`){let e=t.isActive();if(n===`active`&&!e||n===`inactive`&&e)return!1}return!(typeof s==`boolean`&&t.isStale()!==s||i&&i!==t.state.fetchStatus||a&&!a(t))}function E(e,t){let{exact:n,status:r,predicate:i,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(oe(t.options.mutationKey)!==oe(a))return!1}else if(!se(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function ae(e,t){return(t?.queryKeyHashFn||oe)(e)}function oe(e){return JSON.stringify(e,(e,t)=>ue(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}function se(e,t){return e===t?!0:typeof e==typeof t&&e&&t&&typeof e==`object`&&typeof t==`object`?Object.keys(t).every(n=>se(e[n],t[n])):!1}var ce=Object.prototype.hasOwnProperty;function D(e,t,n=0){if(e===t)return e;if(n>500)return t;let r=le(e)&&le(t);if(!r&&!(ue(e)&&ue(t)))return t;let i=(r?e:Object.keys(e)).length,a=r?t:Object.keys(t),o=a.length,s=r?Array(o):{},c=0;for(let l=0;l{x.setTimeout(t,e)})}function k(e,t,n){return typeof n.structuralSharing==`function`?n.structuralSharing(e,t):n.structuralSharing===!1?t:D(e,t)}function A(e,t,n=0){let r=[...e,t];return n&&r.length>n?r.slice(1):r}function pe(e,t,n=0){let r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var me=Symbol();function he(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===me?()=>Promise.reject(Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function ge(e,t){return typeof e==`function`?e(...t):!!e}function _e(e,t,n){let r=!1,i;return Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(i??=t(),r?i:(r=!0,i.aborted?n():i.addEventListener(`abort`,n,{once:!0}),i))}),e}var ve=(()=>{let e=()=>C;return{isServer(){return e()},setIsServer(t){e=t}}})();function ye(){let e,t,n=new Promise((n,r)=>{e=n,t=r});n.status=`pending`,n.catch(()=>{});function r(e){Object.assign(n,e),delete n.resolve,delete n.reject}return n.resolve=t=>{r({status:`fulfilled`,value:t}),e(t)},n.reject=e=>{r({status:`rejected`,reason:e}),t(e)},n}var be=S;function xe(){let e=[],t=0,n=e=>{e()},r=e=>{e()},i=be,a=r=>{t?e.push(r):i(()=>{n(r)})},o=()=>{let t=e;e=[],t.length&&i(()=>{r(()=>{t.forEach(e=>{n(e)})})})};return{batch:e=>{let n;t++;try{n=e()}finally{t--,t||o()}return n},batchCalls:e=>(...t)=>{a(()=>{e(...t)})},schedule:a,setNotifyFunction:e=>{n=e},setBatchNotifyFunction:e=>{r=e},setScheduler:e=>{i=e}}}var Se=xe(),Ce=new class extends v{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e(!0),n=()=>e(!1);return window.addEventListener(`online`,t,!1),window.addEventListener(`offline`,n,!1),()=>{window.removeEventListener(`online`,t),window.removeEventListener(`offline`,n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(t=>{t(e)}))}isOnline(){return this.#e}};function we(e){return Math.min(1e3*2**e,3e4)}function Te(e){return(e??`online`)===`online`?Ce.isOnline():!0}var Ee=class extends Error{constructor(e){super(`CancelledError`),this.revert=e?.revert,this.silent=e?.silent}};function De(e){let t=!1,n=0,r,i=ye(),a=()=>i.status!==`pending`,o=t=>{if(!a()){let n=new Ee(t);f(n),e.onCancel?.(n)}},s=()=>{t=!0},c=()=>{t=!1},l=()=>y.isFocused()&&(e.networkMode===`always`||Ce.isOnline())&&e.canRun(),u=()=>Te(e.networkMode)&&e.canRun(),d=e=>{a()||(r?.(),i.resolve(e))},f=e=>{a()||(r?.(),i.reject(e))},p=()=>new Promise(t=>{r=e=>{(a()||l())&&t(e)},e.onPause?.()}).then(()=>{r=void 0,a()||e.onContinue?.()}),m=()=>{if(a())return;let r,i=n===0?e.initialPromise:void 0;try{r=i??e.fn()}catch(e){r=Promise.reject(e)}Promise.resolve(r).then(d).catch(r=>{if(a())return;let i=e.retry??(ve.isServer()?0:3),o=e.retryDelay??we,s=typeof o==`function`?o(n,r):o,c=i===!0||typeof i==`number`&&nl()?void 0:p()).then(()=>{t?f(r):m()})})};return{promise:i,status:()=>i.status,cancel:o,continue:()=>(r?.(),i),cancelRetry:s,continueRetry:c,canStart:u,start:()=>(u()?m():p().then(m),i)}}var Oe=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),te(this.gcTime)&&(this.#e=x.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(ve.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e&&=(x.clearTimeout(this.#e),void 0)}},ke=class extends Oe{#e;#t;#n;#r;#i;#a;#o;constructor(e){super(),this.#o=!1,this.#a=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#r=e.client,this.#n=this.#r.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#e=Me(this.options),this.state=e.state??this.#e,this.scheduleGc()}get meta(){return this.options.meta}get promise(){return this.#i?.promise}setOptions(e){if(this.options={...this.#a,...e},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){let e=Me(this.options);e.data!==void 0&&(this.setState(je(e.data,e.dataUpdatedAt)),this.#e=e)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus===`idle`&&this.#n.remove(this)}setData(e,t){let n=k(this.state.data,e,this.options);return this.#c({data:n,type:`success`,dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e,t){this.#c({type:`setState`,state:e,setStateOptions:t})}cancel(e){let t=this.#i?.promise;return this.#i?.cancel(e),t?t.then(w).catch(w):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#e}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>re(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===me||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>T(e.options.staleTime,this)===`static`):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e===`static`?!1:this.state.isInvalidated?!0:!ne(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(e=>e.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#i?.continue()}onOnline(){this.observers.find(e=>e.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#i?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#i&&(this.#o||this.#s()?this.#i.cancel({revert:!0}):this.#i.cancelRetry()),this.scheduleGc()),this.#n.notify({type:`observerRemoved`,query:this,observer:e}))}getObserversCount(){return this.observers.length}#s(){return this.state.fetchStatus===`paused`&&this.state.status===`pending`}invalidate(){this.state.isInvalidated||this.#c({type:`invalidate`})}async fetch(e,t){if(this.state.fetchStatus!==`idle`&&this.#i?.status()!==`rejected`){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#i)return this.#i.continueRetry(),this.#i.promise}if(e&&this.setOptions(e),!this.options.queryFn){let e=this.observers.find(e=>e.options.queryFn);e&&this.setOptions(e.options)}let n=new AbortController,r=e=>{Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(this.#o=!0,n.signal)})},i=()=>{let e=he(this.options,t),n=(()=>{let e={client:this.#r,queryKey:this.queryKey,meta:this.meta};return r(e),e})();return this.#o=!1,this.options.persister?this.options.persister(e,n,this):e(n)},a=(()=>{let e={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#r,state:this.state,fetchFn:i};return r(e),e})();this.options.behavior?.onFetch(a,this),this.#t=this.state,(this.state.fetchStatus===`idle`||this.state.fetchMeta!==a.fetchOptions?.meta)&&this.#c({type:`fetch`,meta:a.fetchOptions?.meta}),this.#i=De({initialPromise:t?.initialPromise,fn:a.fetchFn,onCancel:e=>{e instanceof Ee&&e.revert&&this.setState({...this.#t,fetchStatus:`idle`}),n.abort()},onFail:(e,t)=>{this.#c({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#c({type:`pause`})},onContinue:()=>{this.#c({type:`continue`})},retry:a.options.retry,retryDelay:a.options.retryDelay,networkMode:a.options.networkMode,canRun:()=>!0});try{let e=await this.#i.start();if(e===void 0)throw Error(`${this.queryHash} data is undefined`);return this.setData(e),this.#n.config.onSuccess?.(e,this),this.#n.config.onSettled?.(e,this.state.error,this),e}catch(e){if(e instanceof Ee){if(e.silent)return this.#i.promise;if(e.revert){if(this.state.data===void 0)throw e;return this.state.data}}throw this.#c({type:`error`,error:e}),this.#n.config.onError?.(e,this),this.#n.config.onSettled?.(this.state.data,e,this),e}finally{this.scheduleGc()}}#c(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case`pause`:return{...t,fetchStatus:`paused`};case`continue`:return{...t,fetchStatus:`fetching`};case`fetch`:return{...t,...Ae(t.data,this.options),fetchMeta:e.meta??null};case`success`:let n={...t,...je(e.data,e.dataUpdatedAt),dataUpdateCount:t.dataUpdateCount+1,...!e.manual&&{fetchStatus:`idle`,fetchFailureCount:0,fetchFailureReason:null}};return this.#t=e.manual?n:void 0,n;case`error`:let r=e.error;return{...t,error:r,errorUpdateCount:t.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:t.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:`idle`,status:`error`,isInvalidated:!0};case`invalidate`:return{...t,isInvalidated:!0};case`setState`:return{...t,...e.state}}})(this.state),Se.batch(()=>{this.observers.forEach(e=>{e.onQueryUpdate()}),this.#n.notify({query:this,type:`updated`,action:e})})}};function Ae(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:Te(t.networkMode)?`fetching`:`paused`,...e===void 0&&{error:null,status:`pending`}}}function je(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:`success`}}function Me(e){let t=typeof e.initialData==`function`?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt==`function`?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?`success`:`pending`,fetchStatus:`idle`}}var Ne=class extends v{constructor(e,t){super(),this.options=t,this.#e=e,this.#s=null,this.#o=ye(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#i;#a;#o;#s;#c;#l;#u;#d;#f;#p;#m=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),Fe(this.#t,this.options)?this.#h():this.updateResult(),this.#y())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return Ie(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return Ie(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#b(),this.#x(),this.#t.removeObserver(this)}setOptions(e){let t=this.options,n=this.#t;if(this.options=this.#e.defaultQueryOptions(e),this.options.enabled!==void 0&&typeof this.options.enabled!=`boolean`&&typeof this.options.enabled!=`function`&&typeof re(this.options.enabled,this.#t)!=`boolean`)throw Error(`Expected enabled to be a boolean or a callback that returns a boolean`);this.#S(),this.#t.setOptions(this.options),t._defaulted&&!O(this.options,t)&&this.#e.getQueryCache().notify({type:`observerOptionsUpdated`,query:this.#t,observer:this});let r=this.hasListeners();r&&Le(this.#t,n,this.options,t)&&this.#h(),this.updateResult(),r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||T(this.options.staleTime,this.#t)!==T(t.staleTime,this.#t))&&this.#g();let i=this.#_();r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||i!==this.#p)&&this.#v(i)}getOptimisticResult(e){let t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return ze(this,n)&&(this.#r=n,this.#a=this.options,this.#i=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(e,n)=>(this.trackProp(n),t?.(n),n===`promise`&&(this.trackProp(`data`),!this.options.experimental_prefetchInRender&&this.#o.status===`pending`&&this.#o.reject(Error(`experimental_prefetchInRender feature flag is not enabled`))),Reflect.get(e,n))})}trackProp(e){this.#m.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#h({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#h(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(w)),t}#g(){this.#b();let e=T(this.options.staleTime,this.#t);if(ve.isServer()||this.#r.isStale||!te(e))return;let t=ne(this.#r.dataUpdatedAt,e)+1;this.#d=x.setTimeout(()=>{this.#r.isStale||this.updateResult()},t)}#_(){return(typeof this.options.refetchInterval==`function`?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#v(e){this.#x(),this.#p=e,!(ve.isServer()||re(this.options.enabled,this.#t)===!1||!te(this.#p)||this.#p===0)&&(this.#f=x.setInterval(()=>{(this.options.refetchIntervalInBackground||y.isFocused())&&this.#h()},this.#p))}#y(){this.#g(),this.#v(this.#_())}#b(){this.#d&&=(x.clearTimeout(this.#d),void 0)}#x(){this.#f&&=(x.clearInterval(this.#f),void 0)}createResult(e,t){let n=this.#t,r=this.options,i=this.#r,a=this.#i,o=this.#a,s=e===n?this.#n:e.state,{state:c}=e,l={...c},u=!1,d;if(t._optimisticResults){let i=this.hasListeners(),a=!i&&Fe(e,t),o=i&&Le(e,n,t,r);(a||o)&&(l={...l,...Ae(c.data,e.options)}),t._optimisticResults===`isRestoring`&&(l.fetchStatus=`idle`)}let{error:f,errorUpdatedAt:p,status:m}=l;d=l.data;let h=!1;if(t.placeholderData!==void 0&&d===void 0&&m===`pending`){let e;i?.isPlaceholderData&&t.placeholderData===o?.placeholderData?(e=i.data,h=!0):e=typeof t.placeholderData==`function`?t.placeholderData(this.#u?.state.data,this.#u):t.placeholderData,e!==void 0&&(m=`success`,d=k(i?.data,e,t),u=!0)}if(t.select&&d!==void 0&&!h)if(i&&d===a?.data&&t.select===this.#c)d=this.#l;else try{this.#c=t.select,d=t.select(d),d=k(i?.data,d,t),this.#l=d,this.#s=null}catch(e){this.#s=e}this.#s&&(f=this.#s,d=this.#l,p=Date.now(),m=`error`);let g=l.fetchStatus===`fetching`,_=m===`pending`,v=m===`error`,y=_&&g,b=d!==void 0,x={status:m,fetchStatus:l.fetchStatus,isPending:_,isSuccess:m===`success`,isError:v,isInitialLoading:y,isLoading:y,data:d,dataUpdatedAt:l.dataUpdatedAt,error:f,errorUpdatedAt:p,failureCount:l.fetchFailureCount,failureReason:l.fetchFailureReason,errorUpdateCount:l.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:l.dataUpdateCount>s.dataUpdateCount||l.errorUpdateCount>s.errorUpdateCount,isFetching:g,isRefetching:g&&!_,isLoadingError:v&&!b,isPaused:l.fetchStatus===`paused`,isPlaceholderData:u,isRefetchError:v&&b,isStale:Re(e,t),refetch:this.refetch,promise:this.#o,isEnabled:re(t.enabled,e)!==!1};if(this.options.experimental_prefetchInRender){let t=x.data!==void 0,r=x.status===`error`&&!t,i=e=>{r?e.reject(x.error):t&&e.resolve(x.data)},a=()=>{i(this.#o=x.promise=ye())},o=this.#o;switch(o.status){case`pending`:e.queryHash===n.queryHash&&i(o);break;case`fulfilled`:(r||x.data!==o.value)&&a();break;case`rejected`:(!r||x.error!==o.reason)&&a();break}}return x}updateResult(){let e=this.#r,t=this.createResult(this.#t,this.options);this.#i=this.#t.state,this.#a=this.options,this.#i.data!==void 0&&(this.#u=this.#t),!O(t,e)&&(this.#r=t,this.#C({listeners:(()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,n=typeof t==`function`?t():t;if(n===`all`||!n&&!this.#m.size)return!0;let r=new Set(n??this.#m);return this.options.throwOnError&&r.add(`error`),Object.keys(this.#r).some(t=>{let n=t;return this.#r[n]!==e[n]&&r.has(n)})})()}))}#S(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;let t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#y()}#C(e){Se.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:`observerResultsUpdated`})})}};function Pe(e,t){return re(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status===`error`&&t.retryOnMount===!1)}function Fe(e,t){return Pe(e,t)||e.state.data!==void 0&&Ie(e,t,t.refetchOnMount)}function Ie(e,t,n){if(re(t.enabled,e)!==!1&&T(t.staleTime,e)!==`static`){let r=typeof n==`function`?n(e):n;return r===`always`||r!==!1&&Re(e,t)}return!1}function Le(e,t,n,r){return(e!==t||re(r.enabled,e)===!1)&&(!n.suspense||e.state.status!==`error`)&&Re(e,n)}function Re(e,t){return re(t.enabled,e)!==!1&&e.isStaleByTime(T(t.staleTime,e))}function ze(e,t){return!O(e.getCurrentResult(),t)}function Be(e){return{onFetch:(t,n)=>{let r=t.options,i=t.fetchOptions?.meta?.fetchMore?.direction,a=t.state.data?.pages||[],o=t.state.data?.pageParams||[],s={pages:[],pageParams:[]},c=0,l=async()=>{let n=!1,l=e=>{_e(e,()=>t.signal,()=>n=!0)},u=he(t.options,t.fetchOptions),d=async(e,r,i)=>{if(n)return Promise.reject();if(r==null&&e.pages.length)return Promise.resolve(e);let a=await u((()=>{let e={client:t.client,queryKey:t.queryKey,pageParam:r,direction:i?`backward`:`forward`,meta:t.options.meta};return l(e),e})()),{maxPages:o}=t.options,s=i?pe:A;return{pages:s(e.pages,a,o),pageParams:s(e.pageParams,r,o)}};if(i&&a.length){let e=i===`backward`,t=e?He:Ve,n={pages:a,pageParams:o};s=await d(n,t(r,n),e)}else{let t=e??a.length;do{let e=c===0?o[0]??r.initialPageParam:Ve(r,s);if(c>0&&e==null)break;s=await d(s,e),c++}while(ct.options.persister?.(l,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=l}}}function Ve(e,{pages:t,pageParams:n}){let r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function He(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var Ue=class extends Oe{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||We(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){let t=()=>{this.#i({type:`continue`})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=De({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});let r=this.state.status===`pending`,i=!this.#r.canStart();try{if(r)t();else{this.#i({type:`pending`,variables:e,isPaused:i}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);let t=await this.options.onMutate?.(e,n);t!==this.state.context&&this.#i({type:`pending`,context:t,variables:e,isPaused:i})}let a=await this.#r.start();return await this.#n.config.onSuccess?.(a,e,this.state.context,this,n),await this.options.onSuccess?.(a,e,this.state.context,n),await this.#n.config.onSettled?.(a,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(a,null,e,this.state.context,n),this.#i({type:`success`,data:a}),a}catch(t){try{await this.#n.config.onError?.(t,e,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onError?.(t,e,this.state.context,n)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,t,this.state.variables,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,t,e,this.state.context,n)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:t}),t}finally{this.#n.runNext(this)}}#i(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}})(this.state),Se.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function We(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var Ge=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new Ue({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=Ke(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=Ke(e);if(typeof t==`string`){let n=this.#t.get(t);if(n)if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=Ke(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}else return!0}runNext(e){let t=Ke(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){Se.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>E(t,e))}findAll(e={}){return this.getAll().filter(t=>E(e,t))}notify(e){Se.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return Se.batch(()=>Promise.all(e.map(e=>e.continue().catch(w))))}};function Ke(e){return e.options.scope?.id}var qe=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,i=t.queryHash??ae(r,t),a=this.get(i);return a||(a=new ke({client:e,queryKey:r,queryHash:i,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(a)),a}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){Se.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>ie(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>ie(e,t)):t}notify(e){Se.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){Se.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){Se.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},Je=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new qe,this.#t=e.mutationCache||new Ge,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=y.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=Ce.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(T(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,t,n){let r=this.defaultQueryOptions({queryKey:e}),i=this.#e.get(r.queryHash)?.state.data,a=ee(t,i);if(a!==void 0)return this.#e.build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return Se.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;Se.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return Se.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=Se.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(w).catch(w)}invalidateQueries(e,t={}){return Se.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=Se.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(w)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(w)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(T(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(w).catch(w)}fetchInfiniteQuery(e){return e.behavior=Be(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(w).catch(w)}ensureInfiniteQueryData(e){return e.behavior=Be(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return Ce.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(oe(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{se(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(oe(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{se(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=ae(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===me&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},Ye=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),Xe=o(((e,t)=>{t.exports=Ye()})),j=l(d(),1),M=Xe(),Ze=j.createContext(void 0),Qe=e=>{let t=j.useContext(Ze);if(e)return e;if(!t)throw Error(`No QueryClient set, use QueryClientProvider to set one`);return t},$e=({client:e,children:t})=>(j.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),(0,M.jsx)(Ze.Provider,{value:e,children:t})),et=j.createContext(!1),tt=()=>j.useContext(et);et.Provider;function nt(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var rt=j.createContext(nt()),it=()=>j.useContext(rt),at=(e,t,n)=>{let r=n?.state.error&&typeof e.throwOnError==`function`?ge(e.throwOnError,[n.state.error,n]):e.throwOnError;(e.suspense||e.experimental_prefetchInRender||r)&&(t.isReset()||(e.retryOnMount=!1))},ot=e=>{j.useEffect(()=>{e.clearReset()},[e])},st=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||ge(n,[e.error,r])),ct=e=>{if(e.suspense){let t=1e3,n=e=>e===`static`?e:Math.max(e??t,t),r=e.staleTime;e.staleTime=typeof r==`function`?(...e)=>n(r(...e)):n(r),typeof e.gcTime==`number`&&(e.gcTime=Math.max(e.gcTime,t))}},lt=(e,t)=>e.isLoading&&e.isFetching&&!t,ut=(e,t)=>e?.suspense&&t.isPending,dt=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function ft(e,t,n){let r=tt(),i=it(),a=Qe(n),o=a.defaultQueryOptions(e);a.getDefaultOptions().queries?._experimental_beforeQuery?.(o);let s=a.getQueryCache().get(o.queryHash);o._optimisticResults=r?`isRestoring`:`optimistic`,ct(o),at(o,i,s),ot(i);let c=!a.getQueryCache().get(o.queryHash),[l]=j.useState(()=>new t(a,o)),u=l.getOptimisticResult(o),d=!r&&e.subscribed!==!1;if(j.useSyncExternalStore(j.useCallback(e=>{let t=d?l.subscribe(Se.batchCalls(e)):w;return l.updateResult(),t},[l,d]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),j.useEffect(()=>{l.setOptions(o)},[o,l]),ut(o,u))throw dt(o,l,i);if(st({result:u,errorResetBoundary:i,throwOnError:o.throwOnError,query:s,suspense:o.suspense}))throw u.error;return a.getDefaultOptions().queries?._experimental_afterQuery?.(o,u),o.experimental_prefetchInRender&&!ve.isServer()&<(u,r)&&(c?dt(o,l,i):s?.promise)?.catch(w).finally(()=>{l.updateResult()}),o.notifyOnChangeProps?u:l.trackResult(u)}function N(e,t){return ft(e,Ne,t)}function pt(e,t){return function(){return e.apply(t,arguments)}}var{toString:mt}=Object.prototype,{getPrototypeOf:ht}=Object,{iterator:gt,toStringTag:_t}=Symbol,vt=(e=>t=>{let n=mt.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),yt=e=>(e=e.toLowerCase(),t=>vt(t)===e),bt=e=>t=>typeof t===e,{isArray:xt}=Array,St=bt(`undefined`);function Ct(e){return e!==null&&!St(e)&&e.constructor!==null&&!St(e.constructor)&&Dt(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var wt=yt(`ArrayBuffer`);function Tt(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&wt(e.buffer),t}var Et=bt(`string`),Dt=bt(`function`),Ot=bt(`number`),kt=e=>typeof e==`object`&&!!e,At=e=>e===!0||e===!1,jt=e=>{if(vt(e)!==`object`)return!1;let t=ht(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(_t in e)&&!(gt in e)},Mt=e=>{if(!kt(e)||Ct(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Nt=yt(`Date`),Pt=yt(`File`),Ft=e=>!!(e&&e.uri!==void 0),It=e=>e&&e.getParts!==void 0,Lt=yt(`Blob`),Rt=yt(`FileList`),zt=e=>kt(e)&&Dt(e.pipe);function Bt(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var Vt=Bt(),Ht=Vt.FormData===void 0?void 0:Vt.FormData,Ut=e=>{let t;return e&&(Ht&&e instanceof Ht||Dt(e.append)&&((t=vt(e))===`formdata`||t===`object`&&Dt(e.toString)&&e.toString()===`[object FormData]`))},Wt=yt(`URLSearchParams`),[Gt,Kt,qt,Jt]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(yt),Yt=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function Xt(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),xt(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var Qt=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,$t=e=>!St(e)&&e!==Qt;function en(){let{caseless:e,skipUndefined:t}=$t(this)&&this||{},n={},r=(r,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=e&&Zt(n,i)||i;jt(n[a])&&jt(r)?n[a]=en(n[a],r):jt(r)?n[a]=en({},r):xt(r)?n[a]=r.slice():(!t||!St(r))&&(n[a]=r)};for(let e=0,t=arguments.length;e(Xt(t,(t,r)=>{n&&Dt(t)?Object.defineProperty(e,r,{value:pt(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),nn=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),rn=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,`constructor`,{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,`super`,{value:t.prototype}),n&&Object.assign(e.prototype,n)},an=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-- >0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&ht(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},on=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},sn=e=>{if(!e)return null;if(xt(e))return e;let t=e.length;if(!Ot(t))return null;let n=Array(t);for(;t-- >0;)n[t]=e[t];return n},cn=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&ht(Uint8Array)),ln=(e,t)=>{let n=(e&&e[gt]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},un=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},dn=yt(`HTMLFormElement`),fn=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),pn=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),mn=yt(`RegExp`),hn=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};Xt(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},gn=e=>{hn(e,(t,n)=>{if(Dt(e)&&[`arguments`,`caller`,`callee`].indexOf(n)!==-1)return!1;let r=e[n];if(Dt(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},_n=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return xt(e)?r(e):r(String(e).split(t)),n},vn=()=>{},yn=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function bn(e){return!!(e&&Dt(e.append)&&e[_t]===`FormData`&&e[gt])}var xn=e=>{let t=Array(10),n=(e,r)=>{if(kt(e)){if(t.indexOf(e)>=0)return;if(Ct(e))return e;if(!(`toJSON`in e)){t[r]=e;let i=xt(e)?[]:{};return Xt(e,(e,t)=>{let a=n(e,r+1);!St(a)&&(i[t]=a)}),t[r]=void 0,i}}return e};return n(e,0)},Sn=yt(`AsyncFunction`),Cn=e=>e&&(kt(e)||Dt(e))&&Dt(e.then)&&Dt(e.catch),wn=((e,t)=>e?setImmediate:t?((e,t)=>(Qt.addEventListener(`message`,({source:n,data:r})=>{n===Qt&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),Qt.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,Dt(Qt.postMessage)),P={isArray:xt,isArrayBuffer:wt,isBuffer:Ct,isFormData:Ut,isArrayBufferView:Tt,isString:Et,isNumber:Ot,isBoolean:At,isObject:kt,isPlainObject:jt,isEmptyObject:Mt,isReadableStream:Gt,isRequest:Kt,isResponse:qt,isHeaders:Jt,isUndefined:St,isDate:Nt,isFile:Pt,isReactNativeBlob:Ft,isReactNative:It,isBlob:Lt,isRegExp:mn,isFunction:Dt,isStream:zt,isURLSearchParams:Wt,isTypedArray:cn,isFileList:Rt,forEach:Xt,merge:en,extend:tn,trim:Yt,stripBOM:nn,inherits:rn,toFlatObject:an,kindOf:vt,kindOfTest:yt,endsWith:on,toArray:sn,forEachEntry:ln,matchAll:un,isHTMLForm:dn,hasOwnProperty:pn,hasOwnProp:pn,reduceDescriptors:hn,freezeMethods:gn,toObjectSet:_n,toCamelCase:fn,noop:vn,toFiniteNumber:yn,findKey:Zt,global:Qt,isContextDefined:$t,isSpecCompliantForm:bn,toJSONObject:xn,isAsyncFn:Sn,isThenable:Cn,setImmediate:wn,asap:typeof queueMicrotask<`u`?queueMicrotask.bind(Qt):typeof process<`u`&&process.nextTick||wn,isIterable:e=>e!=null&&Dt(e[gt])},F=class e extends Error{static from(t,n,r,i,a,o){let s=new e(t.message,n||t.code,r,i,a);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),o&&Object.assign(s,o),s}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,`message`,{value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:P.toJSONObject(this.config),code:this.code,status:this.status}}};F.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,F.ERR_BAD_OPTION=`ERR_BAD_OPTION`,F.ECONNABORTED=`ECONNABORTED`,F.ETIMEDOUT=`ETIMEDOUT`,F.ERR_NETWORK=`ERR_NETWORK`,F.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,F.ERR_DEPRECATED=`ERR_DEPRECATED`,F.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,F.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,F.ERR_CANCELED=`ERR_CANCELED`,F.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,F.ERR_INVALID_URL=`ERR_INVALID_URL`;function Tn(e){return P.isPlainObject(e)||P.isArray(e)}function En(e){return P.endsWith(e,`[]`)?e.slice(0,-2):e}function Dn(e,t,n){return e?e.concat(t).map(function(e,t){return e=En(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function On(e){return P.isArray(e)&&!e.some(Tn)}var kn=P.toFlatObject(P,{},null,function(e){return/^is[A-Z]/.test(e)});function An(e,t,n){if(!P.isObject(e))throw TypeError(`target must be an object`);t||=new FormData,n=P.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!P.isUndefined(t[e])});let r=n.metaTokens,i=n.visitor||l,a=n.dots,o=n.indexes,s=(n.Blob||typeof Blob<`u`&&Blob)&&P.isSpecCompliantForm(t);if(!P.isFunction(i))throw TypeError(`visitor must be a function`);function c(e){if(e===null)return``;if(P.isDate(e))return e.toISOString();if(P.isBoolean(e))return e.toString();if(!s&&P.isBlob(e))throw new F(`Blob is not supported. Use a Buffer instead.`);return P.isArrayBuffer(e)||P.isTypedArray(e)?s&&typeof Blob==`function`?new Blob([e]):Buffer.from(e):e}function l(e,n,i){let s=e;if(P.isReactNative(t)&&P.isReactNativeBlob(e))return t.append(Dn(i,n,a),c(e)),!1;if(e&&!i&&typeof e==`object`){if(P.endsWith(n,`{}`))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(P.isArray(e)&&On(e)||(P.isFileList(e)||P.endsWith(n,`[]`))&&(s=P.toArray(e)))return n=En(n),s.forEach(function(e,r){!(P.isUndefined(e)||e===null)&&t.append(o===!0?Dn([n],r,a):o===null?n:n+`[]`,c(e))}),!1}return Tn(e)?!0:(t.append(Dn(i,n,a),c(e)),!1)}let u=[],d=Object.assign(kn,{defaultVisitor:l,convertValue:c,isVisitable:Tn});function f(e,n){if(!P.isUndefined(e)){if(u.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));u.push(e),P.forEach(e,function(e,r){(!(P.isUndefined(e)||e===null)&&i.call(t,e,P.isString(r)?r.trim():r,n,d))===!0&&f(e,n?n.concat(r):[r])}),u.pop()}}if(!P.isObject(e))throw TypeError(`data must be an object`);return f(e),t}function jn(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`,"%00":`\0`};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function Mn(e,t){this._pairs=[],e&&An(e,this,t)}var Nn=Mn.prototype;Nn.append=function(e,t){this._pairs.push([e,t])},Nn.toString=function(e){let t=e?function(t){return e.call(this,t,jn)}:jn;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Pn(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function Fn(e,t,n){if(!t)return e;let r=n&&n.encode||Pn,i=P.isFunction(n)?{serialize:n}:n,a=i&&i.serialize,o;if(o=a?a(t,i):P.isURLSearchParams(t)?t.toString():new Mn(t,i).toString(r),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var In=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&=[]}forEach(e){P.forEach(this.handlers,function(t){t!==null&&e(t)})}},Ln={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},Rn={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Mn,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},zn=s({hasBrowserEnv:()=>Bn,hasStandardBrowserEnv:()=>Hn,hasStandardBrowserWebWorkerEnv:()=>Un,navigator:()=>Vn,origin:()=>Wn}),Bn=typeof window<`u`&&typeof document<`u`,Vn=typeof navigator==`object`&&navigator||void 0,Hn=Bn&&(!Vn||[`ReactNative`,`NativeScript`,`NS`].indexOf(Vn.product)<0),Un=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Wn=Bn&&window.location.href||`http://localhost`,Gn={...zn,...Rn};function Kn(e,t){return An(e,new Gn.classes.URLSearchParams,{visitor:function(e,t,n,r){return Gn.isNode&&P.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}function qn(e){return P.matchAll(/\w+|\[(\w*)]/g,e).map(e=>e[0]===`[]`?``:e[1]||e[0])}function Jn(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&P.isArray(r)?r.length:a,s?(P.hasOwnProp(r,a)?r[a]=[r[a],n]:r[a]=n,!o):((!r[a]||!P.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&P.isArray(r[a])&&(r[a]=Jn(r[a])),!o)}if(P.isFormData(e)&&P.isFunction(e.entries)){let n={};return P.forEachEntry(e,(e,r)=>{t(qn(e),r,n,0)}),n}return null}function Xn(e,t,n){if(P.isString(e))try{return(t||JSON.parse)(e),P.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Zn={transitional:Ln,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=P.isObject(e);if(i&&P.isHTMLForm(e)&&(e=new FormData(e)),P.isFormData(e))return r?JSON.stringify(Yn(e)):e;if(P.isArrayBuffer(e)||P.isBuffer(e)||P.isStream(e)||P.isFile(e)||P.isBlob(e)||P.isReadableStream(e))return e;if(P.isArrayBufferView(e))return e.buffer;if(P.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return Kn(e,this.formSerializer).toString();if((a=P.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let t=this.env&&this.env.FormData;return An(a?{"files[]":e}:e,t&&new t,this.formSerializer)}}return i||r?(t.setContentType(`application/json`,!1),Xn(e)):e}],transformResponse:[function(e){let t=this.transitional||Zn.transitional,n=t&&t.forcedJSONParsing,r=this.responseType===`json`;if(P.isResponse(e)||P.isReadableStream(e))return e;if(e&&P.isString(e)&&(n&&!this.responseType||r)){let n=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(n)throw e.name===`SyntaxError`?F.from(e,F.ERR_BAD_RESPONSE,this,null,this.response):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Gn.classes.FormData,Blob:Gn.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};P.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`],e=>{Zn.headers[e]={}});var Qn=P.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),$n=e=>{let t={},n,r,i;return e&&e.split(` +`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim(),!(!n||t[n]&&Qn[n])&&(n===`set-cookie`?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+`, `+r:r)}),t},er=Symbol(`internals`);function tr(e){return e&&String(e).trim().toLowerCase()}function nr(e){return e===!1||e==null?e:P.isArray(e)?e.map(nr):String(e)}function rr(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var ir=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function ar(e,t,n,r,i){if(P.isFunction(r))return r.call(this,t,n);if(i&&(t=n),P.isString(t)){if(P.isString(r))return t.indexOf(r)!==-1;if(P.isRegExp(r))return r.test(t)}}function or(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function sr(e,t){let n=P.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var cr=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=tr(t);if(!i)throw Error(`header name must be a non-empty string`);let a=P.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=nr(e))}let a=(e,t)=>P.forEach(e,(e,n)=>i(e,n,t));if(P.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(P.isString(e)&&(e=e.trim())&&!ir(e))a($n(e),t);else if(P.isObject(e)&&P.isIterable(e)){let n={},r,i;for(let t of e){if(!P.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);n[i=t[0]]=(r=n[i])?P.isArray(r)?[...r,t[1]]:[r,t[1]]:t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=tr(e),e){let n=P.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return rr(e);if(P.isFunction(t))return t.call(this,e,n);if(P.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=tr(e),e){let n=P.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||ar(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=tr(e),e){let i=P.findKey(n,e);i&&(!t||ar(n,n[i],i,t))&&(delete n[i],r=!0)}}return P.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||ar(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return P.forEach(this,(r,i)=>{let a=P.findKey(n,i);if(a){t[a]=nr(r),delete t[i];return}let o=e?or(i):String(i).trim();o!==i&&delete t[i],t[o]=nr(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return P.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&P.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` +`)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[er]=this[er]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=tr(e);t[r]||(sr(n,e),t[r]=!0)}return P.isArray(e)?e.forEach(r):r(e),this}};cr.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),P.reduceDescriptors(cr.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),P.freezeMethods(cr);function lr(e,t){let n=this||Zn,r=t||n,i=cr.from(r.headers),a=r.data;return P.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function ur(e){return!!(e&&e.__CANCEL__)}var dr=class extends F{constructor(e,t,n){super(e??`canceled`,F.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function fr(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new F(`Request failed with status code `+n.status,[F.ERR_BAD_REQUEST,F.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function pr(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function mr(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var gr=(e,t,n=3)=>{let r=0,i=mr(50,250);return hr(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},_r=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},vr=e=>(...t)=>P.asap(()=>e(...t)),yr=Gn.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Gn.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Gn.origin),Gn.navigator&&/(msie|trident)/i.test(Gn.navigator.userAgent)):()=>!0,br=Gn.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];P.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),P.isString(r)&&s.push(`path=${r}`),P.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),P.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function xr(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function Sr(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function Cr(e,t,n){let r=!xr(t);return e&&(r||n==0)?Sr(e,t):t}var wr=e=>e instanceof cr?{...e}:e;function Tr(e,t){t||={};let n={};function r(e,t,n,r){return P.isPlainObject(e)&&P.isPlainObject(t)?P.merge.call({caseless:r},e,t):P.isPlainObject(t)?P.merge({},t):P.isArray(t)?t.slice():t}function i(e,t,n,i){if(!P.isUndefined(t))return r(e,t,n,i);if(!P.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!P.isUndefined(t))return r(void 0,t)}function o(e,t){if(!P.isUndefined(t))return r(void 0,t);if(!P.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(wr(e),wr(t),n,!0)};return P.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=P.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);P.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Er=e=>{let t=Tr({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=cr.from(o),t.url=Fn(Cr(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),P.isFormData(n)){if(Gn.hasStandardBrowserEnv||Gn.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(P.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(Gn.hasStandardBrowserEnv&&(r&&P.isFunction(r)&&(r=r(t)),r||r!==!1&&yr(t.url))){let e=i&&a&&br.read(a);e&&o.set(i,e)}return t},Dr=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Er(e),i=r.data,a=cr.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=cr.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());fr(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new F(`Request aborted`,F.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new F(t&&t.message?t.message:`Network Error`,F.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Ln;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new F(t,i.clarifyTimeoutError?F.ETIMEDOUT:F.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&P.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),P.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=gr(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=gr(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new dr(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=pr(r.url);if(_&&Gn.protocols.indexOf(_)===-1){n(new F(`Unsupported protocol `+_+`:`,F.ERR_BAD_REQUEST,e));return}h.send(i||null)})},Or=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof F?t:new dr(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new F(`timeout of ${t}ms exceeded`,F.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>P.asap(o),s}},kr=function*(e,t){let n=e.byteLength;if(!t||n{let i=Ar(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Nr=64*1024,{isFunction:Pr}=P,Fr=(({Request:e,Response:t})=>({Request:e,Response:t}))(P.global),{ReadableStream:Ir,TextEncoder:Lr}=P.global,Rr=(e,...t)=>{try{return!!e(...t)}catch{return!1}},zr=e=>{e=P.merge.call({skipUndefined:!0},Fr,e);let{fetch:t,Request:n,Response:r}=e,i=t?Pr(t):typeof fetch==`function`,a=Pr(n),o=Pr(r);if(!i)return!1;let s=i&&Pr(Ir),c=i&&(typeof Lr==`function`?(e=>t=>e.encode(t))(new Lr):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&Rr(()=>{let e=!1,t=new n(Gn.origin,{body:new Ir,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return e&&!t}),u=o&&s&&Rr(()=>P.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new F(`Response type '${e}' is not supported`,F.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(P.isBlob(e))return e.size;if(P.isSpecCompliantForm(e))return(await new n(Gn.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(P.isArrayBufferView(e)||P.isArrayBuffer(e))return e.byteLength;if(P.isURLSearchParams(e)&&(e+=``),P.isString(e))return(await c(e)).byteLength},p=async(e,t)=>P.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Er(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=Or([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),ee;try{if(g&&l&&o!==`get`&&o!==`head`&&(ee=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(P.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=_r(ee,gr(vr(g)));s=Mr(e.body,Nr,t,n)}}P.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=P.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&_r(t,gr(vr(h),!0))||[];f=new r(Mr(f.body,Nr,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let te=await d[P.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{fr(t,n,{data:te,headers:cr.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new F(`Network Error`,F.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):F.from(t,t&&t.code,e,C,t&&t.response)}}},Br=new Map,Vr=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Br;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:zr(t)),l=c;return c};Vr();var Hr={http:null,xhr:Dr,fetch:{get:Vr}};P.forEach(Hr,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Ur=e=>`- ${e}`,Wr=e=>P.isFunction(e)||e===null||e===!1;function Gr(e,t){e=P.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new F(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : +`+e.map(Ur).join(` +`):` `+Ur(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var Kr={getAdapter:Gr,adapters:Hr};function qr(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new dr(null,e)}function Jr(e){return qr(e),e.headers=cr.from(e.headers),e.data=lr.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),Kr.getAdapter(e.adapter||Zn.adapter,e)(e).then(function(t){return qr(e),t.data=lr.call(e,e.transformResponse,t),t.headers=cr.from(t.headers),t},function(t){return ur(t)||(qr(e),t&&t.response&&(t.response.data=lr.call(e,e.transformResponse,t.response),t.response.headers=cr.from(t.response.headers))),Promise.reject(t)})}var Yr=`1.13.6`,Xr={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{Xr[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var Zr={};Xr.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Yr+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new F(r(i,` has been removed`+(t?` in `+t:``)),F.ERR_DEPRECATED);return t&&!Zr[i]&&(Zr[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},Xr.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function Qr(e,t,n){if(typeof e!=`object`)throw new F(`options must be an object`,F.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new F(`option `+a+` must be `+n,F.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new F(`Unknown option `+a,F.ERR_BAD_OPTION)}}var $r={assertOptions:Qr,validators:Xr},I=$r.validators,ei=class{constructor(e){this.defaults=e||{},this.interceptors={request:new In,response:new In}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` +`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Tr(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&$r.assertOptions(n,{silentJSONParsing:I.transitional(I.boolean),forcedJSONParsing:I.transitional(I.boolean),clarifyTimeoutError:I.transitional(I.boolean),legacyInterceptorReqResOrdering:I.transitional(I.boolean)},!1),r!=null&&(P.isFunction(r)?t.paramsSerializer={serialize:r}:$r.assertOptions(r,{encode:I.function,serialize:I.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),$r.assertOptions(t,{baseUrl:I.spelling(`baseURL`),withXsrfToken:I.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&P.merge(i.common,i[t.method]);i&&P.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=cr.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Ln;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[Jr.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new dr(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ni(e){return function(t){return e.apply(null,t)}}function ri(e){return P.isObject(e)&&e.isAxiosError===!0}var ii={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(ii).forEach(([e,t])=>{ii[t]=e});function ai(e){let t=new ei(e),n=pt(ei.prototype.request,t);return P.extend(n,ei.prototype,t,{allOwnKeys:!0}),P.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return ai(Tr(e,t))},n}var L=ai(Zn);L.Axios=ei,L.CanceledError=dr,L.CancelToken=ti,L.isCancel=ur,L.VERSION=Yr,L.toFormData=An,L.AxiosError=F,L.Cancel=L.CanceledError,L.all=function(e){return Promise.all(e)},L.spread=ni,L.isAxiosError=ri,L.mergeConfig=Tr,L.AxiosHeaders=cr,L.formToJSON=e=>Yn(P.isHTMLForm(e)?new FormData(e):e),L.getAdapter=Kr.getAdapter,L.HttpStatusCode=ii,L.default=L;var oi=l(_()),si=`order-demo-001`;function ci(e,t,n,r,i,a){return{eventId:e,aggregateId:si,aggregateType:`ORDER`,sequenceNumber:t,eventType:n,payload:a,metadata:JSON.stringify({source:`demo`,correlationId:`corr-demo-${t}`}),timestamp:r,globalPosition:i}}function li(){let e=[],t=Date.parse(`2025-01-15T08:00:00.000Z`);for(let n=1;n<=100;n++){let r=new Date(t+n*45e3).toISOString(),i=5e4+n,a,o;if(n===1)a=`ORDER_PLACED`,o={customerId:`cust-77`,channel:`web`,status:`PENDING`,totalCents:0,itemCount:0};else if(n>=2&&n<=48){a=`LINE_ITEM_ADDED`;let e=350+n*73%1200;o={sku:`SKU-${String(1e4+n*17).slice(-4)}`,qty:n%4+1,lineTotalCents:e,lineIndex:n-1}}else if(n>=49&&n<=58)a=`PAYMENT_PROGRESS`,o={paymentId:`pay-chunk-${n}`,amountCents:1500+n*120,balanceCents:Math.max(0,48e3-n*700)};else if(n>=59&&n<=72){let e=[`inventory`,`fraud_check`,`address_verify`,`manual_review`,`carrier_delay`];a=`FULFILLMENT_BLOCKED`,o={reason:e[n%e.length],caseId:`CASE-${n}`,retryAfterMinutes:15+n%45}}else n>=73&&n<=88?(a=`SHIPMENT_EVENT`,o={leg:n-72,carrier:n%3==0?`FAST`:n%3==1?`ECONOMY`:`OVERNIGHT`,status:`IN_TRANSIT`,trackingToken:`trk-${n}${(n*7919).toString(36)}`}):n>=89&&n<=99?(a=`NOTE_APPENDED`,o={author:`agent-${n%6+1}`,noteId:`n-${n}`,preview:`Ops note #${n}: SLA watch / customer ping`}):(a=`REFUND_ISSUED`,o={refundCents:88e3,balanceCents:-12500,reason:`bulk_settlement_adjustment`});e.push(ci(`evt-demo-${n}`,n,a,r,i,JSON.stringify(o)))}return e}var ui=li();function di(e){return[{code:`NEGATIVE_BALANCE`,severity:`HIGH`,description:`Ledger balance dropped below zero after refund batch`},{code:`REFUND_EXCEEDS_CAPTURE`,severity:`CRITICAL`,description:`Cumulative refunds exceed captured payments for this aggregate`},{code:`DUPLICATE_PAYMENT_CHUNK`,severity:`MEDIUM`,description:`Two payment chunks share the same window and amount fingerprint`},{code:`LINE_ITEM_PRICE_OUTLIER`,severity:`LOW`,description:`Line total deviates >3σ from cohort for this SKU family`},{code:`FULFILLMENT_STALL`,severity:`HIGH`,description:`Order blocked in fulfillment longer than SLA for channel`},{code:`CARRIER_MISMATCH`,severity:`MEDIUM`,description:`Shipment leg carrier differs from preferred routing profile`},{code:`MANUAL_REVIEW_BACKLOG`,severity:`LOW`,description:`Case reopened multiple times without resolution`},{code:`VELOCITY_SPIKE`,severity:`HIGH`,description:`Event rate on this aggregate exceeded rolling baseline`},{code:`ADDRESS_VERIFY_LOOP`,severity:`MEDIUM`,description:`Address verification failed three times with same payload hash`},{code:`INVENTORY_HOLD`,severity:`MEDIUM`,description:`Inventory hold exceeded expected release window`},{code:`FRAUD_SCORE_EDGE`,severity:`LOW`,description:`Fraud score landed in manual-review gray band`},{code:`DISCOUNT_STACK`,severity:`LOW`,description:`Multiple discount signals present without explicit approval event`},{code:`SHIPMENT_GAP`,severity:`HIGH`,description:`Missing scan between expected hub handoffs`},{code:`NOTE_SPAM`,severity:`LOW`,description:`Unusually high operator notes density in short interval`},{code:`PAYMENT_PARTIAL_CLUSTER`,severity:`MEDIUM`,description:`Several partial captures without closing settlement event`},{code:`SKU_QUANTITY_ANOMALY`,severity:`MEDIUM`,description:`Quantity pattern inconsistent with historical order curve`},{code:`CASE_ESCALATION`,severity:`HIGH`,description:`Support case escalated without prior tier-1 closure`},{code:`TRACKING_TOKEN_REUSE`,severity:`CRITICAL`,description:`Tracking token collision across two concurrent legs`},{code:`SLA_BREACH_RISK`,severity:`HIGH`,description:`Projected delivery crosses committed SLA if delay persists`},{code:`SETTLEMENT_BATCH_DRIFT`,severity:`CRITICAL`,description:`Settlement batch totals diverge from summed payment chunks`}].map((t,n)=>{let r=Math.min(100,5+n*5),i=e.find(e=>e.sequenceNumber===r)??e[e.length-1];return{code:t.code,description:t.description,severity:t.severity,aggregateId:si,atSequence:r,triggeringEventType:i.eventType,timestamp:i.timestamp,stateAtAnomaly:{demoIndex:n+1,atSequence:r,code:t.code}}})}var fi=di(ui);function pi(e,t){let n={...e},r={};try{r=JSON.parse(t.payload||`{}`)}catch{}n._version=t.sequenceNumber,n._lastEventType=t.eventType,n._lastUpdated=t.timestamp;let i=t.eventType.toLowerCase();return i.includes(`created`)||i.includes(`opened`)||i.includes(`placed`)||i.includes(`submitted`)||(i.includes(`deleted`)||i.includes(`closed`)||i.includes(`cancelled`)||i.includes(`rejected`))&&(n.status=`DELETED`),Object.assign(n,r),n}function mi(e,t){let n={};for(let r of Object.keys(t)){let i=e[r],a=t[r];JSON.stringify(i)!==JSON.stringify(a)&&(n[r]={oldValue:i,newValue:a})}for(let r of Object.keys(e))r in t||(n[r]={oldValue:e[r],newValue:void 0});return n}function hi(e){let t=[],n={};for(let r of e){let e={...n};n=pi(n,r);let i={...n};t.push({event:r,stateBefore:e,stateAfter:i,diff:mi(e,i)})}return t}var gi=hi(ui);function _i(e){let t=e.trim().toLowerCase();return t.length<2?!1:t.includes(`demo`)||`order-demo-001`.includes(t)}function vi(e){return _i(e)?[si]:[]}function yi(e){let t=Math.min(Math.max(e,1),500);return[...ui].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,t)}function bi(e,t,n){if(e!==`order-demo-001`)return{events:[],totalEvents:0};let r=ui.length,i=Math.max(0,n),a=Math.min(Math.max(t,1),1e3);return i>=r?{events:[],totalEvents:r}:{events:ui.slice(i,i+a),totalEvents:r}}function xi(e){return e===`order-demo-001`?gi:[]}function Si(e){let t=Math.min(Math.max(e,1),500);return fi.slice(0,t)}function Ci(){return[...ui].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,40)}function wi(){return{status:`UP`,version:`demo`,demo:!0}}function Ti(e=1,t=24){let n=Math.max(e,1)*60*60*1e3,r=[...ui].sort((e,t)=>Date.parse(e.timestamp)-Date.parse(t.timestamp)),i=new Map,a=new Map,o=new Map;for(let e of r){let t=new Date(Math.floor(Date.parse(e.timestamp)/n)*n).toISOString();i.set(t,(i.get(t)??0)+1),a.set(e.eventType,(a.get(e.eventType)??0)+1),o.set(e.aggregateType,(o.get(e.aggregateType)??0)+1)}return{totalEvents:ui.length,distinctAggregates:1,eventTypes:[...a.entries()].map(([e,t])=>({type:e,count:t})),aggregateTypes:[...o.entries()].map(([e,t])=>({type:e,count:t})),throughput:[...i.entries()].slice(-Math.max(t,1)).map(([e,t])=>({bucket:e,count:t})),available:!0,message:null}}function R(){return!1}var z=L.create({baseURL:`/api`,withCredentials:!0}),Ei=null;z.interceptors.request.use(e=>{let t=(e.method??`get`).toUpperCase();return Ei&&![`GET`,`HEAD`,`OPTIONS`].includes(t)&&(e.headers=e.headers??{},e.headers[`X-CSRF-Token`]=Ei),e});function Di(e){return new Promise(t=>{setTimeout(t,e)})}function Oi(e,t){return t?`${e}${e.includes(`?`)?`&`:`?`}source=${encodeURIComponent(t)}`:e}function ki(e,t){let n=`${e}:${t}`,r=new TextEncoder().encode(n),i=``;return r.forEach(e=>{i+=String.fromCharCode(e)}),btoa(i)}var Ai=async(e,t=20,n)=>{let r=Oi(`/aggregates/search?q=${encodeURIComponent(e)}&limit=${t}`,n);if(R()){await Di(40);let n=vi(e);try{let e=await z.get(r);return[...new Set([...n,...e.data])].slice(0,t)}catch{return n}}return z.get(r).then(e=>e.data)},ji=async(e,t=500,n=0,r,i=`full`)=>{if(R()&&e===`order-demo-001`)return await Di(50),bi(e,t,n);let a=Oi(`/aggregates/${e}/timeline?limit=${t}&offset=${n}&fields=${i}`,r);return z.get(a).then(e=>e.data)},Mi=async(e,t)=>R()&&e===`order-demo-001`?(await Di(50),xi(e)):z.get(Oi(`/aggregates/${e}/transitions`,t)).then(e=>e.data),B=async(e=100,t)=>R()?(await Di(45),Si(e)):z.get(Oi(`/anomalies/recent?limit=${e}`,t)).then(e=>e.data),V=async(e=50,t)=>R()?(await Di(35),yi(e)):z.get(Oi(`/events/recent?limit=${e}`,t)).then(e=>e.data),Ni=async(e,t=1,n=24)=>{if(R())return await Di(30),Ti(t,n);let r=Oi(`/v1/statistics?bucketHours=${t}&maxBuckets=${n}`,e);return z.get(r).then(e=>e.data)},Pi=async()=>R()?(await Di(20),wi()):z.get(`/health`).then(e=>e.data),Fi=async()=>R()?{authenticated:!0,principal:{userId:`demo-user`,displayName:`Demo User`,authMethod:`demo`,roles:[`demo`]}}:z.get(`/v1/auth/session`).then(e=>e.data),Ii=async(e,t,n)=>z.post(`/v1/auth/login/basic`,{returnHash:n},{headers:{Authorization:`Basic ${ki(e,t)}`}}).then(e=>e.data),Li=async()=>R()?{authenticated:!1}:z.post(`/v1/auth/logout`).then(e=>e.data),Ri=e=>`/api/v1/auth/login/oidc?returnHash=${encodeURIComponent(e)}`,zi=e=>{Ei=e},Bi=async()=>R()?(await Di(20),[{id:`demo-primary`,displayName:`Demo Primary`,status:`ready`,healthMessage:`Frontend demo datasource`,capabilities:[`timeline`,`replay`,`statistics`]}]):z.get(`/v1/datasources`).then(e=>e.data),Vi=async()=>R()?(await Di(20),[{instanceId:`demo-source`,typeId:`demo`,displayName:`Demo Source Plugin`,pluginType:`EVENT_SOURCE`,lifecycle:`ready`,health:{state:`up`,message:`Frontend demo plugin`},lastHealthCheck:new Date().toISOString(),failureReason:null}]):z.get(`/v1/plugins`).then(e=>e.data);function Hi(e,t){let[n,r]=(0,j.useState)(e);return(0,j.useEffect)(()=>{let n=setTimeout(()=>r(e),t);return()=>clearTimeout(n)},[e,t]),n}function Ui({onSelect:e,source:t,selectedValue:n}){let[r,i]=(0,j.useState)(``),[a,o]=(0,j.useState)(!1),s=(0,j.useRef)(null),c=Hi(r,300),{data:l=[]}=N({queryKey:[`search`,c,t??`default`],queryFn:()=>Ai(c,20,t),enabled:c.length>=2,staleTime:5e3});(0,j.useEffect)(()=>{i(n??``)},[n]),(0,j.useEffect)(()=>{let e=e=>{s.current&&!s.current.contains(e.target)&&o(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[]);let u=(0,j.useRef)(null),d=(0,j.useCallback)(()=>{u.current?.focus(),u.current?.select()},[]);(0,j.useEffect)(()=>{let e=document.getElementById(`aggregate-search`);return e?.addEventListener(`focus`,d),()=>e?.removeEventListener(`focus`,d)},[d]);let f=t=>{i(t),o(!1),e(t)};return(0,M.jsxs)(`div`,{className:`search-wrapper`,ref:s,children:[(0,M.jsx)(`span`,{className:`search-icon`,children:`??`}),(0,M.jsx)(`input`,{id:`aggregate-search`,ref:u,type:`text`,className:`search-input`,placeholder:`Search by aggregate ID (e.g. UUID or stream key)`,value:r,onChange:e=>{i(e.target.value),o(!0)},onFocus:()=>r.length>=2&&o(!0),onKeyDown:e=>{e.key===`Enter`&&r.trim()&&f(r.trim()),e.key===`Escape`&&o(!1)},autoComplete:`off`,"aria-autocomplete":`list`}),a&&l.length>0&&(0,M.jsx)(`div`,{className:`search-results`,role:`listbox`,id:`aggregate-search-results`,children:l.map(e=>(0,M.jsxs)(`button`,{type:`button`,className:`search-result-item`,onClick:()=>f(e),role:`option`,children:[(0,M.jsx)(`span`,{className:`search-result-chevron`,"aria-hidden":!0,children:`?`}),(0,M.jsxs)(`span`,{className:`search-result-body`,children:[(0,M.jsx)(`span`,{className:`search-result-label`,children:`ID`}),(0,M.jsx)(`span`,{className:`search-result-colon`,children:`:`}),(0,M.jsx)(`span`,{className:`search-result-value`,children:e})]})]},e))})]})}function Wi(e,t){return N({queryKey:[`timeline`,e,t??`default`,`metadata`],queryFn:()=>ji(e,500,0,t,`metadata`),enabled:!!e})}function Gi(e){if(typeof e==`number`)return Number.isNaN(e)?new Date:e<0xe8d4a51000?new Date(e*1e3):new Date(e);let t=String(e).trim();if(!t)return new Date;if(t.includes(`T`)||/^\d{4}-\d{2}-\d{2}/.test(t)){let e=Date.parse(t);if(!Number.isNaN(e))return new Date(e)}let n=parseFloat(t);return Number.isNaN(n)?new Date:n<0xe8d4a51000?new Date(n*1e3):new Date(n)}function Ki(e){let t=[],n=0;for(;n=4)t.push({kind:`group`,eventType:r,items:e.slice(n,i),startIndex:n});else for(let r=n;re.sequenceNumber===n),o=t===e;r.push({kind:`group`,key:`group-${e}`,eventType:i.eventType,items:i.items,startIndex:i.startIndex,expanded:o,containsSelection:a}),o&&r.push(...i.items.map((t,n)=>({kind:`group-item`,key:`group-item-${t.sequenceNumber}`,event:t,stepNumber:i.startIndex+n+1,parentKey:e})))}return r}function Yi(e,t){return e===t?null:t}var Xi=[1,6,24,168];function Zi(e){let t=e.toLowerCase();return t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`created`:t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`deleted`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`completed`:t.includes(`failed`)||t.includes(`error`)||t.includes(`blocked`)?`failed`:t.includes(`transfer`)?`transfer`:`default`}function Qi(e){switch(e.kind){case`single`:case`group-item`:return e.event.sequenceNumber;case`group`:return e.items[0]?.sequenceNumber??0}}function $i(e,t){return`Event ${e}, sequence ${t.sequenceNumber}, ${t.eventType}`}function ea(e,t){return t==null?!1:e.kind===`single`?e.event.sequenceNumber===t:e.items.some(e=>e.sequenceNumber===t)}function ta(e,t,n,r,i){let a=e.event,o=t===a.sequenceNumber,s=n===a.sequenceNumber,c=e.kind===`group-item`?` timeline-step-compact`:``;return(0,M.jsxs)(`button`,{type:`button`,className:`timeline-step timeline-step-${Zi(a.eventType)} ${o?`active`:``} ${s?`timeline-step-compare`:``}${c}`,onClick:e=>{e.shiftKey&&i?i(Yi(n,a.sequenceNumber)):r(a.sequenceNumber)},"aria-current":o?`step`:void 0,"aria-label":$i(e.stepNumber,a),children:[(0,M.jsxs)(`span`,{className:`timeline-step-badge`,children:[`Event `,e.stepNumber]}),(0,M.jsxs)(`span`,{className:`timeline-step-seq`,children:[`seq #`,a.sequenceNumber]}),(0,M.jsx)(`span`,{className:`timeline-step-type`,children:a.eventType}),(0,M.jsx)(`span`,{className:`timeline-step-seq`,children:Gi(a.timestamp).toLocaleTimeString()})]},e.key)}function na({aggregateId:e,selectedSequence:t,compareSequence:n,onSelectEvent:r,onSelectCompare:i,source:a}){let{data:o,isLoading:s}=Wi(e,a),[c,l]=(0,j.useState)(``),[u,d]=(0,j.useState)(``),[f,p]=(0,j.useState)(`all`),[m,h]=(0,j.useState)(null),g=(0,j.useRef)(null),_=o?.events??[],v=o?.totalEvents??0,y=(0,j.useMemo)(()=>{let e=_;if(c&&(e=e.filter(e=>e.eventType===c)),f!==`all`&&e.length>0){let t=Gi(e[e.length-1].timestamp).getTime()-f*60*60*1e3;e=e.filter(e=>Gi(e.timestamp).getTime()>=t)}return e},[_,c,f]),b=(0,j.useMemo)(()=>Ki(y),[y]),x=(0,j.useMemo)(()=>[...new Set(_.map(e=>e.eventType))].sort(),[_]);(0,j.useEffect)(()=>{if(t!=null){for(let e of b)if(e.kind===`group`&&e.items.some(e=>e.sequenceNumber===t)){h(qi(e.startIndex,e.items.length));return}h(null)}},[b,t]);let S=(0,j.useMemo)(()=>Ji(b,m,t),[m,b,t]),C=(0,j.useMemo)(()=>t==null?-1:S.findIndex(e=>e.kind===`group`?e.items.some(e=>e.sequenceNumber===t):e.event.sequenceNumber===t),[S,t]);if((0,j.useEffect)(()=>{let e=e=>{let t=e.detail;if(S.length&&(t<0||t>0)){let e=C>=0?C:0;r(Qi(S[Math.max(0,Math.min(S.length-1,e+t))]))}};return window.addEventListener(`eventlens:timeline-step`,e),()=>window.removeEventListener(`eventlens:timeline-step`,e)},[r,S,C]),(0,j.useEffect)(()=>{let e=e=>{let n=e.target;if(!(n&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(n.tagName)||!S.length)&&(e.key===`ArrowLeft`||e.key===`ArrowRight`)&&e.shiftKey){e.preventDefault();let n=e.key===`ArrowLeft`?-1:1,i=S.filter(e=>e.kind===`group`);if(!i.length)return;let a=i.findIndex(e=>t!=null&&e.items.some(e=>e.sequenceNumber===t));r(i[a>=0?Math.max(0,Math.min(i.length-1,a+n)):n>0?0:i.length-1].items[0].sequenceNumber)}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r,S,t]),(0,j.useEffect)(()=>{if(!g.current||t==null)return;let e=g.current.querySelector(`[aria-current="step"]`);if(!e)return;let n=g.current.getBoundingClientRect(),r=e.getBoundingClientRect(),i=g.current.scrollLeft,a=i+(r.left-n.left),o=a+r.width,s=i,c=i+g.current.clientWidth;ac-24&&g.current.scrollTo({left:o-g.current.clientWidth+24,behavior:`smooth`})},[m,t]),s)return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,M.jsx)(`div`,{className:`skeleton`,style:{height:160}})]});if(!_.length)return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`},children:`No events found.`})]});let w=y[0]?.sequenceNumber??0,ee=y[y.length-1]?.sequenceNumber??0;return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsxs)(`div`,{className:`timeline-header-row`,children:[(0,M.jsxs)(`div`,{className:`card-title`,style:{marginBottom:0},children:[`Event Sequence`,(0,M.jsxs)(`span`,{className:`timeline-count-pill`,children:[y.length,` / `,v,` events`]})]}),(0,M.jsx)(`div`,{className:`timeline-jump-group`,children:(0,M.jsx)(`input`,{className:`timeline-jump-input`,type:`number`,placeholder:`Jump to seq`,value:u,onChange:e=>d(e.target.value),onKeyDown:e=>{if(e.key===`Enter`){let e=Number(u);y.some(t=>t.sequenceNumber===e)&&(r(e),d(``))}},"aria-label":`Jump to sequence number`})})]}),(0,M.jsx)(`p`,{className:`timeline-hint`,children:`Scroll the rail horizontally from left to right. Repeated event runs collapse into grouped cards; click a group to open the full strip below.`}),(0,M.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Filter by event type`,children:[(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${c?``:`active`}`,onClick:()=>l(``),children:`All`}),x.map(e=>(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${c===e?`active`:``}`,onClick:()=>l(t=>t===e?``:e),children:e},e))]}),(0,M.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Zoom range`,children:[(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${f===`all`?`active`:``}`,onClick:()=>p(`all`),children:`All`}),Xi.map(e=>(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${f===e?`active`:``}`,onClick:()=>p(e),children:e>=24?`${e/24}d`:`${e}h`},e))]}),(0,M.jsx)(`div`,{className:`timeline-rail`,children:(0,M.jsx)(`div`,{ref:g,className:`timeline-stepper`,children:(0,M.jsx)(`div`,{className:`timeline-stepper-track`,children:b.map((e,a)=>{let o=ah(e=>e===s?null:s),"aria-expanded":c,children:[(0,M.jsxs)(`span`,{className:`timeline-group-chip-top`,children:[(0,M.jsxs)(`span`,{className:`timeline-group-count`,children:[`x`,e.items.length]}),(0,M.jsx)(`span`,{className:`timeline-group-chevron`,"aria-hidden":!0,children:c?`v`:`>`})]}),(0,M.jsx)(`span`,{className:`timeline-group-type`,children:e.eventType}),(0,M.jsxs)(`span`,{className:`timeline-group-range`,children:[`steps `,e.startIndex+1,`-`,e.startIndex+e.items.length,` seq #`,e.items[0].sequenceNumber,`-#`,e.items[e.items.length-1].sequenceNumber]})]}),o,c&&(0,M.jsxs)(`div`,{className:`timeline-expanded-deck`,children:[(0,M.jsxs)(`div`,{className:`timeline-expanded-head`,children:[(0,M.jsx)(`div`,{className:`timeline-expanded-title`,children:e.eventType}),(0,M.jsxs)(`div`,{className:`timeline-group-range`,children:[`Expanded run of `,e.items.length,` events`]}),(0,M.jsx)(`button`,{type:`button`,className:`timeline-collapse-btn`,onClick:()=>h(null),children:`Collapse`})]}),(0,M.jsx)(`div`,{className:`timeline-stepper-track`,children:e.items.map((a,o)=>{let c={kind:`group-item`,key:`group-item-${a.sequenceNumber}`,event:a,stepNumber:e.startIndex+o+1,parentKey:s};return(0,M.jsxs)(`div`,{className:`timeline-track-item`,children:[ta(c,t,n,r,i),or(Number(e.target.value)),"aria-label":`Scrub event sequence`}),(0,M.jsxs)(`div`,{className:`timeline-info`,children:[(0,M.jsxs)(`span`,{className:`timeline-info-edge`,children:[`First seq #`,w]}),(0,M.jsx)(`span`,{className:`timeline-info-center`,children:t==null?`Select an event`:(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`strong`,{children:[`Selected seq #`,t]}),n!=null&&` compared with #${n}`,(0,M.jsxs)(`span`,{className:`timeline-info-muted`,children:[` across `,b.length,` visible segments`]})]})}),(0,M.jsxs)(`span`,{className:`timeline-info-edge`,children:[`Last seq #`,ee]})]})]})}function ra(e,t,n=!0){return N({queryKey:[`transitions`,e,t??`default`],queryFn:()=>Mi(e,t),enabled:n&&!!e})}function ia(e,t,n=!0){let[r,i]=(0,j.useState)([]),[a,o]=(0,j.useState)(0),[s,c]=(0,j.useState)(!1),l=(0,j.useMemo)(()=>`${Date.now()}-${Math.random()}`,[e,t]);return(0,j.useEffect)(()=>{if(!n){i([]),o(0),c(!1);return}let r=new Worker(new URL(`/assets/json-diff.worker-DTKMBXSt.js`,``+import.meta.url),{type:`module`});c(!0);let a=e=>{e.data.requestId===l&&(i(e.data.patches),o(e.data.durationMs),c(!1))};return r.addEventListener(`message`,a),r.postMessage({left:e,right:t,requestId:l}),()=>{r.removeEventListener(`message`,a),r.terminate()}},[n,e,t,l]),{patches:r,durationMs:a,loading:s}}function aa({diff:e,patches:t,title:n=`Changes`}){let r=t&&t.length>0?t.map(e=>({field:e.path,oldValue:e.oldValue,newValue:e.newValue,kind:e.type})):Object.entries(e??{}).map(([e,t])=>({field:e,oldValue:t.oldValue,newValue:t.newValue,kind:`changed`}));return r.length?(0,M.jsxs)(`div`,{className:`diff-panel`,children:[(0,M.jsx)(`div`,{className:`diff-toolbar`,children:(0,M.jsxs)(`div`,{className:`diff-toolbar-title`,children:[n,(0,M.jsxs)(`span`,{className:`diff-count-badge`,"aria-live":`polite`,children:[r.length,` `,r.length===1?`change`:`changes`]})]})}),(0,M.jsx)(`div`,{className:`diff-body`,children:(0,M.jsx)(`div`,{className:`diff-scroll`,children:(0,M.jsxs)(`div`,{className:`diff-list diff-list-split`,children:[(0,M.jsxs)(`div`,{className:`diff-split-head`,children:[(0,M.jsx)(`span`,{className:`diff-split-label diff-split-old-label`,children:`Before`}),(0,M.jsx)(`span`,{className:`diff-split-label diff-split-new-label`,children:`After`})]}),r.map((e,t)=>(0,M.jsxs)(`div`,{className:`diff-split-row diff-split-row--${e.kind}`,children:[(0,M.jsx)(`span`,{className:`diff-line-no`,"aria-hidden":!0,children:t+1}),(0,M.jsxs)(`div`,{className:`diff-split-cells`,children:[(0,M.jsxs)(`div`,{className:`diff-split-cell diff-split-old`,children:[(0,M.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,M.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.oldValue)})]}),(0,M.jsxs)(`div`,{className:`diff-split-cell diff-split-new`,children:[(0,M.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,M.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.newValue)})]})]})]},`${e.field}-${t}`))]})})})]}):null}function oa({open:e,onToggle:t}){return(0,M.jsx)(`button`,{type:`button`,className:`json-tree-toggle`,onClick:e=>{e.stopPropagation(),t()},"aria-expanded":e,"aria-label":e?`Collapse`:`Expand`,children:e?`-`:`+`})}function sa({value:e}){return e===null?(0,M.jsx)(`span`,{className:`json-null`,children:`null`}):typeof e==`boolean`?(0,M.jsx)(`span`,{className:`json-boolean`,children:String(e)}):typeof e==`number`?(0,M.jsx)(`span`,{className:`json-number`,children:e}):(0,M.jsx)(`span`,{className:`json-string`,children:JSON.stringify(e)})}function ca({value:e,changedKeys:t}){return(0,M.jsx)(`div`,{className:`json-tree json-tree-root`,children:(0,M.jsx)(la,{value:e,depth:0,changedKeys:t,keyPath:``})})}function la({value:e,depth:t,propertyKey:n,changedKeys:r,keyPath:i=``}){let a=r&&n!==void 0&&r.has(n),o=r&&i&&[...r].some(e=>e.startsWith(i+`.`)),[s,c]=(0,j.useState)(r?t<3||!!a||!!o:t<3),l={paddingLeft:Math.min(t,12)*14},u=a?{background:`rgba(255, 170, 0, 0.12)`,borderRadius:3}:{};if(e===null||typeof e==`boolean`||typeof e==`number`||typeof e==`string`)return(0,M.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(sa,{value:e})]});let d=e=>i?`${i}.${e}`:e;if(Array.isArray(e))return e.length===0?(0,M.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`[]`})]}):(0,M.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,M.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,M.jsx)(oa,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`[`}),!s&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,e.length,` items `]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`]`})]})]}),s&&(0,M.jsxs)(M.Fragment,{children:[e.map((e,n)=>(0,M.jsx)(la,{value:e,depth:t+1,changedKeys:r,keyPath:d(String(n))},n)),(0,M.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,M.jsx)(`span`,{className:`json-punct`,children:`]`})})]})]});if(typeof e==`object`){let i=Object.entries(e);return i.length===0?(0,M.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`{}`})]}):(0,M.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,M.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,M.jsx)(oa,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`{`}),!s&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,i.length,` keys `]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`}`})]})]}),s&&(0,M.jsxs)(M.Fragment,{children:[i.map(([e,n])=>(0,M.jsx)(la,{value:n,depth:t+1,propertyKey:e,changedKeys:r,keyPath:d(e)},e)),(0,M.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,M.jsx)(`span`,{className:`json-punct`,children:`}`})})]})]})}return(0,M.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,M.jsx)(`span`,{className:`json-unknown`,children:String(e)})})}var ua=[{id:`changes`,label:`Changes`},{id:`before-after`,label:`Before / After`},{id:`raw`,label:`Raw JSON`}];function da({aggregateId:e,sequence:t,compareSequence:n,activeTab:r,onTabChange:i,active:a=!1,onActivate:o,source:s}){let{data:c=[],isLoading:l}=ra(e,s),[u,d]=(0,j.useState)(`changes`),f=r??u,p=e=>{d(e),i?.(e)},m=c.find(e=>e.event.sequenceNumber===t)??null,h=n==null?null:c.find(e=>e.event.sequenceNumber===n)??null,g=m!=null&&h!=null&&m.event.sequenceNumber!==h.event.sequenceNumber,_=g?h.stateAfter:m?.stateBefore,v=m?.stateAfter,{patches:y,loading:b,durationMs:x}=ia(_,v,g&&!!_&&!!v),S=(0,j.useMemo)(()=>g?new Set(y.map(e=>e.path.replace(/^\$\./,``).split(`.`)[0])):new Set(Object.keys(m?.diff??{})),[g,y,m?.diff]);return l?(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`State`}),(0,M.jsx)(`div`,{className:`skeleton`,style:{height:160}})]}):m?(0,M.jsxs)(`section`,{className:`card`,role:`region`,"aria-label":`State viewer`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,M.jsxs)(`div`,{className:`card-title`,children:[`State at Event #`,m.event.sequenceNumber,(0,M.jsx)(`span`,{className:`diff-count-badge`,children:m.event.eventType}),g&&(0,M.jsxs)(`span`,{className:`diff-count-badge`,children:[`Compared with #`,h?.event.sequenceNumber]}),g&&!b&&(0,M.jsxs)(`span`,{className:`diff-count-badge`,children:[`Worker `,x.toFixed(1),`ms`]})]}),(0,M.jsx)(`div`,{className:`state-tabs`,role:`tablist`,children:ua.map(e=>(0,M.jsx)(`button`,{type:`button`,role:`tab`,"aria-selected":f===e.id,className:`state-tab ${f===e.id?`active`:``}`,onClick:()=>p(e.id),children:e.label},e.id))}),(0,M.jsxs)(`div`,{className:`state-tab-content`,role:`tabpanel`,children:[f===`changes`&&(0,M.jsx)(`div`,{children:g?b?(0,M.jsx)(`div`,{className:`skeleton`,style:{height:120}}):(0,M.jsx)(aa,{patches:y,title:`Structural diff`}):m.diff&&Object.keys(m.diff).length>0?(0,M.jsx)(aa,{diff:m.diff}):(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`,marginTop:12,fontSize:13},children:`No field changes at this event.`})}),f===`before-after`&&(0,M.jsxs)(`div`,{className:`state-grid`,style:{marginTop:12},children:[(0,M.jsxs)(`div`,{className:`state-panel state-panel-before`,children:[(0,M.jsx)(`h4`,{children:g?`Event #${h?.event.sequenceNumber}`:`Before`}),(0,M.jsx)(ca,{value:_??{},changedKeys:S})]}),(0,M.jsxs)(`div`,{className:`state-panel state-panel-after`,children:[(0,M.jsx)(`h4`,{children:g?`Event #${m.event.sequenceNumber}`:`After`}),(0,M.jsx)(ca,{value:v??{},changedKeys:S})]})]}),f===`raw`&&(0,M.jsx)(`div`,{style:{marginTop:12},children:(0,M.jsx)(`div`,{className:`json-block`,style:{maxHeight:340},children:JSON.stringify(g?{leftEvent:h?.event,rightEvent:m.event,leftState:_,rightState:v}:m.event,null,2)})})]})]}):null}var fa=1e3,pa=3e4;function ma(e,t,n){let r=n?.enabled??!0,[i,a]=(0,j.useState)(()=>r?`connecting`:`connected`),o=(0,j.useRef)(null),s=(0,j.useRef)(t),c=(0,j.useRef)(fa);return s.current=t,(0,j.useEffect)(()=>{if(!r)return a(`connected`),()=>{};let t=!1,n,i=()=>{if(t)return;let r=new WebSocket(`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.host}${e}`);o.current=r,r.onopen=()=>{c.current=fa,a(`connected`)},r.onclose=()=>{if(a(`disconnected`),!t){let e=c.current;n=setTimeout(()=>{c.current=Math.min(e*2,pa),i()},e)}},r.onerror=()=>a(`disconnected`),r.onmessage=e=>{try{let t=JSON.parse(e.data);s.current(t)}catch{}}};return i(),()=>{t=!0,clearTimeout(n),o.current?.close()}},[e,r]),i}var ha=(0,j.createContext)(void 0);function ga({children:e}){let[t,n]=(0,j.useState)([]),r=(0,j.useCallback)(e=>{let t=Date.now();n(n=>[...n,{id:t,message:e}]),setTimeout(()=>{n(e=>e.filter(e=>e.id!==t))},4e3)},[]);return(0,M.jsxs)(ha.Provider,{value:{notify:r},children:[e,(0,M.jsx)(`div`,{className:`toast-container`,children:t.map(e=>(0,M.jsx)(`div`,{className:`toast`,children:e.message},e.id))})]})}function _a(){let e=(0,j.useContext)(ha);if(!e)throw Error(`useToast must be used within ToastProvider`);return e}function va(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`type-deleted`:t.includes(`withdrawn`)||t.includes(`debit`)?`type-withdrawn`:t.includes(`deposited`)||t.includes(`credit`)?`type-deposited`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`type-created`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`type-completed`:t.includes(`failed`)||t.includes(`error`)?`type-failed`:t.includes(`transfer`)?`type-transfer`:`type-default`}function ya(e){return va(e)}var ba=100;function xa(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`✖`:t.includes(`withdrawn`)||t.includes(`debit`)?`↩`:t.includes(`deposited`)||t.includes(`credit`)?`↪`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`✦`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)?`✔`:t.includes(`failed`)||t.includes(`error`)?`⚠`:t.includes(`transfer`)?`⇄`:`◆`}function Sa({source:e,onSelectAggregate:t}){return(0,M.jsx)(Ta,{source:e,onSelectAggregate:t})}function Ca(e){return`type`in e&&e.type===`NO_LIVE_STREAM`}function wa(e){return e?`/ws/live?source=${encodeURIComponent(e)}`:`/ws/live`}function Ta({source:e,onSelectAggregate:t}){let n=R(),[r,i]=(0,j.useState)(()=>n?Ci():[]),[a,o]=(0,j.useState)(!1),[s,c]=(0,j.useState)(null),l=(0,j.useRef)(null),u=(0,j.useRef)(a);u.current=a;let{notify:d}=_a();(0,j.useEffect)(()=>{c(null),i(n?Ci():[])},[e,n]);let f=ma(wa(e),e=>{if(Ca(e)){c(e.source),i([]);return}c(null),!u.current&&i(t=>[...t.slice(-(ba-1)),e])},{enabled:!n}),p=(0,j.useRef)(d);p.current=d;let m=(0,j.useRef)(0);return(0,j.useEffect)(()=>{n||(f===`disconnected`?(m.current++,m.current<=1&&p.current(`Live stream disconnected. Retrying…`)):f===`connected`&&(m.current=0))},[f,n]),(0,j.useEffect)(()=>{!a&&l.current&&(l.current.scrollTop=l.current.scrollHeight)},[r,a]),(0,j.useEffect)(()=>{let e=()=>o(e=>!e);return window.addEventListener(`eventlens:togglestream`,e),()=>window.removeEventListener(`eventlens:togglestream`,e)},[]),(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsxs)(`div`,{className:`live-header`,children:[(0,M.jsx)(`div`,{className:`card-title`,style:{marginBottom:0},children:`📡 Live Event Stream`}),(0,M.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,M.jsxs)(`div`,{className:`live-indicator`,children:[(0,M.jsx)(`span`,{className:`dot ${f===`connected`?`dot-green`:f===`connecting`?`dot-yellow`:`dot-red`}`}),(0,M.jsx)(`span`,{style:{color:f===`connected`?`var(--neon-green)`:`var(--text-muted)`,fontSize:11},children:f===`connected`?a?`Paused`:`Live`:f})]}),(0,M.jsx)(`button`,{className:`pause-btn`,onClick:()=>o(!a),children:a?`▶ Resume`:`⏸ Pause`})]})]}),(0,M.jsxs)(`div`,{className:`event-stream`,ref:l,children:[s&&(0,M.jsxs)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:[`Live stream not available for this source`,e?` (${e})`:s?` (${s})`:``,`.`]}),r.length===0&&(0,M.jsx)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:s?null:n?`Demo stream (static sample events)`:`Waiting for events…`}),r.map(e=>(0,M.jsxs)(`div`,{className:`event-row ${ya(e.eventType)}`,children:[(0,M.jsx)(`span`,{className:`event-icon`,children:xa(e.eventType)}),(0,M.jsx)(`span`,{className:`event-time`,children:Gi(e.timestamp).toLocaleTimeString()}),(0,M.jsx)(`span`,{className:`event-type ${va(e.eventType)}`,children:e.eventType}),t?(0,M.jsx)(`button`,{type:`button`,className:`event-agg event-agg-button`,onClick:()=>t(e.aggregateId),children:e.aggregateId}):(0,M.jsx)(`span`,{className:`event-agg`,children:e.aggregateId})]},e.eventId))]})]})}function Ea(e){switch(e){case`CRITICAL`:return`sev-critical`;case`HIGH`:return`sev-high`;case`MEDIUM`:return`sev-medium`;case`LOW`:return`sev-low`;default:return`sev-low`}}function Da(e){switch(e){case`CRITICAL`:return`Critical`;case`HIGH`:return`High`;case`MEDIUM`:return`Warning`;case`LOW`:return`Info`;default:return e}}function Oa(){return(0,M.jsxs)(`svg`,{viewBox:`0 0 64 64`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,M.jsx)(`defs`,{children:(0,M.jsxs)(`linearGradient`,{id:`shield-grad`,x1:`16`,y1:`8`,x2:`48`,y2:`56`,children:[(0,M.jsx)(`stop`,{offset:`0%`,stopColor:`#00ff88`,stopOpacity:`0.9`}),(0,M.jsx)(`stop`,{offset:`100%`,stopColor:`#00cc66`,stopOpacity:`0.6`})]})}),(0,M.jsx)(`path`,{d:`M32 4 L52 14 L52 32 C52 46 32 58 32 58 C32 58 12 46 12 32 L12 14 Z`,stroke:`url(#shield-grad)`,strokeWidth:`2`,fill:`rgba(0, 255, 136, 0.06)`}),(0,M.jsx)(`path`,{d:`M32 10 L48 18 L48 32 C48 43 32 53 32 53 C32 53 16 43 16 32 L16 18 Z`,stroke:`rgba(0, 255, 136, 0.2)`,strokeWidth:`1`,fill:`none`}),(0,M.jsx)(`polyline`,{points:`22,32 29,40 42,24`,stroke:`#00ff88`,strokeWidth:`3`,strokeLinecap:`round`,strokeLinejoin:`round`,fill:`none`})]})}function ka({color:e}){return(0,M.jsx)(`div`,{className:`gauge-wave`,children:Array.from({length:12},(e,t)=>t).map(t=>(0,M.jsx)(`div`,{className:`gauge-wave-bar ${e}`,style:{animationDelay:`${t*.12}s`}},t))})}function Aa({source:e,onSelectAggregate:t}){let{data:n,isLoading:r}=N({queryKey:[`anomalies`,e??`default`],queryFn:()=>B(100,e),refetchInterval:3e4}),i=n&&n.length>0;return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsxs)(`div`,{className:`card-title anomaly-card-title-row`,children:[(0,M.jsx)(`span`,{className:`anomaly-title-text`,children:`⚠️ Anomaly Detection`}),!r&&i&&(0,M.jsx)(`span`,{className:`anomaly-header-count`,"aria-label":`${n.length} anomalies`,children:n.length})]}),r&&(0,M.jsx)(`div`,{className:`skeleton`,style:{height:120}}),!r&&!i&&(0,M.jsxs)(`div`,{className:`anomaly-panel-inner`,children:[(0,M.jsxs)(`div`,{className:`anomaly-shield`,children:[(0,M.jsx)(`div`,{className:`shield-icon`,children:(0,M.jsx)(Oa,{})}),(0,M.jsx)(`div`,{className:`shield-text`,children:`No anomalies detected`})]}),(0,M.jsxs)(`div`,{className:`gauge-row`,children:[(0,M.jsxs)(`div`,{className:`gauge-card optimal`,children:[(0,M.jsx)(`div`,{className:`gauge-label`,children:`Data Integrity`}),(0,M.jsx)(`div`,{className:`gauge-value optimal`,children:`OPTIMAL`}),(0,M.jsx)(ka,{color:`green`})]}),(0,M.jsxs)(`div`,{className:`gauge-card baseline`,children:[(0,M.jsx)(`div`,{className:`gauge-label`,children:`Pattern Scan`}),(0,M.jsx)(`div`,{className:`gauge-value baseline`,children:`BASELINE`}),(0,M.jsx)(ka,{color:`cyan`})]}),(0,M.jsxs)(`div`,{className:`gauge-card zero`,children:[(0,M.jsx)(`div`,{className:`gauge-label`,children:`Threat Level`}),(0,M.jsx)(`div`,{className:`gauge-value zero`,children:`ZERO`}),(0,M.jsx)(ka,{color:`green`})]})]})]}),!r&&i&&(0,M.jsx)(`div`,{className:`anomaly-scroll-region`,children:(0,M.jsx)(`div`,{className:`anomaly-list-inner`,children:n.map((e,n)=>(0,M.jsxs)(`details`,{className:`anomaly-card ${e.severity}`,children:[(0,M.jsxs)(`summary`,{className:`anomaly-card-summary`,children:[(0,M.jsx)(`span`,{className:`anomaly-severity-badge ${Ea(e.severity)}`,children:Da(e.severity)}),(0,M.jsx)(`span`,{className:`anomaly-card-title`,children:e.description}),(0,M.jsx)(`span`,{className:`anomaly-card-chevron`,"aria-hidden":!0,children:`▼`})]}),(0,M.jsxs)(`div`,{className:`anomaly-card-body`,children:[(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Aggregate`}),t?(0,M.jsx)(`button`,{type:`button`,className:`anomaly-aggregate-link`,onClick:()=>t(e.aggregateId),children:(0,M.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})}):(0,M.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Sequence`}),(0,M.jsxs)(`span`,{className:`anomaly-meta-value`,children:[`#`,e.atSequence]})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Event type`}),(0,M.jsx)(`span`,{className:`anomaly-meta-value`,children:e.triggeringEventType})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`When`}),(0,M.jsx)(`span`,{className:`anomaly-meta-value`,children:Gi(e.timestamp).toLocaleString()})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Code`}),(0,M.jsx)(`code`,{className:`anomaly-meta-value`,children:e.code})]})]})]},`${e.aggregateId}-${e.atSequence}-${n}`))})})]})}var ja=[{keys:`j / k`,desc:`Move selection through the timeline`},{keys:`Shift+Click`,desc:`Pick a compare event for diff mode`},{keys:`1 - 3`,desc:`Switch state tabs`},{keys:`Ctrl/Cmd+K`,desc:`Open command palette`},{keys:`/`,desc:`Focus aggregate search`},{keys:`Space`,desc:`Pause or resume live stream`},{keys:`?`,desc:`Toggle shortcut help`}];function Ma(){let[e,t]=(0,j.useState)(!1);return(0,j.useEffect)(()=>{let e=e=>{e.target.tagName!==`INPUT`&&e.key===`?`&&(e.preventDefault(),t(e=>!e))};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]),(0,M.jsx)(`div`,{className:`keyboard-hints ${e?`keyboard-hints--expanded`:``}`,"aria-label":`Keyboard shortcuts`,children:e?(0,M.jsxs)(`div`,{className:`keyboard-hints-grid`,children:[ja.map(e=>(0,M.jsxs)(`div`,{className:`keyboard-hint-row`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key`,children:e.keys}),(0,M.jsx)(`span`,{className:`keyboard-hint-desc`,children:e.desc})]},e.keys)),(0,M.jsx)(`button`,{type:`button`,className:`keyboard-hints-close`,onClick:()=>t(!1),"aria-label":`Close shortcuts`,children:`Close`})]}):(0,M.jsxs)(`div`,{className:`keyboard-hints-bar`,children:[(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`j/k`}),` Navigate`]}),(0,M.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Shift+Click`}),` Compare`]}),(0,M.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Ctrl/Cmd+K`}),` Palette`]}),(0,M.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`?`}),` All shortcuts`]})]})})}var Na=[2,3,5,10];function Pa({transitions:e,selectedSequence:t,compareSequence:n,onSelectSequence:r,onClearCompare:i,active:a=!1,onActivate:o}){let[s,c]=(0,j.useState)(null),[l,u]=(0,j.useState)(3),[d,f]=(0,j.useState)(!1);if((0,j.useEffect)(()=>{n==null&&s!=null&&(s.type===`autoplay`&&f(!0),r(s.sequence),c(null))},[n,r,s]),!e.length)return null;let p=e.find(e=>e.event.sequenceNumber===t)??e[0],m=e.findIndex(e=>e.event.sequenceNumber===p.event.sequenceNumber),h=n!=null&&n!==p.event.sequenceNumber,g=m>=e.length-1,_=m<=0;(0,j.useEffect)(()=>{h&&d&&f(!1)},[h,d]),(0,j.useEffect)(()=>{if(!d||g){g&&d&&f(!1);return}let t=window.setInterval(()=>{let t=e[Math.min(m+1,e.length-1)];if(!t||t.event.sequenceNumber===p.event.sequenceNumber){f(!1);return}r(t.event.sequenceNumber)},l*1e3);return()=>window.clearInterval(t)},[g,l,p.event.sequenceNumber,m,d,r,e]);let v=e=>{if(e!==p.event.sequenceNumber){if(h&&i){c({type:`select`,sequence:e});return}f(!1),r(e)}},y=()=>{if(d){f(!1);return}if(g)return;let t=e[Math.min(m+1,e.length-1)].event.sequenceNumber;if(h&&i){c({type:`autoplay`,sequence:t});return}f(!0)};return(0,M.jsxs)(`section`,{className:`card replay-debugger`,tabIndex:0,role:`region`,"aria-label":`Replay debugger`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Replay Debugger`}),(0,M.jsxs)(`div`,{className:`replay-toolbar`,children:[(0,M.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.max(m-1,0)].event.sequenceNumber),title:`Step backward`,"aria-label":`Replay previous event`,disabled:_,children:`Previous`}),(0,M.jsxs)(`div`,{className:`replay-controls-center`,children:[(0,M.jsxs)(`div`,{className:`replay-position`,children:[`Event `,m+1,` of `,e.length]}),(0,M.jsxs)(`div`,{className:`replay-auto-controls`,children:[(0,M.jsx)(`button`,{type:`button`,className:`replay-auto-toggle ${d?`active`:``}`,onClick:y,disabled:g,"aria-pressed":d,children:d?`Pause Auto Replay`:`Start Auto Replay`}),(0,M.jsxs)(`label`,{className:`replay-interval-picker`,children:[(0,M.jsx)(`span`,{children:`Speed`}),(0,M.jsx)(`select`,{value:l,onChange:e=>u(Number(e.target.value)),disabled:d,"aria-label":`Auto replay interval`,children:Na.map(e=>(0,M.jsxs)(`option`,{value:e,children:[e,`s / event`]},e))})]})]})]}),(0,M.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.min(m+1,e.length-1)].event.sequenceNumber),title:`Step forward`,"aria-label":`Replay next event`,disabled:g,children:`Next`})]}),h&&(0,M.jsxs)(`div`,{className:`replay-compare-warning`,role:`alert`,"aria-live":`polite`,children:[(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`strong`,{children:`Compare mode is active.`}),` Replay stepping or auto replay will replace the comparison and continue from the selected event.`]}),s==null?(0,M.jsx)(`div`,{className:`replay-warning-hint`,children:`Use Previous, Next, the slider, or auto replay. We’ll ask before clearing compare mode.`}):(0,M.jsxs)(`div`,{className:`replay-compare-actions`,children:[(0,M.jsx)(`button`,{type:`button`,className:`replay-warning-btn replay-warning-btn--primary`,onClick:()=>i?.(),children:s.type===`autoplay`?`Start replay and replace compare`:`Continue and replace compare`}),(0,M.jsx)(`button`,{type:`button`,className:`replay-warning-btn`,onClick:()=>c(null),children:`Keep compare`})]})]}),(0,M.jsx)(`input`,{type:`range`,min:0,max:Math.max(e.length-1,0),value:m,onChange:t=>v(e[Number(t.target.value)].event.sequenceNumber),"aria-label":`Replay position`}),(0,M.jsxs)(`div`,{className:`state-panel replay-state`,children:[(0,M.jsx)(`h4`,{children:`Current State`}),(0,M.jsx)(ca,{value:p.stateAfter,changedKeys:new Set(Object.keys(p.diff))})]})]})}function Fa(e,t=1,n=24){return N({queryKey:[`statistics`,e??`default`,t,n],queryFn:()=>Ni(e,t,n),staleTime:3e4})}function Ia({source:e,onBack:t}){let[n,r]=(0,j.useState)(24),{data:i,isLoading:a}=Fa(e,1,n);return(0,M.jsxs)(`section`,{className:`card statistics-panel`,role:`region`,"aria-label":`Statistics panel`,"aria-busy":a,children:[(0,M.jsxs)(`div`,{className:`statistics-panel-header`,children:[(0,M.jsxs)(`div`,{className:`card-title`,children:[`Statistics `,e?`- ${e}`:``]}),(0,M.jsx)(`button`,{type:`button`,className:`statistics-back-btn`,onClick:t,children:`Back to workspace`})]}),(0,M.jsx)(`div`,{className:`statistics-toolbar`,children:[6,24,72].map(e=>(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${n===e?`active`:``}`,onClick:()=>r(e),children:e===72?`3d`:`${e}h`},e))}),a&&(0,M.jsx)(`div`,{className:`skeleton`,style:{height:140}}),!a&&i&&!i.available&&(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`},children:i.message??`Statistics not available.`}),!a&&i?.available&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`div`,{className:`stats-kpis`,children:[(0,M.jsxs)(`div`,{className:`stat-card`,children:[(0,M.jsx)(`strong`,{children:i.totalEvents}),(0,M.jsx)(`span`,{children:`Total events`})]}),(0,M.jsxs)(`div`,{className:`stat-card`,children:[(0,M.jsx)(`strong`,{children:i.distinctAggregates}),(0,M.jsx)(`span`,{children:`Aggregates`})]}),(0,M.jsxs)(`div`,{className:`stat-card`,children:[(0,M.jsx)(`strong`,{children:i.eventTypes.length}),(0,M.jsx)(`span`,{children:`Event types`})]})]}),(0,M.jsx)(`div`,{className:`stats-chart`,children:i.throughput.map(e=>(0,M.jsxs)(`div`,{className:`stats-bar-row`,children:[(0,M.jsx)(`span`,{children:e.bucket.slice(11,16)}),(0,M.jsx)(`div`,{className:`stats-bar-track`,children:(0,M.jsx)(`div`,{className:`stats-bar-fill`,style:{width:`${Math.max(8,e.count/Math.max(...i.throughput.map(e=>e.count),1)*100)}%`}})}),(0,M.jsx)(`strong`,{children:e.count})]},e.bucket))}),(0,M.jsxs)(`div`,{className:`stats-distribution`,children:[(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`h4`,{children:`Event Types`}),i.eventTypes.map(e=>(0,M.jsxs)(`div`,{className:`stats-list-row`,children:[(0,M.jsx)(`span`,{children:e.type}),(0,M.jsx)(`strong`,{children:e.count})]},e.type))]}),(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`h4`,{children:`Aggregate Types`}),i.aggregateTypes.map(e=>(0,M.jsxs)(`div`,{className:`stats-list-row`,children:[(0,M.jsx)(`span`,{children:e.type}),(0,M.jsx)(`strong`,{children:e.count})]},e.type))]})]})]})]})}function La({open:e,selectedSource:t,onClose:n,onSelectAggregate:r,onOpenHome:i,onOpenStats:a}){let[o,s]=(0,j.useState)(``),[c,l]=(0,j.useState)(0),[u,d]=(0,j.useState)(null),{data:f=[]}=N({queryKey:[`palette-search`,o,t??`default`],queryFn:()=>Ai(o,8,t),enabled:e&&o.trim().length>=2,staleTime:5e3});(0,j.useEffect)(()=>{e?(l(0),d(document.activeElement instanceof HTMLElement?document.activeElement:null)):(s(``),u?.focus())},[e,u]);let p=(0,j.useMemo)(()=>{let e=[{id:`home`,label:`Go to main page`,action:i},{id:`stats`,label:`Go to statistics panel`,action:a}],t=f.map(e=>({id:`agg-${e}`,label:`Open aggregate ${e}`,action:()=>r(e)}));return o.trim()&&f.length===0&&e.unshift({id:`direct`,label:`Open aggregate ${o.trim()}`,action:()=>r(o.trim())}),[...t,...e]},[f,i,a,r,o]);return(0,j.useEffect)(()=>{if(!e)return;let t=e=>{e.key===`ArrowDown`?(e.preventDefault(),l(e=>Math.min(e+1,Math.max(p.length-1,0)))):e.key===`ArrowUp`?(e.preventDefault(),l(e=>Math.max(e-1,0))):e.key===`Enter`&&(e.preventDefault(),p[c]?.action(),n())};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[p,n,e,c]),e?(0,M.jsx)(`div`,{className:`command-palette-backdrop`,onClick:n,children:(0,M.jsxs)(`div`,{className:`command-palette`,role:`dialog`,"aria-modal":`true`,"aria-label":`Command palette`,"aria-describedby":`command-palette-help`,onClick:e=>e.stopPropagation(),children:[(0,M.jsx)(`p`,{id:`command-palette-help`,className:`sr-only`,children:`Search aggregates, return to the main page, or open statistics. Use arrow keys to move and Enter to confirm.`}),(0,M.jsx)(`input`,{autoFocus:!0,className:`command-palette-input`,placeholder:`Search aggregates or jump to a panel`,value:o,onChange:e=>s(e.target.value),"aria-label":`Command palette search`}),(0,M.jsxs)(`ul`,{className:`command-palette-list`,role:`listbox`,"aria-label":`Command results`,children:[p.map((e,t)=>(0,M.jsx)(`li`,{className:`command-palette-item ${t===c?`active`:``}`,role:`option`,"aria-selected":t===c,onMouseEnter:()=>l(t),onClick:()=>{e.action(),n()},children:e.label},e.id)),p.length===0&&(0,M.jsx)(`li`,{className:`command-palette-item muted`,children:`Type at least two characters to search.`})]})]})}):null}function Ra({paletteOpen:e,onOpenPalette:t,onClosePalette:n}){return(0,j.useEffect)(()=>{let r=r=>{let i=r.target,a=i&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(i.tagName);if((r.metaKey||r.ctrlKey)&&r.key.toLowerCase()===`k`){r.preventDefault(),t();return}if(r.key===`/`&&!a){r.preventDefault(),document.getElementById(`aggregate-search`)?.focus();return}if(r.key===`Escape`&&e){r.preventDefault(),n();return}!a&&(r.key===`j`||r.key===`k`)&&window.dispatchEvent(new CustomEvent(`eventlens:timeline-step`,{detail:r.key===`j`?1:-1}))};return window.addEventListener(`keydown`,r),()=>window.removeEventListener(`keydown`,r)},[e,n,t]),null}function za(e){let t=e.toLowerCase();return t===`ready`||t===`up`}function Ba(e){return e.toLowerCase()===`ready`}function Va(){let e=[6,12,8,16,10,14,7,11,15,9];return(0,M.jsx)(`div`,{className:`mini-wave`,"aria-hidden":!0,children:e.map((e,t)=>(0,M.jsx)(`div`,{className:`mini-wave-bar`,style:{height:e,animationDelay:`${t*.1}s`}},t))})}function Ha({isUp:e,selectedSource:t,fallbackCount:n}){let[r,i]=(0,j.useState)(0),[a,o]=(0,j.useState)(n),s=(0,j.useRef)(null);(0,j.useEffect)(()=>{let e=Date.now();return s.current=setInterval(()=>{i(Math.floor((Date.now()-e)/1e3))},1e3),()=>{s.current&&clearInterval(s.current)}},[]),(0,j.useEffect)(()=>{o(n)},[n]),(0,j.useEffect)(()=>{let e=!1,r=()=>{V(500,t||null).then(t=>{e||o(t.length)}).catch(()=>{!e&&n!=null&&o(n)})};r();let i=setInterval(r,15e3);return()=>{e=!0,clearInterval(i)}},[n,t]);let c=e=>{let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60;return t>0?`${t}h ${n}m`:n>0?`${n}m ${r}s`:`${r}s`};return(0,M.jsxs)(`div`,{className:`conn-stats`,"aria-label":`Connection metrics`,children:[(0,M.jsx)(Va,{}),(0,M.jsxs)(`div`,{className:`conn-stat`,children:[(0,M.jsx)(`span`,{className:`conn-stat-label`,children:`API`}),(0,M.jsx)(`span`,{className:`conn-stat-value ${e?`green`:`amber`}`,children:e?`Healthy`:`Down`})]}),(0,M.jsxs)(`div`,{className:`conn-stat conn-stat--metric`,children:[(0,M.jsx)(`span`,{className:`conn-stat-label`,children:`Events`}),(0,M.jsx)(`span`,{className:`conn-stat-value`,children:a??`...`})]}),(0,M.jsxs)(`div`,{className:`conn-stat`,children:[(0,M.jsx)(`span`,{className:`conn-stat-label`,children:`Uptime`}),(0,M.jsx)(`span`,{className:`conn-stat-value green conn-stat-value--uptime`,children:c(r)})]})]})}function Ua(){let e=Qe(),[t,n]=(0,j.useState)(`state`),[r,i]=(0,j.useState)(null),[a,o]=(0,j.useState)(null),[s,c]=(0,j.useState)(null),[l,u]=(0,j.useState)(`changes`),[d,f]=(0,j.useState)(``),[p,m]=(0,j.useState)(window.location.hash||``),[h,g]=(0,j.useState)(!1),[_,v]=(0,j.useState)(!1),[y,b]=(0,j.useState)(``),[x,S]=(0,j.useState)(``),[C,w]=(0,j.useState)(null),[ee,te]=(0,j.useState)(!1),[ne,T]=(0,j.useState)(!1),[re,ie]=(0,j.useState)(null);(0,j.useEffect)(()=>{let e=()=>m(window.location.hash||``);return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,j.useEffect)(()=>{p===`#/plugins`&&(window.location.hash=`#/timeline`)},[p]),(0,j.useEffect)(()=>{let e=e=>{let t=e.detail;t&&u(t)};return window.addEventListener(`eventlens:switchtab`,e),()=>window.removeEventListener(`eventlens:switchtab`,e)},[]),(0,j.useEffect)(()=>{let e=new URLSearchParams(window.location.search),t=e.get(`authError`),r=e.get(`aggregateId`),a=e.get(`seq`),s=e.get(`compare`),l=e.get(`tab`),d=e.get(`panel`),p=e.get(`source`);if(t){ie(t.replace(/_/g,` `)),e.delete(`authError`);let n=e.toString(),r=`${window.location.pathname}${n?`?${n}`:``}${window.location.hash||`#/timeline`}`;window.history.replaceState(null,``,r)}r&&i(r),a&&o(Number(a)),s&&c(Number(s)),l&&[`changes`,`before-after`,`raw`].includes(l)&&u(l),(d===`replay`||d===`state`)&&n(d),p&&f(p)},[]),(0,j.useEffect)(()=>{let e=new URLSearchParams(window.location.search);r?e.set(`aggregateId`,r):e.delete(`aggregateId`),a==null?e.delete(`seq`):e.set(`seq`,String(a)),s==null?e.delete(`compare`):e.set(`compare`,String(s)),e.set(`tab`,l),e.set(`panel`,t),d?e.set(`source`,d):e.delete(`source`);let n=e.toString(),i=`${window.location.pathname}${n?`?${n}`:``}${window.location.hash}`;window.history.replaceState(null,``,i)},[t,l,s,r,a,d]);let E=N({queryKey:[`auth-session`],queryFn:Fi,retry:!1,refetchOnWindowFocus:!1}),ae=R()||E.data?.authenticated===!0,oe=R()||E.isSuccess||E.isError;(0,j.useEffect)(()=>{if(R()){zi(null);return}zi(E.data?.authenticated?E.data.csrfToken??null:null)},[E.data]);let{data:se}=N({queryKey:[`health`],queryFn:Pi,refetchInterval:3e4,enabled:ae}),{data:ce=[]}=N({queryKey:[`datasources`],queryFn:Bi,staleTime:1e4,enabled:ae}),{data:D=[]}=N({queryKey:[`plugins`],queryFn:Vi,staleTime:1e4,enabled:ae}),{data:O=[]}=ra(r??``,d||null,ae),{data:le}=N({queryKey:[`timeline-summary`,r,d||`default`],queryFn:()=>ji(r,500,0,d||null,`metadata`),enabled:ae&&!!r,staleTime:3e4}),ue=se?.status===`UP`,de=p===`#/stats`,fe=ce.filter(e=>za(e.status)).length,k=D.filter(e=>za(e.lifecycle)).length,A=ce.length-fe+(D.length-k),pe=e=>{i(e),o(null),c(null),n(`state`),window.location.hash=`#/timeline`},me=()=>{window.location.hash=`#/timeline`},he=async t=>{t.preventDefault(),w(null),ie(null),te(!0);try{let t=await Ii(y,x,p||`#/timeline`);S(``),e.setQueryData([`auth-session`],t),await e.invalidateQueries(),window.location.hash=t.returnHash||p||`#/timeline`}catch{w(`Login failed. Check your credentials and try again.`)}finally{te(!1)}},ge=async()=>{T(!0),w(null),ie(null);try{await Li(),e.setQueryData([`auth-session`],{authenticated:!1}),await e.invalidateQueries(),S(``),window.location.hash=`#/timeline`}finally{T(!1)}};if(!oe)return(0,M.jsx)(`div`,{className:`app auth-shell`,children:(0,M.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Session status`,children:(0,M.jsxs)(`section`,{className:`auth-card`,children:[(0,M.jsx)(`div`,{className:`auth-eyebrow`,children:`Security Session`}),(0,M.jsx)(`h1`,{className:`auth-title`,children:`Checking active session`}),(0,M.jsx)(`p`,{className:`auth-copy`,children:`We are verifying whether this browser already has an active EventLens session.`})]})})});if(!ae){let e=E.data?.provider??`basic`,t=E.data?.basicLoginEnabled??e!==`oidc`;return(0,M.jsx)(`div`,{className:`app auth-shell`,children:(0,M.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Login`,children:(0,M.jsxs)(`section`,{className:`auth-card`,children:[(0,M.jsx)(`div`,{className:`auth-eyebrow`,children:`EventLens Security`}),(0,M.jsx)(`h1`,{className:`auth-title`,children:E.isError?`Server unavailable`:`Sign in to continue`}),(0,M.jsx)(`p`,{className:`auth-copy`,children:E.isError?`We could not reach the EventLens API to check session state. Start the server or restore connectivity, then refresh this page.`:e===`oidc`?`This deployment expects OpenID Connect sign-in and then creates a server-side browser session for EventLens.`:`This v5 transition uses a server-side browser session. After login, we return you to the current workspace route.`}),re&&(0,M.jsxs)(`div`,{className:`auth-error`,role:`alert`,children:[`OIDC sign-in failed: `,re]}),!E.isError&&e===`oidc`&&(0,M.jsx)(`button`,{className:`auth-submit auth-submit--secondary`,type:`button`,onClick:()=>window.location.assign(Ri(p||`#/timeline`)),children:`Sign in with OpenID Connect`}),!E.isError&&t&&(0,M.jsxs)(`form`,{className:`auth-form`,onSubmit:he,children:[(0,M.jsxs)(`label`,{className:`auth-field`,children:[(0,M.jsx)(`span`,{className:`auth-label`,children:`Username`}),(0,M.jsx)(`input`,{className:`auth-input`,type:`text`,autoComplete:`username`,value:y,onChange:e=>b(e.target.value),disabled:ee,required:!0})]}),(0,M.jsxs)(`label`,{className:`auth-field`,children:[(0,M.jsx)(`span`,{className:`auth-label`,children:`Password`}),(0,M.jsx)(`input`,{className:`auth-input`,type:`password`,autoComplete:`current-password`,value:x,onChange:e=>S(e.target.value),disabled:ee,required:!0})]}),(0,M.jsx)(`div`,{className:`auth-meta`,children:(0,M.jsxs)(`span`,{className:`auth-route`,children:[`Return route: `,p||`#/timeline`]})}),C&&(0,M.jsx)(`div`,{className:`auth-error`,role:`alert`,children:C}),(0,M.jsx)(`button`,{className:`auth-submit`,type:`submit`,disabled:ee,children:ee?`Signing in...`:`Sign in`})]}),E.isError&&(0,M.jsx)(`div`,{className:`auth-error`,role:`alert`,children:`Session check failed. Refresh after the API is available again.`})]})})})}return(0,M.jsxs)(`div`,{className:`app`,children:[(0,M.jsx)(Ra,{paletteOpen:_,onOpenPalette:()=>v(!0),onClosePalette:()=>v(!1)}),(0,M.jsx)(La,{open:_,selectedSource:d||null,onClose:()=>v(!1),onSelectAggregate:pe,onOpenHome:me,onOpenStats:()=>{window.location.hash=`#/stats`}}),(0,M.jsxs)(`header`,{className:`app-header`,children:[(0,M.jsx)(`div`,{className:`brand`,children:(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`div`,{className:`brand-name`,children:`EventLens`}),(0,M.jsx)(`div`,{className:`brand-sub`,children:`Event Store Visual Debugger`})]})}),(0,M.jsxs)(`div`,{className:`header-center`,children:[R()&&(0,M.jsx)(`div`,{className:`header-demo-pill`,role:`status`,children:`Demo mode`}),(0,M.jsx)(`div`,{className:`header-title`,children:`EventLens`})]}),(0,M.jsxs)(`div`,{className:`header-actions`,children:[E.data?.principal&&(0,M.jsxs)(`div`,{className:`header-user`,"aria-label":`Authenticated user`,children:[(0,M.jsxs)(`div`,{className:`header-user-meta`,children:[(0,M.jsx)(`span`,{className:`header-user-label`,children:`Signed in`}),(0,M.jsx)(`strong`,{className:`header-user-name`,children:E.data.principal.displayName})]}),(0,M.jsx)(`button`,{className:`header-user-action`,type:`button`,onClick:ge,disabled:ne,children:ne?`Signing out...`:`Sign out`})]}),(0,M.jsx)(Ha,{isUp:ue,selectedSource:d,fallbackCount:le?.totalEvents??null}),(0,M.jsxs)(`div`,{className:`header-status`,children:[(0,M.jsx)(`span`,{className:`dot ${ue?`dot-green`:`dot-red`}`}),(0,M.jsx)(`span`,{className:`status-text ${ue?``:`offline`}`,children:ue?`Connected`:se?.status??`Connecting`})]})]})]}),(0,M.jsxs)(`aside`,{className:`workspace-dock${h?` workspace-dock--open`:``}`,"aria-label":`Workspace`,children:[(0,M.jsxs)(`div`,{className:`workspace-dock-panel`,id:`workspace-dock-panel`,hidden:!h,children:[(0,M.jsx)(`div`,{className:`workspace-dock-title`,children:`Workspace`}),(0,M.jsxs)(`label`,{className:`workspace-datasource`,children:[(0,M.jsx)(`span`,{className:`workspace-datasource-label`,children:`Datasource`}),(0,M.jsxs)(`select`,{id:`workspace-datasource-select`,className:`workspace-datasource-select`,value:d,onChange:e=>{f(e.target.value),o(null),c(null)},children:[(0,M.jsx)(`option`,{value:``,children:`Auto (primary datasource)`}),ce.map(e=>(0,M.jsxs)(`option`,{value:e.id,disabled:!Ba(e.status),children:[e.id,` [`,e.status,`]`]},e.id))]})]}),(0,M.jsxs)(`div`,{className:`workspace-sidebar-kpis`,children:[(0,M.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,M.jsx)(`span`,{children:`Datasources Healthy`}),(0,M.jsxs)(`strong`,{children:[fe,`/`,ce.length||0]})]}),(0,M.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,M.jsx)(`span`,{children:`Plugins Healthy`}),(0,M.jsxs)(`strong`,{children:[k,`/`,D.length||0]})]}),(0,M.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,M.jsx)(`span`,{children:`Issues`}),(0,M.jsx)(`strong`,{children:A})]})]})]}),(0,M.jsx)(`button`,{type:`button`,className:`workspace-dock-handle`,onClick:()=>g(e=>!e),"aria-expanded":h,"aria-controls":`workspace-dock-panel`,"aria-label":h?`Collapse workspace panel`:`Expand workspace panel`,children:(0,M.jsx)(`span`,{className:`workspace-dock-chevron`,"aria-hidden":!0,children:h?`>`:`<`})})]}),(0,M.jsx)(`main`,{className:`app-main`,role:`main`,"aria-label":`EventLens workspace`,children:(0,M.jsxs)(`div`,{className:`workspace-content`,children:[!de&&(0,M.jsxs)(`div`,{className:`card search-panel card--dropdown-host`,children:[(0,M.jsx)(`label`,{className:`control-field-label`,htmlFor:`aggregate-search`,children:`Search Aggregates`}),(0,M.jsx)(Ui,{onSelect:pe,source:d||null,selectedValue:r}),r&&(0,M.jsxs)(`div`,{className:`selection-summary`,children:[`Viewing: `,(0,M.jsx)(`span`,{style:{color:`var(--neon-cyan)`},children:r}),d?(0,M.jsxs)(`span`,{children:[` on `,d]}):(0,M.jsx)(`span`,{children:` on primary datasource`}),s!=null&&(0,M.jsxs)(`span`,{children:[` comparing with seq #`,s]}),(0,M.jsxs)(`span`,{className:`sr-only`,"aria-live":`polite`,children:[`Current panel `,t]}),(0,M.jsx)(`button`,{className:`selection-clear-btn`,onClick:()=>{i(null),o(null),c(null)},children:`× clear`})]})]}),de?(0,M.jsx)(Ia,{source:d||null,onBack:me}):(0,M.jsxs)(M.Fragment,{children:[r&&(0,M.jsx)(na,{aggregateId:r,selectedSequence:a,compareSequence:s,onSelectEvent:e=>{o(e),n(`state`)},onSelectCompare:c,source:d||null}),r&&a!==null&&(0,M.jsx)(da,{aggregateId:r,sequence:a,compareSequence:s,activeTab:l,onTabChange:e=>{u(e),n(`state`)},active:t===`state`,onActivate:()=>n(`state`),source:d||null}),r&&O.length>0&&(0,M.jsx)(Pa,{transitions:O,selectedSequence:a,compareSequence:s,onSelectSequence:e=>{o(e),n(`replay`)},onClearCompare:()=>c(null),active:t===`replay`,onActivate:()=>n(`replay`)}),(0,M.jsxs)(`div`,{className:`bottom-grid`,children:[(0,M.jsx)(Sa,{source:d||null,onSelectAggregate:pe}),(0,M.jsx)(Aa,{source:d||null,onSelectAggregate:pe})]}),r&&le&&(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Selection Summary`}),(0,M.jsxs)(`p`,{style:{color:`var(--text-secondary)`},children:[le.totalEvents,` events available for this aggregate. Use Shift+Click in the timeline to compare two points in time, or open the command palette with Ctrl/Cmd+K.`]})]})]})]})}),(0,M.jsx)(Ma,{})]})}var Wa=class extends j.Component{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e,t){console.error(`UI error boundary caught:`,e,t)}render(){return this.state.hasError?(0,M.jsx)(`div`,{className:`app`,children:(0,M.jsx)(`main`,{className:`app-main`,children:(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Something went wrong`}),(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`,fontSize:13},children:`The UI hit an unexpected error. Try refreshing the page. If the problem persists, check the browser console and server logs.`})]})})}):this.props.children}},Ga=new Je({defaultOptions:{queries:{staleTime:3e4,retry:2}}});oi.createRoot(document.getElementById(`root`)).render((0,M.jsx)(j.StrictMode,{children:(0,M.jsx)($e,{client:Ga,children:(0,M.jsx)(ga,{children:(0,M.jsx)(Wa,{children:(0,M.jsx)(Ua,{})})})})})); \ No newline at end of file diff --git a/eventlens-api/src/main/resources/web/assets/index-ri1P99Z8.js b/eventlens-api/src/main/resources/web/assets/index-ri1P99Z8.js deleted file mode 100644 index ea0bf31..0000000 --- a/eventlens-api/src/main/resources/web/assets/index-ri1P99Z8.js +++ /dev/null @@ -1,14 +0,0 @@ -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},ee=Object.prototype.hasOwnProperty;function te(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ne(e,t){return te(e.type,t,e.props)}function T(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function re(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var ie=/\/+/g;function ae(e,t){return typeof e==`object`&&e&&e.key!=null?re(``+e.key):t.toString(36)}function oe(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function se(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,se(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+ae(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(ie,`$&/`)+`/`),se(o,r,i,``,function(e){return e})):o!=null&&(T(o)&&(o=ne(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(ie,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,T());else{var t=n(l);t!==null&&ae(x,t.startTime-e)}}var S=!1,C=-1,w=5,ee=-1;function te(){return g?!0:!(e.unstable_now()-eet&&te());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&ae(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?T():S=!1}}}var T;if(typeof y==`function`)T=function(){y(ne)};else if(typeof MessageChannel<`u`){var re=new MessageChannel,ie=re.port2;re.port1.onmessage=ne,T=function(){ie.postMessage(null)}}else T=function(){_(ne,0)};function ae(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,ae(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,T()))),r},e.unstable_shouldYield=te,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=m()})),g=o((e=>{var t=p(),n=d(),r=h();function i(e){var t=`https://react.dev/errors/`+e;if(1fe||(e.current=de[fe],de[fe]=null,fe--)}function O(e,t){fe++,de[fe]=e.current,e.current=t}var he=pe(null),ge=pe(null),_e=pe(null),ve=pe(null);function ye(e,t){switch(O(_e,t),O(ge,e),O(he,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Vd(t),e=Hd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}me(he),O(he,e)}function be(){me(he),me(ge),me(_e)}function xe(e){e.memoizedState!==null&&O(ve,e);var t=he.current,n=Hd(t,e.type);t!==n&&(O(ge,e),O(he,n))}function Se(e){ge.current===e&&(me(he),me(ge)),ve.current===e&&(me(ve),Qf._currentValue=ue)}var Ce,we;function Te(e){if(Ce===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);Ce=t&&t[1]||``,we=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{Ee=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?Te(n):``}function Oe(e,t){switch(e.tag){case 26:case 27:case 5:return Te(e.type);case 16:return Te(`Lazy`);case 13:return e.child!==t&&t!==null?Te(`Suspense Fallback`):Te(`Suspense`);case 19:return Te(`SuspenseList`);case 0:case 15:return De(e.type,!1);case 11:return De(e.type.render,!1);case 1:return De(e.type,!0);case 31:return Te(`Activity`);default:return``}}function ke(e){try{var t=``,n=null;do t+=Oe(e,n),n=e,e=e.return;while(e);return t}catch(e){return` -Error generating stack: `+e.message+` -`+e.stack}}var Ae=Object.prototype.hasOwnProperty,je=t.unstable_scheduleCallback,Me=t.unstable_cancelCallback,Ne=t.unstable_shouldYield,Pe=t.unstable_requestPaint,Fe=t.unstable_now,Ie=t.unstable_getCurrentPriorityLevel,Le=t.unstable_ImmediatePriority,Re=t.unstable_UserBlockingPriority,ze=t.unstable_NormalPriority,Be=t.unstable_LowPriority,Ve=t.unstable_IdlePriority,He=t.log,Ue=t.unstable_setDisableYieldValue,We=null,Ge=null;function Ke(e){if(typeof He==`function`&&Ue(e),Ge&&typeof Ge.setStrictMode==`function`)try{Ge.setStrictMode(We,e)}catch{}}var qe=Math.clz32?Math.clz32:Xe,Je=Math.log,Ye=Math.LN2;function Xe(e){return e>>>=0,e===0?32:31-(Je(e)/Ye|0)|0}var Ze=256,Qe=262144,k=4194304;function A(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function $e(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=A(n))):i=A(o):i=A(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=A(n))):i=A(o)):i=A(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function et(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function tt(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function nt(){var e=k;return k<<=1,!(k&62914560)&&(k=4194304),e}function rt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function it(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function at(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),gn=!1;if(hn)try{var _n={};Object.defineProperty(_n,`passive`,{get:function(){gn=!0}}),window.addEventListener(`test`,_n,_n),window.removeEventListener(`test`,_n,_n)}catch{gn=!1}var vn=null,yn=null,bn=null;function xn(){if(bn)return bn;var e,t=yn,n=t.length,r,i=`value`in vn?vn.value:vn.textContent,a=i.length;for(e=0;e=Zn),er=` `,tr=!1;function nr(e,t){switch(e){case`keyup`:return Yn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function rr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var ir=!1;function ar(e,t){switch(e){case`compositionend`:return rr(t);case`keypress`:return t.which===32?(tr=!0,er):null;case`textInput`:return e=t.data,e===er&&tr?null:e;default:return null}}function or(e,t){if(ir)return e===`compositionend`||!Xn&&nr(e,t)?(e=xn(),bn=yn=vn=null,ir=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Or(n)}}function Ar(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ar(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function jr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Ht(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Ht(e.document)}return t}function Mr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Nr=hn&&`documentMode`in document&&11>=document.documentMode,Pr=null,Fr=null,Ir=null,Lr=!1;function Rr(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Lr||Pr==null||Pr!==Ht(r)||(r=Pr,`selectionStart`in r&&Mr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Ir&&Dr(Ir,r)||(Ir=r,r=Ed(Fr,`onSelect`),0>=o,i-=o,Oi=1<<32-qe(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),R&&Ai(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),R&&Ai(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return R&&Ai(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),R&&Ai(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===T&&Oa(l)===r.type){n(e,r.sibling),c=a(r,o.props),Fa(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=hi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=mi(o.type,o.key,o.props,null,e.mode,c),Fa(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=vi(o,e.mode,c),c.return=e,e=c}return s(e);case T:return o=Oa(o),b(e,r,o,c)}if(le(o))return h(e,r,o,c);if(oe(o)){if(l=oe(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Pa(o),c);if(o.$$typeof===C)return b(e,r,ra(e,o),c);Ia(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=gi(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Na=0;var i=b(e,t,n,r);return Ma=null,i}catch(t){if(t===Sa||t===wa)throw t;var a=ui(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Ra=La(!0),za=La(!1),Ba=!1;function Va(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ha(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Ua(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Wa(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,G&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=si(e),I(e,null,n),t}return ii(e,r,t,n),si(e)}function Ga(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,st(e,n)}}function Ka(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var qa=!1;function Ja(){if(qa){var e=pa;if(e!==null)throw e}}function Ya(e,t,n,r){qa=!1;var i=e.updateQueue;Ba=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(J&f)===f:(r&f)===f){f!==0&&f===fa&&(qa=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=m({},d,f);break a;case 2:Ba=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Gl|=o,e.lanes=o,e.memoizedState=d}}function Xa(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Za(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=E.T,s={};E.T=s,Fs(e,!1,t,n);try{var c=i(),l=E.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ps(e,t,ga(c,r),pu(e)):Ps(e,t,r,pu(e))}catch(n){Ps(e,t,{then:function(){},status:`rejected`,reason:n},pu())}finally{D.p=a,o!==null&&s.types!==null&&(o.types=s.types),E.T=o}}function ws(){}function Ts(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Es(e).queue;Cs(e,a,t,ue,n===null?ws:function(){return Ds(e),n(r)})}function Es(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:ue,baseState:ue,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Io,lastRenderedState:ue},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Io,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Ds(e){var t=Es(e);t.next===null&&(t=e.alternate.memoizedState),Ps(e,t.next.queue,{},pu())}function Os(){return na(Qf)}function ks(){return jo().memoizedState}function As(){return jo().memoizedState}function js(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=pu();e=Ua(n);var r=Wa(t,e,n);r!==null&&(hu(r,t,n),Ga(r,t,n)),t={cache:ca()},e.payload=t;return}t=t.return}}function Ms(e,t,n){var r=pu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Is(e)?Ls(t,n):(n=ai(e,t,n,r),n!==null&&(hu(n,e,r),Rs(n,t,r)))}function Ns(e,t,n){Ps(e,t,n,pu())}function Ps(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Is(e))Ls(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,Er(s,o))return ii(e,t,i,0),K===null&&ri(),!1}catch{}if(n=ai(e,t,i,r),n!==null)return hu(n,e,r),Rs(n,t,r),!0}return!1}function Fs(e,t,n,r){if(r={lane:2,revertLane:dd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Is(e)){if(t)throw Error(i(479))}else t=ai(e,n,r,2),t!==null&&hu(t,e,2)}function Is(e){var t=e.alternate;return e===B||t!==null&&t===B}function Ls(e,t){go=ho=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Rs(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,st(e,n)}}var zs={readContext:na,use:Po,useCallback:H,useContext:H,useEffect:H,useImperativeHandle:H,useLayoutEffect:H,useInsertionEffect:H,useMemo:H,useReducer:H,useRef:H,useState:H,useDebugValue:H,useDeferredValue:H,useTransition:H,useSyncExternalStore:H,useId:H,useHostTransitionStatus:H,useFormState:H,useActionState:H,useOptimistic:H,useMemoCache:H,useCacheRefresh:H};zs.useEffectEvent=H;var Bs={readContext:na,use:Po,useCallback:function(e,t){return Ao().memoizedState=[e,t===void 0?null:t],e},useContext:na,useEffect:us,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),cs(4194308,4,gs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return cs(4194308,4,e,t)},useInsertionEffect:function(e,t){cs(4,2,e,t)},useMemo:function(e,t){var n=Ao();t=t===void 0?null:t;var r=e();if(_o){Ke(!0);try{e()}finally{Ke(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Ao();if(n!==void 0){var i=n(t);if(_o){Ke(!0);try{n(t)}finally{Ke(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=Ms.bind(null,B,e),[r.memoizedState,e]},useRef:function(e){var t=Ao();return e={current:e},t.memoizedState=e},useState:function(e){e=Ko(e);var t=e.queue,n=Ns.bind(null,B,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:vs,useDeferredValue:function(e,t){return xs(Ao(),e,t)},useTransition:function(){var e=Ko(!1);return e=Cs.bind(null,B,e.queue,!0,!1),Ao().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=B,a=Ao();if(R){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),K===null)throw Error(i(349));J&127||Vo(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,us(Uo.bind(null,r,o,e),[e]),r.flags|=2048,os(9,{destroy:void 0},Ho.bind(null,r,o,n,t),null),n},useId:function(){var e=Ao(),t=K.identifierPrefix;if(R){var n=ki,r=Oi;n=(r&~(1<<32-qe(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=vo++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[mt]=t,o[j]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Pd(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Pc(t)}}return U(t),Fc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Pc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=_e.current,Hi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Fi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[mt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Md(e.nodeValue,n)),e||zi(t,!0)}else e=Bd(e).createTextNode(r),e[mt]=t,t.stateNode=e}return U(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Hi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[mt]=t}else Ui(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;U(t),e=!1}else n=Wi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(lo(t),t):(lo(t),null);if(t.flags&128)throw Error(i(558))}return U(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=Hi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[mt]=t}else Ui(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;U(t),a=!1}else a=Wi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(lo(t),t):(lo(t),null)}return lo(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Lc(t,t.updateQueue),U(t),null);case 4:return be(),e===null&&Sd(t.stateNode.containerInfo),U(t),null;case 10:return Xi(t.type),U(t),null;case 19:if(me(uo),r=t.memoizedState,r===null)return U(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Rc(r,!1);else{if(X!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=fo(e),o!==null){for(t.flags|=128,Rc(r,!1),e=o.updateQueue,t.updateQueue=e,Lc(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)pi(n,e),n=n.sibling;return O(uo,uo.current&1|2),R&&Ai(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&Fe()>tu&&(t.flags|=128,a=!0,Rc(r,!1),t.lanes=4194304)}else{if(!a)if(e=fo(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Lc(t,e),Rc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!R)return U(t),null}else 2*Fe()-r.renderingStartTime>tu&&n!==536870912&&(t.flags|=128,a=!0,Rc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(U(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=Fe(),e.sibling=null,n=uo.current,O(uo,a?n&1|2:n&1),R&&Ai(t,r.treeForkCount),e);case 22:case 23:return lo(t),no(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(U(t),t.subtreeFlags&6&&(t.flags|=8192)):U(t),n=t.updateQueue,n!==null&&Lc(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&me(va),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Xi(z),U(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function Bc(e,t){switch(Ni(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Xi(z),be(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Se(t),null;case 31:if(t.memoizedState!==null){if(lo(t),t.alternate===null)throw Error(i(340));Ui()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(lo(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Ui()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return me(uo),null;case 4:return be(),null;case 10:return Xi(t.type),null;case 22:case 23:return lo(t),no(),e!==null&&me(va),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Xi(z),null;case 25:return null;default:return null}}function Vc(e,t){switch(Ni(t),t.tag){case 3:Xi(z),be();break;case 26:case 27:case 5:Se(t);break;case 4:be();break;case 31:t.memoizedState!==null&&lo(t);break;case 13:lo(t);break;case 19:me(uo);break;case 10:Xi(t.type);break;case 22:case 23:lo(t),no(),e!==null&&me(va);break;case 24:Xi(z)}}function Hc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Z(t,t.return,e)}}function Uc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Z(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Z(t,t.return,e)}}function Wc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Za(t,n)}catch(t){Z(e,e.return,t)}}}function Gc(e,t,n){n.props=qs(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Z(e,t,n)}}function Kc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Z(e,t,n)}}function qc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){Z(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Z(e,t,n)}else n.current=null}function Jc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Z(e,e.return,t)}}function Yc(e,t,n){try{var r=e.stateNode;Fd(r,e.type,n,t),r[j]=t}catch(t){Z(e,e.return,t)}}function Xc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Zd(e.type)||e.tag===4}function Zc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Xc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Zd(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Qc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=on));else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Qc(e,t,n),e=e.sibling;e!==null;)Qc(e,t,n),e=e.sibling}function $c(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode),e=e.child,e!==null))for($c(e,t,n),e=e.sibling;e!==null;)$c(e,t,n),e=e.sibling}function el(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Pd(t,r,n),t[mt]=e,t[j]=n}catch(t){Z(e,e.return,t)}}var tl=!1,nl=!1,rl=!1,il=typeof WeakSet==`function`?WeakSet:Set,al=null;function ol(e,t){if(e=e.containerInfo,Rd=sp,e=jr(e),Mr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(zd={focusedElem:e,selectionRange:n},sp=!1,al=t;al!==null;)if(t=al,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,al=e;else for(;al!==null;){switch(t=al,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Pd(o,r,n),o[mt]=e,Et(o),r=o;break a;case`link`:var s=Vf(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=kr(s,h),v=kr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,E.T=null,n=lu,lu=null;var o=au,s=su;if(iu=0,ou=au=null,su=0,G&6)throw Error(i(331));var c=G;if(G|=4,Fl(o.current),Dl(o,o.current,s,n),G=c,id(0,!1),Ge&&typeof Ge.onPostCommitFiberRoot==`function`)try{Ge.onPostCommitFiberRoot(We,o)}catch{}return!0}finally{D.p=a,E.T=r,Vu(e,t)}}function Wu(e,t,n){t=bi(n,t),t=$s(e.stateNode,t,2),e=Wa(e,t,2),e!==null&&(it(e,2),rd(e))}function Z(e,t,n){if(e.tag===3)Wu(e,e,n);else for(;t!==null;){if(t.tag===3){Wu(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(ru===null||!ru.has(r))){e=bi(n,e),n=ec(2),r=Wa(t,n,2),r!==null&&(tc(n,r,t,e),it(r,2),rd(r));break}}t=t.return}}function Gu(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new zl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Ul=!0,i.add(n),e=Ku.bind(null,e,t,n),t.then(e,e))}function Ku(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,K===e&&(J&n)===n&&(X===4||X===3&&(J&62914560)===J&&300>Fe()-$l?!(G&2)&&Su(e,0):ql|=n,Yl===J&&(Yl=0)),rd(e)}function qu(e,t){t===0&&(t=nt()),e=oi(e,t),e!==null&&(it(e,t),rd(e))}function Ju(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),qu(e,n)}function Yu(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),qu(e,n)}function Xu(e,t){return je(e,t)}var Zu=null,Qu=null,$u=!1,ed=!1,td=!1,nd=0;function rd(e){e!==Qu&&e.next===null&&(Qu===null?Zu=Qu=e:Qu=Qu.next=e),ed=!0,$u||($u=!0,ud())}function id(e,t){if(!td&&ed){td=!0;do for(var n=!1,r=Zu;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-qe(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,ld(r,a))}else a=J,a=$e(r,r===K?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||et(r,a)||(n=!0,ld(r,a));r=r.next}while(n);td=!1}}function ad(){od()}function od(){ed=$u=!1;var e=0;nd!==0&&Gd()&&(e=nd);for(var t=Fe(),n=null,r=Zu;r!==null;){var i=r.next,a=sd(r,t);a===0?(r.next=null,n===null?Zu=i:n.next=i,i===null&&(Qu=n)):(n=r,(e!==0||a&3)&&(ed=!0)),r=i}iu!==0&&iu!==5||id(e,!1),nd!==0&&(nd=0)}function sd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Id(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function xf(e,t,n){var r=bf;if(r&&typeof t==`string`&&t){var i=Wt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),hf.has(i)||(hf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Pd(t,`link`,e),Et(t),r.head.appendChild(t)))}}function Sf(e){_f.D(e),xf(`dns-prefetch`,e,null)}function Cf(e,t){_f.C(e,t),xf(`preconnect`,e,t)}function wf(e,t,n){_f.L(e,t,n);var r=bf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Wt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Wt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Wt(n.imageSizes)+`"]`)):i+=`[href="`+Wt(e)+`"]`;var a=i;switch(t){case`style`:a=Af(e);break;case`script`:a=Pf(e)}mf.has(a)||(e=m({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),mf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(jf(a))||t===`script`&&r.querySelector(Ff(a))||(t=r.createElement(`link`),Pd(t,`link`,e),Et(t),r.head.appendChild(t)))}}function Tf(e,t){_f.m(e,t);var n=bf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Wt(r)+`"][href="`+Wt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Pf(e)}if(!mf.has(a)&&(e=m({rel:`modulepreload`,href:e},t),mf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Ff(a)))return}r=n.createElement(`link`),Pd(r,`link`,e),Et(r),n.head.appendChild(r)}}}function Ef(e,t,n){_f.S(e,t,n);var r=bf;if(r&&e){var i=Tt(r).hoistableStyles,a=Af(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(jf(a)))s.loading=5;else{e=m({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=mf.get(a))&&Rf(e,n);var c=o=r.createElement(`link`);Et(c),Pd(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Lf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Df(e,t){_f.X(e,t);var n=bf;if(n&&e){var r=Tt(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),Et(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Of(e,t){_f.M(e,t);var n=bf;if(n&&e){var r=Tt(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0,type:`module`},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),Et(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function kf(e,t,n,r){var a=(a=_e.current)?gf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Af(n.href),n=Tt(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Af(n.href);var o=Tt(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(jf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),mf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},mf.set(e,n),o||Nf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Pf(n),n=Tt(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Af(e){return`href="`+Wt(e)+`"`}function jf(e){return`link[rel="stylesheet"][`+e+`]`}function Mf(e){return m({},e,{"data-precedence":e.precedence,precedence:null})}function Nf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Pd(t,`link`,n),Et(t),e.head.appendChild(t))}function Pf(e){return`[src="`+Wt(e)+`"]`}function Ff(e){return`script[async]`+e}function If(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Wt(n.href)+`"]`);if(r)return t.instance=r,Et(r),r;var a=m({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),Et(r),Pd(r,`style`,a),Lf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Af(n.href);var o=e.querySelector(jf(a));if(o)return t.state.loading|=4,t.instance=o,Et(o),o;r=Mf(n),(a=mf.get(a))&&Rf(r,a),o=(e.ownerDocument||e).createElement(`link`),Et(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Pd(o,`link`,r),t.state.loading|=4,Lf(o,n.precedence,e),t.instance=o;case`script`:return o=Pf(n.src),(a=e.querySelector(Ff(o)))?(t.instance=a,Et(a),a):(r=n,(a=mf.get(o))&&(r=m({},n),zf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),Et(a),Pd(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Lf(r,n.precedence,e));return t.instance}function Lf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Uf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Wf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Gf(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Af(r.href),a=t.querySelector(jf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Jf.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,Et(a);return}a=t.ownerDocument||t,r=Mf(r),(i=mf.get(i))&&Rf(r,i),a=a.createElement(`link`),Et(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Pd(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Jf.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var Kf=0;function qf(e,t){return e.stylesheets&&e.count===0&&Xf(e,e.stylesheets),0Kf?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Jf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Xf(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Yf=null;function Xf(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Yf=new Map,t.forEach(Zf,e),Yf=null,Jf.call(e))}function Zf(e,t){if(!(t.state.loading&4)){var n=Yf.get(e);if(n)var r=n.get(null);else{n=new Map,Yf.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=g()})),v=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},y=new class extends v{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e();return window.addEventListener(`visibilitychange`,t,!1),()=>{window.removeEventListener(`visibilitychange`,t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(e=>{typeof e==`boolean`?this.setFocused(e):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){let e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e==`boolean`?this.#e:globalThis.document?.visibilityState!==`hidden`}},b={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},x=new class{#e=b;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}};function S(e){setTimeout(e,0)}var C=typeof window>`u`||`Deno`in globalThis;function w(){}function ee(e,t){return typeof e==`function`?e(t):e}function te(e){return typeof e==`number`&&e>=0&&e!==1/0}function ne(e,t){return Math.max(e+(t||0)-Date.now(),0)}function T(e,t){return typeof e==`function`?e(t):e}function re(e,t){return typeof e==`function`?e(t):e}function ie(e,t){let{type:n=`all`,exact:r,fetchStatus:i,predicate:a,queryKey:o,stale:s}=e;if(o){if(r){if(t.queryHash!==oe(o,t.options))return!1}else if(!ce(t.queryKey,o))return!1}if(n!==`all`){let e=t.isActive();if(n===`active`&&!e||n===`inactive`&&e)return!1}return!(typeof s==`boolean`&&t.isStale()!==s||i&&i!==t.state.fetchStatus||a&&!a(t))}function ae(e,t){let{exact:n,status:r,predicate:i,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(se(t.options.mutationKey)!==se(a))return!1}else if(!ce(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function oe(e,t){return(t?.queryKeyHashFn||se)(e)}function se(e){return JSON.stringify(e,(e,t)=>de(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}function ce(e,t){return e===t?!0:typeof e==typeof t&&e&&t&&typeof e==`object`&&typeof t==`object`?Object.keys(t).every(n=>ce(e[n],t[n])):!1}var le=Object.prototype.hasOwnProperty;function E(e,t,n=0){if(e===t)return e;if(n>500)return t;let r=ue(e)&&ue(t);if(!r&&!(de(e)&&de(t)))return t;let i=(r?e:Object.keys(e)).length,a=r?t:Object.keys(t),o=a.length,s=r?Array(o):{},c=0;for(let l=0;l{x.setTimeout(t,e)})}function me(e,t,n){return typeof n.structuralSharing==`function`?n.structuralSharing(e,t):n.structuralSharing===!1?t:E(e,t)}function O(e,t,n=0){let r=[...e,t];return n&&r.length>n?r.slice(1):r}function he(e,t,n=0){let r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var ge=Symbol();function _e(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===ge?()=>Promise.reject(Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function ve(e,t){return typeof e==`function`?e(...t):!!e}function ye(e,t,n){let r=!1,i;return Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(i??=t(),r?i:(r=!0,i.aborted?n():i.addEventListener(`abort`,n,{once:!0}),i))}),e}var be=(()=>{let e=()=>C;return{isServer(){return e()},setIsServer(t){e=t}}})();function xe(){let e,t,n=new Promise((n,r)=>{e=n,t=r});n.status=`pending`,n.catch(()=>{});function r(e){Object.assign(n,e),delete n.resolve,delete n.reject}return n.resolve=t=>{r({status:`fulfilled`,value:t}),e(t)},n.reject=e=>{r({status:`rejected`,reason:e}),t(e)},n}var Se=S;function Ce(){let e=[],t=0,n=e=>{e()},r=e=>{e()},i=Se,a=r=>{t?e.push(r):i(()=>{n(r)})},o=()=>{let t=e;e=[],t.length&&i(()=>{r(()=>{t.forEach(e=>{n(e)})})})};return{batch:e=>{let n;t++;try{n=e()}finally{t--,t||o()}return n},batchCalls:e=>(...t)=>{a(()=>{e(...t)})},schedule:a,setNotifyFunction:e=>{n=e},setBatchNotifyFunction:e=>{r=e},setScheduler:e=>{i=e}}}var we=Ce(),Te=new class extends v{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e(!0),n=()=>e(!1);return window.addEventListener(`online`,t,!1),window.addEventListener(`offline`,n,!1),()=>{window.removeEventListener(`online`,t),window.removeEventListener(`offline`,n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(t=>{t(e)}))}isOnline(){return this.#e}};function Ee(e){return Math.min(1e3*2**e,3e4)}function De(e){return(e??`online`)===`online`?Te.isOnline():!0}var Oe=class extends Error{constructor(e){super(`CancelledError`),this.revert=e?.revert,this.silent=e?.silent}};function ke(e){let t=!1,n=0,r,i=xe(),a=()=>i.status!==`pending`,o=t=>{if(!a()){let n=new Oe(t);f(n),e.onCancel?.(n)}},s=()=>{t=!0},c=()=>{t=!1},l=()=>y.isFocused()&&(e.networkMode===`always`||Te.isOnline())&&e.canRun(),u=()=>De(e.networkMode)&&e.canRun(),d=e=>{a()||(r?.(),i.resolve(e))},f=e=>{a()||(r?.(),i.reject(e))},p=()=>new Promise(t=>{r=e=>{(a()||l())&&t(e)},e.onPause?.()}).then(()=>{r=void 0,a()||e.onContinue?.()}),m=()=>{if(a())return;let r,i=n===0?e.initialPromise:void 0;try{r=i??e.fn()}catch(e){r=Promise.reject(e)}Promise.resolve(r).then(d).catch(r=>{if(a())return;let i=e.retry??(be.isServer()?0:3),o=e.retryDelay??Ee,s=typeof o==`function`?o(n,r):o,c=i===!0||typeof i==`number`&&nl()?void 0:p()).then(()=>{t?f(r):m()})})};return{promise:i,status:()=>i.status,cancel:o,continue:()=>(r?.(),i),cancelRetry:s,continueRetry:c,canStart:u,start:()=>(u()?m():p().then(m),i)}}var Ae=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),te(this.gcTime)&&(this.#e=x.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(be.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e&&=(x.clearTimeout(this.#e),void 0)}},je=class extends Ae{#e;#t;#n;#r;#i;#a;#o;constructor(e){super(),this.#o=!1,this.#a=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#r=e.client,this.#n=this.#r.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#e=Pe(this.options),this.state=e.state??this.#e,this.scheduleGc()}get meta(){return this.options.meta}get promise(){return this.#i?.promise}setOptions(e){if(this.options={...this.#a,...e},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){let e=Pe(this.options);e.data!==void 0&&(this.setState(Ne(e.data,e.dataUpdatedAt)),this.#e=e)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus===`idle`&&this.#n.remove(this)}setData(e,t){let n=me(this.state.data,e,this.options);return this.#c({data:n,type:`success`,dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e,t){this.#c({type:`setState`,state:e,setStateOptions:t})}cancel(e){let t=this.#i?.promise;return this.#i?.cancel(e),t?t.then(w).catch(w):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#e}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>re(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===ge||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>T(e.options.staleTime,this)===`static`):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e===`static`?!1:this.state.isInvalidated?!0:!ne(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(e=>e.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#i?.continue()}onOnline(){this.observers.find(e=>e.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#i?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#i&&(this.#o||this.#s()?this.#i.cancel({revert:!0}):this.#i.cancelRetry()),this.scheduleGc()),this.#n.notify({type:`observerRemoved`,query:this,observer:e}))}getObserversCount(){return this.observers.length}#s(){return this.state.fetchStatus===`paused`&&this.state.status===`pending`}invalidate(){this.state.isInvalidated||this.#c({type:`invalidate`})}async fetch(e,t){if(this.state.fetchStatus!==`idle`&&this.#i?.status()!==`rejected`){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#i)return this.#i.continueRetry(),this.#i.promise}if(e&&this.setOptions(e),!this.options.queryFn){let e=this.observers.find(e=>e.options.queryFn);e&&this.setOptions(e.options)}let n=new AbortController,r=e=>{Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(this.#o=!0,n.signal)})},i=()=>{let e=_e(this.options,t),n=(()=>{let e={client:this.#r,queryKey:this.queryKey,meta:this.meta};return r(e),e})();return this.#o=!1,this.options.persister?this.options.persister(e,n,this):e(n)},a=(()=>{let e={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#r,state:this.state,fetchFn:i};return r(e),e})();this.options.behavior?.onFetch(a,this),this.#t=this.state,(this.state.fetchStatus===`idle`||this.state.fetchMeta!==a.fetchOptions?.meta)&&this.#c({type:`fetch`,meta:a.fetchOptions?.meta}),this.#i=ke({initialPromise:t?.initialPromise,fn:a.fetchFn,onCancel:e=>{e instanceof Oe&&e.revert&&this.setState({...this.#t,fetchStatus:`idle`}),n.abort()},onFail:(e,t)=>{this.#c({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#c({type:`pause`})},onContinue:()=>{this.#c({type:`continue`})},retry:a.options.retry,retryDelay:a.options.retryDelay,networkMode:a.options.networkMode,canRun:()=>!0});try{let e=await this.#i.start();if(e===void 0)throw Error(`${this.queryHash} data is undefined`);return this.setData(e),this.#n.config.onSuccess?.(e,this),this.#n.config.onSettled?.(e,this.state.error,this),e}catch(e){if(e instanceof Oe){if(e.silent)return this.#i.promise;if(e.revert){if(this.state.data===void 0)throw e;return this.state.data}}throw this.#c({type:`error`,error:e}),this.#n.config.onError?.(e,this),this.#n.config.onSettled?.(this.state.data,e,this),e}finally{this.scheduleGc()}}#c(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case`pause`:return{...t,fetchStatus:`paused`};case`continue`:return{...t,fetchStatus:`fetching`};case`fetch`:return{...t,...Me(t.data,this.options),fetchMeta:e.meta??null};case`success`:let n={...t,...Ne(e.data,e.dataUpdatedAt),dataUpdateCount:t.dataUpdateCount+1,...!e.manual&&{fetchStatus:`idle`,fetchFailureCount:0,fetchFailureReason:null}};return this.#t=e.manual?n:void 0,n;case`error`:let r=e.error;return{...t,error:r,errorUpdateCount:t.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:t.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:`idle`,status:`error`,isInvalidated:!0};case`invalidate`:return{...t,isInvalidated:!0};case`setState`:return{...t,...e.state}}})(this.state),we.batch(()=>{this.observers.forEach(e=>{e.onQueryUpdate()}),this.#n.notify({query:this,type:`updated`,action:e})})}};function Me(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:De(t.networkMode)?`fetching`:`paused`,...e===void 0&&{error:null,status:`pending`}}}function Ne(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:`success`}}function Pe(e){let t=typeof e.initialData==`function`?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt==`function`?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?`success`:`pending`,fetchStatus:`idle`}}var Fe=class extends v{constructor(e,t){super(),this.options=t,this.#e=e,this.#s=null,this.#o=xe(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#i;#a;#o;#s;#c;#l;#u;#d;#f;#p;#m=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),Le(this.#t,this.options)?this.#h():this.updateResult(),this.#y())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return Re(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return Re(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#b(),this.#x(),this.#t.removeObserver(this)}setOptions(e){let t=this.options,n=this.#t;if(this.options=this.#e.defaultQueryOptions(e),this.options.enabled!==void 0&&typeof this.options.enabled!=`boolean`&&typeof this.options.enabled!=`function`&&typeof re(this.options.enabled,this.#t)!=`boolean`)throw Error(`Expected enabled to be a boolean or a callback that returns a boolean`);this.#S(),this.#t.setOptions(this.options),t._defaulted&&!D(this.options,t)&&this.#e.getQueryCache().notify({type:`observerOptionsUpdated`,query:this.#t,observer:this});let r=this.hasListeners();r&&ze(this.#t,n,this.options,t)&&this.#h(),this.updateResult(),r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||T(this.options.staleTime,this.#t)!==T(t.staleTime,this.#t))&&this.#g();let i=this.#_();r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||i!==this.#p)&&this.#v(i)}getOptimisticResult(e){let t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return Ve(this,n)&&(this.#r=n,this.#a=this.options,this.#i=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(e,n)=>(this.trackProp(n),t?.(n),n===`promise`&&(this.trackProp(`data`),!this.options.experimental_prefetchInRender&&this.#o.status===`pending`&&this.#o.reject(Error(`experimental_prefetchInRender feature flag is not enabled`))),Reflect.get(e,n))})}trackProp(e){this.#m.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#h({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#h(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(w)),t}#g(){this.#b();let e=T(this.options.staleTime,this.#t);if(be.isServer()||this.#r.isStale||!te(e))return;let t=ne(this.#r.dataUpdatedAt,e)+1;this.#d=x.setTimeout(()=>{this.#r.isStale||this.updateResult()},t)}#_(){return(typeof this.options.refetchInterval==`function`?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#v(e){this.#x(),this.#p=e,!(be.isServer()||re(this.options.enabled,this.#t)===!1||!te(this.#p)||this.#p===0)&&(this.#f=x.setInterval(()=>{(this.options.refetchIntervalInBackground||y.isFocused())&&this.#h()},this.#p))}#y(){this.#g(),this.#v(this.#_())}#b(){this.#d&&=(x.clearTimeout(this.#d),void 0)}#x(){this.#f&&=(x.clearInterval(this.#f),void 0)}createResult(e,t){let n=this.#t,r=this.options,i=this.#r,a=this.#i,o=this.#a,s=e===n?this.#n:e.state,{state:c}=e,l={...c},u=!1,d;if(t._optimisticResults){let i=this.hasListeners(),a=!i&&Le(e,t),o=i&&ze(e,n,t,r);(a||o)&&(l={...l,...Me(c.data,e.options)}),t._optimisticResults===`isRestoring`&&(l.fetchStatus=`idle`)}let{error:f,errorUpdatedAt:p,status:m}=l;d=l.data;let h=!1;if(t.placeholderData!==void 0&&d===void 0&&m===`pending`){let e;i?.isPlaceholderData&&t.placeholderData===o?.placeholderData?(e=i.data,h=!0):e=typeof t.placeholderData==`function`?t.placeholderData(this.#u?.state.data,this.#u):t.placeholderData,e!==void 0&&(m=`success`,d=me(i?.data,e,t),u=!0)}if(t.select&&d!==void 0&&!h)if(i&&d===a?.data&&t.select===this.#c)d=this.#l;else try{this.#c=t.select,d=t.select(d),d=me(i?.data,d,t),this.#l=d,this.#s=null}catch(e){this.#s=e}this.#s&&(f=this.#s,d=this.#l,p=Date.now(),m=`error`);let g=l.fetchStatus===`fetching`,_=m===`pending`,v=m===`error`,y=_&&g,b=d!==void 0,x={status:m,fetchStatus:l.fetchStatus,isPending:_,isSuccess:m===`success`,isError:v,isInitialLoading:y,isLoading:y,data:d,dataUpdatedAt:l.dataUpdatedAt,error:f,errorUpdatedAt:p,failureCount:l.fetchFailureCount,failureReason:l.fetchFailureReason,errorUpdateCount:l.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:l.dataUpdateCount>s.dataUpdateCount||l.errorUpdateCount>s.errorUpdateCount,isFetching:g,isRefetching:g&&!_,isLoadingError:v&&!b,isPaused:l.fetchStatus===`paused`,isPlaceholderData:u,isRefetchError:v&&b,isStale:Be(e,t),refetch:this.refetch,promise:this.#o,isEnabled:re(t.enabled,e)!==!1};if(this.options.experimental_prefetchInRender){let t=x.data!==void 0,r=x.status===`error`&&!t,i=e=>{r?e.reject(x.error):t&&e.resolve(x.data)},a=()=>{i(this.#o=x.promise=xe())},o=this.#o;switch(o.status){case`pending`:e.queryHash===n.queryHash&&i(o);break;case`fulfilled`:(r||x.data!==o.value)&&a();break;case`rejected`:(!r||x.error!==o.reason)&&a();break}}return x}updateResult(){let e=this.#r,t=this.createResult(this.#t,this.options);this.#i=this.#t.state,this.#a=this.options,this.#i.data!==void 0&&(this.#u=this.#t),!D(t,e)&&(this.#r=t,this.#C({listeners:(()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,n=typeof t==`function`?t():t;if(n===`all`||!n&&!this.#m.size)return!0;let r=new Set(n??this.#m);return this.options.throwOnError&&r.add(`error`),Object.keys(this.#r).some(t=>{let n=t;return this.#r[n]!==e[n]&&r.has(n)})})()}))}#S(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;let t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#y()}#C(e){we.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:`observerResultsUpdated`})})}};function Ie(e,t){return re(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status===`error`&&t.retryOnMount===!1)}function Le(e,t){return Ie(e,t)||e.state.data!==void 0&&Re(e,t,t.refetchOnMount)}function Re(e,t,n){if(re(t.enabled,e)!==!1&&T(t.staleTime,e)!==`static`){let r=typeof n==`function`?n(e):n;return r===`always`||r!==!1&&Be(e,t)}return!1}function ze(e,t,n,r){return(e!==t||re(r.enabled,e)===!1)&&(!n.suspense||e.state.status!==`error`)&&Be(e,n)}function Be(e,t){return re(t.enabled,e)!==!1&&e.isStaleByTime(T(t.staleTime,e))}function Ve(e,t){return!D(e.getCurrentResult(),t)}function He(e){return{onFetch:(t,n)=>{let r=t.options,i=t.fetchOptions?.meta?.fetchMore?.direction,a=t.state.data?.pages||[],o=t.state.data?.pageParams||[],s={pages:[],pageParams:[]},c=0,l=async()=>{let n=!1,l=e=>{ye(e,()=>t.signal,()=>n=!0)},u=_e(t.options,t.fetchOptions),d=async(e,r,i)=>{if(n)return Promise.reject();if(r==null&&e.pages.length)return Promise.resolve(e);let a=await u((()=>{let e={client:t.client,queryKey:t.queryKey,pageParam:r,direction:i?`backward`:`forward`,meta:t.options.meta};return l(e),e})()),{maxPages:o}=t.options,s=i?he:O;return{pages:s(e.pages,a,o),pageParams:s(e.pageParams,r,o)}};if(i&&a.length){let e=i===`backward`,t=e?We:Ue,n={pages:a,pageParams:o};s=await d(n,t(r,n),e)}else{let t=e??a.length;do{let e=c===0?o[0]??r.initialPageParam:Ue(r,s);if(c>0&&e==null)break;s=await d(s,e),c++}while(ct.options.persister?.(l,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=l}}}function Ue(e,{pages:t,pageParams:n}){let r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function We(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var Ge=class extends Ae{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||Ke(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){let t=()=>{this.#i({type:`continue`})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=ke({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});let r=this.state.status===`pending`,i=!this.#r.canStart();try{if(r)t();else{this.#i({type:`pending`,variables:e,isPaused:i}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);let t=await this.options.onMutate?.(e,n);t!==this.state.context&&this.#i({type:`pending`,context:t,variables:e,isPaused:i})}let a=await this.#r.start();return await this.#n.config.onSuccess?.(a,e,this.state.context,this,n),await this.options.onSuccess?.(a,e,this.state.context,n),await this.#n.config.onSettled?.(a,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(a,null,e,this.state.context,n),this.#i({type:`success`,data:a}),a}catch(t){try{await this.#n.config.onError?.(t,e,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onError?.(t,e,this.state.context,n)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,t,this.state.variables,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,t,e,this.state.context,n)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:t}),t}finally{this.#n.runNext(this)}}#i(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}})(this.state),we.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function Ke(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var qe=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new Ge({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=Je(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=Je(e);if(typeof t==`string`){let n=this.#t.get(t);if(n)if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=Je(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}else return!0}runNext(e){let t=Je(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){we.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>ae(t,e))}findAll(e={}){return this.getAll().filter(t=>ae(e,t))}notify(e){we.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return we.batch(()=>Promise.all(e.map(e=>e.continue().catch(w))))}};function Je(e){return e.options.scope?.id}var Ye=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,i=t.queryHash??oe(r,t),a=this.get(i);return a||(a=new je({client:e,queryKey:r,queryHash:i,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(a)),a}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){we.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>ie(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>ie(e,t)):t}notify(e){we.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){we.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){we.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},Xe=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new Ye,this.#t=e.mutationCache||new qe,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=y.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=Te.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(T(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,t,n){let r=this.defaultQueryOptions({queryKey:e}),i=this.#e.get(r.queryHash)?.state.data,a=ee(t,i);if(a!==void 0)return this.#e.build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return we.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;we.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return we.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=we.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(w).catch(w)}invalidateQueries(e,t={}){return we.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=we.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(w)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(w)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(T(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(w).catch(w)}fetchInfiniteQuery(e){return e.behavior=He(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(w).catch(w)}ensureInfiniteQueryData(e){return e.behavior=He(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return Te.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(se(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{ce(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(se(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{ce(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=oe(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===ge&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},Ze=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),Qe=o(((e,t)=>{t.exports=Ze()})),k=l(d(),1),A=Qe(),$e=k.createContext(void 0),et=e=>{let t=k.useContext($e);if(e)return e;if(!t)throw Error(`No QueryClient set, use QueryClientProvider to set one`);return t},tt=({client:e,children:t})=>(k.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),(0,A.jsx)($e.Provider,{value:e,children:t})),nt=k.createContext(!1),rt=()=>k.useContext(nt);nt.Provider;function it(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var at=k.createContext(it()),ot=()=>k.useContext(at),st=(e,t,n)=>{let r=n?.state.error&&typeof e.throwOnError==`function`?ve(e.throwOnError,[n.state.error,n]):e.throwOnError;(e.suspense||e.experimental_prefetchInRender||r)&&(t.isReset()||(e.retryOnMount=!1))},ct=e=>{k.useEffect(()=>{e.clearReset()},[e])},lt=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||ve(n,[e.error,r])),ut=e=>{if(e.suspense){let t=1e3,n=e=>e===`static`?e:Math.max(e??t,t),r=e.staleTime;e.staleTime=typeof r==`function`?(...e)=>n(r(...e)):n(r),typeof e.gcTime==`number`&&(e.gcTime=Math.max(e.gcTime,t))}},dt=(e,t)=>e.isLoading&&e.isFetching&&!t,ft=(e,t)=>e?.suspense&&t.isPending,pt=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function mt(e,t,n){let r=rt(),i=ot(),a=et(n),o=a.defaultQueryOptions(e);a.getDefaultOptions().queries?._experimental_beforeQuery?.(o);let s=a.getQueryCache().get(o.queryHash);o._optimisticResults=r?`isRestoring`:`optimistic`,ut(o),st(o,i,s),ct(i);let c=!a.getQueryCache().get(o.queryHash),[l]=k.useState(()=>new t(a,o)),u=l.getOptimisticResult(o),d=!r&&e.subscribed!==!1;if(k.useSyncExternalStore(k.useCallback(e=>{let t=d?l.subscribe(we.batchCalls(e)):w;return l.updateResult(),t},[l,d]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),k.useEffect(()=>{l.setOptions(o)},[o,l]),ft(o,u))throw pt(o,l,i);if(lt({result:u,errorResetBoundary:i,throwOnError:o.throwOnError,query:s,suspense:o.suspense}))throw u.error;return a.getDefaultOptions().queries?._experimental_afterQuery?.(o,u),o.experimental_prefetchInRender&&!be.isServer()&&dt(u,r)&&(c?pt(o,l,i):s?.promise)?.catch(w).finally(()=>{l.updateResult()}),o.notifyOnChangeProps?u:l.trackResult(u)}function j(e,t){return mt(e,Fe,t)}function ht(e,t){return function(){return e.apply(t,arguments)}}var{toString:gt}=Object.prototype,{getPrototypeOf:_t}=Object,{iterator:vt,toStringTag:yt}=Symbol,bt=(e=>t=>{let n=gt.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),xt=e=>(e=e.toLowerCase(),t=>bt(t)===e),St=e=>t=>typeof t===e,{isArray:Ct}=Array,wt=St(`undefined`);function Tt(e){return e!==null&&!wt(e)&&e.constructor!==null&&!wt(e.constructor)&&M(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var Et=xt(`ArrayBuffer`);function Dt(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&Et(e.buffer),t}var Ot=St(`string`),M=St(`function`),kt=St(`number`),At=e=>typeof e==`object`&&!!e,jt=e=>e===!0||e===!1,Mt=e=>{if(bt(e)!==`object`)return!1;let t=_t(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(yt in e)&&!(vt in e)},Nt=e=>{if(!At(e)||Tt(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Pt=xt(`Date`),Ft=xt(`File`),It=e=>!!(e&&e.uri!==void 0),Lt=e=>e&&e.getParts!==void 0,Rt=xt(`Blob`),zt=xt(`FileList`),Bt=e=>At(e)&&M(e.pipe);function Vt(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var Ht=Vt(),Ut=Ht.FormData===void 0?void 0:Ht.FormData,Wt=e=>{let t;return e&&(Ut&&e instanceof Ut||M(e.append)&&((t=bt(e))===`formdata`||t===`object`&&M(e.toString)&&e.toString()===`[object FormData]`))},Gt=xt(`URLSearchParams`),[Kt,qt,Jt,Yt]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(xt),Xt=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function Zt(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),Ct(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var $t=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,en=e=>!wt(e)&&e!==$t;function tn(){let{caseless:e,skipUndefined:t}=en(this)&&this||{},n={},r=(r,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=e&&Qt(n,i)||i;Mt(n[a])&&Mt(r)?n[a]=tn(n[a],r):Mt(r)?n[a]=tn({},r):Ct(r)?n[a]=r.slice():(!t||!wt(r))&&(n[a]=r)};for(let e=0,t=arguments.length;e(Zt(t,(t,r)=>{n&&M(t)?Object.defineProperty(e,r,{value:ht(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),rn=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),an=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,`constructor`,{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,`super`,{value:t.prototype}),n&&Object.assign(e.prototype,n)},on=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-- >0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&_t(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},sn=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},cn=e=>{if(!e)return null;if(Ct(e))return e;let t=e.length;if(!kt(t))return null;let n=Array(t);for(;t-- >0;)n[t]=e[t];return n},ln=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&_t(Uint8Array)),un=(e,t)=>{let n=(e&&e[vt]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},dn=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},fn=xt(`HTMLFormElement`),pn=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),mn=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),hn=xt(`RegExp`),gn=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};Zt(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},_n=e=>{gn(e,(t,n)=>{if(M(e)&&[`arguments`,`caller`,`callee`].indexOf(n)!==-1)return!1;let r=e[n];if(M(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},vn=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return Ct(e)?r(e):r(String(e).split(t)),n},yn=()=>{},bn=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function xn(e){return!!(e&&M(e.append)&&e[yt]===`FormData`&&e[vt])}var Sn=e=>{let t=Array(10),n=(e,r)=>{if(At(e)){if(t.indexOf(e)>=0)return;if(Tt(e))return e;if(!(`toJSON`in e)){t[r]=e;let i=Ct(e)?[]:{};return Zt(e,(e,t)=>{let a=n(e,r+1);!wt(a)&&(i[t]=a)}),t[r]=void 0,i}}return e};return n(e,0)},Cn=xt(`AsyncFunction`),wn=e=>e&&(At(e)||M(e))&&M(e.then)&&M(e.catch),Tn=((e,t)=>e?setImmediate:t?((e,t)=>($t.addEventListener(`message`,({source:n,data:r})=>{n===$t&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),$t.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,M($t.postMessage)),N={isArray:Ct,isArrayBuffer:Et,isBuffer:Tt,isFormData:Wt,isArrayBufferView:Dt,isString:Ot,isNumber:kt,isBoolean:jt,isObject:At,isPlainObject:Mt,isEmptyObject:Nt,isReadableStream:Kt,isRequest:qt,isResponse:Jt,isHeaders:Yt,isUndefined:wt,isDate:Pt,isFile:Ft,isReactNativeBlob:It,isReactNative:Lt,isBlob:Rt,isRegExp:hn,isFunction:M,isStream:Bt,isURLSearchParams:Gt,isTypedArray:ln,isFileList:zt,forEach:Zt,merge:tn,extend:nn,trim:Xt,stripBOM:rn,inherits:an,toFlatObject:on,kindOf:bt,kindOfTest:xt,endsWith:sn,toArray:cn,forEachEntry:un,matchAll:dn,isHTMLForm:fn,hasOwnProperty:mn,hasOwnProp:mn,reduceDescriptors:gn,freezeMethods:_n,toObjectSet:vn,toCamelCase:pn,noop:yn,toFiniteNumber:bn,findKey:Qt,global:$t,isContextDefined:en,isSpecCompliantForm:xn,toJSONObject:Sn,isAsyncFn:Cn,isThenable:wn,setImmediate:Tn,asap:typeof queueMicrotask<`u`?queueMicrotask.bind($t):typeof process<`u`&&process.nextTick||Tn,isIterable:e=>e!=null&&M(e[vt])},P=class e extends Error{static from(t,n,r,i,a,o){let s=new e(t.message,n||t.code,r,i,a);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),o&&Object.assign(s,o),s}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,`message`,{value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:N.toJSONObject(this.config),code:this.code,status:this.status}}};P.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,P.ERR_BAD_OPTION=`ERR_BAD_OPTION`,P.ECONNABORTED=`ECONNABORTED`,P.ETIMEDOUT=`ETIMEDOUT`,P.ERR_NETWORK=`ERR_NETWORK`,P.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,P.ERR_DEPRECATED=`ERR_DEPRECATED`,P.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,P.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,P.ERR_CANCELED=`ERR_CANCELED`,P.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,P.ERR_INVALID_URL=`ERR_INVALID_URL`;function En(e){return N.isPlainObject(e)||N.isArray(e)}function Dn(e){return N.endsWith(e,`[]`)?e.slice(0,-2):e}function On(e,t,n){return e?e.concat(t).map(function(e,t){return e=Dn(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function kn(e){return N.isArray(e)&&!e.some(En)}var An=N.toFlatObject(N,{},null,function(e){return/^is[A-Z]/.test(e)});function jn(e,t,n){if(!N.isObject(e))throw TypeError(`target must be an object`);t||=new FormData,n=N.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!N.isUndefined(t[e])});let r=n.metaTokens,i=n.visitor||l,a=n.dots,o=n.indexes,s=(n.Blob||typeof Blob<`u`&&Blob)&&N.isSpecCompliantForm(t);if(!N.isFunction(i))throw TypeError(`visitor must be a function`);function c(e){if(e===null)return``;if(N.isDate(e))return e.toISOString();if(N.isBoolean(e))return e.toString();if(!s&&N.isBlob(e))throw new P(`Blob is not supported. Use a Buffer instead.`);return N.isArrayBuffer(e)||N.isTypedArray(e)?s&&typeof Blob==`function`?new Blob([e]):Buffer.from(e):e}function l(e,n,i){let s=e;if(N.isReactNative(t)&&N.isReactNativeBlob(e))return t.append(On(i,n,a),c(e)),!1;if(e&&!i&&typeof e==`object`){if(N.endsWith(n,`{}`))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(N.isArray(e)&&kn(e)||(N.isFileList(e)||N.endsWith(n,`[]`))&&(s=N.toArray(e)))return n=Dn(n),s.forEach(function(e,r){!(N.isUndefined(e)||e===null)&&t.append(o===!0?On([n],r,a):o===null?n:n+`[]`,c(e))}),!1}return En(e)?!0:(t.append(On(i,n,a),c(e)),!1)}let u=[],d=Object.assign(An,{defaultVisitor:l,convertValue:c,isVisitable:En});function f(e,n){if(!N.isUndefined(e)){if(u.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));u.push(e),N.forEach(e,function(e,r){(!(N.isUndefined(e)||e===null)&&i.call(t,e,N.isString(r)?r.trim():r,n,d))===!0&&f(e,n?n.concat(r):[r])}),u.pop()}}if(!N.isObject(e))throw TypeError(`data must be an object`);return f(e),t}function Mn(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`,"%00":`\0`};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function Nn(e,t){this._pairs=[],e&&jn(e,this,t)}var Pn=Nn.prototype;Pn.append=function(e,t){this._pairs.push([e,t])},Pn.toString=function(e){let t=e?function(t){return e.call(this,t,Mn)}:Mn;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Fn(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function In(e,t,n){if(!t)return e;let r=n&&n.encode||Fn,i=N.isFunction(n)?{serialize:n}:n,a=i&&i.serialize,o;if(o=a?a(t,i):N.isURLSearchParams(t)?t.toString():new Nn(t,i).toString(r),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var Ln=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&=[]}forEach(e){N.forEach(this.handlers,function(t){t!==null&&e(t)})}},Rn={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},zn={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Nn,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},Bn=s({hasBrowserEnv:()=>Vn,hasStandardBrowserEnv:()=>Un,hasStandardBrowserWebWorkerEnv:()=>Wn,navigator:()=>Hn,origin:()=>Gn}),Vn=typeof window<`u`&&typeof document<`u`,Hn=typeof navigator==`object`&&navigator||void 0,Un=Vn&&(!Hn||[`ReactNative`,`NativeScript`,`NS`].indexOf(Hn.product)<0),Wn=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Gn=Vn&&window.location.href||`http://localhost`,Kn={...Bn,...zn};function qn(e,t){return jn(e,new Kn.classes.URLSearchParams,{visitor:function(e,t,n,r){return Kn.isNode&&N.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}function Jn(e){return N.matchAll(/\w+|\[(\w*)]/g,e).map(e=>e[0]===`[]`?``:e[1]||e[0])}function Yn(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&N.isArray(r)?r.length:a,s?(N.hasOwnProp(r,a)?r[a]=[r[a],n]:r[a]=n,!o):((!r[a]||!N.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&N.isArray(r[a])&&(r[a]=Yn(r[a])),!o)}if(N.isFormData(e)&&N.isFunction(e.entries)){let n={};return N.forEachEntry(e,(e,r)=>{t(Jn(e),r,n,0)}),n}return null}function Zn(e,t,n){if(N.isString(e))try{return(t||JSON.parse)(e),N.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Qn={transitional:Rn,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=N.isObject(e);if(i&&N.isHTMLForm(e)&&(e=new FormData(e)),N.isFormData(e))return r?JSON.stringify(Xn(e)):e;if(N.isArrayBuffer(e)||N.isBuffer(e)||N.isStream(e)||N.isFile(e)||N.isBlob(e)||N.isReadableStream(e))return e;if(N.isArrayBufferView(e))return e.buffer;if(N.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return qn(e,this.formSerializer).toString();if((a=N.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let t=this.env&&this.env.FormData;return jn(a?{"files[]":e}:e,t&&new t,this.formSerializer)}}return i||r?(t.setContentType(`application/json`,!1),Zn(e)):e}],transformResponse:[function(e){let t=this.transitional||Qn.transitional,n=t&&t.forcedJSONParsing,r=this.responseType===`json`;if(N.isResponse(e)||N.isReadableStream(e))return e;if(e&&N.isString(e)&&(n&&!this.responseType||r)){let n=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(n)throw e.name===`SyntaxError`?P.from(e,P.ERR_BAD_RESPONSE,this,null,this.response):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Kn.classes.FormData,Blob:Kn.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};N.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`],e=>{Qn.headers[e]={}});var $n=N.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),er=e=>{let t={},n,r,i;return e&&e.split(` -`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim(),!(!n||t[n]&&$n[n])&&(n===`set-cookie`?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+`, `+r:r)}),t},tr=Symbol(`internals`);function nr(e){return e&&String(e).trim().toLowerCase()}function rr(e){return e===!1||e==null?e:N.isArray(e)?e.map(rr):String(e)}function ir(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var ar=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function or(e,t,n,r,i){if(N.isFunction(r))return r.call(this,t,n);if(i&&(t=n),N.isString(t)){if(N.isString(r))return t.indexOf(r)!==-1;if(N.isRegExp(r))return r.test(t)}}function sr(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function cr(e,t){let n=N.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var lr=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=nr(t);if(!i)throw Error(`header name must be a non-empty string`);let a=N.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=rr(e))}let a=(e,t)=>N.forEach(e,(e,n)=>i(e,n,t));if(N.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(N.isString(e)&&(e=e.trim())&&!ar(e))a(er(e),t);else if(N.isObject(e)&&N.isIterable(e)){let n={},r,i;for(let t of e){if(!N.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);n[i=t[0]]=(r=n[i])?N.isArray(r)?[...r,t[1]]:[r,t[1]]:t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=nr(e),e){let n=N.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return ir(e);if(N.isFunction(t))return t.call(this,e,n);if(N.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=nr(e),e){let n=N.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||or(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=nr(e),e){let i=N.findKey(n,e);i&&(!t||or(n,n[i],i,t))&&(delete n[i],r=!0)}}return N.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||or(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return N.forEach(this,(r,i)=>{let a=N.findKey(n,i);if(a){t[a]=rr(r),delete t[i];return}let o=e?sr(i):String(i).trim();o!==i&&delete t[i],t[o]=rr(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return N.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&N.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` -`)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[tr]=this[tr]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=nr(e);t[r]||(cr(n,e),t[r]=!0)}return N.isArray(e)?e.forEach(r):r(e),this}};lr.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),N.reduceDescriptors(lr.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),N.freezeMethods(lr);function ur(e,t){let n=this||Qn,r=t||n,i=lr.from(r.headers),a=r.data;return N.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function dr(e){return!!(e&&e.__CANCEL__)}var fr=class extends P{constructor(e,t,n){super(e??`canceled`,P.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function pr(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new P(`Request failed with status code `+n.status,[P.ERR_BAD_REQUEST,P.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function mr(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function hr(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var _r=(e,t,n=3)=>{let r=0,i=hr(50,250);return gr(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},vr=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},yr=e=>(...t)=>N.asap(()=>e(...t)),br=Kn.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Kn.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Kn.origin),Kn.navigator&&/(msie|trident)/i.test(Kn.navigator.userAgent)):()=>!0,xr=Kn.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];N.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),N.isString(r)&&s.push(`path=${r}`),N.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),N.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function Sr(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function Cr(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function wr(e,t,n){let r=!Sr(t);return e&&(r||n==0)?Cr(e,t):t}var Tr=e=>e instanceof lr?{...e}:e;function Er(e,t){t||={};let n={};function r(e,t,n,r){return N.isPlainObject(e)&&N.isPlainObject(t)?N.merge.call({caseless:r},e,t):N.isPlainObject(t)?N.merge({},t):N.isArray(t)?t.slice():t}function i(e,t,n,i){if(!N.isUndefined(t))return r(e,t,n,i);if(!N.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!N.isUndefined(t))return r(void 0,t)}function o(e,t){if(!N.isUndefined(t))return r(void 0,t);if(!N.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(Tr(e),Tr(t),n,!0)};return N.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=N.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);N.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Dr=e=>{let t=Er({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=lr.from(o),t.url=In(wr(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),N.isFormData(n)){if(Kn.hasStandardBrowserEnv||Kn.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(N.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(Kn.hasStandardBrowserEnv&&(r&&N.isFunction(r)&&(r=r(t)),r||r!==!1&&br(t.url))){let e=i&&a&&xr.read(a);e&&o.set(i,e)}return t},Or=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Dr(e),i=r.data,a=lr.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=lr.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());pr(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new P(`Request aborted`,P.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new P(t&&t.message?t.message:`Network Error`,P.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Rn;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new P(t,i.clarifyTimeoutError?P.ETIMEDOUT:P.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&N.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),N.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=_r(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=_r(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new fr(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=mr(r.url);if(_&&Kn.protocols.indexOf(_)===-1){n(new P(`Unsupported protocol `+_+`:`,P.ERR_BAD_REQUEST,e));return}h.send(i||null)})},kr=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof P?t:new fr(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new P(`timeout of ${t}ms exceeded`,P.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>N.asap(o),s}},Ar=function*(e,t){let n=e.byteLength;if(!t||n{let i=jr(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Pr=64*1024,{isFunction:Fr}=N,Ir=(({Request:e,Response:t})=>({Request:e,Response:t}))(N.global),{ReadableStream:Lr,TextEncoder:Rr}=N.global,zr=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Br=e=>{e=N.merge.call({skipUndefined:!0},Ir,e);let{fetch:t,Request:n,Response:r}=e,i=t?Fr(t):typeof fetch==`function`,a=Fr(n),o=Fr(r);if(!i)return!1;let s=i&&Fr(Lr),c=i&&(typeof Rr==`function`?(e=>t=>e.encode(t))(new Rr):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&zr(()=>{let e=!1,t=new n(Kn.origin,{body:new Lr,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return e&&!t}),u=o&&s&&zr(()=>N.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new P(`Response type '${e}' is not supported`,P.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(N.isBlob(e))return e.size;if(N.isSpecCompliantForm(e))return(await new n(Kn.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(N.isArrayBufferView(e)||N.isArrayBuffer(e))return e.byteLength;if(N.isURLSearchParams(e)&&(e+=``),N.isString(e))return(await c(e)).byteLength},p=async(e,t)=>N.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Dr(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=kr([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),ee;try{if(g&&l&&o!==`get`&&o!==`head`&&(ee=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(N.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=vr(ee,_r(yr(g)));s=Nr(e.body,Pr,t,n)}}N.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=N.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&vr(t,_r(yr(h),!0))||[];f=new r(Nr(f.body,Pr,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let te=await d[N.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{pr(t,n,{data:te,headers:lr.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new P(`Network Error`,P.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):P.from(t,t&&t.code,e,C,t&&t.response)}}},Vr=new Map,Hr=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Vr;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:Br(t)),l=c;return c};Hr();var Ur={http:null,xhr:Or,fetch:{get:Hr}};N.forEach(Ur,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Wr=e=>`- ${e}`,Gr=e=>N.isFunction(e)||e===null||e===!1;function Kr(e,t){e=N.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new P(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : -`+e.map(Wr).join(` -`):` `+Wr(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var qr={getAdapter:Kr,adapters:Ur};function Jr(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new fr(null,e)}function Yr(e){return Jr(e),e.headers=lr.from(e.headers),e.data=ur.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),qr.getAdapter(e.adapter||Qn.adapter,e)(e).then(function(t){return Jr(e),t.data=ur.call(e,e.transformResponse,t),t.headers=lr.from(t.headers),t},function(t){return dr(t)||(Jr(e),t&&t.response&&(t.response.data=ur.call(e,e.transformResponse,t.response),t.response.headers=lr.from(t.response.headers))),Promise.reject(t)})}var Xr=`1.13.6`,Zr={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{Zr[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var Qr={};Zr.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Xr+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new P(r(i,` has been removed`+(t?` in `+t:``)),P.ERR_DEPRECATED);return t&&!Qr[i]&&(Qr[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},Zr.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function $r(e,t,n){if(typeof e!=`object`)throw new P(`options must be an object`,P.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new P(`option `+a+` must be `+n,P.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new P(`Unknown option `+a,P.ERR_BAD_OPTION)}}var ei={assertOptions:$r,validators:Zr},F=ei.validators,ti=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Ln,response:new Ln}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` -`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Er(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&ei.assertOptions(n,{silentJSONParsing:F.transitional(F.boolean),forcedJSONParsing:F.transitional(F.boolean),clarifyTimeoutError:F.transitional(F.boolean),legacyInterceptorReqResOrdering:F.transitional(F.boolean)},!1),r!=null&&(N.isFunction(r)?t.paramsSerializer={serialize:r}:ei.assertOptions(r,{encode:F.function,serialize:F.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),ei.assertOptions(t,{baseUrl:F.spelling(`baseURL`),withXsrfToken:F.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&N.merge(i.common,i[t.method]);i&&N.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=lr.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Rn;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[Yr.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new fr(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ri(e){return function(t){return e.apply(null,t)}}function ii(e){return N.isObject(e)&&e.isAxiosError===!0}var ai={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(ai).forEach(([e,t])=>{ai[t]=e});function oi(e){let t=new ti(e),n=ht(ti.prototype.request,t);return N.extend(n,ti.prototype,t,{allOwnKeys:!0}),N.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return oi(Er(e,t))},n}var I=oi(Qn);I.Axios=ti,I.CanceledError=fr,I.CancelToken=ni,I.isCancel=dr,I.VERSION=Xr,I.toFormData=jn,I.AxiosError=P,I.Cancel=I.CanceledError,I.all=function(e){return Promise.all(e)},I.spread=ri,I.isAxiosError=ii,I.mergeConfig=Er,I.AxiosHeaders=lr,I.formToJSON=e=>Xn(N.isHTMLForm(e)?new FormData(e):e),I.getAdapter=qr.getAdapter,I.HttpStatusCode=ai,I.default=I;var si=l(_()),ci=`order-demo-001`;function li(e,t,n,r,i,a){return{eventId:e,aggregateId:ci,aggregateType:`ORDER`,sequenceNumber:t,eventType:n,payload:a,metadata:JSON.stringify({source:`demo`,correlationId:`corr-demo-${t}`}),timestamp:r,globalPosition:i}}function ui(){let e=[],t=Date.parse(`2025-01-15T08:00:00.000Z`);for(let n=1;n<=100;n++){let r=new Date(t+n*45e3).toISOString(),i=5e4+n,a,o;if(n===1)a=`ORDER_PLACED`,o={customerId:`cust-77`,channel:`web`,status:`PENDING`,totalCents:0,itemCount:0};else if(n>=2&&n<=48){a=`LINE_ITEM_ADDED`;let e=350+n*73%1200;o={sku:`SKU-${String(1e4+n*17).slice(-4)}`,qty:n%4+1,lineTotalCents:e,lineIndex:n-1}}else if(n>=49&&n<=58)a=`PAYMENT_PROGRESS`,o={paymentId:`pay-chunk-${n}`,amountCents:1500+n*120,balanceCents:Math.max(0,48e3-n*700)};else if(n>=59&&n<=72){let e=[`inventory`,`fraud_check`,`address_verify`,`manual_review`,`carrier_delay`];a=`FULFILLMENT_BLOCKED`,o={reason:e[n%e.length],caseId:`CASE-${n}`,retryAfterMinutes:15+n%45}}else n>=73&&n<=88?(a=`SHIPMENT_EVENT`,o={leg:n-72,carrier:n%3==0?`FAST`:n%3==1?`ECONOMY`:`OVERNIGHT`,status:`IN_TRANSIT`,trackingToken:`trk-${n}${(n*7919).toString(36)}`}):n>=89&&n<=99?(a=`NOTE_APPENDED`,o={author:`agent-${n%6+1}`,noteId:`n-${n}`,preview:`Ops note #${n}: SLA watch / customer ping`}):(a=`REFUND_ISSUED`,o={refundCents:88e3,balanceCents:-12500,reason:`bulk_settlement_adjustment`});e.push(li(`evt-demo-${n}`,n,a,r,i,JSON.stringify(o)))}return e}var di=ui();function fi(e){return[{code:`NEGATIVE_BALANCE`,severity:`HIGH`,description:`Ledger balance dropped below zero after refund batch`},{code:`REFUND_EXCEEDS_CAPTURE`,severity:`CRITICAL`,description:`Cumulative refunds exceed captured payments for this aggregate`},{code:`DUPLICATE_PAYMENT_CHUNK`,severity:`MEDIUM`,description:`Two payment chunks share the same window and amount fingerprint`},{code:`LINE_ITEM_PRICE_OUTLIER`,severity:`LOW`,description:`Line total deviates >3σ from cohort for this SKU family`},{code:`FULFILLMENT_STALL`,severity:`HIGH`,description:`Order blocked in fulfillment longer than SLA for channel`},{code:`CARRIER_MISMATCH`,severity:`MEDIUM`,description:`Shipment leg carrier differs from preferred routing profile`},{code:`MANUAL_REVIEW_BACKLOG`,severity:`LOW`,description:`Case reopened multiple times without resolution`},{code:`VELOCITY_SPIKE`,severity:`HIGH`,description:`Event rate on this aggregate exceeded rolling baseline`},{code:`ADDRESS_VERIFY_LOOP`,severity:`MEDIUM`,description:`Address verification failed three times with same payload hash`},{code:`INVENTORY_HOLD`,severity:`MEDIUM`,description:`Inventory hold exceeded expected release window`},{code:`FRAUD_SCORE_EDGE`,severity:`LOW`,description:`Fraud score landed in manual-review gray band`},{code:`DISCOUNT_STACK`,severity:`LOW`,description:`Multiple discount signals present without explicit approval event`},{code:`SHIPMENT_GAP`,severity:`HIGH`,description:`Missing scan between expected hub handoffs`},{code:`NOTE_SPAM`,severity:`LOW`,description:`Unusually high operator notes density in short interval`},{code:`PAYMENT_PARTIAL_CLUSTER`,severity:`MEDIUM`,description:`Several partial captures without closing settlement event`},{code:`SKU_QUANTITY_ANOMALY`,severity:`MEDIUM`,description:`Quantity pattern inconsistent with historical order curve`},{code:`CASE_ESCALATION`,severity:`HIGH`,description:`Support case escalated without prior tier-1 closure`},{code:`TRACKING_TOKEN_REUSE`,severity:`CRITICAL`,description:`Tracking token collision across two concurrent legs`},{code:`SLA_BREACH_RISK`,severity:`HIGH`,description:`Projected delivery crosses committed SLA if delay persists`},{code:`SETTLEMENT_BATCH_DRIFT`,severity:`CRITICAL`,description:`Settlement batch totals diverge from summed payment chunks`}].map((t,n)=>{let r=Math.min(100,5+n*5),i=e.find(e=>e.sequenceNumber===r)??e[e.length-1];return{code:t.code,description:t.description,severity:t.severity,aggregateId:ci,atSequence:r,triggeringEventType:i.eventType,timestamp:i.timestamp,stateAtAnomaly:{demoIndex:n+1,atSequence:r,code:t.code}}})}var pi=fi(di);function mi(e,t){let n={...e},r={};try{r=JSON.parse(t.payload||`{}`)}catch{}n._version=t.sequenceNumber,n._lastEventType=t.eventType,n._lastUpdated=t.timestamp;let i=t.eventType.toLowerCase();return i.includes(`created`)||i.includes(`opened`)||i.includes(`placed`)||i.includes(`submitted`)||(i.includes(`deleted`)||i.includes(`closed`)||i.includes(`cancelled`)||i.includes(`rejected`))&&(n.status=`DELETED`),Object.assign(n,r),n}function hi(e,t){let n={};for(let r of Object.keys(t)){let i=e[r],a=t[r];JSON.stringify(i)!==JSON.stringify(a)&&(n[r]={oldValue:i,newValue:a})}for(let r of Object.keys(e))r in t||(n[r]={oldValue:e[r],newValue:void 0});return n}function gi(e){let t=[],n={};for(let r of e){let e={...n};n=mi(n,r);let i={...n};t.push({event:r,stateBefore:e,stateAfter:i,diff:hi(e,i)})}return t}var _i=gi(di);function vi(e){let t=e.trim().toLowerCase();return t.length<2?!1:t.includes(`demo`)||`order-demo-001`.includes(t)}function yi(e){return vi(e)?[ci]:[]}function bi(e){let t=Math.min(Math.max(e,1),500);return[...di].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,t)}function xi(e,t,n){if(e!==`order-demo-001`)return{events:[],totalEvents:0};let r=di.length,i=Math.max(0,n),a=Math.min(Math.max(t,1),1e3);return i>=r?{events:[],totalEvents:r}:{events:di.slice(i,i+a),totalEvents:r}}function Si(e){return e===`order-demo-001`?_i:[]}function Ci(e){let t=Math.min(Math.max(e,1),500);return pi.slice(0,t)}function wi(){return[...di].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,40)}function Ti(){return{status:`UP`,version:`demo`,demo:!0}}function Ei(e=1,t=24){let n=Math.max(e,1)*60*60*1e3,r=[...di].sort((e,t)=>Date.parse(e.timestamp)-Date.parse(t.timestamp)),i=new Map,a=new Map,o=new Map;for(let e of r){let t=new Date(Math.floor(Date.parse(e.timestamp)/n)*n).toISOString();i.set(t,(i.get(t)??0)+1),a.set(e.eventType,(a.get(e.eventType)??0)+1),o.set(e.aggregateType,(o.get(e.aggregateType)??0)+1)}return{totalEvents:di.length,distinctAggregates:1,eventTypes:[...a.entries()].map(([e,t])=>({type:e,count:t})),aggregateTypes:[...o.entries()].map(([e,t])=>({type:e,count:t})),throughput:[...i.entries()].slice(-Math.max(t,1)).map(([e,t])=>({bucket:e,count:t})),available:!0,message:null}}function Di(){return!1}var Oi=I.create({baseURL:`/api`});function ki(e){return new Promise(t=>{setTimeout(t,e)})}function Ai(e,t){return t?`${e}${e.includes(`?`)?`&`:`?`}source=${encodeURIComponent(t)}`:e}var ji=async(e,t=20,n)=>{let r=Ai(`/aggregates/search?q=${encodeURIComponent(e)}&limit=${t}`,n);if(Di()){await ki(40);let n=yi(e);try{let e=await Oi.get(r);return[...new Set([...n,...e.data])].slice(0,t)}catch{return n}}return Oi.get(r).then(e=>e.data)},Mi=async(e,t=500,n=0,r,i=`full`)=>{if(Di()&&e===`order-demo-001`)return await ki(50),xi(e,t,n);let a=Ai(`/aggregates/${e}/timeline?limit=${t}&offset=${n}&fields=${i}`,r);return Oi.get(a).then(e=>e.data)},Ni=async(e,t)=>Di()&&e===`order-demo-001`?(await ki(50),Si(e)):Oi.get(Ai(`/aggregates/${e}/transitions`,t)).then(e=>e.data),Pi=async(e=100,t)=>Di()?(await ki(45),Ci(e)):Oi.get(Ai(`/anomalies/recent?limit=${e}`,t)).then(e=>e.data),Fi=async(e=50,t)=>Di()?(await ki(35),bi(e)):Oi.get(Ai(`/events/recent?limit=${e}`,t)).then(e=>e.data),L=async(e,t=1,n=24)=>{if(Di())return await ki(30),Ei(t,n);let r=Ai(`/v1/statistics?bucketHours=${t}&maxBuckets=${n}`,e);return Oi.get(r).then(e=>e.data)},R=async()=>Di()?(await ki(20),Ti()):Oi.get(`/health`).then(e=>e.data),Ii=async()=>Di()?(await ki(20),[{id:`demo-primary`,displayName:`Demo Primary`,status:`ready`,healthMessage:`Frontend demo datasource`,capabilities:[`timeline`,`replay`,`statistics`]}]):Oi.get(`/v1/datasources`).then(e=>e.data),Li=async()=>Di()?(await ki(20),[{instanceId:`demo-source`,typeId:`demo`,displayName:`Demo Source Plugin`,pluginType:`EVENT_SOURCE`,lifecycle:`ready`,health:{state:`up`,message:`Frontend demo plugin`},lastHealthCheck:new Date().toISOString(),failureReason:null}]):Oi.get(`/v1/plugins`).then(e=>e.data);function Ri(e,t){let[n,r]=(0,k.useState)(e);return(0,k.useEffect)(()=>{let n=setTimeout(()=>r(e),t);return()=>clearTimeout(n)},[e,t]),n}function zi({onSelect:e,source:t,selectedValue:n}){let[r,i]=(0,k.useState)(``),[a,o]=(0,k.useState)(!1),s=(0,k.useRef)(null),c=Ri(r,300),{data:l=[]}=j({queryKey:[`search`,c,t??`default`],queryFn:()=>ji(c,20,t),enabled:c.length>=2,staleTime:5e3});(0,k.useEffect)(()=>{i(n??``)},[n]),(0,k.useEffect)(()=>{let e=e=>{s.current&&!s.current.contains(e.target)&&o(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[]);let u=(0,k.useRef)(null),d=(0,k.useCallback)(()=>{u.current?.focus(),u.current?.select()},[]);(0,k.useEffect)(()=>{let e=document.getElementById(`aggregate-search`);return e?.addEventListener(`focus`,d),()=>e?.removeEventListener(`focus`,d)},[d]);let f=t=>{i(t),o(!1),e(t)};return(0,A.jsxs)(`div`,{className:`search-wrapper`,ref:s,children:[(0,A.jsx)(`span`,{className:`search-icon`,children:`??`}),(0,A.jsx)(`input`,{id:`aggregate-search`,ref:u,type:`text`,className:`search-input`,placeholder:`Search by aggregate ID (e.g. UUID or stream key)`,value:r,onChange:e=>{i(e.target.value),o(!0)},onFocus:()=>r.length>=2&&o(!0),onKeyDown:e=>{e.key===`Enter`&&r.trim()&&f(r.trim()),e.key===`Escape`&&o(!1)},autoComplete:`off`,"aria-controls":`aggregate-search-results`,"aria-autocomplete":`list`}),a&&l.length>0&&(0,A.jsx)(`div`,{className:`search-results`,role:`listbox`,id:`aggregate-search-results`,children:l.map(e=>(0,A.jsxs)(`button`,{type:`button`,className:`search-result-item`,onClick:()=>f(e),role:`option`,children:[(0,A.jsx)(`span`,{className:`search-result-chevron`,"aria-hidden":!0,children:`?`}),(0,A.jsxs)(`span`,{className:`search-result-body`,children:[(0,A.jsx)(`span`,{className:`search-result-label`,children:`ID`}),(0,A.jsx)(`span`,{className:`search-result-colon`,children:`:`}),(0,A.jsx)(`span`,{className:`search-result-value`,children:e})]})]},e))})]})}function Bi(e,t){return j({queryKey:[`timeline`,e,t??`default`,`metadata`],queryFn:()=>Mi(e,500,0,t,`metadata`),enabled:!!e})}function Vi(e){if(typeof e==`number`)return Number.isNaN(e)?new Date:e<0xe8d4a51000?new Date(e*1e3):new Date(e);let t=String(e).trim();if(!t)return new Date;if(t.includes(`T`)||/^\d{4}-\d{2}-\d{2}/.test(t)){let e=Date.parse(t);if(!Number.isNaN(e))return new Date(e)}let n=parseFloat(t);return Number.isNaN(n)?new Date:n<0xe8d4a51000?new Date(n*1e3):new Date(n)}function Hi(e){let t=[],n=0;for(;n=4)t.push({kind:`group`,eventType:r,items:e.slice(n,i),startIndex:n});else for(let r=n;re.sequenceNumber===n),o=t===e;r.push({kind:`group`,key:`group-${e}`,eventType:i.eventType,items:i.items,startIndex:i.startIndex,expanded:o,containsSelection:a}),o&&r.push(...i.items.map((t,n)=>({kind:`group-item`,key:`group-item-${t.sequenceNumber}`,event:t,stepNumber:i.startIndex+n+1,parentKey:e})))}return r}function Gi(e,t){return e===t?null:t}var Ki=[1,6,24,168];function qi(e){let t=e.toLowerCase();return t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`created`:t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`deleted`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`completed`:t.includes(`failed`)||t.includes(`error`)||t.includes(`blocked`)?`failed`:t.includes(`transfer`)?`transfer`:`default`}function Ji(e){switch(e.kind){case`single`:case`group-item`:return e.event.sequenceNumber;case`group`:return e.items[0]?.sequenceNumber??0}}function Yi(e,t){return`Event ${e}, sequence ${t.sequenceNumber}, ${t.eventType}`}function Xi(e,t){return t==null?!1:e.kind===`single`?e.event.sequenceNumber===t:e.items.some(e=>e.sequenceNumber===t)}function Zi(e,t,n,r,i){let a=e.event,o=t===a.sequenceNumber,s=n===a.sequenceNumber,c=e.kind===`group-item`?` timeline-step-compact`:``;return(0,A.jsxs)(`button`,{type:`button`,className:`timeline-step timeline-step-${qi(a.eventType)} ${o?`active`:``} ${s?`timeline-step-compare`:``}${c}`,onClick:e=>{e.shiftKey&&i?i(Gi(n,a.sequenceNumber)):r(a.sequenceNumber)},"aria-current":o?`step`:void 0,"aria-label":Yi(e.stepNumber,a),children:[(0,A.jsxs)(`span`,{className:`timeline-step-badge`,children:[`Event `,e.stepNumber]}),(0,A.jsxs)(`span`,{className:`timeline-step-seq`,children:[`seq #`,a.sequenceNumber]}),(0,A.jsx)(`span`,{className:`timeline-step-type`,children:a.eventType}),(0,A.jsx)(`span`,{className:`timeline-step-seq`,children:Vi(a.timestamp).toLocaleTimeString()})]},e.key)}function Qi({aggregateId:e,selectedSequence:t,compareSequence:n,onSelectEvent:r,onSelectCompare:i,source:a}){let{data:o,isLoading:s}=Bi(e,a),[c,l]=(0,k.useState)(``),[u,d]=(0,k.useState)(``),[f,p]=(0,k.useState)(`all`),[m,h]=(0,k.useState)(null),g=(0,k.useRef)(null),_=o?.events??[],v=o?.totalEvents??0,y=(0,k.useMemo)(()=>{let e=_;if(c&&(e=e.filter(e=>e.eventType===c)),f!==`all`&&e.length>0){let t=Vi(e[e.length-1].timestamp).getTime()-f*60*60*1e3;e=e.filter(e=>Vi(e.timestamp).getTime()>=t)}return e},[_,c,f]),b=(0,k.useMemo)(()=>Hi(y),[y]),x=(0,k.useMemo)(()=>[...new Set(_.map(e=>e.eventType))].sort(),[_]);(0,k.useEffect)(()=>{if(t!=null){for(let e of b)if(e.kind===`group`&&e.items.some(e=>e.sequenceNumber===t)){h(Ui(e.startIndex,e.items.length));return}h(null)}},[b,t]);let S=(0,k.useMemo)(()=>Wi(b,m,t),[m,b,t]),C=(0,k.useMemo)(()=>t==null?-1:S.findIndex(e=>e.kind===`group`?e.items.some(e=>e.sequenceNumber===t):e.event.sequenceNumber===t),[S,t]);if((0,k.useEffect)(()=>{let e=e=>{let t=e.detail;if(S.length&&(t<0||t>0)){let e=C>=0?C:0;r(Ji(S[Math.max(0,Math.min(S.length-1,e+t))]))}};return window.addEventListener(`eventlens:timeline-step`,e),()=>window.removeEventListener(`eventlens:timeline-step`,e)},[r,S,C]),(0,k.useEffect)(()=>{let e=e=>{let n=e.target;if(!(n&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(n.tagName)||!S.length)&&(e.key===`ArrowLeft`||e.key===`ArrowRight`)&&e.shiftKey){e.preventDefault();let n=e.key===`ArrowLeft`?-1:1,i=S.filter(e=>e.kind===`group`);if(!i.length)return;let a=i.findIndex(e=>t!=null&&e.items.some(e=>e.sequenceNumber===t));r(i[a>=0?Math.max(0,Math.min(i.length-1,a+n)):n>0?0:i.length-1].items[0].sequenceNumber)}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r,S,t]),(0,k.useEffect)(()=>{if(!g.current||t==null)return;let e=g.current.querySelector(`[aria-current="step"]`);if(!e)return;let n=g.current.getBoundingClientRect(),r=e.getBoundingClientRect(),i=g.current.scrollLeft,a=i+(r.left-n.left),o=a+r.width,s=i,c=i+g.current.clientWidth;ac-24&&g.current.scrollTo({left:o-g.current.clientWidth+24,behavior:`smooth`})},[m,t]),s)return(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,A.jsx)(`div`,{className:`skeleton`,style:{height:160}})]});if(!_.length)return(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,A.jsx)(`p`,{style:{color:`var(--text-muted)`},children:`No events found.`})]});let w=y[0]?.sequenceNumber??0,ee=y[y.length-1]?.sequenceNumber??0;return(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsxs)(`div`,{className:`timeline-header-row`,children:[(0,A.jsxs)(`div`,{className:`card-title`,style:{marginBottom:0},children:[`Event Sequence`,(0,A.jsxs)(`span`,{className:`timeline-count-pill`,children:[y.length,` / `,v,` events`]})]}),(0,A.jsx)(`div`,{className:`timeline-jump-group`,children:(0,A.jsx)(`input`,{className:`timeline-jump-input`,type:`number`,placeholder:`Jump to seq`,value:u,onChange:e=>d(e.target.value),onKeyDown:e=>{if(e.key===`Enter`){let e=Number(u);y.some(t=>t.sequenceNumber===e)&&(r(e),d(``))}},"aria-label":`Jump to sequence number`})})]}),(0,A.jsx)(`p`,{className:`timeline-hint`,children:`Scroll the rail horizontally from left to right. Repeated event runs collapse into grouped cards; click a group to open the full strip below.`}),(0,A.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Filter by event type`,children:[(0,A.jsx)(`button`,{type:`button`,className:`filter-chip ${c?``:`active`}`,onClick:()=>l(``),children:`All`}),x.map(e=>(0,A.jsx)(`button`,{type:`button`,className:`filter-chip ${c===e?`active`:``}`,onClick:()=>l(t=>t===e?``:e),children:e},e))]}),(0,A.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Zoom range`,children:[(0,A.jsx)(`button`,{type:`button`,className:`filter-chip ${f===`all`?`active`:``}`,onClick:()=>p(`all`),children:`All`}),Ki.map(e=>(0,A.jsx)(`button`,{type:`button`,className:`filter-chip ${f===e?`active`:``}`,onClick:()=>p(e),children:e>=24?`${e/24}d`:`${e}h`},e))]}),(0,A.jsx)(`div`,{className:`timeline-rail`,children:(0,A.jsx)(`div`,{ref:g,className:`timeline-stepper`,children:(0,A.jsx)(`div`,{className:`timeline-stepper-track`,children:b.map((e,a)=>{let o=ah(e=>e===s?null:s),"aria-expanded":c,children:[(0,A.jsxs)(`span`,{className:`timeline-group-chip-top`,children:[(0,A.jsxs)(`span`,{className:`timeline-group-count`,children:[`x`,e.items.length]}),(0,A.jsx)(`span`,{className:`timeline-group-chevron`,"aria-hidden":!0,children:c?`v`:`>`})]}),(0,A.jsx)(`span`,{className:`timeline-group-type`,children:e.eventType}),(0,A.jsxs)(`span`,{className:`timeline-group-range`,children:[`steps `,e.startIndex+1,`-`,e.startIndex+e.items.length,` seq #`,e.items[0].sequenceNumber,`-#`,e.items[e.items.length-1].sequenceNumber]})]}),o,c&&(0,A.jsxs)(`div`,{className:`timeline-expanded-deck`,children:[(0,A.jsxs)(`div`,{className:`timeline-expanded-head`,children:[(0,A.jsx)(`div`,{className:`timeline-expanded-title`,children:e.eventType}),(0,A.jsxs)(`div`,{className:`timeline-group-range`,children:[`Expanded run of `,e.items.length,` events`]}),(0,A.jsx)(`button`,{type:`button`,className:`timeline-collapse-btn`,onClick:()=>h(null),children:`Collapse`})]}),(0,A.jsx)(`div`,{className:`timeline-stepper-track`,children:e.items.map((a,o)=>{let c={kind:`group-item`,key:`group-item-${a.sequenceNumber}`,event:a,stepNumber:e.startIndex+o+1,parentKey:s};return(0,A.jsxs)(`div`,{className:`timeline-track-item`,children:[Zi(c,t,n,r,i),or(Number(e.target.value)),"aria-label":`Scrub event sequence`}),(0,A.jsxs)(`div`,{className:`timeline-info`,children:[(0,A.jsxs)(`span`,{className:`timeline-info-edge`,children:[`First seq #`,w]}),(0,A.jsx)(`span`,{className:`timeline-info-center`,children:t==null?`Select an event`:(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`strong`,{children:[`Selected seq #`,t]}),n!=null&&` compared with #${n}`,(0,A.jsxs)(`span`,{className:`timeline-info-muted`,children:[` across `,b.length,` visible segments`]})]})}),(0,A.jsxs)(`span`,{className:`timeline-info-edge`,children:[`Last seq #`,ee]})]})]})}function $i(e,t){return j({queryKey:[`transitions`,e,t??`default`],queryFn:()=>Ni(e,t),enabled:!!e})}function ea(e,t,n=!0){let[r,i]=(0,k.useState)([]),[a,o]=(0,k.useState)(0),[s,c]=(0,k.useState)(!1),l=(0,k.useMemo)(()=>`${Date.now()}-${Math.random()}`,[e,t]);return(0,k.useEffect)(()=>{if(!n){i([]),o(0),c(!1);return}let r=new Worker(new URL(`/assets/json-diff.worker-DTKMBXSt.js`,``+import.meta.url),{type:`module`});c(!0);let a=e=>{e.data.requestId===l&&(i(e.data.patches),o(e.data.durationMs),c(!1))};return r.addEventListener(`message`,a),r.postMessage({left:e,right:t,requestId:l}),()=>{r.removeEventListener(`message`,a),r.terminate()}},[n,e,t,l]),{patches:r,durationMs:a,loading:s}}function ta({diff:e,patches:t,title:n=`Changes`}){let r=t&&t.length>0?t.map(e=>({field:e.path,oldValue:e.oldValue,newValue:e.newValue,kind:e.type})):Object.entries(e??{}).map(([e,t])=>({field:e,oldValue:t.oldValue,newValue:t.newValue,kind:`changed`}));return r.length?(0,A.jsxs)(`div`,{className:`diff-panel`,children:[(0,A.jsx)(`div`,{className:`diff-toolbar`,children:(0,A.jsxs)(`div`,{className:`diff-toolbar-title`,children:[n,(0,A.jsxs)(`span`,{className:`diff-count-badge`,"aria-live":`polite`,children:[r.length,` `,r.length===1?`change`:`changes`]})]})}),(0,A.jsx)(`div`,{className:`diff-body`,children:(0,A.jsx)(`div`,{className:`diff-scroll`,children:(0,A.jsxs)(`div`,{className:`diff-list diff-list-split`,children:[(0,A.jsxs)(`div`,{className:`diff-split-head`,children:[(0,A.jsx)(`span`,{className:`diff-split-label diff-split-old-label`,children:`Before`}),(0,A.jsx)(`span`,{className:`diff-split-label diff-split-new-label`,children:`After`})]}),r.map((e,t)=>(0,A.jsxs)(`div`,{className:`diff-split-row diff-split-row--${e.kind}`,children:[(0,A.jsx)(`span`,{className:`diff-line-no`,"aria-hidden":!0,children:t+1}),(0,A.jsxs)(`div`,{className:`diff-split-cells`,children:[(0,A.jsxs)(`div`,{className:`diff-split-cell diff-split-old`,children:[(0,A.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,A.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.oldValue)})]}),(0,A.jsxs)(`div`,{className:`diff-split-cell diff-split-new`,children:[(0,A.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,A.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.newValue)})]})]})]},`${e.field}-${t}`))]})})})]}):null}function na({open:e,onToggle:t}){return(0,A.jsx)(`button`,{type:`button`,className:`json-tree-toggle`,onClick:e=>{e.stopPropagation(),t()},"aria-expanded":e,"aria-label":e?`Collapse`:`Expand`,children:e?`-`:`+`})}function ra({value:e}){return e===null?(0,A.jsx)(`span`,{className:`json-null`,children:`null`}):typeof e==`boolean`?(0,A.jsx)(`span`,{className:`json-boolean`,children:String(e)}):typeof e==`number`?(0,A.jsx)(`span`,{className:`json-number`,children:e}):(0,A.jsx)(`span`,{className:`json-string`,children:JSON.stringify(e)})}function ia({value:e,changedKeys:t}){return(0,A.jsx)(`div`,{className:`json-tree json-tree-root`,children:(0,A.jsx)(aa,{value:e,depth:0,changedKeys:t,keyPath:``})})}function aa({value:e,depth:t,propertyKey:n,changedKeys:r,keyPath:i=``}){let a=r&&n!==void 0&&r.has(n),o=r&&i&&[...r].some(e=>e.startsWith(i+`.`)),[s,c]=(0,k.useState)(r?t<3||!!a||!!o:t<3),l={paddingLeft:Math.min(t,12)*14},u=a?{background:`rgba(255, 170, 0, 0.12)`,borderRadius:3}:{};if(e===null||typeof e==`boolean`||typeof e==`number`||typeof e==`string`)return(0,A.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[n!==void 0&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,A.jsx)(ra,{value:e})]});let d=e=>i?`${i}.${e}`:e;if(Array.isArray(e))return e.length===0?(0,A.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`[]`})]}):(0,A.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,A.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,A.jsx)(na,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`[`}),!s&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,e.length,` items `]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`]`})]})]}),s&&(0,A.jsxs)(A.Fragment,{children:[e.map((e,n)=>(0,A.jsx)(aa,{value:e,depth:t+1,changedKeys:r,keyPath:d(String(n))},n)),(0,A.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,A.jsx)(`span`,{className:`json-punct`,children:`]`})})]})]});if(typeof e==`object`){let i=Object.entries(e);return i.length===0?(0,A.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`{}`})]}):(0,A.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,A.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,A.jsx)(na,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`{`}),!s&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,i.length,` keys `]}),(0,A.jsx)(`span`,{className:`json-punct`,children:`}`})]})]}),s&&(0,A.jsxs)(A.Fragment,{children:[i.map(([e,n])=>(0,A.jsx)(aa,{value:n,depth:t+1,propertyKey:e,changedKeys:r,keyPath:d(e)},e)),(0,A.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,A.jsx)(`span`,{className:`json-punct`,children:`}`})})]})]})}return(0,A.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,A.jsx)(`span`,{className:`json-unknown`,children:String(e)})})}var oa=[{id:`changes`,label:`Changes`},{id:`before-after`,label:`Before / After`},{id:`raw`,label:`Raw JSON`}];function sa({aggregateId:e,sequence:t,compareSequence:n,activeTab:r,onTabChange:i,active:a=!1,onActivate:o,source:s}){let{data:c=[],isLoading:l}=$i(e,s),[u,d]=(0,k.useState)(`changes`),f=r??u,p=e=>{d(e),i?.(e)},m=c.find(e=>e.event.sequenceNumber===t)??null,h=n==null?null:c.find(e=>e.event.sequenceNumber===n)??null,g=m!=null&&h!=null&&m.event.sequenceNumber!==h.event.sequenceNumber,_=g?h.stateAfter:m?.stateBefore,v=m?.stateAfter,{patches:y,loading:b,durationMs:x}=ea(_,v,g&&!!_&&!!v),S=(0,k.useMemo)(()=>g?new Set(y.map(e=>e.path.replace(/^\$\./,``).split(`.`)[0])):new Set(Object.keys(m?.diff??{})),[g,y,m?.diff]);return l?(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsx)(`div`,{className:`card-title`,children:`State`}),(0,A.jsx)(`div`,{className:`skeleton`,style:{height:160}})]}):m?(0,A.jsxs)(`section`,{className:`card`,role:`region`,"aria-label":`State viewer`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,A.jsxs)(`div`,{className:`card-title`,children:[`State at Event #`,m.event.sequenceNumber,(0,A.jsx)(`span`,{className:`diff-count-badge`,children:m.event.eventType}),g&&(0,A.jsxs)(`span`,{className:`diff-count-badge`,children:[`Compared with #`,h?.event.sequenceNumber]}),g&&!b&&(0,A.jsxs)(`span`,{className:`diff-count-badge`,children:[`Worker `,x.toFixed(1),`ms`]})]}),(0,A.jsx)(`div`,{className:`state-tabs`,role:`tablist`,children:oa.map(e=>(0,A.jsx)(`button`,{type:`button`,role:`tab`,"aria-selected":f===e.id,className:`state-tab ${f===e.id?`active`:``}`,onClick:()=>p(e.id),children:e.label},e.id))}),(0,A.jsxs)(`div`,{className:`state-tab-content`,role:`tabpanel`,children:[f===`changes`&&(0,A.jsx)(`div`,{children:g?b?(0,A.jsx)(`div`,{className:`skeleton`,style:{height:120}}):(0,A.jsx)(ta,{patches:y,title:`Structural diff`}):m.diff&&Object.keys(m.diff).length>0?(0,A.jsx)(ta,{diff:m.diff}):(0,A.jsx)(`p`,{style:{color:`var(--text-muted)`,marginTop:12,fontSize:13},children:`No field changes at this event.`})}),f===`before-after`&&(0,A.jsxs)(`div`,{className:`state-grid`,style:{marginTop:12},children:[(0,A.jsxs)(`div`,{className:`state-panel state-panel-before`,children:[(0,A.jsx)(`h4`,{children:g?`Event #${h?.event.sequenceNumber}`:`Before`}),(0,A.jsx)(ia,{value:_??{},changedKeys:S})]}),(0,A.jsxs)(`div`,{className:`state-panel state-panel-after`,children:[(0,A.jsx)(`h4`,{children:g?`Event #${m.event.sequenceNumber}`:`After`}),(0,A.jsx)(ia,{value:v??{},changedKeys:S})]})]}),f===`raw`&&(0,A.jsx)(`div`,{style:{marginTop:12},children:(0,A.jsx)(`div`,{className:`json-block`,style:{maxHeight:340},children:JSON.stringify(g?{leftEvent:h?.event,rightEvent:m.event,leftState:_,rightState:v}:m.event,null,2)})})]})]}):null}var z=1e3,ca=3e4;function la(e,t,n){let r=n?.enabled??!0,[i,a]=(0,k.useState)(()=>r?`connecting`:`connected`),o=(0,k.useRef)(null),s=(0,k.useRef)(t),c=(0,k.useRef)(z);return s.current=t,(0,k.useEffect)(()=>{if(!r)return a(`connected`),()=>{};let t=!1,n,i=()=>{if(t)return;let r=new WebSocket(`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.host}${e}`);o.current=r,r.onopen=()=>{c.current=z,a(`connected`)},r.onclose=()=>{if(a(`disconnected`),!t){let e=c.current;n=setTimeout(()=>{c.current=Math.min(e*2,ca),i()},e)}},r.onerror=()=>a(`disconnected`),r.onmessage=e=>{try{let t=JSON.parse(e.data);s.current(t)}catch{}}};return i(),()=>{t=!0,clearTimeout(n),o.current?.close()}},[e,r]),i}var ua=(0,k.createContext)(void 0);function da({children:e}){let[t,n]=(0,k.useState)([]),r=(0,k.useCallback)(e=>{let t=Date.now();n(n=>[...n,{id:t,message:e}]),setTimeout(()=>{n(e=>e.filter(e=>e.id!==t))},4e3)},[]);return(0,A.jsxs)(ua.Provider,{value:{notify:r},children:[e,(0,A.jsx)(`div`,{className:`toast-container`,children:t.map(e=>(0,A.jsx)(`div`,{className:`toast`,children:e.message},e.id))})]})}function fa(){let e=(0,k.useContext)(ua);if(!e)throw Error(`useToast must be used within ToastProvider`);return e}function pa(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`type-deleted`:t.includes(`withdrawn`)||t.includes(`debit`)?`type-withdrawn`:t.includes(`deposited`)||t.includes(`credit`)?`type-deposited`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`type-created`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`type-completed`:t.includes(`failed`)||t.includes(`error`)?`type-failed`:t.includes(`transfer`)?`type-transfer`:`type-default`}function ma(e){return pa(e)}var ha=100;function ga(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`✖`:t.includes(`withdrawn`)||t.includes(`debit`)?`↩`:t.includes(`deposited`)||t.includes(`credit`)?`↪`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`✦`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)?`✔`:t.includes(`failed`)||t.includes(`error`)?`⚠`:t.includes(`transfer`)?`⇄`:`◆`}function _a({source:e,onSelectAggregate:t}){return(0,A.jsx)(ba,{source:e,onSelectAggregate:t})}function va(e){return`type`in e&&e.type===`NO_LIVE_STREAM`}function ya(e){return e?`/ws/live?source=${encodeURIComponent(e)}`:`/ws/live`}function ba({source:e,onSelectAggregate:t}){let n=Di(),[r,i]=(0,k.useState)(()=>n?wi():[]),[a,o]=(0,k.useState)(!1),[s,c]=(0,k.useState)(null),l=(0,k.useRef)(null),u=(0,k.useRef)(a);u.current=a;let{notify:d}=fa();(0,k.useEffect)(()=>{c(null),i(n?wi():[])},[e,n]);let f=la(ya(e),e=>{if(va(e)){c(e.source),i([]);return}c(null),!u.current&&i(t=>[...t.slice(-(ha-1)),e])},{enabled:!n}),p=(0,k.useRef)(d);p.current=d;let m=(0,k.useRef)(0);return(0,k.useEffect)(()=>{n||(f===`disconnected`?(m.current++,m.current<=1&&p.current(`Live stream disconnected. Retrying…`)):f===`connected`&&(m.current=0))},[f,n]),(0,k.useEffect)(()=>{!a&&l.current&&(l.current.scrollTop=l.current.scrollHeight)},[r,a]),(0,k.useEffect)(()=>{let e=()=>o(e=>!e);return window.addEventListener(`eventlens:togglestream`,e),()=>window.removeEventListener(`eventlens:togglestream`,e)},[]),(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsxs)(`div`,{className:`live-header`,children:[(0,A.jsx)(`div`,{className:`card-title`,style:{marginBottom:0},children:`📡 Live Event Stream`}),(0,A.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,A.jsxs)(`div`,{className:`live-indicator`,children:[(0,A.jsx)(`span`,{className:`dot ${f===`connected`?`dot-green`:f===`connecting`?`dot-yellow`:`dot-red`}`}),(0,A.jsx)(`span`,{style:{color:f===`connected`?`var(--neon-green)`:`var(--text-muted)`,fontSize:11},children:f===`connected`?a?`Paused`:`Live`:f})]}),(0,A.jsx)(`button`,{className:`pause-btn`,onClick:()=>o(!a),children:a?`▶ Resume`:`⏸ Pause`})]})]}),(0,A.jsxs)(`div`,{className:`event-stream`,ref:l,children:[s&&(0,A.jsxs)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:[`Live stream not available for this source`,e?` (${e})`:s?` (${s})`:``,`.`]}),r.length===0&&(0,A.jsx)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:s?null:n?`Demo stream (static sample events)`:`Waiting for events…`}),r.map(e=>(0,A.jsxs)(`div`,{className:`event-row ${ma(e.eventType)}`,children:[(0,A.jsx)(`span`,{className:`event-icon`,children:ga(e.eventType)}),(0,A.jsx)(`span`,{className:`event-time`,children:Vi(e.timestamp).toLocaleTimeString()}),(0,A.jsx)(`span`,{className:`event-type ${pa(e.eventType)}`,children:e.eventType}),t?(0,A.jsx)(`button`,{type:`button`,className:`event-agg event-agg-button`,onClick:()=>t(e.aggregateId),children:e.aggregateId}):(0,A.jsx)(`span`,{className:`event-agg`,children:e.aggregateId})]},e.eventId))]})]})}function xa(e){switch(e){case`CRITICAL`:return`sev-critical`;case`HIGH`:return`sev-high`;case`MEDIUM`:return`sev-medium`;case`LOW`:return`sev-low`;default:return`sev-low`}}function Sa(e){switch(e){case`CRITICAL`:return`Critical`;case`HIGH`:return`High`;case`MEDIUM`:return`Warning`;case`LOW`:return`Info`;default:return e}}function Ca(){return(0,A.jsxs)(`svg`,{viewBox:`0 0 64 64`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,A.jsx)(`defs`,{children:(0,A.jsxs)(`linearGradient`,{id:`shield-grad`,x1:`16`,y1:`8`,x2:`48`,y2:`56`,children:[(0,A.jsx)(`stop`,{offset:`0%`,stopColor:`#00ff88`,stopOpacity:`0.9`}),(0,A.jsx)(`stop`,{offset:`100%`,stopColor:`#00cc66`,stopOpacity:`0.6`})]})}),(0,A.jsx)(`path`,{d:`M32 4 L52 14 L52 32 C52 46 32 58 32 58 C32 58 12 46 12 32 L12 14 Z`,stroke:`url(#shield-grad)`,strokeWidth:`2`,fill:`rgba(0, 255, 136, 0.06)`}),(0,A.jsx)(`path`,{d:`M32 10 L48 18 L48 32 C48 43 32 53 32 53 C32 53 16 43 16 32 L16 18 Z`,stroke:`rgba(0, 255, 136, 0.2)`,strokeWidth:`1`,fill:`none`}),(0,A.jsx)(`polyline`,{points:`22,32 29,40 42,24`,stroke:`#00ff88`,strokeWidth:`3`,strokeLinecap:`round`,strokeLinejoin:`round`,fill:`none`})]})}function wa({color:e}){return(0,A.jsx)(`div`,{className:`gauge-wave`,children:Array.from({length:12},(e,t)=>t).map(t=>(0,A.jsx)(`div`,{className:`gauge-wave-bar ${e}`,style:{animationDelay:`${t*.12}s`}},t))})}function Ta({source:e,onSelectAggregate:t}){let{data:n,isLoading:r}=j({queryKey:[`anomalies`,e??`default`],queryFn:()=>Pi(100,e),refetchInterval:3e4}),i=n&&n.length>0;return(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsxs)(`div`,{className:`card-title anomaly-card-title-row`,children:[(0,A.jsx)(`span`,{className:`anomaly-title-text`,children:`⚠️ Anomaly Detection`}),!r&&i&&(0,A.jsx)(`span`,{className:`anomaly-header-count`,"aria-label":`${n.length} anomalies`,children:n.length})]}),r&&(0,A.jsx)(`div`,{className:`skeleton`,style:{height:120}}),!r&&!i&&(0,A.jsxs)(`div`,{className:`anomaly-panel-inner`,children:[(0,A.jsxs)(`div`,{className:`anomaly-shield`,children:[(0,A.jsx)(`div`,{className:`shield-icon`,children:(0,A.jsx)(Ca,{})}),(0,A.jsx)(`div`,{className:`shield-text`,children:`No anomalies detected`})]}),(0,A.jsxs)(`div`,{className:`gauge-row`,children:[(0,A.jsxs)(`div`,{className:`gauge-card optimal`,children:[(0,A.jsx)(`div`,{className:`gauge-label`,children:`Data Integrity`}),(0,A.jsx)(`div`,{className:`gauge-value optimal`,children:`OPTIMAL`}),(0,A.jsx)(wa,{color:`green`})]}),(0,A.jsxs)(`div`,{className:`gauge-card baseline`,children:[(0,A.jsx)(`div`,{className:`gauge-label`,children:`Pattern Scan`}),(0,A.jsx)(`div`,{className:`gauge-value baseline`,children:`BASELINE`}),(0,A.jsx)(wa,{color:`cyan`})]}),(0,A.jsxs)(`div`,{className:`gauge-card zero`,children:[(0,A.jsx)(`div`,{className:`gauge-label`,children:`Threat Level`}),(0,A.jsx)(`div`,{className:`gauge-value zero`,children:`ZERO`}),(0,A.jsx)(wa,{color:`green`})]})]})]}),!r&&i&&(0,A.jsx)(`div`,{className:`anomaly-scroll-region`,children:(0,A.jsx)(`div`,{className:`anomaly-list-inner`,children:n.map((e,n)=>(0,A.jsxs)(`details`,{className:`anomaly-card ${e.severity}`,children:[(0,A.jsxs)(`summary`,{className:`anomaly-card-summary`,children:[(0,A.jsx)(`span`,{className:`anomaly-severity-badge ${xa(e.severity)}`,children:Sa(e.severity)}),(0,A.jsx)(`span`,{className:`anomaly-card-title`,children:e.description}),(0,A.jsx)(`span`,{className:`anomaly-card-chevron`,"aria-hidden":!0,children:`▼`})]}),(0,A.jsxs)(`div`,{className:`anomaly-card-body`,children:[(0,A.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,A.jsx)(`span`,{className:`anomaly-meta-label`,children:`Aggregate`}),t?(0,A.jsx)(`button`,{type:`button`,className:`anomaly-aggregate-link`,onClick:()=>t(e.aggregateId),children:(0,A.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})}):(0,A.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})]}),(0,A.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,A.jsx)(`span`,{className:`anomaly-meta-label`,children:`Sequence`}),(0,A.jsxs)(`span`,{className:`anomaly-meta-value`,children:[`#`,e.atSequence]})]}),(0,A.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,A.jsx)(`span`,{className:`anomaly-meta-label`,children:`Event type`}),(0,A.jsx)(`span`,{className:`anomaly-meta-value`,children:e.triggeringEventType})]}),(0,A.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,A.jsx)(`span`,{className:`anomaly-meta-label`,children:`When`}),(0,A.jsx)(`span`,{className:`anomaly-meta-value`,children:Vi(e.timestamp).toLocaleString()})]}),(0,A.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,A.jsx)(`span`,{className:`anomaly-meta-label`,children:`Code`}),(0,A.jsx)(`code`,{className:`anomaly-meta-value`,children:e.code})]})]})]},`${e.aggregateId}-${e.atSequence}-${n}`))})})]})}var Ea=[{keys:`j / k`,desc:`Move selection through the timeline`},{keys:`Shift+Click`,desc:`Pick a compare event for diff mode`},{keys:`1 - 3`,desc:`Switch state tabs`},{keys:`Ctrl/Cmd+K`,desc:`Open command palette`},{keys:`/`,desc:`Focus aggregate search`},{keys:`Space`,desc:`Pause or resume live stream`},{keys:`?`,desc:`Toggle shortcut help`}];function Da(){let[e,t]=(0,k.useState)(!1);return(0,k.useEffect)(()=>{let e=e=>{e.target.tagName!==`INPUT`&&e.key===`?`&&(e.preventDefault(),t(e=>!e))};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]),(0,A.jsx)(`div`,{className:`keyboard-hints ${e?`keyboard-hints--expanded`:``}`,"aria-label":`Keyboard shortcuts`,children:e?(0,A.jsxs)(`div`,{className:`keyboard-hints-grid`,children:[Ea.map(e=>(0,A.jsxs)(`div`,{className:`keyboard-hint-row`,children:[(0,A.jsx)(`kbd`,{className:`keyboard-key`,children:e.keys}),(0,A.jsx)(`span`,{className:`keyboard-hint-desc`,children:e.desc})]},e.keys)),(0,A.jsx)(`button`,{type:`button`,className:`keyboard-hints-close`,onClick:()=>t(!1),"aria-label":`Close shortcuts`,children:`Close`})]}):(0,A.jsxs)(`div`,{className:`keyboard-hints-bar`,children:[(0,A.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,A.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`j/k`}),` Navigate`]}),(0,A.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,A.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,A.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Shift+Click`}),` Compare`]}),(0,A.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,A.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,A.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Ctrl/Cmd+K`}),` Palette`]}),(0,A.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,A.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,A.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`?`}),` All shortcuts`]})]})})}var Oa=[2,3,5,10];function ka({transitions:e,selectedSequence:t,compareSequence:n,onSelectSequence:r,onClearCompare:i,active:a=!1,onActivate:o}){let[s,c]=(0,k.useState)(null),[l,u]=(0,k.useState)(3),[d,f]=(0,k.useState)(!1);if((0,k.useEffect)(()=>{n==null&&s!=null&&(s.type===`autoplay`&&f(!0),r(s.sequence),c(null))},[n,r,s]),!e.length)return null;let p=e.find(e=>e.event.sequenceNumber===t)??e[0],m=e.findIndex(e=>e.event.sequenceNumber===p.event.sequenceNumber),h=n!=null&&n!==p.event.sequenceNumber,g=m>=e.length-1,_=m<=0;(0,k.useEffect)(()=>{h&&d&&f(!1)},[h,d]),(0,k.useEffect)(()=>{if(!d||g){g&&d&&f(!1);return}let t=window.setInterval(()=>{let t=e[Math.min(m+1,e.length-1)];if(!t||t.event.sequenceNumber===p.event.sequenceNumber){f(!1);return}r(t.event.sequenceNumber)},l*1e3);return()=>window.clearInterval(t)},[g,l,p.event.sequenceNumber,m,d,r,e]);let v=e=>{if(e!==p.event.sequenceNumber){if(h&&i){c({type:`select`,sequence:e});return}f(!1),r(e)}},y=()=>{if(d){f(!1);return}if(g)return;let t=e[Math.min(m+1,e.length-1)].event.sequenceNumber;if(h&&i){c({type:`autoplay`,sequence:t});return}f(!0)};return(0,A.jsxs)(`section`,{className:`card replay-debugger`,tabIndex:0,role:`region`,"aria-label":`Replay debugger`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,A.jsx)(`div`,{className:`card-title`,children:`Replay Debugger`}),(0,A.jsxs)(`div`,{className:`replay-toolbar`,children:[(0,A.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.max(m-1,0)].event.sequenceNumber),title:`Step backward`,"aria-label":`Replay previous event`,disabled:_,children:`Previous`}),(0,A.jsxs)(`div`,{className:`replay-controls-center`,children:[(0,A.jsxs)(`div`,{className:`replay-position`,children:[`Event `,m+1,` of `,e.length]}),(0,A.jsxs)(`div`,{className:`replay-auto-controls`,children:[(0,A.jsx)(`button`,{type:`button`,className:`replay-auto-toggle ${d?`active`:``}`,onClick:y,disabled:g,"aria-pressed":d,children:d?`Pause Auto Replay`:`Start Auto Replay`}),(0,A.jsxs)(`label`,{className:`replay-interval-picker`,children:[(0,A.jsx)(`span`,{children:`Speed`}),(0,A.jsx)(`select`,{value:l,onChange:e=>u(Number(e.target.value)),disabled:d,"aria-label":`Auto replay interval`,children:Oa.map(e=>(0,A.jsxs)(`option`,{value:e,children:[e,`s / event`]},e))})]})]})]}),(0,A.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.min(m+1,e.length-1)].event.sequenceNumber),title:`Step forward`,"aria-label":`Replay next event`,disabled:g,children:`Next`})]}),h&&(0,A.jsxs)(`div`,{className:`replay-compare-warning`,role:`alert`,"aria-live":`polite`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:`Compare mode is active.`}),` Replay stepping or auto replay will replace the comparison and continue from the selected event.`]}),s==null?(0,A.jsx)(`div`,{className:`replay-warning-hint`,children:`Use Previous, Next, the slider, or auto replay. We’ll ask before clearing compare mode.`}):(0,A.jsxs)(`div`,{className:`replay-compare-actions`,children:[(0,A.jsx)(`button`,{type:`button`,className:`replay-warning-btn replay-warning-btn--primary`,onClick:()=>i?.(),children:s.type===`autoplay`?`Start replay and replace compare`:`Continue and replace compare`}),(0,A.jsx)(`button`,{type:`button`,className:`replay-warning-btn`,onClick:()=>c(null),children:`Keep compare`})]})]}),(0,A.jsx)(`input`,{type:`range`,min:0,max:Math.max(e.length-1,0),value:m,onChange:t=>v(e[Number(t.target.value)].event.sequenceNumber),"aria-label":`Replay position`}),(0,A.jsxs)(`div`,{className:`state-panel replay-state`,children:[(0,A.jsx)(`h4`,{children:`Current State`}),(0,A.jsx)(ia,{value:p.stateAfter,changedKeys:new Set(Object.keys(p.diff))})]})]})}function Aa(e,t=1,n=24){return j({queryKey:[`statistics`,e??`default`,t,n],queryFn:()=>L(e,t,n),staleTime:3e4})}function ja({source:e,onBack:t}){let[n,r]=(0,k.useState)(24),{data:i,isLoading:a}=Aa(e,1,n);return(0,A.jsxs)(`section`,{className:`card statistics-panel`,role:`region`,"aria-label":`Statistics panel`,"aria-busy":a,children:[(0,A.jsxs)(`div`,{className:`statistics-panel-header`,children:[(0,A.jsxs)(`div`,{className:`card-title`,children:[`Statistics `,e?`- ${e}`:``]}),(0,A.jsx)(`button`,{type:`button`,className:`statistics-back-btn`,onClick:t,children:`Back to workspace`})]}),(0,A.jsx)(`div`,{className:`statistics-toolbar`,children:[6,24,72].map(e=>(0,A.jsx)(`button`,{type:`button`,className:`filter-chip ${n===e?`active`:``}`,onClick:()=>r(e),children:e===72?`3d`:`${e}h`},e))}),a&&(0,A.jsx)(`div`,{className:`skeleton`,style:{height:140}}),!a&&i&&!i.available&&(0,A.jsx)(`p`,{style:{color:`var(--text-muted)`},children:i.message??`Statistics not available.`}),!a&&i?.available&&(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`div`,{className:`stats-kpis`,children:[(0,A.jsxs)(`div`,{className:`stat-card`,children:[(0,A.jsx)(`strong`,{children:i.totalEvents}),(0,A.jsx)(`span`,{children:`Total events`})]}),(0,A.jsxs)(`div`,{className:`stat-card`,children:[(0,A.jsx)(`strong`,{children:i.distinctAggregates}),(0,A.jsx)(`span`,{children:`Aggregates`})]}),(0,A.jsxs)(`div`,{className:`stat-card`,children:[(0,A.jsx)(`strong`,{children:i.eventTypes.length}),(0,A.jsx)(`span`,{children:`Event types`})]})]}),(0,A.jsx)(`div`,{className:`stats-chart`,children:i.throughput.map(e=>(0,A.jsxs)(`div`,{className:`stats-bar-row`,children:[(0,A.jsx)(`span`,{children:e.bucket.slice(11,16)}),(0,A.jsx)(`div`,{className:`stats-bar-track`,children:(0,A.jsx)(`div`,{className:`stats-bar-fill`,style:{width:`${Math.max(8,e.count/Math.max(...i.throughput.map(e=>e.count),1)*100)}%`}})}),(0,A.jsx)(`strong`,{children:e.count})]},e.bucket))}),(0,A.jsxs)(`div`,{className:`stats-distribution`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h4`,{children:`Event Types`}),i.eventTypes.map(e=>(0,A.jsxs)(`div`,{className:`stats-list-row`,children:[(0,A.jsx)(`span`,{children:e.type}),(0,A.jsx)(`strong`,{children:e.count})]},e.type))]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h4`,{children:`Aggregate Types`}),i.aggregateTypes.map(e=>(0,A.jsxs)(`div`,{className:`stats-list-row`,children:[(0,A.jsx)(`span`,{children:e.type}),(0,A.jsx)(`strong`,{children:e.count})]},e.type))]})]})]})]})}function Ma({open:e,selectedSource:t,onClose:n,onSelectAggregate:r,onOpenHome:i,onOpenStats:a}){let[o,s]=(0,k.useState)(``),[c,l]=(0,k.useState)(0),[u,d]=(0,k.useState)(null),{data:f=[]}=j({queryKey:[`palette-search`,o,t??`default`],queryFn:()=>ji(o,8,t),enabled:e&&o.trim().length>=2,staleTime:5e3});(0,k.useEffect)(()=>{e?(l(0),d(document.activeElement instanceof HTMLElement?document.activeElement:null)):(s(``),u?.focus())},[e,u]);let p=(0,k.useMemo)(()=>{let e=[{id:`home`,label:`Go to main page`,action:i},{id:`stats`,label:`Go to statistics panel`,action:a}],t=f.map(e=>({id:`agg-${e}`,label:`Open aggregate ${e}`,action:()=>r(e)}));return o.trim()&&f.length===0&&e.unshift({id:`direct`,label:`Open aggregate ${o.trim()}`,action:()=>r(o.trim())}),[...t,...e]},[f,i,a,r,o]);return(0,k.useEffect)(()=>{if(!e)return;let t=e=>{e.key===`ArrowDown`?(e.preventDefault(),l(e=>Math.min(e+1,Math.max(p.length-1,0)))):e.key===`ArrowUp`?(e.preventDefault(),l(e=>Math.max(e-1,0))):e.key===`Enter`&&(e.preventDefault(),p[c]?.action(),n())};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[p,n,e,c]),e?(0,A.jsx)(`div`,{className:`command-palette-backdrop`,onClick:n,children:(0,A.jsxs)(`div`,{className:`command-palette`,role:`dialog`,"aria-modal":`true`,"aria-label":`Command palette`,"aria-describedby":`command-palette-help`,onClick:e=>e.stopPropagation(),children:[(0,A.jsx)(`p`,{id:`command-palette-help`,className:`sr-only`,children:`Search aggregates, return to the main page, or open statistics. Use arrow keys to move and Enter to confirm.`}),(0,A.jsx)(`input`,{autoFocus:!0,className:`command-palette-input`,placeholder:`Search aggregates or jump to a panel`,value:o,onChange:e=>s(e.target.value),"aria-label":`Command palette search`}),(0,A.jsxs)(`ul`,{className:`command-palette-list`,role:`listbox`,"aria-label":`Command results`,children:[p.map((e,t)=>(0,A.jsx)(`li`,{className:`command-palette-item ${t===c?`active`:``}`,role:`option`,"aria-selected":t===c,onMouseEnter:()=>l(t),onClick:()=>{e.action(),n()},children:e.label},e.id)),p.length===0&&(0,A.jsx)(`li`,{className:`command-palette-item muted`,children:`Type at least two characters to search.`})]})]})}):null}function Na({paletteOpen:e,onOpenPalette:t,onClosePalette:n}){return(0,k.useEffect)(()=>{let r=r=>{let i=r.target,a=i&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(i.tagName);if((r.metaKey||r.ctrlKey)&&r.key.toLowerCase()===`k`){r.preventDefault(),t();return}if(r.key===`/`&&!a){r.preventDefault(),document.getElementById(`aggregate-search`)?.focus();return}if(r.key===`Escape`&&e){r.preventDefault(),n();return}!a&&(r.key===`j`||r.key===`k`)&&window.dispatchEvent(new CustomEvent(`eventlens:timeline-step`,{detail:r.key===`j`?1:-1}))};return window.addEventListener(`keydown`,r),()=>window.removeEventListener(`keydown`,r)},[e,n,t]),null}function Pa(e){let t=e.toLowerCase();return t===`ready`||t===`up`}function Fa(e){return e.toLowerCase()===`ready`}function Ia(){let e=[6,12,8,16,10,14,7,11,15,9];return(0,A.jsx)(`div`,{className:`mini-wave`,"aria-hidden":!0,children:e.map((e,t)=>(0,A.jsx)(`div`,{className:`mini-wave-bar`,style:{height:e,animationDelay:`${t*.1}s`}},t))})}function La({isUp:e,selectedSource:t,fallbackCount:n}){let[r,i]=(0,k.useState)(0),[a,o]=(0,k.useState)(n),s=(0,k.useRef)(null);(0,k.useEffect)(()=>{let e=Date.now();return s.current=setInterval(()=>{i(Math.floor((Date.now()-e)/1e3))},1e3),()=>{s.current&&clearInterval(s.current)}},[]),(0,k.useEffect)(()=>{o(n)},[n]),(0,k.useEffect)(()=>{let e=!1,r=()=>{Fi(500,t||null).then(t=>{e||o(t.length)}).catch(()=>{!e&&n!=null&&o(n)})};r();let i=setInterval(r,15e3);return()=>{e=!0,clearInterval(i)}},[n,t]);let c=e=>{let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60;return t>0?`${t}h ${n}m`:n>0?`${n}m ${r}s`:`${r}s`};return(0,A.jsxs)(`div`,{className:`conn-stats`,"aria-label":`Connection metrics`,children:[(0,A.jsx)(Ia,{}),(0,A.jsxs)(`div`,{className:`conn-stat`,children:[(0,A.jsx)(`span`,{className:`conn-stat-label`,children:`API`}),(0,A.jsx)(`span`,{className:`conn-stat-value ${e?`green`:`amber`}`,children:e?`Healthy`:`Down`})]}),(0,A.jsxs)(`div`,{className:`conn-stat conn-stat--metric`,children:[(0,A.jsx)(`span`,{className:`conn-stat-label`,children:`Events`}),(0,A.jsx)(`span`,{className:`conn-stat-value`,children:a??`...`})]}),(0,A.jsxs)(`div`,{className:`conn-stat`,children:[(0,A.jsx)(`span`,{className:`conn-stat-label`,children:`Uptime`}),(0,A.jsx)(`span`,{className:`conn-stat-value green conn-stat-value--uptime`,children:c(r)})]})]})}function Ra(){let[e,t]=(0,k.useState)(`state`),[n,r]=(0,k.useState)(null),[i,a]=(0,k.useState)(null),[o,s]=(0,k.useState)(null),[c,l]=(0,k.useState)(`changes`),[u,d]=(0,k.useState)(``),[f,p]=(0,k.useState)(window.location.hash||``),[m,h]=(0,k.useState)(!1),[g,_]=(0,k.useState)(!1);(0,k.useEffect)(()=>{let e=()=>p(window.location.hash||``);return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,k.useEffect)(()=>{f===`#/plugins`&&(window.location.hash=`#/timeline`)},[f]),(0,k.useEffect)(()=>{let e=e=>{let t=e.detail;t&&l(t)};return window.addEventListener(`eventlens:switchtab`,e),()=>window.removeEventListener(`eventlens:switchtab`,e)},[]),(0,k.useEffect)(()=>{let e=new URLSearchParams(window.location.search),n=e.get(`aggregateId`),i=e.get(`seq`),o=e.get(`compare`),c=e.get(`tab`),u=e.get(`panel`),f=e.get(`source`);n&&r(n),i&&a(Number(i)),o&&s(Number(o)),c&&[`changes`,`before-after`,`raw`].includes(c)&&l(c),(u===`replay`||u===`state`)&&t(u),f&&d(f)},[]),(0,k.useEffect)(()=>{let t=new URLSearchParams(window.location.search);n?t.set(`aggregateId`,n):t.delete(`aggregateId`),i==null?t.delete(`seq`):t.set(`seq`,String(i)),o==null?t.delete(`compare`):t.set(`compare`,String(o)),t.set(`tab`,c),t.set(`panel`,e),u?t.set(`source`,u):t.delete(`source`);let r=t.toString(),a=`${window.location.pathname}${r?`?${r}`:``}${window.location.hash}`;window.history.replaceState(null,``,a)},[e,c,o,n,i,u]);let{data:v}=j({queryKey:[`health`],queryFn:R,refetchInterval:3e4}),{data:y=[]}=j({queryKey:[`datasources`],queryFn:Ii,staleTime:1e4}),{data:b=[]}=j({queryKey:[`plugins`],queryFn:Li,staleTime:1e4}),{data:x=[]}=$i(n??``,u||null),{data:S}=j({queryKey:[`timeline-summary`,n,u||`default`],queryFn:()=>Mi(n,500,0,u||null,`metadata`),enabled:!!n,staleTime:3e4}),C=v?.status===`UP`,w=f===`#/stats`,ee=y.filter(e=>Pa(e.status)).length,te=b.filter(e=>Pa(e.lifecycle)).length,ne=y.length-ee+(b.length-te),T=e=>{r(e),a(null),s(null),t(`state`),window.location.hash=`#/timeline`},re=()=>{window.location.hash=`#/timeline`};return(0,A.jsxs)(`div`,{className:`app`,children:[(0,A.jsx)(Na,{paletteOpen:g,onOpenPalette:()=>_(!0),onClosePalette:()=>_(!1)}),(0,A.jsx)(Ma,{open:g,selectedSource:u||null,onClose:()=>_(!1),onSelectAggregate:T,onOpenHome:re,onOpenStats:()=>{window.location.hash=`#/stats`}}),(0,A.jsxs)(`header`,{className:`app-header`,children:[(0,A.jsx)(`div`,{className:`brand`,children:(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`div`,{className:`brand-name`,children:`EventLens`}),(0,A.jsx)(`div`,{className:`brand-sub`,children:`Event Store Visual Debugger`})]})}),(0,A.jsxs)(`div`,{className:`header-center`,children:[Di()&&(0,A.jsx)(`div`,{className:`header-demo-pill`,role:`status`,children:`Demo mode`}),(0,A.jsx)(`div`,{className:`header-title`,children:`EventLens`})]}),(0,A.jsxs)(`div`,{className:`header-actions`,children:[(0,A.jsx)(La,{isUp:C,selectedSource:u,fallbackCount:S?.totalEvents??null}),(0,A.jsxs)(`div`,{className:`header-status`,children:[(0,A.jsx)(`span`,{className:`dot ${C?`dot-green`:`dot-red`}`}),(0,A.jsx)(`span`,{className:`status-text ${C?``:`offline`}`,children:C?`Connected`:v?.status??`Connecting`})]})]})]}),(0,A.jsxs)(`aside`,{className:`workspace-dock${m?` workspace-dock--open`:``}`,"aria-label":`Workspace`,children:[(0,A.jsxs)(`div`,{className:`workspace-dock-panel`,id:`workspace-dock-panel`,hidden:!m,children:[(0,A.jsx)(`div`,{className:`workspace-dock-title`,children:`Workspace`}),(0,A.jsxs)(`label`,{className:`workspace-datasource`,children:[(0,A.jsx)(`span`,{className:`workspace-datasource-label`,children:`Datasource`}),(0,A.jsxs)(`select`,{id:`workspace-datasource-select`,className:`workspace-datasource-select`,value:u,onChange:e=>{d(e.target.value),a(null),s(null)},children:[(0,A.jsx)(`option`,{value:``,children:`Auto (primary datasource)`}),y.map(e=>(0,A.jsxs)(`option`,{value:e.id,disabled:!Fa(e.status),children:[e.id,` [`,e.status,`]`]},e.id))]})]}),(0,A.jsxs)(`div`,{className:`workspace-sidebar-kpis`,children:[(0,A.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,A.jsx)(`span`,{children:`Datasources Healthy`}),(0,A.jsxs)(`strong`,{children:[ee,`/`,y.length||0]})]}),(0,A.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,A.jsx)(`span`,{children:`Plugins Healthy`}),(0,A.jsxs)(`strong`,{children:[te,`/`,b.length||0]})]}),(0,A.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,A.jsx)(`span`,{children:`Issues`}),(0,A.jsx)(`strong`,{children:ne})]})]})]}),(0,A.jsx)(`button`,{type:`button`,className:`workspace-dock-handle`,onClick:()=>h(e=>!e),"aria-expanded":m,"aria-controls":`workspace-dock-panel`,"aria-label":m?`Collapse workspace panel`:`Expand workspace panel`,children:(0,A.jsx)(`span`,{className:`workspace-dock-chevron`,"aria-hidden":!0,children:m?`>`:`<`})})]}),(0,A.jsx)(`main`,{className:`app-main`,role:`main`,"aria-label":`EventLens workspace`,children:(0,A.jsxs)(`div`,{className:`workspace-content`,children:[!w&&(0,A.jsxs)(`div`,{className:`card search-panel card--dropdown-host`,children:[(0,A.jsx)(`label`,{className:`control-field-label`,htmlFor:`aggregate-search`,children:`Search Aggregates`}),(0,A.jsx)(zi,{onSelect:T,source:u||null,selectedValue:n}),n&&(0,A.jsxs)(`div`,{className:`selection-summary`,children:[`Viewing: `,(0,A.jsx)(`span`,{style:{color:`var(--neon-cyan)`},children:n}),u?(0,A.jsxs)(`span`,{children:[` on `,u]}):(0,A.jsx)(`span`,{children:` on primary datasource`}),o!=null&&(0,A.jsxs)(`span`,{children:[` comparing with seq #`,o]}),(0,A.jsxs)(`span`,{className:`sr-only`,"aria-live":`polite`,children:[`Current panel `,e]}),(0,A.jsx)(`button`,{className:`selection-clear-btn`,onClick:()=>{r(null),a(null),s(null)},children:`× clear`})]})]}),w?(0,A.jsx)(ja,{source:u||null,onBack:re}):(0,A.jsxs)(A.Fragment,{children:[n&&(0,A.jsx)(Qi,{aggregateId:n,selectedSequence:i,compareSequence:o,onSelectEvent:e=>{a(e),t(`state`)},onSelectCompare:s,source:u||null}),n&&i!==null&&(0,A.jsx)(sa,{aggregateId:n,sequence:i,compareSequence:o,activeTab:c,onTabChange:e=>{l(e),t(`state`)},active:e===`state`,onActivate:()=>t(`state`),source:u||null}),n&&x.length>0&&(0,A.jsx)(ka,{transitions:x,selectedSequence:i,compareSequence:o,onSelectSequence:e=>{a(e),t(`replay`)},onClearCompare:()=>s(null),active:e===`replay`,onActivate:()=>t(`replay`)}),(0,A.jsxs)(`div`,{className:`bottom-grid`,children:[(0,A.jsx)(_a,{source:u||null,onSelectAggregate:T}),(0,A.jsx)(Ta,{source:u||null,onSelectAggregate:T})]}),n&&S&&(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsx)(`div`,{className:`card-title`,children:`Selection Summary`}),(0,A.jsxs)(`p`,{style:{color:`var(--text-secondary)`},children:[S.totalEvents,` events available for this aggregate. Use Shift+Click in the timeline to compare two points in time, or open the command palette with Ctrl/Cmd+K.`]})]})]})]})}),(0,A.jsx)(Da,{})]})}var za=class extends k.Component{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e,t){console.error(`UI error boundary caught:`,e,t)}render(){return this.state.hasError?(0,A.jsx)(`div`,{className:`app`,children:(0,A.jsx)(`main`,{className:`app-main`,children:(0,A.jsxs)(`div`,{className:`card`,children:[(0,A.jsx)(`div`,{className:`card-title`,children:`Something went wrong`}),(0,A.jsx)(`p`,{style:{color:`var(--text-muted)`,fontSize:13},children:`The UI hit an unexpected error. Try refreshing the page. If the problem persists, check the browser console and server logs.`})]})})}):this.props.children}},Ba=new Xe({defaultOptions:{queries:{staleTime:3e4,retry:2}}});si.createRoot(document.getElementById(`root`)).render((0,A.jsx)(k.StrictMode,{children:(0,A.jsx)(tt,{client:Ba,children:(0,A.jsx)(da,{children:(0,A.jsx)(za,{children:(0,A.jsx)(Ra,{})})})})})); \ No newline at end of file diff --git a/eventlens-api/src/main/resources/web/index.html b/eventlens-api/src/main/resources/web/index.html index 92de5ce..a017a52 100644 --- a/eventlens-api/src/main/resources/web/index.html +++ b/eventlens-api/src/main/resources/web/index.html @@ -9,8 +9,8 @@ - - + +
diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index 185fbfb..307b592 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -1,5 +1,14 @@ package io.eventlens.api; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; import io.eventlens.core.EventLensConfig; import io.eventlens.core.aggregator.ReducerRegistry; import io.eventlens.core.engine.AnomalyDetector; @@ -7,33 +16,50 @@ import io.eventlens.core.engine.DiffEngine; import io.eventlens.core.engine.ExportEngine; import io.eventlens.core.engine.ReplayEngine; +import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.model.StoredEvent; import io.eventlens.core.plugin.PluginManager; import io.eventlens.core.spi.EventStoreReader; +import io.eventlens.api.security.oidc.OidcLoginStateService; +import io.javalin.Javalin; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import java.net.CookieManager; +import java.net.CookiePolicy; import java.net.ServerSocket; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.time.Instant; import java.util.Base64; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import static org.assertj.core.api.Assertions.assertThat; class AuthenticationIntegrationTest { private EventLensServer server; + private FakeOidcProvider oidcProvider; + @TempDir + Path tempDir; @AfterEach void tearDown() { if (server != null) { server.stop(); } + if (oidcProvider != null) { + oidcProvider.stop(); + } } @Test @@ -88,7 +114,156 @@ void authDisabledStillAllowsExistingReadFlow() throws Exception { assertThat(response.body()).contains("ORD-1001"); } + @Test + void basicLoginCanCreateSessionCookieForSubsequentRequests() throws Exception { + int port = freePort(); + server = startServer(port, true); + + var client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + + var loginRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/login/basic".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{\"returnHash\":\"#/timeline\"}")) + .build(); + + HttpResponse loginResponse = client.send(loginRequest, HttpResponse.BodyHandlers.ofString()); + String setCookie = loginResponse.headers().firstValue("Set-Cookie").orElse(""); + + assertThat(loginResponse.statusCode()).isEqualTo(200); + assertThat(setCookie).contains("eventlens_test_session="); + + String cookieHeader = setCookie.substring(0, setCookie.indexOf(';')); + var apiRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("Cookie", cookieHeader) + .GET() + .build(); + + HttpResponse apiResponse = client.send(apiRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(apiResponse.statusCode()).isEqualTo(200); + assertThat(apiResponse.body()).contains("ORD-1001"); + assertThat(loginResponse.body()).contains("\"csrfToken\""); + } + + @Test + void logoutRequiresCsrfTokenForSessionAuthenticatedBrowserFlow() throws Exception { + int port = freePort(); + server = startServer(port, true); + + var client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + + var loginRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/login/basic".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{\"returnHash\":\"#/timeline\"}")) + .build(); + HttpResponse loginResponse = client.send(loginRequest, HttpResponse.BodyHandlers.ofString()); + String setCookie = loginResponse.headers().firstValue("Set-Cookie").orElse(""); + String cookieHeader = setCookie.substring(0, setCookie.indexOf(';')); + String csrfToken = extractJsonField(loginResponse.body(), "csrfToken"); + + var logoutWithoutCsrf = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/logout".formatted(port))) + .header("Cookie", cookieHeader) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse forbidden = client.send(logoutWithoutCsrf, HttpResponse.BodyHandlers.ofString()); + assertThat(forbidden.statusCode()).isEqualTo(403); + + var logoutWithCsrf = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/logout".formatted(port))) + .header("Cookie", cookieHeader) + .header("X-CSRF-Token", csrfToken) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse allowed = client.send(logoutWithCsrf, HttpResponse.BodyHandlers.ofString()); + assertThat(allowed.statusCode()).isEqualTo(200); + assertThat(allowed.body()).contains("\"authenticated\":false"); + } + + @Test + void oidcLoginCreatesBrowserSessionAndRedirectsBackToHashRoute() throws Exception { + int providerPort = freePort(); + int appPort = freePort(); + oidcProvider = new FakeOidcProvider(providerPort); + oidcProvider.start(); + + server = startOidcServer(appPort, "http://localhost:" + providerPort); + + var cookies = new CookieManager(null, CookiePolicy.ACCEPT_ALL); + var client = HttpClient.newBuilder() + .cookieHandler(cookies) + .followRedirects(HttpClient.Redirect.NEVER) + .build(); + + var startRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/login/oidc?returnHash=%%23/stats".formatted(appPort))) + .GET() + .build(); + HttpResponse startResponse = client.send(startRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(startResponse.statusCode()).isEqualTo(302); + String providerAuthorize = startResponse.headers().firstValue("Location").orElseThrow(); + assertThat(providerAuthorize).contains("state="); + + var authorizeRequest = HttpRequest.newBuilder() + .uri(URI.create(providerAuthorize)) + .GET() + .build(); + HttpResponse authorizeResponse = client.send(authorizeRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(authorizeResponse.statusCode()).isEqualTo(302); + String callbackUrl = authorizeResponse.headers().firstValue("Location").orElseThrow(); + assertThat(callbackUrl).contains("/api/v1/auth/callback"); + + var callbackRequest = HttpRequest.newBuilder() + .uri(URI.create(callbackUrl)) + .GET() + .build(); + HttpResponse callbackResponse = client.send(callbackRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(callbackResponse.statusCode()).isEqualTo(302); + assertThat(callbackResponse.headers().firstValue("Location")).hasValue("/#/stats"); + String sessionCookie = callbackResponse.headers().allValues("Set-Cookie").stream() + .filter(value -> value.startsWith("eventlens_test_session=")) + .findFirst() + .orElse(""); + assertThat(sessionCookie).contains("eventlens_test_session="); + + var sessionRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/session".formatted(appPort))) + .GET() + .build(); + HttpResponse sessionResponse = client.send(sessionRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(sessionResponse.statusCode()).isEqualTo(200); + assertThat(sessionResponse.body()).contains("\"authenticated\":true"); + assertThat(sessionResponse.body()).contains("\"authMethod\":\"oidc\""); + + var apiRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(appPort))) + .GET() + .build(); + HttpResponse apiResponse = client.send(apiRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(apiResponse.statusCode()).isEqualTo(200); + assertThat(apiResponse.body()).contains("ORD-1001"); + } + private EventLensServer startServer(int port, boolean authEnabled) { + return startServer(port, authEnabled, null); + } + + private EventLensServer startOidcServer(int port, String issuer) { + return startServer(port, false, issuer); + } + + private EventLensServer startServer(int port, boolean authEnabled, String oidcIssuer) { EventStoreReader reader = new EventStoreReader() { @Override public List getEvents(String aggregateId) { @@ -139,12 +314,24 @@ public List searchAggregates(String query, int limit) { cfg.getServer().getAuth().setUsername("admin"); cfg.getServer().getAuth().setPassword("correct horse battery"); cfg.getServer().getSecurity().getRateLimit().setEnabled(false); + cfg.getSecurity().getMetadata().setEnabled(true); + cfg.getSecurity().getMetadata().setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("auth-" + port + ".db")); + cfg.getSecurity().getAuth().getSession().setCookieName("eventlens_test_session"); + cfg.getSecurity().getAuth().getSession().setSecureCookie(false); + if (oidcIssuer != null) { + cfg.getSecurity().getAuth().setProvider("oidc"); + cfg.getSecurity().getAuth().getOidc().setIssuer(oidcIssuer); + cfg.getSecurity().getAuth().getOidc().setClientId("eventlens-ui"); + cfg.getSecurity().getAuth().getOidc().setClientSecret("super-secret"); + cfg.getSecurity().getAuth().getOidc().setRedirectPath("/api/v1/auth/callback"); + } var replayEngine = new ReplayEngine(reader, new ReducerRegistry()); var bisectEngine = new BisectEngine(replayEngine, reader); var anomalyDetector = new AnomalyDetector(reader, replayEngine, cfg.getAnomaly()); var exportEngine = new ExportEngine(reader, replayEngine); var diffEngine = new DiffEngine(replayEngine); + var metadataDatabase = MetadataDatabase.open(cfg.getSecurity().getMetadata()); EventLensServer eventLensServer = new EventLensServer( cfg, @@ -156,7 +343,9 @@ public List searchAggregates(String query, int limit) { bisectEngine, anomalyDetector, exportEngine, - diffEngine); + diffEngine, + java.util.Map.of(), + metadataDatabase); eventLensServer.start(); return eventLensServer; } @@ -171,4 +360,126 @@ private static int freePort() throws Exception { return socket.getLocalPort(); } } + + private static String extractJsonField(String json, String field) { + String needle = "\"" + field + "\":\""; + int start = json.indexOf(needle); + if (start < 0) { + return ""; + } + int valueStart = start + needle.length(); + int valueEnd = json.indexOf('"', valueStart); + return valueEnd > valueStart ? json.substring(valueStart, valueEnd) : ""; + } + + private static final class FakeOidcProvider { + + private final int port; + private final Javalin app; + private final String issuer; + private final RSAKey rsaJwk; + private final RSASSASigner signer; + private final Map codes = new ConcurrentHashMap<>(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + private FakeOidcProvider(int port) throws Exception { + this.port = port; + this.issuer = "http://localhost:" + port; + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + this.rsaJwk = new RSAKey.Builder((java.security.interfaces.RSAPublicKey) pair.getPublic()) + .privateKey((java.security.interfaces.RSAPrivateKey) pair.getPrivate()) + .keyID("test-key") + .algorithm(JWSAlgorithm.RS256) + .build(); + this.signer = new RSASSASigner(rsaJwk); + + this.app = Javalin.create(config -> { + config.routes.get("/.well-known/openid-configuration", ctx -> ctx.json(Map.of( + "issuer", issuer, + "authorization_endpoint", issuer + "/authorize", + "token_endpoint", issuer + "/token", + "jwks_uri", issuer + "/jwks" + ))); + config.routes.get("/jwks", ctx -> { + JWKSet jwkSet = new JWKSet(rsaJwk.toPublicJWK()); + ctx.result(objectMapper.writeValueAsString(jwkSet.toJSONObject())); + }); + config.routes.get("/authorize", ctx -> { + String state = ctx.queryParam("state"); + String redirectUri = ctx.queryParam("redirect_uri"); + String nonce = ctx.queryParam("nonce"); + String codeChallenge = ctx.queryParam("code_challenge"); + String code = "code-" + System.nanoTime(); + codes.put(code, new AuthorizationState(nonce, codeChallenge)); + ctx.redirect(redirectUri + "?code=" + code + "&state=" + state); + }); + config.routes.post("/token", ctx -> { + String form = ctx.body(); + Map params = parseForm(form); + String code = params.get("code"); + AuthorizationState state = codes.remove(code); + if (state == null) { + ctx.status(400).json(Map.of("error", "invalid_grant")); + return; + } + String codeVerifier = params.get("code_verifier"); + if (!OidcLoginStateService.codeChallenge(codeVerifier).equals(state.codeChallenge())) { + ctx.status(400).json(Map.of("error", "invalid_grant")); + return; + } + + String idToken = signedToken(state.nonce()); + ctx.json(Map.of( + "access_token", "access-token", + "token_type", "Bearer", + "expires_in", 300, + "id_token", idToken + )); + }); + }); + } + + private void start() { + app.start(port); + } + + private void stop() { + app.stop(); + } + + private String signedToken(String nonce) throws JOSEException { + Instant now = Instant.now(); + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .issuer(issuer) + .audience("eventlens-ui") + .subject("oidc-user-1") + .claim("name", "OIDC Test User") + .claim("email", "oidc@example.test") + .claim("nonce", nonce) + .issueTime(java.util.Date.from(now)) + .expirationTime(java.util.Date.from(now.plusSeconds(300))) + .build(); + SignedJWT jwt = new SignedJWT( + new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJwk.getKeyID()).build(), + claims + ); + jwt.sign(signer); + return jwt.serialize(); + } + + private static Map parseForm(String body) { + return java.util.Arrays.stream(body.split("&")) + .map(part -> part.split("=", 2)) + .collect(java.util.stream.Collectors.toMap( + part -> java.net.URLDecoder.decode(part[0], StandardCharsets.UTF_8), + part -> part.length > 1 ? java.net.URLDecoder.decode(part[1], StandardCharsets.UTF_8) : "", + (left, right) -> right + )); + } + + private record AuthorizationState(String nonce, String codeChallenge) { + } + } } diff --git a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java index 5b134ab..2158864 100644 --- a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java +++ b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java @@ -110,7 +110,8 @@ public void run() { anomalyDetector, exportEngine, diffEngine, - datasourceStreamBindings(config)); + datasourceStreamBindings(config), + metadataDatabase); Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { diff --git a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java index e445ff9..ec8401e 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java +++ b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java @@ -4,6 +4,7 @@ import io.eventlens.core.exception.ConfigurationException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -88,6 +89,7 @@ public static List validate(EventLensConfig config) { } validateMetadata(config, issues); + validateSecurityAuth(config, issues); validateLegacyDatasource(config, issues); validateDatasourceInstances(config.getDatasourcesOrLegacy(), issues); validateStreamInstances(config.getStreamsOrLegacy(), issues); @@ -242,6 +244,91 @@ private static void validateMetadata(EventLensConfig config, List issues) { + var security = config.getSecurity(); + if (security == null || security.getAuth() == null) { + return; + } + + var auth = security.getAuth(); + String provider = auth.getProvider() == null ? "disabled" : auth.getProvider().trim().toLowerCase(); + if (!Set.of("disabled", "basic", "oidc").contains(provider)) { + issues.add(error("security.auth.provider", "Must be one of: disabled, basic, oidc")); + return; + } + + var session = auth.getSession(); + if (session != null) { + if (isBlank(session.getCookieName())) { + issues.add(error("security.auth.session.cookie-name", "Required")); + } + if (session.getIdleTimeoutSeconds() < 60 || session.getIdleTimeoutSeconds() > 86_400) { + issues.add(error("security.auth.session.idle-timeout-seconds", "Must be between 60 and 86400")); + } + if (session.getAbsoluteTimeoutSeconds() < session.getIdleTimeoutSeconds()) { + issues.add(error("security.auth.session.absolute-timeout-seconds", + "Must be >= idle-timeout-seconds")); + } + if (session.getAbsoluteTimeoutSeconds() > 604_800) { + issues.add(error("security.auth.session.absolute-timeout-seconds", "Must be <= 604800")); + } + if (!Set.of("lax", "strict", "none").contains(normalize(session.getSameSite()))) { + issues.add(error("security.auth.session.same-site", "Must be one of: Lax, Strict, None")); + } + if ("none".equals(normalize(session.getSameSite())) && !session.isSecureCookie()) { + issues.add(error("security.auth.session.secure-cookie", + "Must be true when same-site is None")); + } + if (session.getCookieName() != null + && session.getCookieName().startsWith("__Host-") + && !session.isSecureCookie()) { + issues.add(error("security.auth.session.secure-cookie", + "Must be true for __Host- prefixed cookies")); + } + } + + if ("oidc".equals(provider)) { + if (security.getMetadata() == null || !security.getMetadata().isEnabled()) { + issues.add(error("security.metadata.enabled", "Must be enabled when security.auth.provider is oidc")); + } + + var oidc = auth.getOidc(); + if (oidc == null) { + issues.add(error("security.auth.oidc", "Required when provider is oidc")); + return; + } + if (isBlank(oidc.getIssuer())) { + issues.add(error("security.auth.oidc.issuer", "Required when provider is oidc")); + } + if (isBlank(oidc.getClientId())) { + issues.add(error("security.auth.oidc.client-id", "Required when provider is oidc")); + } + if (isBlank(oidc.getClientSecret())) { + issues.add(error("security.auth.oidc.client-secret", "Required when provider is oidc")); + } + if (isBlank(oidc.getRedirectPath()) || !oidc.getRedirectPath().startsWith("/")) { + issues.add(error("security.auth.oidc.redirect-path", "Must start with '/'")); + } + if (isBlank(oidc.getPostLogoutRedirectPath()) || !oidc.getPostLogoutRedirectPath().startsWith("/")) { + issues.add(error("security.auth.oidc.post-logout-redirect-path", "Must start with '/'")); + } + if (oidc.getScopes() == null || oidc.getScopes().isEmpty()) { + issues.add(error("security.auth.oidc.scopes", "At least one scope is required")); + } else { + var normalizedScopes = new HashSet(); + for (int i = 0; i < oidc.getScopes().size(); i++) { + String scope = oidc.getScopes().get(i); + String path = "security.auth.oidc.scopes[%d]".formatted(i); + if (isBlank(scope)) { + issues.add(error(path, "Scope must not be blank")); + } else if (!normalizedScopes.add(scope.trim().toLowerCase())) { + issues.add(warning(path, "Duplicate scope")); + } + } + } + } + } + private static void validatePool(String base, EventLensConfig.PoolConfig pool, List issues) { if (pool == null) { return; @@ -290,4 +377,5 @@ public static void validateOrThrow(EventLensConfig config) { private static ValidationError error(String path, String message) { return new ValidationError(path, message, ValidationError.Severity.ERROR); } private static ValidationError warning(String path, String message) { return new ValidationError(path, message, ValidationError.Severity.WARNING); } private static boolean isBlank(String s) { return s == null || s.isBlank(); } + private static String normalize(String s) { return s == null ? "" : s.trim().toLowerCase(); } } diff --git a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java index 963c079..22de324 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java +++ b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java @@ -129,9 +129,66 @@ public static class SecurityConfig { public static class SecurityFeaturesConfig { private MetadataConfig metadata = new MetadataConfig(); + private AuthProviderConfig auth = new AuthProviderConfig(); public MetadataConfig getMetadata() { return metadata; } public void setMetadata(MetadataConfig metadata) { this.metadata = metadata; } + public AuthProviderConfig getAuth() { return auth; } + public void setAuth(AuthProviderConfig auth) { this.auth = auth; } + } + + public static class AuthProviderConfig { + private String provider = "disabled"; + private SessionConfig session = new SessionConfig(); + private OidcConfig oidc = new OidcConfig(); + + public String getProvider() { return provider; } + public void setProvider(String provider) { this.provider = provider; } + public SessionConfig getSession() { return session; } + public void setSession(SessionConfig session) { this.session = session; } + public OidcConfig getOidc() { return oidc; } + public void setOidc(OidcConfig oidc) { this.oidc = oidc; } + } + + public static class SessionConfig { + private String cookieName = "__Host-eventlens_session"; + private int idleTimeoutSeconds = 1_800; + private int absoluteTimeoutSeconds = 28_800; + private boolean secureCookie = true; + private String sameSite = "Lax"; + + public String getCookieName() { return cookieName; } + public void setCookieName(String cookieName) { this.cookieName = cookieName; } + public int getIdleTimeoutSeconds() { return idleTimeoutSeconds; } + public void setIdleTimeoutSeconds(int idleTimeoutSeconds) { this.idleTimeoutSeconds = idleTimeoutSeconds; } + public int getAbsoluteTimeoutSeconds() { return absoluteTimeoutSeconds; } + public void setAbsoluteTimeoutSeconds(int absoluteTimeoutSeconds) { this.absoluteTimeoutSeconds = absoluteTimeoutSeconds; } + public boolean isSecureCookie() { return secureCookie; } + public void setSecureCookie(boolean secureCookie) { this.secureCookie = secureCookie; } + public String getSameSite() { return sameSite; } + public void setSameSite(String sameSite) { this.sameSite = sameSite; } + } + + public static class OidcConfig { + private String issuer; + private String clientId; + private String clientSecret; + private String redirectPath = "/api/v1/auth/callback"; + private String postLogoutRedirectPath = "/"; + private List scopes = List.of("openid", "profile", "email"); + + public String getIssuer() { return issuer; } + public void setIssuer(String issuer) { this.issuer = issuer; } + public String getClientId() { return clientId; } + public void setClientId(String clientId) { this.clientId = clientId; } + public String getClientSecret() { return clientSecret; } + public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } + public String getRedirectPath() { return redirectPath; } + public void setRedirectPath(String redirectPath) { this.redirectPath = redirectPath; } + public String getPostLogoutRedirectPath() { return postLogoutRedirectPath; } + public void setPostLogoutRedirectPath(String postLogoutRedirectPath) { this.postLogoutRedirectPath = postLogoutRedirectPath; } + public List getScopes() { return scopes; } + public void setScopes(List scopes) { this.scopes = scopes == null ? List.of() : scopes; } } public static class MetadataConfig { diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java b/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java new file mode 100644 index 0000000..0e36a6d --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java @@ -0,0 +1,107 @@ +package io.eventlens.core.security; + +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.metadata.SessionRecord; +import io.eventlens.core.metadata.SessionRepository; + +import java.time.Clock; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * Server-side opaque session lifecycle helper for v5 auth flows. + */ +public final class SessionService { + + private final SessionRepository sessionRepository; + private final EventLensConfig.SessionConfig sessionConfig; + private final Clock clock; + + public SessionService(SessionRepository sessionRepository, EventLensConfig.SessionConfig sessionConfig) { + this(sessionRepository, sessionConfig, Clock.systemUTC()); + } + + SessionService(SessionRepository sessionRepository, EventLensConfig.SessionConfig sessionConfig, Clock clock) { + this.sessionRepository = sessionRepository; + this.sessionConfig = sessionConfig; + this.clock = clock; + } + + public SessionRecord create(Principal principal, Map attributes) { + Instant now = clock.instant(); + SessionRecord record = new SessionRecord( + newSessionId(), + principal.userId(), + principal.displayName(), + principal.authMethod(), + List.copyOf(principal.roles()), + attributes, + now, + now, + now.plusSeconds(sessionConfig.getIdleTimeoutSeconds()), + now.plusSeconds(sessionConfig.getAbsoluteTimeoutSeconds())); + sessionRepository.upsert(record); + return record; + } + + public Optional findActive(String sessionId) { + if (sessionId == null || sessionId.isBlank()) { + return Optional.empty(); + } + Optional record = sessionRepository.findById(sessionId); + if (record.isEmpty()) { + return Optional.empty(); + } + Instant now = clock.instant(); + SessionRecord session = record.get(); + if (isExpired(session, now)) { + sessionRepository.deleteById(sessionId); + return Optional.empty(); + } + return Optional.of(session); + } + + public Optional touch(String sessionId) { + Optional record = findActive(sessionId); + if (record.isEmpty()) { + return Optional.empty(); + } + Instant now = clock.instant(); + Instant idleExpiry = now.plusSeconds(sessionConfig.getIdleTimeoutSeconds()); + sessionRepository.touch(sessionId, now, idleExpiry); + SessionRecord current = record.get(); + return Optional.of(new SessionRecord( + current.sessionId(), + current.principalUserId(), + current.displayName(), + current.authMethod(), + current.roles(), + current.attributes(), + current.createdAt(), + now, + idleExpiry, + current.absoluteExpiresAt())); + } + + public void invalidate(String sessionId) { + if (sessionId == null || sessionId.isBlank()) { + return; + } + sessionRepository.deleteById(sessionId); + } + + public int deleteExpiredSessions() { + return sessionRepository.deleteExpired(clock.instant()); + } + + private static boolean isExpired(SessionRecord session, Instant now) { + return !session.idleExpiresAt().isAfter(now) || !session.absoluteExpiresAt().isAfter(now); + } + + private static String newSessionId() { + return UUID.randomUUID().toString().replace("-", ""); + } +} diff --git a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java index 54e501a..f770a85 100644 --- a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java +++ b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java @@ -62,4 +62,31 @@ void metadataStorageMustUseSqliteWhenEnabled() { assertThat(issues.stream().anyMatch(i -> i.path().equals("security.metadata.jdbc-url") && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); } + + @Test + void oidcProviderRequiresMetadataAndCoreSettings() { + var cfg = new EventLensConfig(); + cfg.getSecurity().getAuth().setProvider("oidc"); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.metadata.enabled") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.auth.oidc.issuer") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.auth.oidc.client-id") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.auth.oidc.client-secret") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } + + @Test + void sessionCookieNoneRequiresSecureCookie() { + var cfg = new EventLensConfig(); + cfg.getSecurity().getAuth().getSession().setSameSite("None"); + cfg.getSecurity().getAuth().getSession().setSecureCookie(false); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.auth.session.secure-cookie") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } } diff --git a/eventlens-core/src/test/java/io/eventlens/core/security/SessionServiceTest.java b/eventlens-core/src/test/java/io/eventlens/core/security/SessionServiceTest.java new file mode 100644 index 0000000..a239d65 --- /dev/null +++ b/eventlens-core/src/test/java/io/eventlens/core/security/SessionServiceTest.java @@ -0,0 +1,78 @@ +package io.eventlens.core.security; + +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.metadata.MetadataDatabase; +import io.eventlens.core.metadata.SessionRecord; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneOffset; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class SessionServiceTest { + + @TempDir + Path tempDir; + + @Test + void createAndTouchSessionPersistsServerSideState() { + EventLensConfig.MetadataConfig metadataConfig = new EventLensConfig.MetadataConfig(); + metadataConfig.setEnabled(true); + metadataConfig.setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("sessions.db")); + + EventLensConfig.SessionConfig sessionConfig = new EventLensConfig.SessionConfig(); + sessionConfig.setIdleTimeoutSeconds(300); + sessionConfig.setAbsoluteTimeoutSeconds(3600); + + try (MetadataDatabase database = MetadataDatabase.open(metadataConfig)) { + Instant now = Instant.parse("2026-03-27T00:00:00Z"); + SessionService service = new SessionService( + database.repositories().sessions(), + sessionConfig, + Clock.fixed(now, ZoneOffset.UTC)); + + SessionRecord created = service.create( + Principal.basic("alice"), + Map.of("returnHash", "#/timeline")); + + assertThat(service.findActive(created.sessionId())).isPresent(); + assertThat(service.touch(created.sessionId())).isPresent() + .get() + .extracting(SessionRecord::idleExpiresAt) + .isEqualTo(now.plusSeconds(300)); + } + } + + @Test + void expiredSessionIsRejectedAndDeleted() { + EventLensConfig.MetadataConfig metadataConfig = new EventLensConfig.MetadataConfig(); + metadataConfig.setEnabled(true); + metadataConfig.setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("expired-sessions.db")); + + EventLensConfig.SessionConfig sessionConfig = new EventLensConfig.SessionConfig(); + sessionConfig.setIdleTimeoutSeconds(60); + sessionConfig.setAbsoluteTimeoutSeconds(120); + + try (MetadataDatabase database = MetadataDatabase.open(metadataConfig)) { + Instant issuedAt = Instant.parse("2026-03-27T00:00:00Z"); + SessionService creator = new SessionService( + database.repositories().sessions(), + sessionConfig, + Clock.fixed(issuedAt, ZoneOffset.UTC)); + SessionRecord created = creator.create(Principal.basic("alice"), Map.of()); + + SessionService expiredReader = new SessionService( + database.repositories().sessions(), + sessionConfig, + Clock.fixed(issuedAt.plusSeconds(61), ZoneOffset.UTC)); + + assertThat(expiredReader.findActive(created.sessionId())).isEmpty(); + assertThat(database.repositories().sessions().findById(created.sessionId())).isEmpty(); + } + } +} diff --git a/eventlens-ui/src/App.tsx b/eventlens-ui/src/App.tsx index 1c83fab..b7335f1 100644 --- a/eventlens-ui/src/App.tsx +++ b/eventlens-ui/src/App.tsx @@ -1,5 +1,5 @@ -import { useEffect, useRef, useState } from 'react'; -import { useQuery } from '@tanstack/react-query'; +import { FormEvent, useEffect, useRef, useState } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import SearchBar from './components/SearchBar'; import Timeline from './components/Timeline'; import StateViewer, { type TabId } from './components/StateViewer'; @@ -11,11 +11,16 @@ import StatisticsPanel from './components/StatisticsPanel'; import CommandPalette from './components/CommandPalette'; import KeyboardManager from './components/KeyboardManager'; import { + buildOidcLoginUrl, + getAuthSession, getDatasources, getHealth, getPlugins, getRecentEvents, getTimeline, + loginWithBasicSession, + logoutSession, + setCsrfToken, } from './api/client'; import { isDemoMode } from './demo/demoMode'; import { useReplay } from './hooks/useReplay'; @@ -125,6 +130,7 @@ function ConnectionStats({ } export default function App() { + const queryClient = useQueryClient(); const [activePanel, setActivePanel] = useState<'state' | 'replay'>('state'); const [selectedAggregate, setSelectedAggregate] = useState(null); const [selectedSequence, setSelectedSequence] = useState(null); @@ -134,6 +140,12 @@ export default function App() { const [currentHash, setCurrentHash] = useState(window.location.hash || ''); const [workspaceDockOpen, setWorkspaceDockOpen] = useState(false); const [paletteOpen, setPaletteOpen] = useState(false); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [authError, setAuthError] = useState(null); + const [isSubmittingLogin, setIsSubmittingLogin] = useState(false); + const [isSubmittingLogout, setIsSubmittingLogout] = useState(false); + const [oidcError, setOidcError] = useState(null); useEffect(() => { const syncHash = () => setCurrentHash(window.location.hash || ''); @@ -158,12 +170,20 @@ export default function App() { useEffect(() => { const params = new URLSearchParams(window.location.search); + const authErrorParam = params.get('authError'); const aggregateId = params.get('aggregateId'); const seq = params.get('seq'); const compare = params.get('compare'); const tab = params.get('tab') as TabId | null; const panel = params.get('panel'); const source = params.get('source'); + if (authErrorParam) { + setOidcError(authErrorParam.replace(/_/g, ' ')); + params.delete('authError'); + const qs = params.toString(); + const newUrl = `${window.location.pathname}${qs ? `?${qs}` : ''}${window.location.hash || '#/timeline'}`; + window.history.replaceState(null, '', newUrl); + } if (aggregateId) setSelectedAggregate(aggregateId); if (seq) setSelectedSequence(Number(seq)); if (compare) setCompareSequence(Number(compare)); @@ -185,15 +205,47 @@ export default function App() { window.history.replaceState(null, '', newUrl); }, [activePanel, activeTab, compareSequence, selectedAggregate, selectedSequence, selectedSource]); - const { data: health } = useQuery({ queryKey: ['health'], queryFn: getHealth, refetchInterval: 30_000 }); - const { data: datasources = [] } = useQuery({ queryKey: ['datasources'], queryFn: getDatasources, staleTime: 10_000 }); - const { data: plugins = [] } = useQuery({ queryKey: ['plugins'], queryFn: getPlugins, staleTime: 10_000 }); - const { data: transitions = [] } = useReplay(selectedAggregate ?? '', selectedSource || null); + const authQuery = useQuery({ + queryKey: ['auth-session'], + queryFn: getAuthSession, + retry: false, + refetchOnWindowFocus: false, + }); + const isAuthenticated = isDemoMode() || authQuery.data?.authenticated === true; + const authReady = isDemoMode() || authQuery.isSuccess || authQuery.isError; + + useEffect(() => { + if (isDemoMode()) { + setCsrfToken(null); + return; + } + setCsrfToken(authQuery.data?.authenticated ? (authQuery.data.csrfToken ?? null) : null); + }, [authQuery.data]); + + const { data: health } = useQuery({ + queryKey: ['health'], + queryFn: getHealth, + refetchInterval: 30_000, + enabled: isAuthenticated, + }); + const { data: datasources = [] } = useQuery({ + queryKey: ['datasources'], + queryFn: getDatasources, + staleTime: 10_000, + enabled: isAuthenticated, + }); + const { data: plugins = [] } = useQuery({ + queryKey: ['plugins'], + queryFn: getPlugins, + staleTime: 10_000, + enabled: isAuthenticated, + }); + const { data: transitions = [] } = useReplay(selectedAggregate ?? '', selectedSource || null, isAuthenticated); const { data: timelineSummary } = useQuery({ queryKey: ['timeline-summary', selectedAggregate, selectedSource || 'default'], queryFn: () => getTimeline(selectedAggregate!, 500, 0, selectedSource || null, 'metadata'), - enabled: !!selectedAggregate, + enabled: isAuthenticated && !!selectedAggregate, staleTime: 30_000, }); @@ -215,6 +267,127 @@ export default function App() { window.location.hash = '#/timeline'; }; + const handleLogin = async (event: FormEvent) => { + event.preventDefault(); + setAuthError(null); + setOidcError(null); + setIsSubmittingLogin(true); + try { + const session = await loginWithBasicSession(username, password, currentHash || '#/timeline'); + setPassword(''); + queryClient.setQueryData(['auth-session'], session); + await queryClient.invalidateQueries(); + window.location.hash = session.returnHash || currentHash || '#/timeline'; + } catch { + setAuthError('Login failed. Check your credentials and try again.'); + } finally { + setIsSubmittingLogin(false); + } + }; + + const handleLogout = async () => { + setIsSubmittingLogout(true); + setAuthError(null); + setOidcError(null); + try { + await logoutSession(); + queryClient.setQueryData(['auth-session'], { authenticated: false }); + await queryClient.invalidateQueries(); + setPassword(''); + window.location.hash = '#/timeline'; + } finally { + setIsSubmittingLogout(false); + } + }; + + if (!authReady) { + return ( +
+
+
+
Security Session
+

Checking active session

+

+ We are verifying whether this browser already has an active EventLens session. +

+
+
+
+ ); + } + + if (!isAuthenticated) { + const provider = authQuery.data?.provider ?? 'basic'; + const showBasicLogin = authQuery.data?.basicLoginEnabled ?? provider !== 'oidc'; + return ( +
+
+
+
EventLens Security
+

{authQuery.isError ? 'Server unavailable' : 'Sign in to continue'}

+

+ {authQuery.isError + ? 'We could not reach the EventLens API to check session state. Start the server or restore connectivity, then refresh this page.' + : provider === 'oidc' + ? 'This deployment expects OpenID Connect sign-in and then creates a server-side browser session for EventLens.' + : 'This v5 transition uses a server-side browser session. After login, we return you to the current workspace route.'} +

+ {oidcError &&
OIDC sign-in failed: {oidcError}
} + {!authQuery.isError && provider === 'oidc' && ( + + )} + {!authQuery.isError && showBasicLogin && ( +
+ + +
+ Return route: {currentHash || '#/timeline'} +
+ {authError &&
{authError}
} + +
+ )} + {authQuery.isError && ( +
+ Session check failed. Refresh after the API is available again. +
+ )} +
+
+
+ ); + } + return (
setPaletteOpen(true)} onClosePalette={() => setPaletteOpen(false)} /> @@ -238,6 +411,17 @@ export default function App() {
EventLens
+ {authQuery.data?.principal && ( +
+
+ Signed in + {authQuery.data.principal.displayName} +
+ +
+ )}
diff --git a/eventlens-ui/src/api/client.ts b/eventlens-ui/src/api/client.ts index 1cd1000..4905c7c 100644 --- a/eventlens-ui/src/api/client.ts +++ b/eventlens-ui/src/api/client.ts @@ -25,7 +25,37 @@ import { } from '../demo/demoData'; import { isDemoMode } from '../demo/demoMode'; -const api = axios.create({ baseURL: '/api' }); +const api = axios.create({ + baseURL: '/api', + withCredentials: true, +}); + +let csrfToken: string | null = null; + +api.interceptors.request.use(config => { + const method = (config.method ?? 'get').toUpperCase(); + if (csrfToken && !['GET', 'HEAD', 'OPTIONS'].includes(method)) { + config.headers = config.headers ?? {}; + config.headers['X-CSRF-Token'] = csrfToken; + } + return config; +}); + +export interface AuthPrincipal { + userId: string; + displayName: string; + authMethod: string; + roles: string[]; +} + +export interface AuthSessionResponse { + authenticated: boolean; + principal?: AuthPrincipal; + returnHash?: string; + provider?: string; + basicLoginEnabled?: boolean; + csrfToken?: string; +} function delay(ms: number) { return new Promise(resolve => { @@ -41,6 +71,16 @@ function withOptionalSource(path: string, source?: string | null) { return `${path}${separator}source=${encodeURIComponent(source)}`; } +function encodeBasicCredentials(username: string, password: string) { + const credentials = `${username}:${password}`; + const bytes = new TextEncoder().encode(credentials); + let binary = ''; + bytes.forEach(byte => { + binary += String.fromCharCode(byte); + }); + return btoa(binary); +} + export type { AnomalyReport, BisectResult, @@ -156,6 +196,47 @@ export const getHealth = async () => { return api.get('/health').then(r => r.data); }; +export const getAuthSession = async () => { + if (isDemoMode()) { + return { + authenticated: true, + principal: { + userId: 'demo-user', + displayName: 'Demo User', + authMethod: 'demo', + roles: ['demo'], + }, + } satisfies AuthSessionResponse; + } + return api.get('/v1/auth/session').then(r => r.data); +}; + +export const loginWithBasicSession = async (username: string, password: string, returnHash: string) => { + return api.post( + '/v1/auth/login/basic', + { returnHash }, + { + headers: { + Authorization: `Basic ${encodeBasicCredentials(username, password)}`, + }, + } + ).then(r => r.data); +}; + +export const logoutSession = async () => { + if (isDemoMode()) { + return { authenticated: false } satisfies AuthSessionResponse; + } + return api.post('/v1/auth/logout').then(r => r.data); +}; + +export const buildOidcLoginUrl = (returnHash: string) => + `/api/v1/auth/login/oidc?returnHash=${encodeURIComponent(returnHash)}`; + +export const setCsrfToken = (value: string | null) => { + csrfToken = value; +}; + export const getDatasources = async () => { if (isDemoMode()) { await delay(20); diff --git a/eventlens-ui/src/hooks/useReplay.ts b/eventlens-ui/src/hooks/useReplay.ts index b7f68eb..269623d 100644 --- a/eventlens-ui/src/hooks/useReplay.ts +++ b/eventlens-ui/src/hooks/useReplay.ts @@ -1,10 +1,10 @@ import { useQuery } from '@tanstack/react-query'; import { getTransitions } from '../api/client'; -export function useReplay(aggregateId: string, source?: string | null) { +export function useReplay(aggregateId: string, source?: string | null, enabled = true) { return useQuery({ queryKey: ['transitions', aggregateId, source ?? 'default'], queryFn: () => getTransitions(aggregateId, source), - enabled: !!aggregateId, + enabled: enabled && !!aggregateId, }); } diff --git a/eventlens-ui/src/index.css b/eventlens-ui/src/index.css index 359eda1..f95e7f7 100644 --- a/eventlens-ui/src/index.css +++ b/eventlens-ui/src/index.css @@ -73,6 +73,143 @@ body::after { /* ── App shell ────────────────────────────────────────────────────────────── */ .app { display: flex; flex-direction: column; min-height: 100vh; } +.auth-shell { + background: + radial-gradient(circle at top left, rgba(0, 240, 255, 0.08), transparent 28%), + radial-gradient(circle at top right, rgba(255, 0, 229, 0.08), transparent 26%), + linear-gradient(180deg, #060811 0%, #050508 100%); +} + +.auth-screen { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 32px 20px; +} + +.auth-card { + width: min(460px, 100%); + padding: 28px; + border-radius: 18px; + border: 1px solid rgba(0, 240, 255, 0.14); + background: + linear-gradient(160deg, rgba(14, 18, 34, 0.96), rgba(8, 11, 20, 0.98)), + radial-gradient(circle at top, rgba(0, 240, 255, 0.06), transparent 56%); + box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55), 0 0 40px rgba(0, 240, 255, 0.08); +} + +.auth-eyebrow { + font-family: var(--font-display); + font-size: 11px; + letter-spacing: 2px; + text-transform: uppercase; + color: var(--neon-cyan); + margin-bottom: 10px; +} + +.auth-title { + font-family: var(--font-display); + font-size: 26px; + line-height: 1.2; + letter-spacing: 1px; + margin-bottom: 10px; +} + +.auth-copy { + color: var(--text-secondary); + margin-bottom: 20px; +} + +.auth-form { + display: grid; + gap: 14px; +} + +.auth-field { + display: grid; + gap: 6px; +} + +.auth-label { + color: var(--text-muted); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.auth-input { + width: 100%; + padding: 12px 14px; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.14); + background: rgba(10, 14, 26, 0.92); + color: var(--text-primary); + font-family: var(--font-mono); + font-size: 13px; + transition: border-color var(--transition), box-shadow var(--transition); +} + +.auth-input:focus { + outline: none; + border-color: var(--neon-cyan-mid); + box-shadow: 0 0 0 1px rgba(0, 240, 255, 0.2); +} + +.auth-meta { + display: flex; + justify-content: space-between; + gap: 10px; + color: var(--text-muted); + font-size: 12px; + font-family: var(--font-mono); +} + +.auth-route { + overflow-wrap: anywhere; +} + +.auth-error { + padding: 10px 12px; + border-radius: 10px; + border: 1px solid rgba(255, 51, 85, 0.24); + background: rgba(255, 51, 85, 0.08); + color: #ff9caf; + font-size: 12px; +} + +.auth-submit { + border: 1px solid rgba(0, 240, 255, 0.24); + border-radius: 12px; + background: linear-gradient(145deg, rgba(0, 240, 255, 0.14), rgba(79, 156, 249, 0.08)); + color: var(--text-primary); + padding: 12px 16px; + font-family: var(--font-display); + font-size: 12px; + letter-spacing: 1px; + text-transform: uppercase; + cursor: pointer; + transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition); +} + +.auth-submit:hover { + border-color: var(--neon-cyan-mid); + box-shadow: 0 0 14px rgba(0, 240, 255, 0.14); + transform: translateY(-1px); +} + +.auth-submit--secondary { + width: 100%; + margin-bottom: 14px; +} + +.auth-submit:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + .app-header { --header-pad-x: 24px; --header-pad-right: 48px; /* room for workspace chevron; mirrors visual weight of left brand */ @@ -186,6 +323,63 @@ body::after { min-width: 0; } +.header-user { + display: inline-flex; + align-items: center; + gap: 12px; + padding: 6px 10px 6px 12px; + border-radius: 999px; + border: 1px solid rgba(0, 240, 255, 0.16); + background: rgba(9, 13, 24, 0.88); +} + +.header-user-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 2px; +} + +.header-user-label { + font-size: 9px; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.header-user-name { + color: var(--text-primary); + font-size: 12px; + font-weight: 600; + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.header-user-action { + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 999px; + background: transparent; + color: var(--text-secondary); + padding: 6px 10px; + font-family: var(--font-mono); + font-size: 11px; + cursor: pointer; + transition: border-color var(--transition), color var(--transition), background var(--transition); +} + +.header-user-action:hover { + color: var(--neon-cyan); + border-color: var(--neon-cyan-mid); + background: rgba(0, 240, 255, 0.06); +} + +.header-user-action:disabled { + opacity: 0.6; + cursor: not-allowed; +} + .header-actions .conn-stats, .header-actions .header-status { flex-shrink: 0; From 2620702d655dbbce884b8967639b4795386e408f Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 16:23:31 +0200 Subject: [PATCH 05/11] feat: add authorization seam and scoped rbac --- .../io/eventlens/api/EventLensServer.java | 27 +++-- .../eventlens/api/routes/AggregateRoutes.java | 22 +++- .../eventlens/api/routes/AnomalyRoutes.java | 13 ++- .../api/routes/AsyncExportRoutes.java | 15 ++- .../io/eventlens/api/routes/BisectRoutes.java | 9 +- .../api/routes/DatasourceRoutes.java | 12 +- .../io/eventlens/api/routes/ExportRoutes.java | 9 +- .../eventlens/api/routes/MetricsRoutes.java | 12 +- .../eventlens/api/routes/OpenApiRoutes.java | 12 +- .../io/eventlens/api/routes/PluginRoutes.java | 9 +- .../api/routes/StatisticsRoutes.java | 12 +- .../eventlens/api/routes/TimelineRoutes.java | 43 +++++++- .../api/security/RouteAuthorizer.java | 47 ++++++++ .../api/AuthenticationIntegrationTest.java | 65 ++++++++++- .../TimelineMetadataPayloadBenchmarkTest.java | 5 +- .../io/eventlens/core/ConfigValidator.java | 69 ++++++++++++ .../io/eventlens/core/EventLensConfig.java | 35 ++++++ .../core/security/AuthorizationDecision.java | 21 ++++ .../security/AuthorizationDecisionReason.java | 9 ++ .../core/security/AuthorizationService.java | 104 ++++++++++++++++++ .../eventlens/core/security/Permission.java | 29 +++++ .../eventlens/core/ConfigValidatorTest.java | 18 +++ .../security/AuthorizationServiceTest.java | 97 ++++++++++++++++ 23 files changed, 663 insertions(+), 31 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecision.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecisionReason.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationService.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/Permission.java create mode 100644 eventlens-core/src/test/java/io/eventlens/core/security/AuthorizationServiceTest.java diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index 8bd185b..8d5cca9 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -11,6 +11,7 @@ import io.eventlens.api.routes.MetricsRoutes; import io.eventlens.api.http.RequestContextMdcFilter; import io.eventlens.api.security.BasicAuthenticator; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.security.SessionAuthenticator; import io.eventlens.api.security.oidc.OidcIdTokenValidator; import io.eventlens.api.security.oidc.OidcLoginStateService; @@ -25,6 +26,7 @@ import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.pii.PiiMasker; import io.eventlens.core.plugin.PluginManager; +import io.eventlens.core.security.AuthorizationService; import io.eventlens.core.security.Principal; import io.eventlens.core.security.SessionService; import io.eventlens.core.spi.EventStoreReader; @@ -131,6 +133,9 @@ public EventLensServer( var queryCache = new QueryResultCache( config.getQueryCache().isEnabled(), config.getQueryCache().getMaxEntries()); + var authorizationConfig = config.getSecurity() != null ? config.getSecurity().getAuthorization() : null; + var authorizationService = new AuthorizationService(authorizationConfig); + var routeAuthorizer = new RouteAuthorizer(authorizationService); // ── Security middleware preparation ──────────────────────────────── var rateLimitCfg = config.getServer().getSecurity() != null @@ -142,19 +147,19 @@ public EventLensServer( // ── Route handler instances ─────────────────────────────────────── var aggregateRoutes = new AggregateRoutes(sourceRegistry, auditLogger, queryCache, - Duration.ofSeconds(config.getQueryCache().getSearchTtlSeconds())); + Duration.ofSeconds(config.getQueryCache().getSearchTtlSeconds()), routeAuthorizer); var timelineRoutes = new TimelineRoutes(sourceRegistry, auditLogger, piiMasker, queryCache, - Duration.ofSeconds(config.getQueryCache().getTimelineTtlSeconds())); - var datasourceRoutes = new DatasourceRoutes(sourceRegistry); - var pluginRoutes = new PluginRoutes(sourceRegistry); - var statisticsRoutes = new StatisticsRoutes(sourceRegistry); - var bisectRoutes = new BisectRoutes(bisectEngine); - var anomalyRoutes = new AnomalyRoutes(sourceRegistry, config.getAnomaly(), auditLogger); - var exportRoutes = new ExportRoutes(exportEngine, auditLogger); - var asyncExportRoutes = new AsyncExportRoutes(exportService); + Duration.ofSeconds(config.getQueryCache().getTimelineTtlSeconds()), routeAuthorizer); + var datasourceRoutes = new DatasourceRoutes(sourceRegistry, routeAuthorizer); + var pluginRoutes = new PluginRoutes(sourceRegistry, routeAuthorizer); + var statisticsRoutes = new StatisticsRoutes(sourceRegistry, routeAuthorizer); + var bisectRoutes = new BisectRoutes(bisectEngine, routeAuthorizer); + var anomalyRoutes = new AnomalyRoutes(sourceRegistry, config.getAnomaly(), auditLogger, routeAuthorizer); + var exportRoutes = new ExportRoutes(exportEngine, auditLogger, routeAuthorizer); + var asyncExportRoutes = new AsyncExportRoutes(exportService, routeAuthorizer); var healthRoutes = new HealthRoutes(reader, config.getVersion()); - var metricsRoutes = new MetricsRoutes(); - var openApiRoutes = new OpenApiRoutes(); + var metricsRoutes = new MetricsRoutes(routeAuthorizer); + var openApiRoutes = new OpenApiRoutes(routeAuthorizer); var liveTailWs = new LiveTailWebSocket(sourceRegistry, pluginManager, auditLogger, defaultSourceId, sourceStreamBindings); var authConfig = config.getServer().getAuth(); diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java index cfb2576..bae6753 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AggregateRoutes.java @@ -3,10 +3,12 @@ import io.eventlens.api.cache.QueryResultCache; import io.eventlens.api.http.ConditionalGet; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; +import io.eventlens.core.security.Permission; import io.eventlens.core.spi.EventStoreReader; import io.javalin.http.Context; @@ -27,16 +29,19 @@ public class AggregateRoutes { private final AuditLogger auditLogger; private final QueryResultCache queryCache; private final Duration searchTtl; + private final RouteAuthorizer routeAuthorizer; public AggregateRoutes( SourceRegistry sourceRegistry, AuditLogger auditLogger, QueryResultCache queryCache, - Duration searchTtl) { + Duration searchTtl, + RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; this.auditLogger = auditLogger; this.queryCache = queryCache; this.searchTtl = searchTtl; + this.routeAuthorizer = routeAuthorizer; } public void search(Context ctx) { @@ -50,6 +55,9 @@ public void search(Context ctx) { InputValidator.validateLimit(ctx.queryParam("limit"), 20, MAX_LIMIT), MAX_LIMIT); var source = sourceRegistry.resolve(ctx.queryParam("source")); + if (!routeAuthorizer.require(ctx, Permission.SEARCH_AGGREGATES, source.id(), null)) { + return; + } var result = queryCache.getOrCompute( "aggregate-search", @@ -71,7 +79,11 @@ public void search(Context ctx) { } public void types(Context ctx) { - EventStoreReader reader = sourceRegistry.resolve(ctx.queryParam("source")).reader(); + var source = sourceRegistry.resolve(ctx.queryParam("source")); + if (!routeAuthorizer.require(ctx, Permission.VIEW_AGGREGATE_TYPES, source.id(), null)) { + return; + } + EventStoreReader reader = source.reader(); ConditionalGet.json(ctx, reader.getAggregateTypes()); } @@ -79,7 +91,11 @@ public void recentEvents(Context ctx) { int limit = Math.min( InputValidator.validateLimit(ctx.queryParam("limit"), 50, MAX_LIMIT), MAX_LIMIT); - EventStoreReader reader = sourceRegistry.resolve(ctx.queryParam("source")).reader(); + var source = sourceRegistry.resolve(ctx.queryParam("source")); + if (!routeAuthorizer.require(ctx, Permission.VIEW_RECENT_EVENTS, source.id(), null)) { + return; + } + EventStoreReader reader = source.reader(); ConditionalGet.json(ctx, reader.getRecentEvents(limit)); } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java index 4a72a7e..d77ecd1 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java @@ -2,11 +2,13 @@ import io.eventlens.api.source.SourceRegistry; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.EventLensConfig; import io.eventlens.core.InputValidator; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.engine.AnomalyDetector; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; import java.util.Map; @@ -25,14 +27,17 @@ public class AnomalyRoutes { private final EventLensConfig.AnomalyConfig anomalyConfig; private final AuditLogger auditLogger; private final Map detectors = new ConcurrentHashMap<>(); + private final RouteAuthorizer routeAuthorizer; public AnomalyRoutes( SourceRegistry sourceRegistry, EventLensConfig.AnomalyConfig anomalyConfig, - AuditLogger auditLogger) { + AuditLogger auditLogger, + RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; this.anomalyConfig = anomalyConfig; this.auditLogger = auditLogger; + this.routeAuthorizer = routeAuthorizer; } /** GET /api/aggregates/{id}/anomalies */ @@ -40,6 +45,9 @@ public void scanAggregate(Context ctx) { String id = InputValidator.validateAggregateId(ctx.pathParam("id")); var source = sourceRegistry.resolve(ctx.queryParam("source")); var result = detectorFor(source.id(), source).scan(id); + if (!routeAuthorizer.require(ctx, Permission.VIEW_ANOMALIES, source.id(), null)) { + return; + } auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_VIEW_ANOMALIES) @@ -59,6 +67,9 @@ public void scanRecent(Context ctx) { InputValidator.validateLimit(ctx.queryParam("limit"), 100, MAX_SCAN_LIMIT), MAX_SCAN_LIMIT); var source = sourceRegistry.resolve(ctx.queryParam("source")); + if (!routeAuthorizer.require(ctx, Permission.VIEW_ANOMALIES, source.id(), null)) { + return; + } var result = detectorFor(source.id(), source).scanRecent(limit); auditLogger.log(SecurityContext.audit(ctx) diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java index fbe0948..600fab2 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java @@ -3,7 +3,9 @@ import io.eventlens.api.export.ExportJob; import io.eventlens.api.export.ExportService; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.InputValidator; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; import java.nio.file.Files; @@ -13,9 +15,11 @@ public final class AsyncExportRoutes { private final ExportService exportService; + private final RouteAuthorizer routeAuthorizer; - public AsyncExportRoutes(ExportService exportService) { + public AsyncExportRoutes(ExportService exportService, RouteAuthorizer routeAuthorizer) { this.exportService = exportService; + this.routeAuthorizer = routeAuthorizer; } public record ExportRequest(String aggregateId, String format, Integer limit) { @@ -31,6 +35,9 @@ public void start(Context ctx) { String aggregateId = InputValidator.validateAggregateId(req.aggregateId); int limit = req.limit != null ? Math.max(1, req.limit) : 50_000; + if (!routeAuthorizer.require(ctx, Permission.START_EXPORT, null, null)) { + return; + } ExportJob job = exportService.startAggregateExport( aggregateId, @@ -53,6 +60,9 @@ public void start(Context ctx) { /** GET /api/events/export/{exportId} */ public void status(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.START_EXPORT, null, null)) { + return; + } String exportId = ctx.pathParam("exportId"); ExportJob job = exportService.get(exportId); if (job == null) { @@ -103,6 +113,9 @@ public void status(Context ctx) { /** GET /api/events/export/{exportId}/download */ public void download(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.START_EXPORT, null, null)) { + return; + } String exportId = ctx.pathParam("exportId"); var file = exportService.getDownloadFile(exportId); if (file == null) { diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/BisectRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/BisectRoutes.java index 3ce48f6..aaf6c1c 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/BisectRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/BisectRoutes.java @@ -3,6 +3,8 @@ import io.eventlens.core.InputValidator; import io.eventlens.core.engine.BisectEngine; import io.eventlens.core.exception.ConditionParseException; +import io.eventlens.api.security.RouteAuthorizer; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; import java.util.Map; @@ -14,14 +16,19 @@ public class BisectRoutes { private final BisectEngine bisectEngine; + private final RouteAuthorizer routeAuthorizer; - public BisectRoutes(BisectEngine bisectEngine) { + public BisectRoutes(BisectEngine bisectEngine, RouteAuthorizer routeAuthorizer) { this.bisectEngine = bisectEngine; + this.routeAuthorizer = routeAuthorizer; } /** POST /api/aggregates/{id}/bisect — body: "balance < 0" */ public void bisect(Context ctx) { String id = InputValidator.validateAggregateId(ctx.pathParam("id")); + if (!routeAuthorizer.require(ctx, Permission.EXECUTE_BISECT, null, null)) { + return; + } String expression = ctx.body().trim(); if (expression.isBlank()) { ctx.status(400).json(Map.of("error", "Request body must contain a condition expression")); diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/DatasourceRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/DatasourceRoutes.java index 88ccf9f..137f5dc 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/DatasourceRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/DatasourceRoutes.java @@ -1,22 +1,32 @@ package io.eventlens.api.routes; import io.eventlens.api.http.ConditionalGet; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.source.SourceRegistry; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; public final class DatasourceRoutes { private final SourceRegistry sourceRegistry; + private final RouteAuthorizer routeAuthorizer; - public DatasourceRoutes(SourceRegistry sourceRegistry) { + public DatasourceRoutes(SourceRegistry sourceRegistry, RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; + this.routeAuthorizer = routeAuthorizer; } public void list(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.VIEW_DATASOURCES, null, null)) { + return; + } ConditionalGet.json(ctx, sourceRegistry.listDatasources()); } public void health(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.VIEW_DATASOURCES, ctx.pathParam("id"), null)) { + return; + } ConditionalGet.json(ctx, sourceRegistry.datasourceHealth(ctx.pathParam("id"))); } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java index 5ba384b..e8b7f27 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java @@ -2,9 +2,11 @@ import io.eventlens.core.InputValidator; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.engine.ExportEngine; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; import java.util.Map; @@ -19,10 +21,12 @@ public class ExportRoutes { private final ExportEngine exportEngine; private final AuditLogger auditLogger; + private final RouteAuthorizer routeAuthorizer; - public ExportRoutes(ExportEngine exportEngine, AuditLogger auditLogger) { + public ExportRoutes(ExportEngine exportEngine, AuditLogger auditLogger, RouteAuthorizer routeAuthorizer) { this.exportEngine = exportEngine; this.auditLogger = auditLogger; + this.routeAuthorizer = routeAuthorizer; } /** GET /api/aggregates/{id}/export?format=json|markdown|csv|junit */ @@ -38,6 +42,9 @@ public void export(Context ctx) { }; String content = exportEngine.export(id, format); + if (!routeAuthorizer.require(ctx, Permission.EXPORT_AGGREGATE, null, null)) { + return; + } String contentType = switch (format) { case MARKDOWN -> "text/markdown"; diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/MetricsRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/MetricsRoutes.java index abbed14..2dc59eb 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/MetricsRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/MetricsRoutes.java @@ -1,14 +1,24 @@ package io.eventlens.api.routes; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.metrics.EventLensMetrics; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; public final class MetricsRoutes { + private final RouteAuthorizer routeAuthorizer; + + public MetricsRoutes(RouteAuthorizer routeAuthorizer) { + this.routeAuthorizer = routeAuthorizer; + } + /** GET /api/v1/metrics */ public void metrics(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.VIEW_METRICS, null, null)) { + return; + } ctx.contentType("text/plain; version=0.0.4; charset=utf-8"); ctx.result(EventLensMetrics.registry.scrape()); } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/OpenApiRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/OpenApiRoutes.java index eb44749..1c6f9f4 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/OpenApiRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/OpenApiRoutes.java @@ -1,13 +1,24 @@ package io.eventlens.api.routes; +import io.eventlens.api.security.RouteAuthorizer; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; import java.io.InputStream; public final class OpenApiRoutes { + private final RouteAuthorizer routeAuthorizer; + + public OpenApiRoutes(RouteAuthorizer routeAuthorizer) { + this.routeAuthorizer = routeAuthorizer; + } + /** GET /api/v1/openapi.json */ public void spec(Context ctx) throws Exception { + if (!routeAuthorizer.require(ctx, Permission.VIEW_OPENAPI, null, null)) { + return; + } InputStream in = getClass().getClassLoader().getResourceAsStream("openapi.json"); if (in == null) { ctx.status(404).result("openapi.json not found"); @@ -17,4 +28,3 @@ public void spec(Context ctx) throws Exception { ctx.result(in); } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/PluginRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/PluginRoutes.java index c1f14cf..56ae431 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/PluginRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/PluginRoutes.java @@ -1,18 +1,25 @@ package io.eventlens.api.routes; import io.eventlens.api.http.ConditionalGet; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.source.SourceRegistry; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; public final class PluginRoutes { private final SourceRegistry sourceRegistry; + private final RouteAuthorizer routeAuthorizer; - public PluginRoutes(SourceRegistry sourceRegistry) { + public PluginRoutes(SourceRegistry sourceRegistry, RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; + this.routeAuthorizer = routeAuthorizer; } public void list(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.VIEW_PLUGINS, null, null)) { + return; + } ConditionalGet.json(ctx, sourceRegistry.listPlugins()); } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/StatisticsRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/StatisticsRoutes.java index e2ff47c..9a0e57e 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/StatisticsRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/StatisticsRoutes.java @@ -1,21 +1,29 @@ package io.eventlens.api.routes; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.source.SourceRegistry; +import io.eventlens.core.security.Permission; import io.eventlens.spi.EventStatisticsQuery; import io.javalin.http.Context; public final class StatisticsRoutes { private final SourceRegistry sourceRegistry; + private final RouteAuthorizer routeAuthorizer; - public StatisticsRoutes(SourceRegistry sourceRegistry) { + public StatisticsRoutes(SourceRegistry sourceRegistry, RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; + this.routeAuthorizer = routeAuthorizer; } public void get(Context ctx) { int bucketHours = parsePositiveInt(ctx.queryParam("bucketHours"), 1); int maxBuckets = parsePositiveInt(ctx.queryParam("maxBuckets"), 24); - ctx.json(sourceRegistry.statistics(ctx.queryParam("source"), new EventStatisticsQuery(bucketHours, maxBuckets))); + var source = sourceRegistry.resolve(ctx.queryParam("source")); + if (!routeAuthorizer.require(ctx, Permission.VIEW_STATISTICS, source.id(), null)) { + return; + } + ctx.json(sourceRegistry.statistics(source.id(), new EventStatisticsQuery(bucketHours, maxBuckets))); } private static int parsePositiveInt(String value, int fallback) { diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java index d061e26..693d868 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java @@ -6,6 +6,7 @@ import io.eventlens.api.cache.QueryResultCache; import io.eventlens.api.http.ConditionalGet; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; import io.eventlens.core.audit.AuditEvent; @@ -14,6 +15,7 @@ import io.eventlens.core.model.StoredEvent; import io.eventlens.core.pagination.CursorCodec; import io.eventlens.core.pii.PiiMasker; +import io.eventlens.core.security.Permission; import io.javalin.http.Context; import java.time.Duration; @@ -33,18 +35,21 @@ public class TimelineRoutes { private final QueryResultCache queryCache; private final Duration timelineTtl; private final ObjectMapper mapper; + private final RouteAuthorizer routeAuthorizer; public TimelineRoutes( SourceRegistry sourceRegistry, AuditLogger auditLogger, PiiMasker piiMasker, QueryResultCache queryCache, - Duration timelineTtl) { + Duration timelineTtl, + RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; this.auditLogger = auditLogger; this.piiMasker = piiMasker; this.queryCache = queryCache; this.timelineTtl = timelineTtl; + this.routeAuthorizer = routeAuthorizer; this.mapper = new ObjectMapper() .findAndRegisterModules() .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); @@ -59,12 +64,18 @@ public void getTimeline(Context ctx) { int offset = InputValidator.validateOffset(ctx.queryParam("offset")); String fields = normalizeFields(ctx.queryParam("fields")); var source = sourceRegistry.resolve(ctx.queryParam("source")); + if (!routeAuthorizer.require(ctx, Permission.VIEW_TIMELINE, source.id(), null)) { + return; + } TimelineEnvelope envelope = queryCache.getOrCompute( "timeline", source.id() + "|" + id + "|" + limit + "|" + offset + "|" + fields + "|" + (cursorParam == null ? "" : cursorParam), timelineTtl, () -> buildTimelineEnvelope(source, id, limit, offset, cursorParam, fields)); + if (!routeAuthorizer.require(ctx, Permission.VIEW_TIMELINE, source.id(), envelope.timeline().aggregateType())) { + return; + } auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_VIEW_TIMELINE) @@ -100,20 +111,44 @@ public void getTimeline(Context ctx) { public void replay(Context ctx) { String id = InputValidator.validateAggregateId(ctx.pathParam("id")); var source = sourceRegistry.resolve(ctx.queryParam("source")); - ctx.json(source.replayEngine().replayFull(id)); + if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), null)) { + return; + } + var transitions = source.replayEngine().replayFull(id); + String aggregateType = transitions.isEmpty() ? null : transitions.getFirst().event().aggregateType(); + if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), aggregateType)) { + return; + } + ctx.json(transitions); } public void replayTo(Context ctx) { String id = InputValidator.validateAggregateId(ctx.pathParam("id")); long seq = Long.parseLong(ctx.pathParam("seq")); var source = sourceRegistry.resolve(ctx.queryParam("source")); - ctx.json(source.replayEngine().replayTo(id, seq)); + if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), null)) { + return; + } + var replay = source.replayEngine().replayTo(id, seq); + String aggregateType = replay.transitions().isEmpty() ? null : replay.transitions().getFirst().event().aggregateType(); + if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), aggregateType)) { + return; + } + ctx.json(replay); } public void transitions(Context ctx) { String id = InputValidator.validateAggregateId(ctx.pathParam("id")); var source = sourceRegistry.resolve(ctx.queryParam("source")); - ctx.json(source.replayEngine().replayFull(id)); + if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), null)) { + return; + } + var transitions = source.replayEngine().replayFull(id); + String aggregateType = transitions.isEmpty() ? null : transitions.getFirst().event().aggregateType(); + if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), aggregateType)) { + return; + } + ctx.json(transitions); } private TimelineEnvelope buildTimelineEnvelope( diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java b/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java new file mode 100644 index 0000000..2ab3008 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java @@ -0,0 +1,47 @@ +package io.eventlens.api.security; + +import io.eventlens.api.http.SecurityContext; +import io.eventlens.core.security.AuthorizationDecision; +import io.eventlens.core.security.AuthorizationDecisionReason; +import io.eventlens.core.security.AuthorizationService; +import io.eventlens.core.security.Permission; +import io.javalin.http.Context; + +import java.util.LinkedHashMap; +import java.util.Map; + +public final class RouteAuthorizer { + + private final AuthorizationService authorizationService; + + public RouteAuthorizer(AuthorizationService authorizationService) { + this.authorizationService = authorizationService; + } + + public boolean require(Context ctx, Permission permission, String sourceId, String aggregateType) { + AuthorizationDecision decision = authorizationService.authorize( + SecurityContext.principal(ctx), + permission, + sourceId, + aggregateType + ); + if (decision.allowed()) { + return true; + } + + Map payload = new LinkedHashMap<>(); + payload.put("error", decision.reason() == AuthorizationDecisionReason.DENY_AUTH_REQUIRED ? "auth_required" : "forbidden"); + payload.put("reason", decision.reason().name()); + payload.put("permission", permission.name()); + if (sourceId != null && !sourceId.isBlank()) { + payload.put("source", sourceId); + } + if (aggregateType != null && !aggregateType.isBlank()) { + payload.put("aggregateType", aggregateType); + } + payload.put("roles", decision.resolvedRoles()); + + ctx.status(decision.reason() == AuthorizationDecisionReason.DENY_AUTH_REQUIRED ? 401 : 403).json(payload); + return false; + } +} diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index 307b592..07a58c8 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; @@ -255,15 +256,74 @@ void oidcLoginCreatesBrowserSessionAndRedirectsBackToHashRoute() throws Exceptio assertThat(apiResponse.body()).contains("ORD-1001"); } + @Test + void authorizationRejectsAuthenticatedUserWithoutPermission() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("datasource-reader"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("datasource-reader"); + role.setPermissions(List.of("VIEW_DATASOURCES")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(403); + assertThat(response.body()).contains("\"reason\":\"DENY_MISSING_PERMISSION\""); + assertThat(response.body()).contains("\"permission\":\"SEARCH_AGGREGATES\""); + } + + @Test + void authorizationRejectsSourceOutsideRoleScope() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("stats-reader"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("stats-reader"); + role.setPermissions(List.of("VIEW_STATISTICS")); + role.setAllowedSources(List.of("restricted")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/statistics?source=default".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(403); + assertThat(response.body()).contains("\"reason\":\"DENY_SOURCE_SCOPE\""); + assertThat(response.body()).contains("\"source\":\"default\""); + } + private EventLensServer startServer(int port, boolean authEnabled) { - return startServer(port, authEnabled, null); + return startServer(port, authEnabled, null, cfg -> {}); } private EventLensServer startOidcServer(int port, String issuer) { - return startServer(port, false, issuer); + return startServer(port, false, issuer, cfg -> {}); } private EventLensServer startServer(int port, boolean authEnabled, String oidcIssuer) { + return startServer(port, authEnabled, oidcIssuer, cfg -> {}); + } + + private EventLensServer startServer(int port, boolean authEnabled, String oidcIssuer, Consumer configCustomizer) { EventStoreReader reader = new EventStoreReader() { @Override public List getEvents(String aggregateId) { @@ -325,6 +385,7 @@ public List searchAggregates(String query, int limit) { cfg.getSecurity().getAuth().getOidc().setClientSecret("super-secret"); cfg.getSecurity().getAuth().getOidc().setRedirectPath("/api/v1/auth/callback"); } + configCustomizer.accept(cfg); var replayEngine = new ReplayEngine(reader, new ReducerRegistry()); var bisectEngine = new BisectEngine(replayEngine, reader); diff --git a/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java b/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java index ef17096..3b6d25a 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java @@ -2,10 +2,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.eventlens.api.cache.QueryResultCache; +import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.EventLensConfig; import io.eventlens.core.model.AggregateTimeline; import io.eventlens.core.model.StoredEvent; import io.eventlens.core.pii.PiiMasker; +import io.eventlens.core.security.AuthorizationService; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; @@ -33,7 +35,8 @@ void metadataOnlyShapeReducesSerializedPayloadByAtLeastSeventyPercent() throws E null, new PiiMasker(new EventLensConfig.PiiConfig()), new QueryResultCache(false, 1), - Duration.ofSeconds(1)); + Duration.ofSeconds(1), + new RouteAuthorizer(new AuthorizationService(null))); Method metadataOnly = TimelineRoutes.class.getDeclaredMethod("metadataOnly", AggregateTimeline.class); metadataOnly.setAccessible(true); diff --git a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java index ec8401e..51b7f55 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java +++ b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java @@ -2,9 +2,11 @@ import io.eventlens.core.engine.BisectEngine; import io.eventlens.core.exception.ConfigurationException; +import io.eventlens.core.security.Permission; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -90,6 +92,7 @@ public static List validate(EventLensConfig config) { validateMetadata(config, issues); validateSecurityAuth(config, issues); + validateAuthorization(config, issues); validateLegacyDatasource(config, issues); validateDatasourceInstances(config.getDatasourcesOrLegacy(), issues); validateStreamInstances(config.getStreamsOrLegacy(), issues); @@ -329,6 +332,72 @@ private static void validateSecurityAuth(EventLensConfig config, List issues) { + var security = config.getSecurity(); + if (security == null || security.getAuthorization() == null) { + return; + } + + var authorization = security.getAuthorization(); + if (!authorization.isEnabled()) { + return; + } + + Map rolesById = new LinkedHashMap<>(); + for (int i = 0; i < authorization.getRoles().size(); i++) { + EventLensConfig.RoleConfig role = authorization.getRoles().get(i); + String path = "security.authorization.roles[%d]".formatted(i); + if (role == null || isBlank(role.getId())) { + issues.add(error(path + ".id", "Role id is required")); + continue; + } + if (rolesById.putIfAbsent(role.getId(), role) != null) { + issues.add(error(path + ".id", "Duplicate role id: " + role.getId())); + } + if (role.getPermissions() == null || role.getPermissions().isEmpty()) { + issues.add(warning(path + ".permissions", "Role has no permissions")); + } else { + for (int permissionIndex = 0; permissionIndex < role.getPermissions().size(); permissionIndex++) { + String permissionValue = role.getPermissions().get(permissionIndex); + if (Permission.fromConfigValue(permissionValue).isEmpty()) { + issues.add(error( + path + ".permissions[%d]".formatted(permissionIndex), + "Unknown permission: " + permissionValue)); + } + } + } + } + + if (authorization.getDefaultRoles().isEmpty() && authorization.getPrincipalRoles().isEmpty()) { + issues.add(warning("security.authorization", "Authorization is enabled with no default roles or principal role assignments")); + } + + for (int i = 0; i < authorization.getDefaultRoles().size(); i++) { + String roleId = authorization.getDefaultRoles().get(i); + if (!rolesById.containsKey(roleId)) { + issues.add(error("security.authorization.default-roles[%d]".formatted(i), "Unknown role: " + roleId)); + } + } + + for (Map.Entry> entry : authorization.getPrincipalRoles().entrySet()) { + String principalId = entry.getKey(); + if (isBlank(principalId)) { + issues.add(error("security.authorization.principal-roles", "Principal role assignments must use a non-blank principal id")); + continue; + } + List assignedRoles = entry.getValue() == null ? List.of() : entry.getValue(); + if (assignedRoles.isEmpty()) { + issues.add(warning("security.authorization.principal-roles." + principalId, "Principal has no assigned roles")); + } + for (int i = 0; i < assignedRoles.size(); i++) { + String roleId = assignedRoles.get(i); + if (!rolesById.containsKey(roleId)) { + issues.add(error("security.authorization.principal-roles.%s[%d]".formatted(principalId, i), "Unknown role: " + roleId)); + } + } + } + } + private static void validatePool(String base, EventLensConfig.PoolConfig pool, List issues) { if (pool == null) { return; diff --git a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java index 22de324..d5fa70f 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java +++ b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java @@ -130,11 +130,14 @@ public static class SecurityConfig { public static class SecurityFeaturesConfig { private MetadataConfig metadata = new MetadataConfig(); private AuthProviderConfig auth = new AuthProviderConfig(); + private AuthorizationConfig authorization = new AuthorizationConfig(); public MetadataConfig getMetadata() { return metadata; } public void setMetadata(MetadataConfig metadata) { this.metadata = metadata; } public AuthProviderConfig getAuth() { return auth; } public void setAuth(AuthProviderConfig auth) { this.auth = auth; } + public AuthorizationConfig getAuthorization() { return authorization; } + public void setAuthorization(AuthorizationConfig authorization) { this.authorization = authorization; } } public static class AuthProviderConfig { @@ -221,6 +224,38 @@ private static PoolConfig defaultPool() { } } + public static class AuthorizationConfig { + private boolean enabled = false; + private List defaultRoles = List.of(); + private Map> principalRoles = Map.of(); + private List roles = List.of(); + + public boolean isEnabled() { return enabled; } + public void setEnabled(boolean enabled) { this.enabled = enabled; } + public List getDefaultRoles() { return defaultRoles; } + public void setDefaultRoles(List defaultRoles) { this.defaultRoles = defaultRoles == null ? List.of() : defaultRoles; } + public Map> getPrincipalRoles() { return principalRoles; } + public void setPrincipalRoles(Map> principalRoles) { this.principalRoles = principalRoles == null ? Map.of() : principalRoles; } + public List getRoles() { return roles; } + public void setRoles(List roles) { this.roles = roles == null ? List.of() : roles; } + } + + public static class RoleConfig { + private String id; + private List permissions = List.of(); + private List allowedSources = List.of(); + private List allowedAggregateTypes = List.of(); + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public List getPermissions() { return permissions; } + public void setPermissions(List permissions) { this.permissions = permissions == null ? List.of() : permissions; } + public List getAllowedSources() { return allowedSources; } + public void setAllowedSources(List allowedSources) { this.allowedSources = allowedSources == null ? List.of() : allowedSources; } + public List getAllowedAggregateTypes() { return allowedAggregateTypes; } + public void setAllowedAggregateTypes(List allowedAggregateTypes) { this.allowedAggregateTypes = allowedAggregateTypes == null ? List.of() : allowedAggregateTypes; } + } + public static class RateLimitConfig { private boolean enabled = false; private int requestsPerMinute = 120; diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecision.java b/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecision.java new file mode 100644 index 0000000..3ae9b69 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecision.java @@ -0,0 +1,21 @@ +package io.eventlens.core.security; + +import java.util.Set; + +public record AuthorizationDecision( + boolean allowed, + AuthorizationDecisionReason reason, + Permission permission, + Set resolvedRoles +) { + public static AuthorizationDecision allow(Permission permission, Set resolvedRoles) { + return new AuthorizationDecision(true, AuthorizationDecisionReason.ALLOW, permission, resolvedRoles); + } + + public static AuthorizationDecision deny( + AuthorizationDecisionReason reason, + Permission permission, + Set resolvedRoles) { + return new AuthorizationDecision(false, reason, permission, resolvedRoles); + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecisionReason.java b/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecisionReason.java new file mode 100644 index 0000000..658aa00 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationDecisionReason.java @@ -0,0 +1,9 @@ +package io.eventlens.core.security; + +public enum AuthorizationDecisionReason { + ALLOW, + DENY_AUTH_REQUIRED, + DENY_MISSING_PERMISSION, + DENY_SOURCE_SCOPE, + DENY_AGGREGATE_TYPE_SCOPE +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationService.java b/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationService.java new file mode 100644 index 0000000..e4ce8ac --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/AuthorizationService.java @@ -0,0 +1,104 @@ +package io.eventlens.core.security; + +import io.eventlens.core.EventLensConfig; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +public final class AuthorizationService { + + private final EventLensConfig.AuthorizationConfig config; + private final Map rolesById; + + public AuthorizationService(EventLensConfig.AuthorizationConfig config) { + this.config = config == null ? new EventLensConfig.AuthorizationConfig() : config; + this.rolesById = new LinkedHashMap<>(); + for (EventLensConfig.RoleConfig role : this.config.getRoles()) { + if (role.getId() != null && !role.getId().isBlank()) { + rolesById.put(role.getId(), role); + } + } + } + + public AuthorizationDecision authorize( + Principal principal, + Permission permission, + String sourceId, + String aggregateType) { + if (!config.isEnabled()) { + return AuthorizationDecision.allow(permission, Set.of()); + } + if (principal == null || !principal.authenticated()) { + return AuthorizationDecision.deny(AuthorizationDecisionReason.DENY_AUTH_REQUIRED, permission, Set.of()); + } + + Set resolvedRoleIds = resolveRoleIds(principal); + if (resolvedRoleIds.isEmpty()) { + return AuthorizationDecision.deny(AuthorizationDecisionReason.DENY_MISSING_PERMISSION, permission, Set.of()); + } + + boolean hasPermission = false; + boolean deniedBySource = false; + boolean deniedByAggregateType = false; + + for (String roleId : resolvedRoleIds) { + EventLensConfig.RoleConfig role = rolesById.get(roleId); + if (role == null) { + continue; + } + if (!hasPermission(role, permission)) { + continue; + } + + hasPermission = true; + if (!matchesScope(role.getAllowedSources(), sourceId)) { + deniedBySource = true; + continue; + } + if (!matchesScope(role.getAllowedAggregateTypes(), aggregateType)) { + deniedByAggregateType = true; + continue; + } + return AuthorizationDecision.allow(permission, resolvedRoleIds); + } + + if (!hasPermission) { + return AuthorizationDecision.deny(AuthorizationDecisionReason.DENY_MISSING_PERMISSION, permission, resolvedRoleIds); + } + if (deniedBySource) { + return AuthorizationDecision.deny(AuthorizationDecisionReason.DENY_SOURCE_SCOPE, permission, resolvedRoleIds); + } + if (deniedByAggregateType) { + return AuthorizationDecision.deny(AuthorizationDecisionReason.DENY_AGGREGATE_TYPE_SCOPE, permission, resolvedRoleIds); + } + return AuthorizationDecision.deny(AuthorizationDecisionReason.DENY_MISSING_PERMISSION, permission, resolvedRoleIds); + } + + private Set resolveRoleIds(Principal principal) { + Set roles = new LinkedHashSet<>(principal.roles()); + roles.addAll(config.getDefaultRoles()); + List principalRoles = config.getPrincipalRoles().get(principal.userId()); + if (principalRoles != null) { + roles.addAll(principalRoles); + } + roles.removeIf(roleId -> !rolesById.containsKey(roleId)); + return Set.copyOf(roles); + } + + private static boolean hasPermission(EventLensConfig.RoleConfig role, Permission permission) { + return role.getPermissions().stream() + .map(value -> value == null ? "" : value.trim().toUpperCase(Locale.ROOT).replace('-', '_').replace(' ', '_')) + .anyMatch(permission.name()::equals); + } + + private static boolean matchesScope(List allowedValues, String value) { + if (allowedValues == null || allowedValues.isEmpty() || value == null || value.isBlank()) { + return true; + } + return allowedValues.stream().anyMatch(candidate -> candidate.equalsIgnoreCase(value)); + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java new file mode 100644 index 0000000..7da9665 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java @@ -0,0 +1,29 @@ +package io.eventlens.core.security; + +import java.util.Arrays; +import java.util.Optional; + +public enum Permission { + SEARCH_AGGREGATES, + VIEW_RECENT_EVENTS, + VIEW_AGGREGATE_TYPES, + VIEW_TIMELINE, + VIEW_REPLAY, + VIEW_ANOMALIES, + EXPORT_AGGREGATE, + START_EXPORT, + VIEW_DATASOURCES, + VIEW_PLUGINS, + VIEW_STATISTICS, + VIEW_METRICS, + VIEW_OPENAPI, + EXECUTE_BISECT; + + public static Optional fromConfigValue(String value) { + if (value == null || value.isBlank()) { + return Optional.empty(); + } + String normalized = value.trim().toUpperCase().replace('-', '_').replace(' ', '_'); + return Arrays.stream(values()).filter(permission -> permission.name().equals(normalized)).findFirst(); + } +} diff --git a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java index f770a85..38cddee 100644 --- a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java +++ b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java @@ -89,4 +89,22 @@ void sessionCookieNoneRequiresSecureCookie() { assertThat(issues.stream().anyMatch(i -> i.path().equals("security.auth.session.secure-cookie") && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); } + + @Test + void authorizationRequiresKnownRolesAndPermissions() { + var cfg = new EventLensConfig(); + cfg.getSecurity().getAuthorization().setEnabled(true); + + var role = new EventLensConfig.RoleConfig(); + role.setId("viewer"); + role.setPermissions(java.util.List.of("view_timeline", "not_real_permission")); + cfg.getSecurity().getAuthorization().setRoles(java.util.List.of(role)); + cfg.getSecurity().getAuthorization().setDefaultRoles(java.util.List.of("missing-role")); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.authorization.roles[0].permissions[1]") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.authorization.default-roles[0]") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } } diff --git a/eventlens-core/src/test/java/io/eventlens/core/security/AuthorizationServiceTest.java b/eventlens-core/src/test/java/io/eventlens/core/security/AuthorizationServiceTest.java new file mode 100644 index 0000000..35858fb --- /dev/null +++ b/eventlens-core/src/test/java/io/eventlens/core/security/AuthorizationServiceTest.java @@ -0,0 +1,97 @@ +package io.eventlens.core.security; + +import io.eventlens.core.EventLensConfig; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class AuthorizationServiceTest { + + @Test + void disabledAuthorizationAllowsRequests() { + AuthorizationService service = new AuthorizationService(new EventLensConfig.AuthorizationConfig()); + + AuthorizationDecision decision = service.authorize( + Principal.anonymous(), + Permission.VIEW_TIMELINE, + "primary", + "Order"); + + assertThat(decision.allowed()).isTrue(); + assertThat(decision.reason()).isEqualTo(AuthorizationDecisionReason.ALLOW); + } + + @Test + void enabledAuthorizationDeniesMissingPermission() { + EventLensConfig.AuthorizationConfig config = new EventLensConfig.AuthorizationConfig(); + config.setEnabled(true); + + EventLensConfig.RoleConfig role = new EventLensConfig.RoleConfig(); + role.setId("viewer"); + role.setPermissions(List.of("view_timeline")); + config.setRoles(List.of(role)); + config.setDefaultRoles(List.of("viewer")); + + AuthorizationService service = new AuthorizationService(config); + AuthorizationDecision decision = service.authorize( + new Principal("alice", "alice", "oidc", Set.of(), true), + Permission.EXPORT_AGGREGATE, + "primary", + "Order"); + + assertThat(decision.allowed()).isFalse(); + assertThat(decision.reason()).isEqualTo(AuthorizationDecisionReason.DENY_MISSING_PERMISSION); + } + + @Test + void enabledAuthorizationDeniesSourceOutsideScope() { + EventLensConfig.AuthorizationConfig config = new EventLensConfig.AuthorizationConfig(); + config.setEnabled(true); + + EventLensConfig.RoleConfig role = new EventLensConfig.RoleConfig(); + role.setId("viewer"); + role.setPermissions(List.of("view_timeline")); + role.setAllowedSources(List.of("primary")); + config.setRoles(List.of(role)); + config.setPrincipalRoles(Map.of("alice", List.of("viewer"))); + + AuthorizationService service = new AuthorizationService(config); + AuthorizationDecision decision = service.authorize( + new Principal("alice", "alice", "oidc", Set.of(), true), + Permission.VIEW_TIMELINE, + "secondary", + "Order"); + + assertThat(decision.allowed()).isFalse(); + assertThat(decision.reason()).isEqualTo(AuthorizationDecisionReason.DENY_SOURCE_SCOPE); + } + + @Test + void enabledAuthorizationAllowsMatchingRoleAndScope() { + EventLensConfig.AuthorizationConfig config = new EventLensConfig.AuthorizationConfig(); + config.setEnabled(true); + + EventLensConfig.RoleConfig role = new EventLensConfig.RoleConfig(); + role.setId("operator"); + role.setPermissions(List.of("view_timeline", "export_aggregate")); + role.setAllowedSources(List.of("primary")); + role.setAllowedAggregateTypes(List.of("Order")); + config.setRoles(List.of(role)); + config.setPrincipalRoles(Map.of("alice", List.of("operator"))); + + AuthorizationService service = new AuthorizationService(config); + AuthorizationDecision decision = service.authorize( + new Principal("alice", "alice", "oidc", Set.of(), true), + Permission.EXPORT_AGGREGATE, + "primary", + "Order"); + + assertThat(decision.allowed()).isTrue(); + assertThat(decision.reason()).isEqualTo(AuthorizationDecisionReason.ALLOW); + assertThat(decision.resolvedRoles()).contains("operator"); + } +} From 1f8ceb4272815e18a3ba14ba9f771fbf7508708c Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 16:36:42 +0200 Subject: [PATCH 06/11] feat: add masked reads and pii reveal flow --- .../io/eventlens/api/EventLensServer.java | 12 +- .../eventlens/api/export/ExportService.java | 15 +- .../eventlens/api/routes/AnomalyRoutes.java | 10 +- .../io/eventlens/api/routes/ExportRoutes.java | 27 ++- .../eventlens/api/routes/PiiRevealRoutes.java | 71 ++++++++ .../eventlens/api/routes/TimelineRoutes.java | 65 +------ .../api/AuthenticationIntegrationTest.java | 129 ++++++++++++- .../TimelineMetadataPayloadBenchmarkTest.java | 3 +- .../io/eventlens/core/audit/AuditEvent.java | 1 + .../eventlens/core/engine/ExportEngine.java | 9 +- .../core/pii/SensitiveDataProtector.java | 172 ++++++++++++++++++ .../eventlens/core/security/Permission.java | 1 + .../core/pii/SensitiveDataProtectorTest.java | 105 +++++++++++ 13 files changed, 539 insertions(+), 81 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/pii/SensitiveDataProtector.java create mode 100644 eventlens-core/src/test/java/io/eventlens/core/pii/SensitiveDataProtectorTest.java diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index 8d5cca9..2778488 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -25,6 +25,7 @@ import io.eventlens.core.exception.QueryTimeoutException; import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.pii.PiiMasker; +import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.plugin.PluginManager; import io.eventlens.core.security.AuthorizationService; import io.eventlens.core.security.Principal; @@ -122,9 +123,10 @@ public EventLensServer( // ── 1.9 PII Masker ──────────────────────────────────────────────── final PiiMasker piiMasker = new PiiMasker( config.getDataProtection().getPii()); + final SensitiveDataProtector sensitiveDataProtector = new SensitiveDataProtector(piiMasker); // ── 2.6 Async Export ─────────────────────────────────────────────── - this.exportService = new ExportService(reader, auditLogger, config.getExport()); + this.exportService = new ExportService(reader, auditLogger, config.getExport(), sensitiveDataProtector); // 4.1 Metrics: JVM binders + per-request instrumentation EventLensMetrics.initJvmMetrics(EventLensMetrics.registry); @@ -148,18 +150,19 @@ public EventLensServer( // ── Route handler instances ─────────────────────────────────────── var aggregateRoutes = new AggregateRoutes(sourceRegistry, auditLogger, queryCache, Duration.ofSeconds(config.getQueryCache().getSearchTtlSeconds()), routeAuthorizer); - var timelineRoutes = new TimelineRoutes(sourceRegistry, auditLogger, piiMasker, queryCache, + var timelineRoutes = new TimelineRoutes(sourceRegistry, auditLogger, sensitiveDataProtector, queryCache, Duration.ofSeconds(config.getQueryCache().getTimelineTtlSeconds()), routeAuthorizer); var datasourceRoutes = new DatasourceRoutes(sourceRegistry, routeAuthorizer); var pluginRoutes = new PluginRoutes(sourceRegistry, routeAuthorizer); var statisticsRoutes = new StatisticsRoutes(sourceRegistry, routeAuthorizer); var bisectRoutes = new BisectRoutes(bisectEngine, routeAuthorizer); - var anomalyRoutes = new AnomalyRoutes(sourceRegistry, config.getAnomaly(), auditLogger, routeAuthorizer); - var exportRoutes = new ExportRoutes(exportEngine, auditLogger, routeAuthorizer); + var anomalyRoutes = new AnomalyRoutes(sourceRegistry, config.getAnomaly(), auditLogger, routeAuthorizer, sensitiveDataProtector); + var exportRoutes = new ExportRoutes(sourceRegistry, exportEngine, auditLogger, routeAuthorizer, sensitiveDataProtector); var asyncExportRoutes = new AsyncExportRoutes(exportService, routeAuthorizer); var healthRoutes = new HealthRoutes(reader, config.getVersion()); var metricsRoutes = new MetricsRoutes(routeAuthorizer); var openApiRoutes = new OpenApiRoutes(routeAuthorizer); + var piiRevealRoutes = new PiiRevealRoutes(sourceRegistry, auditLogger, routeAuthorizer); var liveTailWs = new LiveTailWebSocket(sourceRegistry, pluginManager, auditLogger, defaultSourceId, sourceStreamBindings); var authConfig = config.getServer().getAuth(); @@ -482,6 +485,7 @@ public EventLensServer( // Export (v1 + legacy) cfg.routes.get("/api/v1/aggregates/{id}/export", exportRoutes::export); + cfg.routes.post("/api/v1/aggregates/{id}/events/{seq}/reveal", piiRevealRoutes::revealEvent); cfg.routes.get("/api/aggregates/{id}/export", ctx -> { markDeprecated(ctx, "/api/v1/aggregates/" + ctx.pathParam("id") + "/export"); exportRoutes.export(ctx); diff --git a/eventlens-api/src/main/java/io/eventlens/api/export/ExportService.java b/eventlens-api/src/main/java/io/eventlens/api/export/ExportService.java index 1e0ebbf..670c667 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/export/ExportService.java +++ b/eventlens-api/src/main/java/io/eventlens/api/export/ExportService.java @@ -6,6 +6,7 @@ import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.model.StoredEvent; +import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.spi.EventStoreReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +28,7 @@ public final class ExportService implements AutoCloseable { private final EventStoreReader reader; private final AuditLogger auditLogger; + private final SensitiveDataProtector sensitiveDataProtector; private final ExportConfig cfg; private final Path exportDir; private final Semaphore concurrent; @@ -34,9 +36,10 @@ public final class ExportService implements AutoCloseable { private final ScheduledExecutorService janitor; private final ConcurrentHashMap jobs = new ConcurrentHashMap<>(); - public ExportService(EventStoreReader reader, AuditLogger auditLogger, ExportConfig cfg) { + public ExportService(EventStoreReader reader, AuditLogger auditLogger, ExportConfig cfg, SensitiveDataProtector sensitiveDataProtector) { this.reader = reader; this.auditLogger = auditLogger; + this.sensitiveDataProtector = sensitiveDataProtector; this.cfg = cfg != null ? cfg : new ExportConfig(); this.exportDir = Path.of(this.cfg.getDirectory()); this.concurrent = new Semaphore(Math.max(1, this.cfg.getMaxConcurrent())); @@ -111,12 +114,13 @@ private void runAggregateExport(ExportJob job, String aggregateId, int limit, Ma if (batch.isEmpty()) break; for (StoredEvent e : batch) { + StoredEvent masked = sensitiveDataProtector.maskEvent(e); w.write(e.sequenceNumber() + ","); - w.write(csv(e.eventType())); + w.write(csv(masked.eventType())); w.write(","); - w.write(csv(e.timestamp().toString())); + w.write(csv(masked.timestamp().toString())); w.write(","); - w.write(csv(e.payload())); + w.write(csv(masked.payload())); w.write("\n"); processed++; afterSeq = e.sequenceNumber(); @@ -142,7 +146,7 @@ private void runAggregateExport(ExportJob job, String aggregateId, int limit, Ma if (batch.isEmpty()) break; for (StoredEvent e : batch) { - JsonUtil.mapper().writeValue(g, e); + JsonUtil.mapper().writeValue(g, sensitiveDataProtector.maskEvent(e)); processed++; afterSeq = e.sequenceNumber(); } @@ -218,4 +222,3 @@ public void close() { janitor.shutdownNow(); } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java index d77ecd1..af459f3 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AnomalyRoutes.java @@ -8,6 +8,7 @@ import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.engine.AnomalyDetector; +import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.security.Permission; import io.javalin.http.Context; @@ -28,16 +29,19 @@ public class AnomalyRoutes { private final AuditLogger auditLogger; private final Map detectors = new ConcurrentHashMap<>(); private final RouteAuthorizer routeAuthorizer; + private final SensitiveDataProtector sensitiveDataProtector; public AnomalyRoutes( SourceRegistry sourceRegistry, EventLensConfig.AnomalyConfig anomalyConfig, AuditLogger auditLogger, - RouteAuthorizer routeAuthorizer) { + RouteAuthorizer routeAuthorizer, + SensitiveDataProtector sensitiveDataProtector) { this.sourceRegistry = sourceRegistry; this.anomalyConfig = anomalyConfig; this.auditLogger = auditLogger; this.routeAuthorizer = routeAuthorizer; + this.sensitiveDataProtector = sensitiveDataProtector; } /** GET /api/aggregates/{id}/anomalies */ @@ -58,7 +62,7 @@ public void scanAggregate(Context ctx) { "source", source.id())) .build()); - ctx.json(result); + ctx.json(sensitiveDataProtector.maskAnomalies(result)); } /** GET /api/anomalies/recent?limit=100 */ @@ -81,7 +85,7 @@ public void scanRecent(Context ctx) { "source", source.id())) .build()); - ctx.json(result); + ctx.json(sensitiveDataProtector.maskAnomalies(result)); } private AnomalyDetector detectorFor(String sourceId, SourceRegistry.ResolvedSource source) { diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java index e8b7f27..8da4db8 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java @@ -1,14 +1,17 @@ package io.eventlens.api.routes; +import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; import io.eventlens.api.http.SecurityContext; import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; import io.eventlens.core.engine.ExportEngine; +import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.security.Permission; import io.javalin.http.Context; +import java.util.List; import java.util.Map; /** @@ -19,14 +22,23 @@ */ public class ExportRoutes { + private final SourceRegistry sourceRegistry; private final ExportEngine exportEngine; private final AuditLogger auditLogger; private final RouteAuthorizer routeAuthorizer; + private final SensitiveDataProtector sensitiveDataProtector; - public ExportRoutes(ExportEngine exportEngine, AuditLogger auditLogger, RouteAuthorizer routeAuthorizer) { + public ExportRoutes( + SourceRegistry sourceRegistry, + ExportEngine exportEngine, + AuditLogger auditLogger, + RouteAuthorizer routeAuthorizer, + SensitiveDataProtector sensitiveDataProtector) { + this.sourceRegistry = sourceRegistry; this.exportEngine = exportEngine; this.auditLogger = auditLogger; this.routeAuthorizer = routeAuthorizer; + this.sensitiveDataProtector = sensitiveDataProtector; } /** GET /api/aggregates/{id}/export?format=json|markdown|csv|junit */ @@ -41,10 +53,17 @@ public void export(Context ctx) { default -> ExportEngine.Format.JSON; }; - String content = exportEngine.export(id, format); - if (!routeAuthorizer.require(ctx, Permission.EXPORT_AGGREGATE, null, null)) { + var source = sourceRegistry.resolve(ctx.queryParam("source")); + List events = source.reader().getEvents(id); + String aggregateType = events.isEmpty() ? null : events.getFirst().aggregateType(); + if (!routeAuthorizer.require(ctx, Permission.EXPORT_AGGREGATE, source.id(), aggregateType)) { return; } + String content = exportEngine.export( + id, + events.stream().map(sensitiveDataProtector::maskEvent).toList(), + format == ExportEngine.Format.MARKDOWN ? sensitiveDataProtector.maskTransitions(source.replayEngine().replayFull(id)) : null, + format); String contentType = switch (format) { case MARKDOWN -> "text/markdown"; @@ -58,7 +77,7 @@ public void export(Context ctx) { .action(AuditEvent.ACTION_EXPORT) .resourceType(AuditEvent.RT_EXPORT) .resourceId(id) - .details(Map.of("format", formatStr, "byteCount", content.length())) + .details(Map.of("format", formatStr, "byteCount", content.length(), "source", source.id())) .build()); ctx.contentType(contentType).result(content); diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java new file mode 100644 index 0000000..2e29f17 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java @@ -0,0 +1,71 @@ +package io.eventlens.api.routes; + +import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; +import io.eventlens.api.source.SourceRegistry; +import io.eventlens.core.InputValidator; +import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.audit.AuditLogger; +import io.eventlens.core.model.StoredEvent; +import io.eventlens.core.security.Permission; +import io.javalin.http.Context; + +import java.util.Map; + +public final class PiiRevealRoutes { + + private final SourceRegistry sourceRegistry; + private final AuditLogger auditLogger; + private final RouteAuthorizer routeAuthorizer; + + public PiiRevealRoutes(SourceRegistry sourceRegistry, AuditLogger auditLogger, RouteAuthorizer routeAuthorizer) { + this.sourceRegistry = sourceRegistry; + this.auditLogger = auditLogger; + this.routeAuthorizer = routeAuthorizer; + } + + public record RevealRequest(String reason) { + } + + public void revealEvent(Context ctx) { + String aggregateId = InputValidator.validateAggregateId(ctx.pathParam("id")); + long sequence = Long.parseLong(ctx.pathParam("seq")); + RevealRequest request = ctx.bodyAsClass(RevealRequest.class); + String reason = request != null ? request.reason : null; + if (reason == null || reason.isBlank()) { + ctx.status(400).json(Map.of("error", "reason_required")); + return; + } + + var source = sourceRegistry.resolve(ctx.queryParam("source")); + StoredEvent event = source.reader().getEventsUpTo(aggregateId, sequence).stream() + .filter(candidate -> candidate.sequenceNumber() == sequence) + .reduce((left, right) -> right) + .orElse(null); + if (event == null) { + ctx.status(404).json(Map.of("error", "not_found", "message", "Event not found")); + return; + } + + if (!routeAuthorizer.require(ctx, Permission.REVEAL_PII, source.id(), event.aggregateType())) { + return; + } + + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_REVEAL_PII) + .resourceType(AuditEvent.RT_EVENT) + .resourceId(event.eventId()) + .details(Map.of( + "aggregateId", aggregateId, + "sequence", sequence, + "source", source.id(), + "reason", reason + )) + .build()); + + ctx.json(Map.of( + "event", event, + "reason", reason + )); + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java index 693d868..f7ad963 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/TimelineRoutes.java @@ -1,8 +1,5 @@ package io.eventlens.api.routes; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import io.eventlens.api.cache.QueryResultCache; import io.eventlens.api.http.ConditionalGet; import io.eventlens.api.http.SecurityContext; @@ -14,7 +11,7 @@ import io.eventlens.core.model.AggregateTimeline; import io.eventlens.core.model.StoredEvent; import io.eventlens.core.pagination.CursorCodec; -import io.eventlens.core.pii.PiiMasker; +import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.security.Permission; import io.javalin.http.Context; @@ -31,28 +28,24 @@ public class TimelineRoutes { private final SourceRegistry sourceRegistry; private final AuditLogger auditLogger; - private final PiiMasker piiMasker; + private final SensitiveDataProtector sensitiveDataProtector; private final QueryResultCache queryCache; private final Duration timelineTtl; - private final ObjectMapper mapper; private final RouteAuthorizer routeAuthorizer; public TimelineRoutes( SourceRegistry sourceRegistry, AuditLogger auditLogger, - PiiMasker piiMasker, + SensitiveDataProtector sensitiveDataProtector, QueryResultCache queryCache, Duration timelineTtl, RouteAuthorizer routeAuthorizer) { this.sourceRegistry = sourceRegistry; this.auditLogger = auditLogger; - this.piiMasker = piiMasker; + this.sensitiveDataProtector = sensitiveDataProtector; this.queryCache = queryCache; this.timelineTtl = timelineTtl; this.routeAuthorizer = routeAuthorizer; - this.mapper = new ObjectMapper() - .findAndRegisterModules() - .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } public void getTimeline(Context ctx) { @@ -119,7 +112,7 @@ public void replay(Context ctx) { if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), aggregateType)) { return; } - ctx.json(transitions); + ctx.json(sensitiveDataProtector.maskTransitions(transitions)); } public void replayTo(Context ctx) { @@ -134,7 +127,7 @@ public void replayTo(Context ctx) { if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), aggregateType)) { return; } - ctx.json(replay); + ctx.json(sensitiveDataProtector.maskReplayResult(replay)); } public void transitions(Context ctx) { @@ -148,7 +141,7 @@ public void transitions(Context ctx) { if (!routeAuthorizer.require(ctx, Permission.VIEW_REPLAY, source.id(), aggregateType)) { return; } - ctx.json(transitions); + ctx.json(sensitiveDataProtector.maskTransitions(transitions)); } private TimelineEnvelope buildTimelineEnvelope( @@ -175,7 +168,7 @@ private TimelineEnvelope buildTimelineEnvelope( timeline = source.replayEngine().buildTimeline(aggregateId, limit, offset); } - AggregateTimeline masked = maskTimeline(timeline); + AggregateTimeline masked = sensitiveDataProtector.maskTimeline(timeline); AggregateTimeline shaped = "metadata".equals(fields) ? metadataOnly(masked) : masked; return new TimelineEnvelope(shaped, hasMore, nextCursor); } @@ -210,48 +203,6 @@ private AggregateTimeline metadataOnly(AggregateTimeline timeline) { events, timeline.totalEvents()); } - - private AggregateTimeline maskTimeline(AggregateTimeline timeline) { - if (timeline == null || timeline.events() == null) { - return timeline; - } - - List maskedEvents = timeline.events().stream() - .map(this::maskEvent) - .toList(); - - return new AggregateTimeline( - timeline.aggregateId(), - timeline.aggregateType(), - maskedEvents, - timeline.totalEvents()); - } - - private StoredEvent maskEvent(StoredEvent event) { - if (event == null || event.payload() == null) { - return event; - } - try { - String raw = event.payload(); - JsonNode tree = mapper.readTree(raw); - JsonNode maskedTree = piiMasker.mask(tree, raw); - if (maskedTree == tree) { - return event; - } - return new StoredEvent( - event.eventId(), - event.aggregateId(), - event.aggregateType(), - event.sequenceNumber(), - event.eventType(), - mapper.writeValueAsString(maskedTree), - event.metadata(), - event.timestamp(), - event.globalPosition()); - } catch (Exception e) { - return event; - } - } private record TimelineEnvelope(AggregateTimeline timeline, boolean hasMore, String nextCursor) { } } diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index 07a58c8..a33ae2f 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -311,6 +311,108 @@ void authorizationRejectsSourceOutsideRoleScope() throws Exception { assertThat(response.body()).contains("\"source\":\"default\""); } + @Test + void timelineMasksSensitivePayloadsByDefault() throws Exception { + int port = freePort(); + server = startServer(port, true); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/ORD-1001/timeline".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).contains("***@***.***"); + assertThat(response.body()).doesNotContain("alice@example.com"); + } + + @Test + void exportMasksSensitivePayloadsByDefault() throws Exception { + int port = freePort(); + server = startServer(port, true); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/ORD-1001/export?format=json".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).contains("***@***.***"); + assertThat(response.body()).doesNotContain("alice@example.com"); + } + + @Test + void revealRequiresPermissionAndReasonThenReturnsUnmaskedPayload() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("pii-operator"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("pii-operator"); + role.setPermissions(List.of("SEARCH_AGGREGATES", "VIEW_TIMELINE", "REVEAL_PII")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newHttpClient(); + + var missingReasonRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/ORD-1001/events/1/reveal".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{}")) + .build(); + HttpResponse badRequest = client.send(missingReasonRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(badRequest.statusCode()).isEqualTo(400); + + var allowedRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/ORD-1001/events/1/reveal".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{\"reason\":\"Investigating ticket SEC-123\"}")) + .build(); + HttpResponse allowed = client.send(allowedRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(allowed.statusCode()).isEqualTo(200); + assertThat(allowed.body()).contains("alice@example.com"); + } + + @Test + void revealRejectsUserWithoutRevealPermission() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("reader"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("reader"); + role.setPermissions(List.of("SEARCH_AGGREGATES", "VIEW_TIMELINE")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/ORD-1001/events/1/reveal".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{\"reason\":\"Need raw payload\"}")) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(403); + assertThat(response.body()).contains("\"reason\":\"DENY_MISSING_PERMISSION\""); + assertThat(response.body()).contains("\"permission\":\"REVEAL_PII\""); + } + private EventLensServer startServer(int port, boolean authEnabled) { return startServer(port, authEnabled, null, cfg -> {}); } @@ -327,12 +429,32 @@ private EventLensServer startServer(int port, boolean authEnabled, String oidcIs EventStoreReader reader = new EventStoreReader() { @Override public List getEvents(String aggregateId) { - return List.of(); + if (!"ORD-1001".equals(aggregateId)) { + return List.of(); + } + return List.of( + new StoredEvent( + "evt-1", "ORD-1001", "Order", 1, "ORDER_CREATED", + "{\"email\":\"alice@example.com\",\"status\":\"created\"}", + "{\"source\":\"default\"}", + Instant.parse("2026-01-01T00:00:00Z"), + 1L + ), + new StoredEvent( + "evt-2", "ORD-1001", "Order", 2, "ORDER_CONFIRMED", + "{\"status\":\"confirmed\"}", + "{\"source\":\"default\"}", + Instant.parse("2026-01-01T00:01:00Z"), + 2L + ) + ); } @Override public List getEventsUpTo(String aggregateId, long maxSequence) { - return List.of(); + return getEvents(aggregateId).stream() + .filter(event -> event.sequenceNumber() <= maxSequence) + .toList(); } @Override @@ -354,7 +476,7 @@ public List getEventsAfter(long globalPosition, int limit) { @Override public long countEvents(String aggregateId) { - return 0; + return getEvents(aggregateId).size(); } @Override @@ -374,6 +496,7 @@ public List searchAggregates(String query, int limit) { cfg.getServer().getAuth().setUsername("admin"); cfg.getServer().getAuth().setPassword("correct horse battery"); cfg.getServer().getSecurity().getRateLimit().setEnabled(false); + cfg.getDataProtection().getPii().setEnabled(true); cfg.getSecurity().getMetadata().setEnabled(true); cfg.getSecurity().getMetadata().setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("auth-" + port + ".db")); cfg.getSecurity().getAuth().getSession().setCookieName("eventlens_test_session"); diff --git a/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java b/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java index 3b6d25a..50900f3 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java @@ -7,6 +7,7 @@ import io.eventlens.core.model.AggregateTimeline; import io.eventlens.core.model.StoredEvent; import io.eventlens.core.pii.PiiMasker; +import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.security.AuthorizationService; import org.junit.jupiter.api.Test; @@ -33,7 +34,7 @@ void metadataOnlyShapeReducesSerializedPayloadByAtLeastSeventyPercent() throws E TimelineRoutes routes = new TimelineRoutes( null, null, - new PiiMasker(new EventLensConfig.PiiConfig()), + new SensitiveDataProtector(new PiiMasker(new EventLensConfig.PiiConfig())), new QueryResultCache(false, 1), Duration.ofSeconds(1), new RouteAuthorizer(new AuthorizationService(null))); diff --git a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java index 6cb8740..31caeb5 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java +++ b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java @@ -28,6 +28,7 @@ public record AuditEvent( public static final String ACTION_VIEW_TIMELINE = "VIEW_TIMELINE"; public static final String ACTION_VIEW_ANOMALIES = "VIEW_ANOMALIES"; public static final String ACTION_EXPORT = "EXPORT"; + public static final String ACTION_REVEAL_PII = "REVEAL_PII"; public static final String ACTION_LOGIN = "LOGIN"; public static final String ACTION_LOGIN_FAILED = "LOGIN_FAILED"; public static final String ACTION_VIEW_LIVE_STREAM = "VIEW_LIVE_STREAM"; diff --git a/eventlens-core/src/main/java/io/eventlens/core/engine/ExportEngine.java b/eventlens-core/src/main/java/io/eventlens/core/engine/ExportEngine.java index 58ad6f8..807fbb0 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/engine/ExportEngine.java +++ b/eventlens-core/src/main/java/io/eventlens/core/engine/ExportEngine.java @@ -38,10 +38,14 @@ public enum Format { public String export(String aggregateId, Format format) { log.info("Exporting aggregate '{}' as {}", aggregateId, format); List events = reader.getEvents(aggregateId); + return export(aggregateId, events, null, format); + } + + public String export(String aggregateId, List events, List transitions, Format format) { return switch (format) { case JSON -> exportJson(aggregateId, events); case JUNIT_FIXTURE -> exportJunitFixture(aggregateId, events); - case MARKDOWN -> exportMarkdown(aggregateId, events); + case MARKDOWN -> exportMarkdown(aggregateId, transitions != null ? transitions : replayEngine.replayFull(aggregateId)); case CSV -> exportCsv(events); }; } @@ -80,8 +84,7 @@ private String exportJunitFixture(String aggregateId, List events) return sb.toString(); } - private String exportMarkdown(String aggregateId, List events) { - var transitions = replayEngine.replayFull(aggregateId); + private String exportMarkdown(String aggregateId, List transitions) { StringBuilder sb = new StringBuilder(); sb.append("# Event History: ").append(aggregateId).append("\n\n"); sb.append("| # | Event Type | Timestamp | Changes |\n"); diff --git a/eventlens-core/src/main/java/io/eventlens/core/pii/SensitiveDataProtector.java b/eventlens-core/src/main/java/io/eventlens/core/pii/SensitiveDataProtector.java new file mode 100644 index 0000000..ac5a4e1 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/pii/SensitiveDataProtector.java @@ -0,0 +1,172 @@ +package io.eventlens.core.pii; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.eventlens.core.engine.ReplayEngine; +import io.eventlens.core.model.AggregateTimeline; +import io.eventlens.core.model.AnomalyReport; +import io.eventlens.core.model.StateTransition; +import io.eventlens.core.model.StoredEvent; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class SensitiveDataProtector { + + private static final TypeReference> MAP_TYPE = new TypeReference<>() { + }; + + private final PiiMasker piiMasker; + private final ObjectMapper mapper; + + public SensitiveDataProtector(PiiMasker piiMasker) { + this.piiMasker = piiMasker; + this.mapper = new ObjectMapper().findAndRegisterModules(); + } + + public StoredEvent maskEvent(StoredEvent event) { + if (event == null || event.payload() == null) { + return event; + } + String maskedPayload = maskJsonString(event.payload()); + if (maskedPayload.equals(event.payload())) { + return event; + } + return new StoredEvent( + event.eventId(), + event.aggregateId(), + event.aggregateType(), + event.sequenceNumber(), + event.eventType(), + maskedPayload, + event.metadata(), + event.timestamp(), + event.globalPosition() + ); + } + + public AggregateTimeline maskTimeline(AggregateTimeline timeline) { + if (timeline == null || timeline.events() == null) { + return timeline; + } + return new AggregateTimeline( + timeline.aggregateId(), + timeline.aggregateType(), + timeline.events().stream().map(this::maskEvent).toList(), + timeline.totalEvents() + ); + } + + public ReplayEngine.ReplayResult maskReplayResult(ReplayEngine.ReplayResult replay) { + if (replay == null) { + return null; + } + return new ReplayEngine.ReplayResult( + replay.aggregateId(), + replay.atSequence(), + maskMap(replay.state()), + replay.transitions().stream().map(this::maskTransition).toList() + ); + } + + public List maskTransitions(List transitions) { + if (transitions == null) { + return List.of(); + } + return transitions.stream().map(this::maskTransition).toList(); + } + + public AnomalyReport maskAnomaly(AnomalyReport report) { + if (report == null) { + return null; + } + return new AnomalyReport( + report.code(), + report.description(), + report.severity(), + report.aggregateId(), + report.atSequence(), + report.triggeringEventType(), + report.timestamp(), + maskMap(report.stateAtAnomaly()) + ); + } + + public List maskAnomalies(List reports) { + if (reports == null) { + return List.of(); + } + return reports.stream().map(this::maskAnomaly).toList(); + } + + public String maskJsonString(String rawJson) { + if (rawJson == null || rawJson.isBlank()) { + return rawJson; + } + try { + JsonNode original = mapper.readTree(rawJson); + JsonNode masked = piiMasker.mask(original, rawJson); + if (masked == original) { + return rawJson; + } + return mapper.writeValueAsString(masked); + } catch (Exception e) { + return rawJson; + } + } + + private StateTransition maskTransition(StateTransition transition) { + if (transition == null) { + return null; + } + return new StateTransition( + maskEvent(transition.event()), + maskMap(transition.stateBefore()), + maskMap(transition.stateAfter()), + maskDiff(transition.diff()) + ); + } + + private Map maskDiff(Map diff) { + if (diff == null || diff.isEmpty()) { + return Map.of(); + } + Map masked = new LinkedHashMap<>(); + for (var entry : diff.entrySet()) { + var change = entry.getValue(); + masked.put(entry.getKey(), new StateTransition.FieldChange( + maskValue(change.oldValue()), + maskValue(change.newValue()) + )); + } + return Map.copyOf(masked); + } + + private Map maskMap(Map value) { + if (value == null || value.isEmpty()) { + return value == null ? Map.of() : value; + } + try { + String rawJson = mapper.writeValueAsString(value); + JsonNode masked = piiMasker.mask(mapper.valueToTree(value), rawJson); + return mapper.convertValue(masked, MAP_TYPE); + } catch (Exception e) { + return value; + } + } + + private Object maskValue(Object value) { + if (value == null) { + return null; + } + try { + String rawJson = mapper.writeValueAsString(value); + JsonNode masked = piiMasker.mask(mapper.valueToTree(value), rawJson); + return mapper.convertValue(masked, Object.class); + } catch (Exception e) { + return value; + } + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java index 7da9665..4d184a5 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java +++ b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java @@ -12,6 +12,7 @@ public enum Permission { VIEW_ANOMALIES, EXPORT_AGGREGATE, START_EXPORT, + REVEAL_PII, VIEW_DATASOURCES, VIEW_PLUGINS, VIEW_STATISTICS, diff --git a/eventlens-core/src/test/java/io/eventlens/core/pii/SensitiveDataProtectorTest.java b/eventlens-core/src/test/java/io/eventlens/core/pii/SensitiveDataProtectorTest.java new file mode 100644 index 0000000..74f2171 --- /dev/null +++ b/eventlens-core/src/test/java/io/eventlens/core/pii/SensitiveDataProtectorTest.java @@ -0,0 +1,105 @@ +package io.eventlens.core.pii; + +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.engine.ReplayEngine; +import io.eventlens.core.model.AnomalyReport; +import io.eventlens.core.model.StateTransition; +import io.eventlens.core.model.StoredEvent; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class SensitiveDataProtectorTest { + + private final SensitiveDataProtector protector = new SensitiveDataProtector(enabledMasker()); + + @Test + void maskEventMasksSensitivePayloadButPreservesSafeFields() { + StoredEvent event = new StoredEvent( + "evt-1", + "ACC-1", + "Account", + 1, + "AccountCreated", + "{\"email\":\"alice@example.com\",\"status\":\"active\"}", + "{\"source\":\"test\"}", + Instant.parse("2026-01-01T00:00:00Z"), + 1L + ); + + StoredEvent masked = protector.maskEvent(event); + + assertThat(masked.payload()).contains("***@***.***"); + assertThat(masked.payload()).contains("\"status\":\"active\""); + assertThat(masked.metadata()).isEqualTo(event.metadata()); + } + + @Test + void maskReplayResultMasksStateSnapshotsAndDiffValues() { + StoredEvent event = new StoredEvent( + "evt-1", + "ACC-1", + "Account", + 1, + "AccountCreated", + "{\"email\":\"alice@example.com\"}", + "{}", + Instant.parse("2026-01-01T00:00:00Z"), + 1L + ); + StateTransition transition = new StateTransition( + event, + Map.of("email", "alice@example.com"), + Map.of("email", "alice@example.com", "status", "active"), + Map.of( + "email", new StateTransition.FieldChange(null, "alice@example.com"), + "status", new StateTransition.FieldChange(null, "active") + ) + ); + ReplayEngine.ReplayResult replay = new ReplayEngine.ReplayResult( + "ACC-1", + 1L, + Map.of("email", "alice@example.com", "status", "active"), + List.of(transition) + ); + + ReplayEngine.ReplayResult masked = protector.maskReplayResult(replay); + + assertThat(masked.state()).containsEntry("email", "***@***.***"); + assertThat(masked.transitions()).singleElement().satisfies(result -> { + assertThat(result.event().payload()).contains("***@***.***"); + assertThat(result.stateAfter()).containsEntry("email", "***@***.***"); + assertThat(result.diff().get("email").newValue()).isEqualTo("***@***.***"); + assertThat(result.diff().get("status").newValue()).isEqualTo("active"); + }); + } + + @Test + void maskAnomalyMasksStateAtAnomaly() { + AnomalyReport report = new AnomalyReport( + "EMAIL_LEAK", + "Suspicious state", + AnomalyReport.Severity.HIGH, + "ACC-1", + 2L, + "AccountUpdated", + Instant.parse("2026-01-01T00:00:00Z"), + Map.of("email", "alice@example.com", "status", "active") + ); + + AnomalyReport masked = protector.maskAnomaly(report); + + assertThat(masked.stateAtAnomaly()).containsEntry("email", "***@***.***"); + assertThat(masked.stateAtAnomaly()).containsEntry("status", "active"); + } + + private static PiiMasker enabledMasker() { + var config = new EventLensConfig.PiiConfig(); + config.setEnabled(true); + return new PiiMasker(config); + } +} From d04f1f4b1972ee88a21c845168e6da8948fdf412 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 16:47:12 +0200 Subject: [PATCH 07/11] feat: add metadata-backed audit access --- .../io/eventlens/api/EventLensServer.java | 5 +- .../io/eventlens/api/routes/AuditRoutes.java | 72 +++++++++++++++++++ .../api/AuthenticationIntegrationTest.java | 65 +++++++++++++++++ .../io/eventlens/core/audit/AuditLogger.java | 16 +++++ .../core/metadata/AuditLogRepository.java | 32 ++++++++- .../eventlens/core/security/Permission.java | 1 + .../eventlens/core/audit/AuditLoggerTest.java | 34 +++++++++ 7 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/routes/AuditRoutes.java diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index 2778488..02115f1 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -118,7 +118,8 @@ public EventLensServer( // ── 1.8 Audit Logger ────────────────────────────────────────────── final AuditLogger auditLogger = new AuditLogger( - config.getAudit().isEnabled()); + config.getAudit().isEnabled(), + this.metadataDatabase.isEnabled() ? this.metadataDatabase.repositories().auditLogs() : null); // ── 1.9 PII Masker ──────────────────────────────────────────────── final PiiMasker piiMasker = new PiiMasker( @@ -161,6 +162,7 @@ public EventLensServer( var asyncExportRoutes = new AsyncExportRoutes(exportService, routeAuthorizer); var healthRoutes = new HealthRoutes(reader, config.getVersion()); var metricsRoutes = new MetricsRoutes(routeAuthorizer); + var auditRoutes = new AuditRoutes(this.metadataDatabase.isEnabled() ? this.metadataDatabase.repositories().auditLogs() : null, routeAuthorizer); var openApiRoutes = new OpenApiRoutes(routeAuthorizer); var piiRevealRoutes = new PiiRevealRoutes(sourceRegistry, auditLogger, routeAuthorizer); var liveTailWs = new LiveTailWebSocket(sourceRegistry, pluginManager, auditLogger, defaultSourceId, sourceStreamBindings); @@ -401,6 +403,7 @@ public EventLensServer( // Metrics (4.1) cfg.routes.get("/api/v1/metrics", metricsRoutes::metrics); + cfg.routes.get("/api/v1/audit", auditRoutes::recent); // OpenAPI (5.2) cfg.routes.get("/api/v1/openapi.json", openApiRoutes::spec); diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AuditRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AuditRoutes.java new file mode 100644 index 0000000..216f6ee --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AuditRoutes.java @@ -0,0 +1,72 @@ +package io.eventlens.api.routes; + +import io.eventlens.api.security.RouteAuthorizer; +import io.eventlens.core.InputValidator; +import io.eventlens.core.JsonUtil; +import io.eventlens.core.metadata.AuditLogRecord; +import io.eventlens.core.metadata.AuditLogRepository; +import io.eventlens.core.security.Permission; +import io.javalin.http.Context; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class AuditRoutes { + + private final AuditLogRepository auditLogRepository; + private final RouteAuthorizer routeAuthorizer; + + public AuditRoutes(AuditLogRepository auditLogRepository, RouteAuthorizer routeAuthorizer) { + this.auditLogRepository = auditLogRepository; + this.routeAuthorizer = routeAuthorizer; + } + + public void recent(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.VIEW_AUDIT_LOG, null, null)) { + return; + } + if (auditLogRepository == null) { + ctx.status(503).json(Map.of( + "error", "audit_unavailable", + "message", "Metadata-backed audit storage is disabled" + )); + return; + } + + int limit = InputValidator.validateLimit(ctx.queryParam("limit"), 50, 500); + String action = blankToNull(ctx.queryParam("action")); + String userId = blankToNull(ctx.queryParam("userId")); + + List> entries = auditLogRepository.findRecent(limit, action, userId).stream() + .map(AuditRoutes::toPayload) + .toList(); + + Map response = new LinkedHashMap<>(); + response.put("entries", entries); + response.put("limit", limit); + response.put("action", action); + response.put("userId", userId); + ctx.json(response); + } + + private static Map toPayload(AuditLogRecord record) { + Map payload = new LinkedHashMap<>(); + payload.put("auditId", record.auditId()); + payload.put("action", record.action()); + payload.put("resourceType", record.resourceType()); + payload.put("resourceId", record.resourceId()); + payload.put("userId", record.userId()); + payload.put("authMethod", record.authMethod()); + payload.put("clientIp", record.clientIp()); + payload.put("requestId", record.requestId()); + payload.put("userAgent", record.userAgent()); + payload.put("details", JsonUtil.parseMap(record.detailsJson())); + payload.put("createdAt", record.createdAt()); + return payload; + } + + private static String blankToNull(String value) { + return value == null || value.isBlank() ? null : value; + } +} diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index a33ae2f..0268aa9 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -413,6 +413,71 @@ void revealRejectsUserWithoutRevealPermission() throws Exception { assertThat(response.body()).contains("\"permission\":\"REVEAL_PII\""); } + @Test + void auditEndpointReturnsPersistedAuditEntries() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("auditor"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("auditor"); + role.setPermissions(List.of("SEARCH_AGGREGATES", "VIEW_AUDIT_LOG")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newHttpClient(); + var searchRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + HttpResponse searchResponse = client.send(searchRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(searchResponse.statusCode()).isEqualTo(200); + + var auditRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/audit?limit=10&action=SEARCH".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + HttpResponse auditResponse = client.send(auditRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(auditResponse.statusCode()).isEqualTo(200); + var auditJson = new ObjectMapper().readTree(auditResponse.body()); + assertThat(auditJson.path("entries").isArray()).isTrue(); + assertThat(auditJson.path("entries")) + .anySatisfy(node -> { + assertThat(node.path("action").asText()).isEqualTo("SEARCH"); + assertThat(node.path("userId").asText()).isEqualTo("admin"); + }); + } + + @Test + void auditEndpointRejectsUserWithoutAuditPermission() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("reader"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("reader"); + role.setPermissions(List.of("SEARCH_AGGREGATES")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/audit?limit=10".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(403); + assertThat(response.body()).contains("\"permission\":\"VIEW_AUDIT_LOG\""); + } + private EventLensServer startServer(int port, boolean authEnabled) { return startServer(port, authEnabled, null, cfg -> {}); } diff --git a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java index 948015a..819b317 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java +++ b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java @@ -3,9 +3,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.eventlens.core.metadata.AuditLogRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.Instant; + /** * Writes structured audit entries to the {@code eventlens.audit} logger. * @@ -27,9 +30,15 @@ public final class AuditLogger { private final ObjectMapper mapper; private final boolean enabled; + private final AuditLogRepository auditLogRepository; public AuditLogger(boolean enabled) { + this(enabled, null); + } + + public AuditLogger(boolean enabled, AuditLogRepository auditLogRepository) { this.enabled = enabled; + this.auditLogRepository = auditLogRepository; this.mapper = new ObjectMapper() .registerModule(new JavaTimeModule()) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); @@ -49,5 +58,12 @@ public void log(AuditEvent event) { auditLog.info("{\"action\":\"{}\",\"requestId\":\"{}\",\"error\":\"serialisation failed\"}", event.action(), event.requestId()); } + if (auditLogRepository != null) { + try { + auditLogRepository.append(event, event.timestamp() != null ? event.timestamp() : Instant.now()); + } catch (Exception ex) { + auditLog.warn("Failed to persist audit event requestId={} action={}", event.requestId(), event.action(), ex); + } + } } } diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java index 359bba6..dde9cba 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/AuditLogRepository.java @@ -47,11 +47,37 @@ INSERT INTO audit_log( } public List findRecent(int limit) { + return findRecent(limit, null, null); + } + + public List findRecent(int limit, String action, String userId) { List results = new ArrayList<>(); + StringBuilder sql = new StringBuilder("SELECT * FROM audit_log"); + List params = new ArrayList<>(); + if (action != null || userId != null) { + sql.append(" WHERE "); + boolean appended = false; + if (action != null) { + sql.append("action = ?"); + params.add(action); + appended = true; + } + if (userId != null) { + if (appended) { + sql.append(" AND "); + } + sql.append("user_id = ?"); + params.add(userId); + } + } + sql.append(" ORDER BY created_at DESC LIMIT ?"); + params.add(limit); + try (var connection = dataSource.getConnection(); - PreparedStatement ps = connection.prepareStatement( - "SELECT * FROM audit_log ORDER BY created_at DESC LIMIT ?")) { - ps.setInt(1, limit); + PreparedStatement ps = connection.prepareStatement(sql.toString())) { + for (int i = 0; i < params.size(); i++) { + ps.setObject(i + 1, params.get(i)); + } try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { results.add(map(rs)); diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java index 4d184a5..1041d0f 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java +++ b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java @@ -17,6 +17,7 @@ public enum Permission { VIEW_PLUGINS, VIEW_STATISTICS, VIEW_METRICS, + VIEW_AUDIT_LOG, VIEW_OPENAPI, EXECUTE_BISECT; diff --git a/eventlens-core/src/test/java/io/eventlens/core/audit/AuditLoggerTest.java b/eventlens-core/src/test/java/io/eventlens/core/audit/AuditLoggerTest.java index 9f41dc3..e0318a9 100644 --- a/eventlens-core/src/test/java/io/eventlens/core/audit/AuditLoggerTest.java +++ b/eventlens-core/src/test/java/io/eventlens/core/audit/AuditLoggerTest.java @@ -1,8 +1,13 @@ package io.eventlens.core.audit; import com.fasterxml.jackson.databind.ObjectMapper; +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.metadata.AuditLogRecord; +import io.eventlens.core.metadata.MetadataDatabase; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -11,6 +16,9 @@ class AuditLoggerTest { + @TempDir + Path tempDir; + @Test void log_enabled_doesNotThrow() { var logger = new AuditLogger(true); @@ -81,4 +89,30 @@ void auditEvent_allActionConstants_areNonBlank() { ); assertThat(constants).allSatisfy(c -> assertThat(c).isNotBlank()); } + + @Test + void log_persistsToMetadataRepositoryWhenConfigured() { + EventLensConfig.MetadataConfig config = new EventLensConfig.MetadataConfig(); + config.setEnabled(true); + config.setJdbcUrl("jdbc:sqlite:" + tempDir.resolve("audit.db")); + + try (MetadataDatabase database = MetadataDatabase.open(config)) { + var logger = new AuditLogger(true, database.repositories().auditLogs()); + var event = AuditEvent.builder() + .action(AuditEvent.ACTION_EXPORT) + .resourceType(AuditEvent.RT_EXPORT) + .resourceId("agg-1") + .userId("alice") + .authMethod("basic") + .requestId("req-1") + .details(Map.of("format", "json")) + .build(); + + logger.log(event); + + assertThat(database.repositories().auditLogs().findRecent(10)) + .extracting(AuditLogRecord::requestId) + .contains("req-1"); + } + } } From 22ecf167c314203a33f15eb94f9515c863817298 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 18:19:42 +0200 Subject: [PATCH 08/11] feat: add api key authentication and admin routes --- .../io/eventlens/api/EventLensServer.java | 44 ++++- .../io/eventlens/api/routes/ApiKeyRoutes.java | 131 ++++++++++++++ .../api/security/ApiKeyAuthenticator.java | 43 +++++ .../api/AuthenticationIntegrationTest.java | 124 ++++++++++++++ eventlens-core/build.gradle.kts | 1 + .../io/eventlens/core/ConfigValidator.java | 15 ++ .../io/eventlens/core/EventLensConfig.java | 16 ++ .../io/eventlens/core/audit/AuditEvent.java | 2 + .../core/security/ApiKeyService.java | 162 ++++++++++++++++++ .../eventlens/core/security/Permission.java | 1 + .../eventlens/core/ConfigValidatorTest.java | 13 ++ .../core/security/ApiKeyServiceTest.java | 91 ++++++++++ 12 files changed, 640 insertions(+), 3 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java create mode 100644 eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java create mode 100644 eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java create mode 100644 eventlens-core/src/test/java/io/eventlens/core/security/ApiKeyServiceTest.java diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index 02115f1..e4184e8 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -10,6 +10,7 @@ import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.routes.MetricsRoutes; import io.eventlens.api.http.RequestContextMdcFilter; +import io.eventlens.api.security.ApiKeyAuthenticator; import io.eventlens.api.security.BasicAuthenticator; import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.security.SessionAuthenticator; @@ -27,6 +28,7 @@ import io.eventlens.core.pii.PiiMasker; import io.eventlens.core.pii.SensitiveDataProtector; import io.eventlens.core.plugin.PluginManager; +import io.eventlens.core.security.ApiKeyService; import io.eventlens.core.security.AuthorizationService; import io.eventlens.core.security.Principal; import io.eventlens.core.security.SessionService; @@ -170,16 +172,24 @@ public EventLensServer( var authConfig = config.getServer().getAuth(); var v5AuthConfig = config.getSecurity() != null ? config.getSecurity().getAuth() : null; var authProvider = v5AuthConfig != null ? v5AuthConfig.getProvider() : "disabled"; + var apiKeysConfig = v5AuthConfig != null ? v5AuthConfig.getApiKeys() : new EventLensConfig.ApiKeysConfig(); + boolean apiKeysEnabled = apiKeysConfig != null && apiKeysConfig.isEnabled() && this.metadataDatabase.isEnabled(); boolean sessionProviderEnabled = "basic".equalsIgnoreCase(authProvider) || "oidc".equalsIgnoreCase(authProvider); boolean oidcEnabled = "oidc".equalsIgnoreCase(authProvider); boolean basicCompatibilityEnabled = authConfig.isEnabled(); - boolean authEnabled = basicCompatibilityEnabled || sessionProviderEnabled; + boolean authEnabled = basicCompatibilityEnabled || sessionProviderEnabled || apiKeysEnabled; var sessionConfig = v5AuthConfig != null ? v5AuthConfig.getSession() : new EventLensConfig.SessionConfig(); var oidcCallbackPath = oidcEnabled ? v5AuthConfig.getOidc().getRedirectPath() : "/api/v1/auth/callback"; var basicAuthenticator = new BasicAuthenticator( authConfig.getUsername(), authConfig.getPassword(), "EventLens"); + var apiKeyService = apiKeysEnabled + ? new ApiKeyService(this.metadataDatabase.repositories().apiKeys(), apiKeysConfig) + : null; + var apiKeyAuthenticator = apiKeyService != null + ? new ApiKeyAuthenticator(apiKeyService, apiKeysConfig.getHeaderName()) + : null; var sessionService = this.metadataDatabase.isEnabled() ? new SessionService(this.metadataDatabase.repositories().sessions(), sessionConfig) : null; @@ -200,6 +210,7 @@ public EventLensServer( new OidcLoginStateService(this.metadataDatabase.repositories().sessions()), auditLogger) : null; + var apiKeyRoutes = apiKeyService != null ? new ApiKeyRoutes(apiKeyService, routeAuthorizer, auditLogger) : null; // ── Javalin 7: all routes + handlers inside cfg.routes ──────────── this.app = Javalin.create(cfg -> { @@ -337,7 +348,13 @@ public EventLensServer( return; } - var authHolder = resolveAuthentication(ctx, sessionAuthenticator, basicCompatibilityEnabled ? basicAuthenticator : null, sessionConfig.getCookieName()); + var authHolder = resolveAuthentication( + ctx, + apiKeyAuthenticator, + sessionAuthenticator, + basicCompatibilityEnabled ? basicAuthenticator : null, + apiKeysConfig != null ? apiKeysConfig.getHeaderName() : "X-API-Key", + sessionConfig.getCookieName()); var authResult = authHolder.result(); if (!authResult.success()) { // 1.8 - emit LOGIN_FAILED @@ -377,7 +394,13 @@ public EventLensServer( } }); cfg.routes.before("/ws/*", ctx -> { - var authResult = resolveAuthentication(ctx, sessionAuthenticator, basicCompatibilityEnabled ? basicAuthenticator : null, sessionConfig.getCookieName()).result(); + var authResult = resolveAuthentication( + ctx, + apiKeyAuthenticator, + sessionAuthenticator, + basicCompatibilityEnabled ? basicAuthenticator : null, + apiKeysConfig != null ? apiKeysConfig.getHeaderName() : "X-API-Key", + sessionConfig.getCookieName()).result(); if (!authResult.success()) { ctx.status(401); if (authResult.challengeHeader() != null) { @@ -404,6 +427,11 @@ public EventLensServer( // Metrics (4.1) cfg.routes.get("/api/v1/metrics", metricsRoutes::metrics); cfg.routes.get("/api/v1/audit", auditRoutes::recent); + if (apiKeyRoutes != null) { + cfg.routes.get("/api/v1/admin/api-keys", apiKeyRoutes::list); + cfg.routes.post("/api/v1/admin/api-keys", apiKeyRoutes::create); + cfg.routes.post("/api/v1/admin/api-keys/{id}/revoke", apiKeyRoutes::revoke); + } // OpenAPI (5.2) cfg.routes.get("/api/v1/openapi.json", openApiRoutes::spec); @@ -620,9 +648,14 @@ private static boolean isMutatingMethod(String method) { private static AuthenticationResultHolder resolveAuthentication( io.javalin.http.Context ctx, + ApiKeyAuthenticator apiKeyAuthenticator, SessionAuthenticator sessionAuthenticator, BasicAuthenticator basicAuthenticator, + String apiKeyHeaderName, String sessionCookieName) { + if (apiKeyAuthenticator != null && hasApiKeyHeader(ctx, apiKeyHeaderName)) { + return new AuthenticationResultHolder("api-key", apiKeyAuthenticator.authenticate(ctx)); + } if (sessionAuthenticator != null && hasSessionCookie(ctx, sessionCookieName)) { return new AuthenticationResultHolder("session", sessionAuthenticator.authenticate(ctx)); } @@ -632,6 +665,11 @@ private static AuthenticationResultHolder resolveAuthentication( return new AuthenticationResultHolder("session", io.eventlens.api.security.AuthenticationResult.failure(null, "missing_session", null)); } + private static boolean hasApiKeyHeader(io.javalin.http.Context ctx, String headerName) { + String value = ctx.header(headerName); + return value != null && !value.isBlank(); + } + private record AuthenticationResultHolder(String method, io.eventlens.api.security.AuthenticationResult result) { } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java new file mode 100644 index 0000000..03d9039 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java @@ -0,0 +1,131 @@ +package io.eventlens.api.routes; + +import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.security.RouteAuthorizer; +import io.eventlens.core.audit.AuditEvent; +import io.eventlens.core.audit.AuditLogger; +import io.eventlens.core.metadata.ApiKeyRecord; +import io.eventlens.core.security.ApiKeyService; +import io.eventlens.core.security.Permission; +import io.javalin.http.Context; + +import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class ApiKeyRoutes { + + private final ApiKeyService apiKeyService; + private final RouteAuthorizer routeAuthorizer; + private final AuditLogger auditLogger; + + public ApiKeyRoutes(ApiKeyService apiKeyService, RouteAuthorizer routeAuthorizer, AuditLogger auditLogger) { + this.apiKeyService = apiKeyService; + this.routeAuthorizer = routeAuthorizer; + this.auditLogger = auditLogger; + } + + public void list(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.MANAGE_API_KEYS, null, null)) { + return; + } + List> entries = apiKeyService.list().stream() + .map(ApiKeyRoutes::toPayload) + .toList(); + ctx.json(Map.of("entries", entries)); + } + + public void create(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.MANAGE_API_KEYS, null, null)) { + return; + } + + CreateApiKeyRequest request = ctx.bodyAsClass(CreateApiKeyRequest.class); + if (request == null || blank(request.principalUserId())) { + ctx.status(400).json(Map.of("error", "principal_user_id_required")); + return; + } + if (request.roles() == null || request.roles().isEmpty()) { + ctx.status(400).json(Map.of("error", "roles_required")); + return; + } + + Instant expiresAt = null; + if (!blank(request.expiresAt())) { + try { + expiresAt = Instant.parse(request.expiresAt()); + } catch (Exception e) { + ctx.status(400).json(Map.of("error", "invalid_expires_at")); + return; + } + } + + ApiKeyService.IssuedApiKey issued = apiKeyService.issue( + request.principalUserId().trim(), + request.roles().stream().filter(role -> role != null && !role.isBlank()).map(String::trim).toList(), + blank(request.description()) ? null : request.description().trim(), + expiresAt); + + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_CREATE_API_KEY) + .resourceType(AuditEvent.RT_AUTH) + .resourceId(issued.apiKeyId()) + .details(Map.of( + "principalUserId", issued.principalUserId(), + "roles", issued.scopes(), + "keyPrefix", issued.keyPrefix() + )) + .build()); + + Map response = new LinkedHashMap<>(); + response.put("apiKeyId", issued.apiKeyId()); + response.put("keyPrefix", issued.keyPrefix()); + response.put("apiKey", issued.apiKey()); + response.put("description", issued.description()); + response.put("principalUserId", issued.principalUserId()); + response.put("roles", issued.scopes()); + response.put("createdAt", issued.createdAt()); + response.put("expiresAt", issued.expiresAt()); + ctx.status(201).json(response); + } + + public void revoke(Context ctx) { + if (!routeAuthorizer.require(ctx, Permission.MANAGE_API_KEYS, null, null)) { + return; + } + + String apiKeyId = ctx.pathParam("id"); + apiKeyService.revoke(apiKeyId, Instant.now()); + + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_REVOKE_API_KEY) + .resourceType(AuditEvent.RT_AUTH) + .resourceId(apiKeyId) + .details(Map.of("path", ctx.path())) + .build()); + + ctx.json(Map.of("apiKeyId", apiKeyId, "revoked", true)); + } + + private static Map toPayload(ApiKeyRecord record) { + Map payload = new LinkedHashMap<>(); + payload.put("apiKeyId", record.apiKeyId()); + payload.put("keyPrefix", record.keyPrefix()); + payload.put("description", record.description()); + payload.put("principalUserId", record.principalUserId()); + payload.put("roles", record.scopes()); + payload.put("createdAt", record.createdAt()); + payload.put("expiresAt", record.expiresAt()); + payload.put("revokedAt", record.revokedAt()); + payload.put("lastUsedAt", record.lastUsedAt()); + return payload; + } + + private static boolean blank(String value) { + return value == null || value.isBlank(); + } + + public record CreateApiKeyRequest(String principalUserId, List roles, String description, String expiresAt) { + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java b/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java new file mode 100644 index 0000000..131aed5 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java @@ -0,0 +1,43 @@ +package io.eventlens.api.security; + +import io.eventlens.core.metadata.ApiKeyRecord; +import io.eventlens.core.security.ApiKeyService; +import io.eventlens.core.security.Principal; +import io.javalin.http.Context; + +import java.util.Optional; +import java.util.Set; + +public final class ApiKeyAuthenticator implements RequestAuthenticator { + + private final ApiKeyService apiKeyService; + private final String headerName; + + public ApiKeyAuthenticator(ApiKeyService apiKeyService, String headerName) { + this.apiKeyService = apiKeyService; + this.headerName = headerName; + } + + @Override + public AuthenticationResult authenticate(Context ctx) { + String rawApiKey = ctx.header(headerName); + if (rawApiKey == null || rawApiKey.isBlank()) { + return AuthenticationResult.failure(null, "missing_api_key", null); + } + + Optional record = apiKeyService.authenticate(rawApiKey); + if (record.isEmpty()) { + return AuthenticationResult.failure(null, "invalid_api_key", null); + } + + ApiKeyRecord apiKey = record.get(); + Principal principal = new Principal( + apiKey.principalUserId(), + apiKey.principalUserId(), + "api-key", + Set.copyOf(apiKey.scopes()), + true + ); + return AuthenticationResult.success(principal); + } +} diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index 0268aa9..316a5b8 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -478,6 +478,130 @@ void auditEndpointRejectsUserWithoutAuditPermission() throws Exception { assertThat(response.body()).contains("\"permission\":\"VIEW_AUDIT_LOG\""); } + @Test + void apiKeyCanAuthenticateRequestsAfterAdminCreation() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuth().getApiKeys().setEnabled(true); + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("key-admin"))); + + var adminRole = new EventLensConfig.RoleConfig(); + adminRole.setId("key-admin"); + adminRole.setPermissions(List.of("SEARCH_AGGREGATES", "MANAGE_API_KEYS")); + + var apiReader = new EventLensConfig.RoleConfig(); + apiReader.setId("api-reader"); + apiReader.setPermissions(List.of("SEARCH_AGGREGATES")); + + cfg.getSecurity().getAuthorization().setRoles(List.of(adminRole, apiReader)); + }); + + var client = HttpClient.newHttpClient(); + var createRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/admin/api-keys".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(""" + {"principalUserId":"svc-orders","roles":["api-reader"],"description":"Orders automation"} + """)) + .build(); + HttpResponse createResponse = client.send(createRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(createResponse.statusCode()).isEqualTo(201); + String apiKey = extractJsonField(createResponse.body(), "apiKey"); + assertThat(apiKey).startsWith("el_"); + + var searchRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("X-API-Key", apiKey) + .GET() + .build(); + HttpResponse searchResponse = client.send(searchRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(searchResponse.statusCode()).isEqualTo(200); + assertThat(searchResponse.body()).contains("ORD-1001"); + + var listRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/admin/api-keys".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + HttpResponse listResponse = client.send(listRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(listResponse.statusCode()).isEqualTo(200); + assertThat(listResponse.body()).contains("\"principalUserId\":\"svc-orders\""); + assertThat(listResponse.body()).contains("\"lastUsedAt\""); + assertThat(listResponse.body()).doesNotContain(apiKey); + } + + @Test + void invalidApiKeyHeaderDoesNotFallBackToBasicAuth() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> cfg.getSecurity().getAuth().getApiKeys().setEnabled(true)); + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("X-API-Key", "el_test.invalid") + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(401); + } + + @Test + void revokedApiKeyCanNoLongerAuthenticate() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuth().getApiKeys().setEnabled(true); + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("key-admin"))); + + var adminRole = new EventLensConfig.RoleConfig(); + adminRole.setId("key-admin"); + adminRole.setPermissions(List.of("SEARCH_AGGREGATES", "MANAGE_API_KEYS")); + + var apiReader = new EventLensConfig.RoleConfig(); + apiReader.setId("api-reader"); + apiReader.setPermissions(List.of("SEARCH_AGGREGATES")); + + cfg.getSecurity().getAuthorization().setRoles(List.of(adminRole, apiReader)); + }); + + var client = HttpClient.newHttpClient(); + var createRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/admin/api-keys".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(""" + {"principalUserId":"svc-orders","roles":["api-reader"],"description":"Orders automation"} + """)) + .build(); + HttpResponse createResponse = client.send(createRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(createResponse.statusCode()).isEqualTo(201); + String apiKey = extractJsonField(createResponse.body(), "apiKey"); + String apiKeyId = extractJsonField(createResponse.body(), "apiKeyId"); + + var revokeRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/admin/api-keys/%s/revoke".formatted(port, apiKeyId))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse revokeResponse = client.send(revokeRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(revokeResponse.statusCode()).isEqualTo(200); + + var searchRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("X-API-Key", apiKey) + .GET() + .build(); + HttpResponse searchResponse = client.send(searchRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(searchResponse.statusCode()).isEqualTo(401); + } + private EventLensServer startServer(int port, boolean authEnabled) { return startServer(port, authEnabled, null, cfg -> {}); } diff --git a/eventlens-core/build.gradle.kts b/eventlens-core/build.gradle.kts index 95a6731..03d5942 100644 --- a/eventlens-core/build.gradle.kts +++ b/eventlens-core/build.gradle.kts @@ -6,4 +6,5 @@ dependencies { implementation("com.github.ben-manes.caffeine:caffeine:3.2.3") implementation("com.zaxxer:HikariCP:5.1.0") implementation("org.xerial:sqlite-jdbc:3.50.3.0") + implementation("de.mkammerer:argon2-jvm:2.12") } diff --git a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java index 51b7f55..f9f9927 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java +++ b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java @@ -261,6 +261,7 @@ private static void validateSecurityAuth(EventLensConfig config, List scopes) { this.scopes = scopes == null ? List.of() : scopes; } } + public static class ApiKeysConfig { + private boolean enabled = false; + private String headerName = "X-API-Key"; + private String keyPrefix = "el"; + + public boolean isEnabled() { return enabled; } + public void setEnabled(boolean enabled) { this.enabled = enabled; } + public String getHeaderName() { return headerName; } + public void setHeaderName(String headerName) { this.headerName = headerName; } + public String getKeyPrefix() { return keyPrefix; } + public void setKeyPrefix(String keyPrefix) { this.keyPrefix = keyPrefix; } + } + public static class MetadataConfig { private boolean enabled = false; private String jdbcUrl = "jdbc:sqlite:./data/eventlens-metadata.db"; diff --git a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java index 31caeb5..860cf61 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java +++ b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditEvent.java @@ -29,6 +29,8 @@ public record AuditEvent( public static final String ACTION_VIEW_ANOMALIES = "VIEW_ANOMALIES"; public static final String ACTION_EXPORT = "EXPORT"; public static final String ACTION_REVEAL_PII = "REVEAL_PII"; + public static final String ACTION_CREATE_API_KEY = "CREATE_API_KEY"; + public static final String ACTION_REVOKE_API_KEY = "REVOKE_API_KEY"; public static final String ACTION_LOGIN = "LOGIN"; public static final String ACTION_LOGIN_FAILED = "LOGIN_FAILED"; public static final String ACTION_VIEW_LIVE_STREAM = "VIEW_LIVE_STREAM"; diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java b/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java new file mode 100644 index 0000000..5cde951 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java @@ -0,0 +1,162 @@ +package io.eventlens.core.security; + +import de.mkammerer.argon2.Argon2; +import de.mkammerer.argon2.Argon2Factory; +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.metadata.ApiKeyRecord; +import io.eventlens.core.metadata.ApiKeyRepository; + +import java.security.SecureRandom; +import java.time.Instant; +import java.util.Base64; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public final class ApiKeyService { + + private static final int ARGON2_ITERATIONS = 2; + private static final int ARGON2_MEMORY_KB = 19_456; + private static final int ARGON2_PARALLELISM = 1; + + private final ApiKeyRepository repository; + private final EventLensConfig.ApiKeysConfig config; + private final SecureRandom secureRandom = new SecureRandom(); + + public ApiKeyService(ApiKeyRepository repository, EventLensConfig.ApiKeysConfig config) { + this.repository = repository; + this.config = config == null ? new EventLensConfig.ApiKeysConfig() : config; + } + + public IssuedApiKey issue(String principalUserId, List roleScopes, String description, Instant expiresAt) { + Instant now = Instant.now(); + String apiKeyId = UUID.randomUUID().toString(); + String keyPrefix = nextKeyPrefix(); + String secret = randomToken(32); + String rawApiKey = keyPrefix + "." + secret; + String keyHash = hash(rawApiKey); + + ApiKeyRecord record = new ApiKeyRecord( + apiKeyId, + keyPrefix, + keyHash, + description, + principalUserId, + roleScopes == null ? List.of() : List.copyOf(roleScopes), + now, + expiresAt, + null, + null + ); + repository.insert(record); + return new IssuedApiKey( + apiKeyId, + keyPrefix, + rawApiKey, + description, + principalUserId, + record.scopes(), + now, + expiresAt + ); + } + + public Optional authenticate(String rawApiKey) { + if (rawApiKey == null || rawApiKey.isBlank()) { + return Optional.empty(); + } + + String prefix = extractPrefix(rawApiKey); + if (prefix == null) { + return Optional.empty(); + } + + Optional loaded = repository.findByPrefix(prefix); + if (loaded.isEmpty()) { + return Optional.empty(); + } + + ApiKeyRecord record = loaded.get(); + if (record.revokedAt() != null) { + return Optional.empty(); + } + if (record.expiresAt() != null && record.expiresAt().isBefore(Instant.now())) { + return Optional.empty(); + } + if (!verify(record.keyHash(), rawApiKey)) { + return Optional.empty(); + } + + Instant lastUsedAt = Instant.now(); + repository.markUsed(record.apiKeyId(), lastUsedAt); + return Optional.of(new ApiKeyRecord( + record.apiKeyId(), + record.keyPrefix(), + record.keyHash(), + record.description(), + record.principalUserId(), + record.scopes(), + record.createdAt(), + record.expiresAt(), + record.revokedAt(), + lastUsedAt + )); + } + + public List list() { + return repository.list(); + } + + public void revoke(String apiKeyId, Instant revokedAt) { + repository.revoke(apiKeyId, revokedAt); + } + + private String nextKeyPrefix() { + return config.getKeyPrefix() + "_" + randomToken(9); + } + + private String randomToken(int bytes) { + byte[] raw = new byte[bytes]; + secureRandom.nextBytes(raw); + return Base64.getUrlEncoder().withoutPadding().encodeToString(raw); + } + + private static String extractPrefix(String rawApiKey) { + int separator = rawApiKey.indexOf('.'); + if (separator <= 0) { + return null; + } + return rawApiKey.substring(0, separator); + } + + private static String hash(String rawApiKey) { + Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id); + char[] chars = rawApiKey.toCharArray(); + try { + return argon2.hash(ARGON2_ITERATIONS, ARGON2_MEMORY_KB, ARGON2_PARALLELISM, chars); + } finally { + argon2.wipeArray(chars); + } + } + + private static boolean verify(String encodedHash, String rawApiKey) { + Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id); + char[] chars = rawApiKey.toCharArray(); + try { + return argon2.verify(encodedHash, chars); + } finally { + argon2.wipeArray(chars); + } + } + + public record IssuedApiKey( + String apiKeyId, + String keyPrefix, + String apiKey, + String description, + String principalUserId, + List scopes, + Instant createdAt, + Instant expiresAt) { + } +} diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java index 1041d0f..bc8468a 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java +++ b/eventlens-core/src/main/java/io/eventlens/core/security/Permission.java @@ -18,6 +18,7 @@ public enum Permission { VIEW_STATISTICS, VIEW_METRICS, VIEW_AUDIT_LOG, + MANAGE_API_KEYS, VIEW_OPENAPI, EXECUTE_BISECT; diff --git a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java index 38cddee..64b2ae1 100644 --- a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java +++ b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java @@ -90,6 +90,19 @@ void sessionCookieNoneRequiresSecureCookie() { && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); } + @Test + void apiKeysRequireMetadataAndHeaderNameWhenEnabled() { + var cfg = new EventLensConfig(); + cfg.getSecurity().getAuth().getApiKeys().setEnabled(true); + cfg.getSecurity().getAuth().getApiKeys().setHeaderName(" "); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.metadata.enabled") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.auth.api-keys.header-name") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } + @Test void authorizationRequiresKnownRolesAndPermissions() { var cfg = new EventLensConfig(); diff --git a/eventlens-core/src/test/java/io/eventlens/core/security/ApiKeyServiceTest.java b/eventlens-core/src/test/java/io/eventlens/core/security/ApiKeyServiceTest.java new file mode 100644 index 0000000..ad0480a --- /dev/null +++ b/eventlens-core/src/test/java/io/eventlens/core/security/ApiKeyServiceTest.java @@ -0,0 +1,91 @@ +package io.eventlens.core.security; + +import io.eventlens.core.EventLensConfig; +import io.eventlens.core.metadata.MetadataDatabase; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; +import java.time.Instant; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class ApiKeyServiceTest { + + @TempDir + Path tempDir; + + @Test + void issuePersistsHashedKeyAndAuthenticateMarksUsage() { + try (MetadataDatabase database = MetadataDatabase.open(metadataConfig("api-keys.db"))) { + var service = new ApiKeyService( + database.repositories().apiKeys(), + apiKeyConfig()); + + ApiKeyService.IssuedApiKey issued = service.issue( + "svc-orders", + List.of("api-reader"), + "Orders automation", + null); + + assertThat(issued.apiKey()).startsWith(issued.keyPrefix() + "."); + assertThat(database.repositories().apiKeys().findByPrefix(issued.keyPrefix())) + .isPresent() + .get() + .satisfies(record -> { + assertThat(record.keyHash()).doesNotContain(issued.apiKey()); + assertThat(record.principalUserId()).isEqualTo("svc-orders"); + assertThat(record.scopes()).containsExactly("api-reader"); + }); + + assertThat(service.authenticate(issued.apiKey())) + .isPresent() + .get() + .satisfies(record -> { + assertThat(record.apiKeyId()).isEqualTo(issued.apiKeyId()); + assertThat(record.lastUsedAt()).isNotNull(); + }); + } + } + + @Test + void revokedAndExpiredKeysCannotAuthenticate() { + try (MetadataDatabase database = MetadataDatabase.open(metadataConfig("api-keys-revoked.db"))) { + var service = new ApiKeyService( + database.repositories().apiKeys(), + apiKeyConfig()); + + ApiKeyService.IssuedApiKey expired = service.issue( + "svc-expired", + List.of("api-reader"), + "Expired key", + Instant.now().minusSeconds(60)); + assertThat(service.authenticate(expired.apiKey())).isEmpty(); + + ApiKeyService.IssuedApiKey active = service.issue( + "svc-active", + List.of("api-reader"), + "Active key", + Instant.now().plusSeconds(3600)); + service.revoke(active.apiKeyId(), Instant.now()); + + assertThat(service.authenticate(active.apiKey())).isEmpty(); + } + } + + private EventLensConfig.MetadataConfig metadataConfig(String fileName) { + EventLensConfig.MetadataConfig config = new EventLensConfig.MetadataConfig(); + config.setEnabled(true); + config.setJdbcUrl("jdbc:sqlite:" + tempDir.resolve(fileName)); + return config; + } + + private EventLensConfig.ApiKeysConfig apiKeyConfig() { + EventLensConfig.ApiKeysConfig config = new EventLensConfig.ApiKeysConfig(); + config.setEnabled(true); + config.setKeyPrefix("el_test"); + config.setHeaderName("X-API-Key"); + return config; + } +} From b6fff49747ea81826670a7160f6a6ea56c93e0b2 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 18:27:22 +0200 Subject: [PATCH 09/11] feat: add security admin workspace --- eventlens-ui/src/App.tsx | 18 +- eventlens-ui/src/api/client.ts | 64 +++++ eventlens-ui/src/components/AdminConsole.tsx | 234 ++++++++++++++++++ .../src/components/CommandPalette.tsx | 8 +- eventlens-ui/src/index.css | 154 ++++++++++++ eventlens-ui/src/utils/apiErrors.test.ts | 29 +++ eventlens-ui/src/utils/apiErrors.ts | 34 +++ 7 files changed, 537 insertions(+), 4 deletions(-) create mode 100644 eventlens-ui/src/components/AdminConsole.tsx create mode 100644 eventlens-ui/src/utils/apiErrors.test.ts create mode 100644 eventlens-ui/src/utils/apiErrors.ts diff --git a/eventlens-ui/src/App.tsx b/eventlens-ui/src/App.tsx index b7335f1..2fc85ca 100644 --- a/eventlens-ui/src/App.tsx +++ b/eventlens-ui/src/App.tsx @@ -10,6 +10,7 @@ import ReplayDebugger from './components/ReplayDebugger'; import StatisticsPanel from './components/StatisticsPanel'; import CommandPalette from './components/CommandPalette'; import KeyboardManager from './components/KeyboardManager'; +import AdminConsole from './components/AdminConsole'; import { buildOidcLoginUrl, getAuthSession, @@ -251,6 +252,7 @@ export default function App() { const isUp = health?.status === 'UP'; const statsView = currentHash === '#/stats'; + const adminView = currentHash === '#/admin'; const healthySources = datasources.filter(source => isHealthyStatus(source.status)).length; const healthyPlugins = plugins.filter(plugin => isHealthyStatus(plugin.lifecycle)).length; const issueCount = (datasources.length - healthySources) + (plugins.length - healthyPlugins); @@ -267,6 +269,10 @@ export default function App() { window.location.hash = '#/timeline'; }; + const openAdminPage = () => { + window.location.hash = '#/admin'; + }; + const handleLogin = async (event: FormEvent) => { event.preventDefault(); setAuthError(null); @@ -398,6 +404,7 @@ export default function App() { onSelectAggregate={openAggregate} onOpenHome={openMainPage} onOpenStats={() => { window.location.hash = '#/stats'; }} + onOpenAdmin={openAdminPage} />
@@ -417,6 +424,13 @@ export default function App() { Signed in {authQuery.data.principal.displayName}
+ @@ -471,7 +485,7 @@ export default function App() {
- {!statsView && ( + {!statsView && !adminView && (
@@ -489,6 +503,8 @@ export default function App() { {statsView ? ( + ) : adminView ? ( + ) : ( <> {selectedAggregate && ( diff --git a/eventlens-ui/src/api/client.ts b/eventlens-ui/src/api/client.ts index 4905c7c..6c7612b 100644 --- a/eventlens-ui/src/api/client.ts +++ b/eventlens-ui/src/api/client.ts @@ -57,6 +57,54 @@ export interface AuthSessionResponse { csrfToken?: string; } +export interface AuditEntry { + auditId: number; + action: string; + resourceType: string; + resourceId: string | null; + userId: string; + authMethod: string; + clientIp: string | null; + requestId: string | null; + userAgent: string | null; + details: Record; + createdAt: string; +} + +export interface AuditEntriesResponse { + entries: AuditEntry[]; + limit: number; + action?: string | null; + userId?: string | null; +} + +export interface ManagedApiKey { + apiKeyId: string; + keyPrefix: string; + description?: string | null; + principalUserId: string; + roles: string[]; + createdAt: string; + expiresAt?: string | null; + revokedAt?: string | null; + lastUsedAt?: string | null; +} + +export interface ManagedApiKeysResponse { + entries: ManagedApiKey[]; +} + +export interface CreateApiKeyRequest { + principalUserId: string; + roles: string[]; + description?: string; + expiresAt?: string; +} + +export interface CreateApiKeyResponse extends ManagedApiKey { + apiKey: string; +} + function delay(ms: number) { return new Promise(resolve => { setTimeout(resolve, ms); @@ -211,6 +259,22 @@ export const getAuthSession = async () => { return api.get('/v1/auth/session').then(r => r.data); }; +export const getAuditEntries = async (limit = 25) => { + return api.get(`/v1/audit?limit=${limit}`).then(r => r.data); +}; + +export const getManagedApiKeys = async () => { + return api.get('/v1/admin/api-keys').then(r => r.data); +}; + +export const createManagedApiKey = async (payload: CreateApiKeyRequest) => { + return api.post('/v1/admin/api-keys', payload).then(r => r.data); +}; + +export const revokeManagedApiKey = async (apiKeyId: string) => { + return api.post<{ apiKeyId: string; revoked: boolean }>(`/v1/admin/api-keys/${encodeURIComponent(apiKeyId)}/revoke`).then(r => r.data); +}; + export const loginWithBasicSession = async (username: string, password: string, returnHash: string) => { return api.post( '/v1/auth/login/basic', diff --git a/eventlens-ui/src/components/AdminConsole.tsx b/eventlens-ui/src/components/AdminConsole.tsx new file mode 100644 index 0000000..b93abd9 --- /dev/null +++ b/eventlens-ui/src/components/AdminConsole.tsx @@ -0,0 +1,234 @@ +import { FormEvent, useState } from 'react'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + createManagedApiKey, + getAuditEntries, + getManagedApiKeys, + revokeManagedApiKey, +} from '../api/client'; +import { useToast } from './ToastProvider'; +import { describeApiError } from '../utils/apiErrors'; + +function SecurityStateCard({ + title, + detail, + variant = 'error', +}: { + title: string; + detail: string; + variant?: 'error' | 'warning'; +}) { + return ( +
+ {title} +

{detail}

+
+ ); +} + +export default function AdminConsole() { + const queryClient = useQueryClient(); + const { notify } = useToast(); + const [principalUserId, setPrincipalUserId] = useState(''); + const [roles, setRoles] = useState('api-reader'); + const [description, setDescription] = useState(''); + const [expiresAt, setExpiresAt] = useState(''); + const [issuedKey, setIssuedKey] = useState<{ apiKey: string; keyPrefix: string } | null>(null); + + const auditQuery = useQuery({ + queryKey: ['admin-audit'], + queryFn: () => getAuditEntries(25), + retry: false, + refetchOnWindowFocus: false, + }); + + const apiKeysQuery = useQuery({ + queryKey: ['admin-api-keys'], + queryFn: getManagedApiKeys, + retry: false, + refetchOnWindowFocus: false, + }); + + const createKeyMutation = useMutation({ + mutationFn: createManagedApiKey, + onSuccess: async created => { + setIssuedKey({ apiKey: created.apiKey, keyPrefix: created.keyPrefix }); + setPrincipalUserId(''); + setRoles('api-reader'); + setDescription(''); + setExpiresAt(''); + notify(`Created API key ${created.keyPrefix}`); + await queryClient.invalidateQueries({ queryKey: ['admin-api-keys'] }); + await queryClient.invalidateQueries({ queryKey: ['admin-audit'] }); + }, + }); + + const revokeKeyMutation = useMutation({ + mutationFn: revokeManagedApiKey, + onSuccess: async revoked => { + notify(`Revoked API key ${revoked.apiKeyId}`); + await queryClient.invalidateQueries({ queryKey: ['admin-api-keys'] }); + await queryClient.invalidateQueries({ queryKey: ['admin-audit'] }); + }, + }); + + const handleCreateKey = async (event: FormEvent) => { + event.preventDefault(); + setIssuedKey(null); + await createKeyMutation.mutateAsync({ + principalUserId: principalUserId.trim(), + roles: roles.split(',').map(role => role.trim()).filter(Boolean), + description: description.trim() || undefined, + expiresAt: expiresAt.trim() || undefined, + }); + }; + + const auditError = auditQuery.error ? describeApiError(auditQuery.error) : null; + const apiKeysError = apiKeysQuery.error ? describeApiError(apiKeysQuery.error) : null; + const createError = createKeyMutation.error ? describeApiError(createKeyMutation.error) : null; + + return ( +
+
+
Security Administration
+

+ Review recent security activity, issue machine credentials, and revoke keys without leaving the current EventLens workspace. +

+
+ +
+
+
Recent Audit Activity
+ {auditQuery.isLoading &&

Loading the latest audit entries from metadata-backed storage.

} + {auditError?.status === 403 && ( + + )} + {auditError && auditError.status !== 403 && ( + + )} + {!auditQuery.isLoading && !auditError && ( +
+ + + + + + + + + + + + {auditQuery.data?.entries.map(entry => ( + + + + + + + + ))} + +
WhenActionActorAuthResource
{new Date(entry.createdAt).toLocaleString()}{entry.action}{entry.userId}{entry.authMethod}{entry.resourceType}{entry.resourceId ? `:${entry.resourceId}` : ''}
+
+ )} +
+ +
+
API Key Management
+

+ Issue one-time machine credentials tied to existing RBAC roles. Raw keys are shown once and never listed again. +

+ + {apiKeysError?.status === 403 ? ( + + ) : apiKeysError ? ( + + ) : ( + <> +
+ + + + + {createError && } + + + + {issuedKey && ( +
+ Copy this key now. +

{issuedKey.apiKey}

+ Stored prefix: {issuedKey.keyPrefix} +
+ )} + +
+ + + + + + + + + + + + {apiKeysQuery.data?.entries.map(entry => ( + + + + + + + + + ))} + +
PrefixPrincipalRolesLast usedStatus +
{entry.keyPrefix}{entry.principalUserId}{entry.roles.join(', ')}{entry.lastUsedAt ? new Date(entry.lastUsedAt).toLocaleString() : 'Never'}{entry.revokedAt ? 'Revoked' : entry.expiresAt ? `Expires ${new Date(entry.expiresAt).toLocaleString()}` : 'Active'} + {!entry.revokedAt && ( + + )} +
+
+ + )} +
+
+
+ ); +} diff --git a/eventlens-ui/src/components/CommandPalette.tsx b/eventlens-ui/src/components/CommandPalette.tsx index 6e1671c..aebc9e6 100644 --- a/eventlens-ui/src/components/CommandPalette.tsx +++ b/eventlens-ui/src/components/CommandPalette.tsx @@ -9,6 +9,7 @@ interface Props { onSelectAggregate: (id: string) => void; onOpenHome: () => void; onOpenStats: () => void; + onOpenAdmin: () => void; } interface CommandItem { @@ -17,7 +18,7 @@ interface CommandItem { action: () => void; } -export default function CommandPalette({ open, selectedSource, onClose, onSelectAggregate, onOpenHome, onOpenStats }: Props) { +export default function CommandPalette({ open, selectedSource, onClose, onSelectAggregate, onOpenHome, onOpenStats, onOpenAdmin }: Props) { const [query, setQuery] = useState(''); const [selectedIndex, setSelectedIndex] = useState(0); const [previousFocus, setPreviousFocus] = useState(null); @@ -42,6 +43,7 @@ export default function CommandPalette({ open, selectedSource, onClose, onSelect const base: CommandItem[] = [ { id: 'home', label: 'Go to main page', action: onOpenHome }, { id: 'stats', label: 'Go to statistics panel', action: onOpenStats }, + { id: 'admin', label: 'Open security administration', action: onOpenAdmin }, ]; const aggregateCommands = aggregateResults.map(id => ({ id: `agg-${id}`, @@ -52,7 +54,7 @@ export default function CommandPalette({ open, selectedSource, onClose, onSelect base.unshift({ id: 'direct', label: `Open aggregate ${query.trim()}`, action: () => onSelectAggregate(query.trim()) }); } return [...aggregateCommands, ...base]; - }, [aggregateResults, onOpenHome, onOpenStats, onSelectAggregate, query]); + }, [aggregateResults, onOpenAdmin, onOpenHome, onOpenStats, onSelectAggregate, query]); useEffect(() => { if (!open) return; @@ -78,7 +80,7 @@ export default function CommandPalette({ open, selectedSource, onClose, onSelect return (
e.stopPropagation()}> -

Search aggregates, return to the main page, or open statistics. Use arrow keys to move and Enter to confirm.

+

Search aggregates, return to the main page, open statistics, or open security administration. Use arrow keys to move and Enter to confirm.

{ + it('extracts structured fields from axios-style errors', () => { + const details = describeApiError({ + isAxiosError: true, + message: 'Request failed with status code 403', + response: { + status: 403, + data: { + error: 'forbidden', + reason: 'DENY_MISSING_PERMISSION', + permission: 'VIEW_AUDIT_LOG', + }, + }, + }); + + expect(details.status).toBe(403); + expect(details.error).toBe('forbidden'); + expect(details.reason).toBe('DENY_MISSING_PERMISSION'); + expect(details.permission).toBe('VIEW_AUDIT_LOG'); + }); + + it('falls back to error message for non-axios errors', () => { + const details = describeApiError(new Error('boom')); + expect(details.message).toBe('boom'); + }); +}); diff --git a/eventlens-ui/src/utils/apiErrors.ts b/eventlens-ui/src/utils/apiErrors.ts new file mode 100644 index 0000000..da6dfb9 --- /dev/null +++ b/eventlens-ui/src/utils/apiErrors.ts @@ -0,0 +1,34 @@ +import axios from 'axios'; + +export interface ApiErrorDetails { + status?: number; + error?: string; + reason?: string; + permission?: string; + message: string; +} + +export function describeApiError(error: unknown): ApiErrorDetails { + if (axios.isAxiosError(error)) { + const status = error.response?.status; + const data = (error.response?.data ?? {}) as Record; + return { + status, + error: typeof data.error === 'string' ? data.error : undefined, + reason: typeof data.reason === 'string' ? data.reason : undefined, + permission: typeof data.permission === 'string' ? data.permission : undefined, + message: + typeof data.message === 'string' + ? data.message + : typeof data.error === 'string' + ? data.error + : error.message || 'Request failed', + }; + } + + if (error instanceof Error) { + return { message: error.message }; + } + + return { message: 'Request failed' }; +} From ce55eaf597c3514e8651bdf3d7aafb03f7cc019b Mon Sep 17 00:00:00 2001 From: Ahmed Date: Fri, 27 Mar 2026 18:43:08 +0200 Subject: [PATCH 10/11] feat: harden security release guardrails --- .github/workflows/release.yml | 15 ++++- README.md | 18 +++++- SECURITY.md | 14 +++- build.gradle.kts | 7 ++ .../io/eventlens/api/EventLensServer.java | 17 +++-- .../eventlens/api/health/HealthService.java | 31 +++++++-- .../api/metrics/EventLensMetrics.java | 64 ++++++++++++++++++- .../io/eventlens/api/routes/ApiKeyRoutes.java | 3 + .../api/routes/AsyncExportRoutes.java | 4 ++ .../io/eventlens/api/routes/AuthRoutes.java | 6 ++ .../io/eventlens/api/routes/ExportRoutes.java | 2 + .../io/eventlens/api/routes/HealthRoutes.java | 7 +- .../eventlens/api/routes/OidcAuthRoutes.java | 8 +++ .../eventlens/api/routes/PiiRevealRoutes.java | 4 ++ .../api/security/ApiKeyAuthenticator.java | 2 + .../api/security/RouteAuthorizer.java | 3 + .../api/security/SessionAuthenticator.java | 2 + .../api/AuthenticationIntegrationTest.java | 62 +++++++++++++++++- .../java/io/eventlens/cli/ServeCommand.java | 4 ++ .../io/eventlens/core/ConfigValidator.java | 57 +++++++++++++++++ .../io/eventlens/core/EventLensConfig.java | 3 + .../io/eventlens/core/audit/AuditLogger.java | 11 +++- .../core/metadata/SessionRepository.java | 15 +++++ .../core/security/SessionService.java | 4 ++ .../src/main/resources/eventlens-example.yaml | 27 +++++++- .../eventlens/core/ConfigValidatorTest.java | 31 +++++++++ eventlens.yaml.example | 18 ++++++ 27 files changed, 418 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df05a45..759071a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,11 +36,24 @@ jobs: - name: Build the Fat JAR run: ./gradlew clean :eventlens-app:shadowJar --no-daemon + + - name: Generate SBOM and dependency report + run: ./gradlew sbom dependencyScan --no-daemon + + - name: Upload release security artifacts + uses: actions/upload-artifact@v4 + with: + name: release-security-artifacts + path: | + build/reports/** + build/libs/** - name: Create Release uses: softprops/action-gh-release@v2 with: - files: eventlens-app/build/libs/eventlens*.jar + files: | + eventlens-app/build/libs/eventlens*.jar + build/reports/** generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 83bed9f..5be50db 100644 --- a/README.md +++ b/README.md @@ -614,8 +614,13 @@ EventLens primarily runs **read-only queries** against your event store. To keep - **Health endpoint**: `GET /api/health` - Verifies: - DB connectivity, - - event store statistics, - - Kafka consumer status (if enabled). + - metadata DB connectivity when v5 security metadata is enabled, + - event store statistics. +- **Readiness endpoints**: + - `GET /api/v1/health/live` + - `GET /api/v1/health/ready` +- **Metrics endpoint**: `GET /api/v1/metrics` + - Includes JVM metrics plus security counters for auth attempts, authz denials, session lifecycle, sensitive actions, API key lifecycle, and audit persistence failures. - **Logging**: - Controlled via `logback.xml` in `eventlens-core`. - Includes: @@ -649,9 +654,16 @@ In containerized environments, direct logs to stdout/stderr and collect them via - Configure a **read-only** Postgres user in your environment. - Add or adjust indexes to match your event table shape. -- Enable basic auth **behind an HTTPS reverse proxy** in `eventlens.yaml` for production deployments. +- For shared environments, set `security.production-mode: true`, configure explicit `server.allowed-origins`, and keep metadata/audit enabled for OIDC, RBAC, API keys, or PII reveal. +- Enable auth **behind an HTTPS reverse proxy** in `eventlens.yaml` for production deployments. - Build and run the Docker image alongside your own PostgreSQL/Kafka infrastructure, or reuse the provided `docker-compose.yml` for local debugging. +## 8. Release Security Artifacts + +- Generate an OWASP dependency report with `./gradlew dependencyScan` +- Generate a CycloneDX SBOM with `./gradlew sbom` +- Release CI attaches these artifacts so operators can review the shipped dependency surface + --- ## Project Info diff --git a/SECURITY.md b/SECURITY.md index 4ac5229..831a73c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,12 +22,20 @@ Instead, please send an email to the maintainers or use GitHub Security Advisori EventLens is designed to be a safe, read-only window into your event store, but you should still follow these best practices when deploying it: 1. **Read-Only Database Access**: Ensure EventLens connects to your database using a read-only role, as it never mutates data. -2. **Authentication & HTTPS**: When exposing EventLens in a shared environment, enable basic authentication (`server.auth.enabled: true` in your configuration) and place EventLens behind a reverse proxy with TLS (HTTPS) enabled. -3. **CORS Restrictions**: For production deployments, change `server.allowed-origins` in your configuration to specifically whitelist the domains that are permitted to reach your dashboard. -4. **Regular Updates**: Keep your EventLens deployment updated to the latest version to receive security patches and updates to dependencies. +2. **Production Mode**: For shared environments, enable `security.production-mode: true`. This turns unsafe startup combinations into hard configuration failures instead of soft warnings. +3. **Authentication & HTTPS**: In shared environments, enable either legacy basic auth, OIDC browser sessions, or API keys, and place EventLens behind TLS (HTTPS). Production mode rejects completely unauthenticated deployments. +4. **CORS Restrictions**: In production mode, `server.allowed-origins` must be an explicit allowlist. Wildcard origins are rejected. +5. **Audit & Metadata**: Keep `audit.enabled: true` when using OIDC, RBAC, API keys, or PII reveal. Use file-backed SQLite metadata instead of in-memory metadata for persisted sessions, audit, and API keys. +6. **Regular Updates**: Keep your EventLens deployment updated to the latest version to receive security patches and updates to dependencies. ## Information about Dependencies EventLens strives to keep all dependencies and packages up to date. We rely on standard open-source tooling like Dependabot to alert us to potential problems in our upstream dependencies. If you are using a Docker image for EventLens, we periodically rebuild images to ensure the base images contain the latest security updates. Please make sure to pull the latest `alphasudo2/eventlens-app:latest` or specific version tag to stay protected. + +Release builds now generate: +- an OWASP dependency check report +- a CycloneDX SBOM (`./gradlew sbom`) + +These artifacts should be retained with the release so operators can review the exact dependency surface they are deploying. diff --git a/build.gradle.kts b/build.gradle.kts index db33f27..4b5df75 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ plugins { java id("org.owasp.dependencycheck") version "12.2.0" + id("org.cyclonedx.bom") version "3.2.0" } // Shared versions @@ -71,6 +72,12 @@ tasks.register("dependencyScan") { dependsOn("dependencyCheckAggregate") } +tasks.register("sbom") { + group = "verification" + description = "Generate a CycloneDX software bill of materials" + dependsOn("cyclonedxBom") +} + dependencyCheck { // Do not fail local builds if the NVD feed cannot be updated (e.g. no API key). failOnError = false diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index e4184e8..c4eade2 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -121,7 +121,8 @@ public EventLensServer( // ── 1.8 Audit Logger ────────────────────────────────────────────── final AuditLogger auditLogger = new AuditLogger( config.getAudit().isEnabled(), - this.metadataDatabase.isEnabled() ? this.metadataDatabase.repositories().auditLogs() : null); + this.metadataDatabase.isEnabled() ? this.metadataDatabase.repositories().auditLogs() : null, + () -> EventLensMetrics.recordAuditWriteFailure("metadata")); // ── 1.9 PII Masker ──────────────────────────────────────────────── final PiiMasker piiMasker = new PiiMasker( @@ -162,7 +163,7 @@ public EventLensServer( var anomalyRoutes = new AnomalyRoutes(sourceRegistry, config.getAnomaly(), auditLogger, routeAuthorizer, sensitiveDataProtector); var exportRoutes = new ExportRoutes(sourceRegistry, exportEngine, auditLogger, routeAuthorizer, sensitiveDataProtector); var asyncExportRoutes = new AsyncExportRoutes(exportService, routeAuthorizer); - var healthRoutes = new HealthRoutes(reader, config.getVersion()); + var healthRoutes = new HealthRoutes(reader, config.getVersion(), this.metadataDatabase); var metricsRoutes = new MetricsRoutes(routeAuthorizer); var auditRoutes = new AuditRoutes(this.metadataDatabase.isEnabled() ? this.metadataDatabase.repositories().auditLogs() : null, routeAuthorizer); var openApiRoutes = new OpenApiRoutes(routeAuthorizer); @@ -193,6 +194,9 @@ public EventLensServer( var sessionService = this.metadataDatabase.isEnabled() ? new SessionService(this.metadataDatabase.repositories().sessions(), sessionConfig) : null; + if (sessionService != null) { + EventLensMetrics.bindActiveSessionsGauge(state -> ((SessionService) state).activeSessionCount(), sessionService); + } var sessionAuthenticator = sessionService != null ? new SessionAuthenticator(sessionService, sessionConfig.getCookieName()) : null; @@ -357,6 +361,7 @@ public EventLensServer( sessionConfig.getCookieName()); var authResult = authHolder.result(); if (!authResult.success()) { + EventLensMetrics.recordAuthAttempt(authHolder.method(), "failure"); // 1.8 - emit LOGIN_FAILED auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_LOGIN_FAILED) @@ -373,6 +378,7 @@ public EventLensServer( ctx.json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { + EventLensMetrics.recordAuthAttempt(authHolder.method(), "success"); SecurityContext.setPrincipal(ctx, authResult.principal()); new RequestContextMdcFilter().handle(ctx); if (isMutatingMethod(ctx.method().name()) && SecurityContext.session(ctx) != null) { @@ -394,14 +400,16 @@ public EventLensServer( } }); cfg.routes.before("/ws/*", ctx -> { - var authResult = resolveAuthentication( + var authHolder = resolveAuthentication( ctx, apiKeyAuthenticator, sessionAuthenticator, basicCompatibilityEnabled ? basicAuthenticator : null, apiKeysConfig != null ? apiKeysConfig.getHeaderName() : "X-API-Key", - sessionConfig.getCookieName()).result(); + sessionConfig.getCookieName()); + var authResult = authHolder.result(); if (!authResult.success()) { + EventLensMetrics.recordAuthAttempt(authHolder.method(), "failure"); ctx.status(401); if (authResult.challengeHeader() != null) { ctx.header("WWW-Authenticate", authResult.challengeHeader()); @@ -409,6 +417,7 @@ public EventLensServer( ctx.json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { + EventLensMetrics.recordAuthAttempt(authHolder.method(), "success"); SecurityContext.setPrincipal(ctx, authResult.principal()); new RequestContextMdcFilter().handle(ctx); } diff --git a/eventlens-api/src/main/java/io/eventlens/api/health/HealthService.java b/eventlens-api/src/main/java/io/eventlens/api/health/HealthService.java index 996654c..c69d345 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/health/HealthService.java +++ b/eventlens-api/src/main/java/io/eventlens/api/health/HealthService.java @@ -1,7 +1,9 @@ package io.eventlens.api.health; +import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.spi.EventStoreReader; +import java.sql.Statement; import java.time.Duration; import java.time.Instant; import java.util.HashMap; @@ -35,7 +37,7 @@ public static Map live(String version) { return body; } - public static Map ready(EventStoreReader reader, String version) { + public static Map ready(EventStoreReader reader, String version, MetadataDatabase metadataDatabase) { Map root = new HashMap<>(); if (shuttingDown) { @@ -67,9 +69,31 @@ public static Map ready(EventStoreReader reader, String version) )); } - // For now, Kafka and disk checks are omitted; they can be added in v4 observability. + boolean metadataUp = true; + if (metadataDatabase != null && metadataDatabase.isEnabled()) { + long startMetadata = System.nanoTime(); + try (var connection = metadataDatabase.dataSource().getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("SELECT 1"); + long elapsedMs = (System.nanoTime() - startMetadata) / 1_000_000L; + checks.put("metadata", Map.of( + "status", "UP", + "responseTimeMs", elapsedMs + )); + } catch (Exception e) { + long elapsedMs = (System.nanoTime() - startMetadata) / 1_000_000L; + metadataUp = false; + checks.put("metadata", Map.of( + "status", "DOWN", + "error", e.getClass().getSimpleName() + ": " + (e.getMessage() != null ? e.getMessage() : ""), + "responseTimeMs", elapsedMs + )); + } + } + + // For now, Kafka and disk checks are omitted; they can be added later if needed. - root.put("status", postgresUp ? "UP" : "DOWN"); + root.put("status", (postgresUp && metadataUp) ? "UP" : "DOWN"); root.put("version", version); root.put("checks", checks); return root; @@ -83,4 +107,3 @@ private static String formatUptime(Duration d) { return "%dh %dm %ds".formatted(h, m, s); } } - diff --git a/eventlens-api/src/main/java/io/eventlens/api/metrics/EventLensMetrics.java b/eventlens-api/src/main/java/io/eventlens/api/metrics/EventLensMetrics.java index 4606555..83699c6 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/metrics/EventLensMetrics.java +++ b/eventlens-api/src/main/java/io/eventlens/api/metrics/EventLensMetrics.java @@ -14,6 +14,7 @@ import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.ToDoubleFunction; public final class EventLensMetrics { @@ -29,6 +30,8 @@ public final class EventLensMetrics { .description("Active WebSocket connections") .register(registry); + private static final AtomicInteger securityMetricsBound = new AtomicInteger(0); + private EventLensMetrics() { } @@ -65,5 +68,64 @@ public static void recordHttpDuration(String method, String matchedPath, long du public static void setWebsocketConnections(int count) { websocketConnectionsValue.set(Math.max(0, count)); } -} + public static void recordAuthAttempt(String method, String outcome) { + Counter.builder("eventlens_security_auth_attempts_total") + .description("Authentication attempts by method and outcome") + .tags("method", normalize(method), "outcome", normalize(outcome)) + .register(registry) + .increment(); + } + + public static void recordAuthorizationDenied(String permission, String reason) { + Counter.builder("eventlens_security_authz_denied_total") + .description("Authorization denials by permission and reason") + .tags("permission", normalize(permission), "reason", normalize(reason)) + .register(registry) + .increment(); + } + + public static void recordSessionLifecycle(String action) { + Counter.builder("eventlens_security_sessions_total") + .description("Session lifecycle events") + .tags("action", normalize(action)) + .register(registry) + .increment(); + } + + public static void bindActiveSessionsGauge(ToDoubleFunction valueFunction, Object stateObject) { + if (securityMetricsBound.compareAndSet(0, 1)) { + Gauge.builder("eventlens_security_sessions_active", stateObject, valueFunction) + .description("Active browser sessions backed by metadata storage") + .register(registry); + } + } + + public static void recordSensitiveAction(String action, String outcome) { + Counter.builder("eventlens_security_sensitive_actions_total") + .description("Sensitive actions such as export and PII reveal") + .tags("action", normalize(action), "outcome", normalize(outcome)) + .register(registry) + .increment(); + } + + public static void recordApiKeyLifecycle(String action) { + Counter.builder("eventlens_security_api_keys_total") + .description("API key lifecycle events") + .tags("action", normalize(action)) + .register(registry) + .increment(); + } + + public static void recordAuditWriteFailure(String sink) { + Counter.builder("eventlens_security_audit_write_failures_total") + .description("Audit persistence failures by sink") + .tags("sink", normalize(sink)) + .register(registry) + .increment(); + } + + private static String normalize(String value) { + return value == null || value.isBlank() ? "unknown" : value; + } +} diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java index 03d9039..9d73f26 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/ApiKeyRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; @@ -66,6 +67,7 @@ public void create(Context ctx) { request.roles().stream().filter(role -> role != null && !role.isBlank()).map(String::trim).toList(), blank(request.description()) ? null : request.description().trim(), expiresAt); + EventLensMetrics.recordApiKeyLifecycle("created"); auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_CREATE_API_KEY) @@ -97,6 +99,7 @@ public void revoke(Context ctx) { String apiKeyId = ctx.pathParam("id"); apiKeyService.revoke(apiKeyId, Instant.now()); + EventLensMetrics.recordApiKeyLifecycle("revoked"); auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_REVOKE_API_KEY) diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java index 600fab2..2690a36 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AsyncExportRoutes.java @@ -3,6 +3,7 @@ import io.eventlens.api.export.ExportJob; import io.eventlens.api.export.ExportService; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.InputValidator; import io.eventlens.core.security.Permission; @@ -50,6 +51,7 @@ public void start(Context ctx) { "requestId", SecurityContext.requestId(ctx), "userAgent", ctx.userAgent() != null ? ctx.userAgent() : "unknown" )); + EventLensMetrics.recordSensitiveAction("export_async", "started"); ctx.status(202).json(Map.of( "exportId", job.exportId(), @@ -131,7 +133,9 @@ public void download(Context ctx) { ctx.contentType(contentType); try { ctx.result(Files.newInputStream(file)); + EventLensMetrics.recordSensitiveAction("export_async_download", "success"); } catch (Exception e) { + EventLensMetrics.recordSensitiveAction("export_async_download", "failure"); ctx.status(500).json(Map.of("error", "download_failed", "message", e.getMessage())); } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java index 83127f2..63c983e 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.security.BasicAuthenticator; import io.eventlens.api.security.CsrfTokens; import io.eventlens.core.EventLensConfig; @@ -65,6 +66,7 @@ public void logout(Context ctx) { String expectedCsrf = SecurityContext.csrfToken(ctx); String suppliedCsrf = ctx.header("X-CSRF-Token"); if (expectedCsrf != null && !expectedCsrf.equals(suppliedCsrf)) { + EventLensMetrics.recordSessionLifecycle("logout_csrf_rejected"); ctx.status(403).json(Map.of("error", "csrf_required")); return; } @@ -72,6 +74,7 @@ public void logout(Context ctx) { String sessionId = ctx.cookie(sessionConfig.getCookieName()); if (sessionId != null && !sessionId.isBlank()) { sessionService.invalidate(sessionId); + EventLensMetrics.recordSessionLifecycle("invalidated"); } expireCookie(ctx); @@ -87,6 +90,7 @@ public void logout(Context ctx) { public void createBasicSession(Context ctx) { var authResult = basicAuthenticator.authenticate(ctx); if (!authResult.success()) { + EventLensMetrics.recordAuthAttempt("basic", "failure"); auditLogger.log(SecurityContext.audit(ctx) .action(AuditEvent.ACTION_LOGIN_FAILED) .resourceType(AuditEvent.RT_AUTH) @@ -101,6 +105,7 @@ public void createBasicSession(Context ctx) { return; } + EventLensMetrics.recordAuthAttempt("basic", "success"); Principal principal = authResult.principal(); String returnHash = sanitizeReturnHash(ctx.bodyAsClass(BasicSessionRequest.class).returnHash()); String csrfToken = CsrfTokens.generate(); @@ -108,6 +113,7 @@ public void createBasicSession(Context ctx) { "returnHash", returnHash, "csrfToken", csrfToken )); + EventLensMetrics.recordSessionLifecycle("created"); setSessionCookie(ctx, session.sessionId()); SecurityContext.setPrincipal(ctx, principal); SecurityContext.setSession(ctx, session); diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java index 8da4db8..4b031ee 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/ExportRoutes.java @@ -3,6 +3,7 @@ import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.core.audit.AuditEvent; import io.eventlens.core.audit.AuditLogger; @@ -64,6 +65,7 @@ public void export(Context ctx) { events.stream().map(sensitiveDataProtector::maskEvent).toList(), format == ExportEngine.Format.MARKDOWN ? sensitiveDataProtector.maskTransitions(source.replayEngine().replayFull(id)) : null, format); + EventLensMetrics.recordSensitiveAction("export", "success"); String contentType = switch (format) { case MARKDOWN -> "text/markdown"; diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/HealthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/HealthRoutes.java index a237e88..d2a5104 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/HealthRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/HealthRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.api.health.HealthService; +import io.eventlens.core.metadata.MetadataDatabase; import io.eventlens.core.spi.EventStoreReader; import io.javalin.http.Context; @@ -11,10 +12,12 @@ public class HealthRoutes { private final EventStoreReader reader; private final String version; + private final MetadataDatabase metadataDatabase; - public HealthRoutes(EventStoreReader reader, String version) { + public HealthRoutes(EventStoreReader reader, String version, MetadataDatabase metadataDatabase) { this.reader = reader; this.version = version; + this.metadataDatabase = metadataDatabase; } /** GET /api/v1/health/live */ @@ -24,7 +27,7 @@ public void live(Context ctx) { /** GET /api/v1/health/ready */ public void ready(Context ctx) { - var body = HealthService.ready(reader, version); + var body = HealthService.ready(reader, version, metadataDatabase); if ("DOWN".equals(body.get("status"))) { ctx.status(503); } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java index 06504f4..e4161ce 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.security.CsrfTokens; import io.eventlens.api.security.oidc.OidcIdTokenValidator; import io.eventlens.api.security.oidc.OidcLoginStateService; @@ -73,24 +74,28 @@ public void callback(Context ctx) { expireStateCookie(ctx); if (!safeEquals(state, cookieState)) { + EventLensMetrics.recordAuthAttempt("oidc", "failure"); failCallback(ctx, "state_mismatch"); return; } String providerError = ctx.queryParam("error"); if (providerError != null && !providerError.isBlank()) { + EventLensMetrics.recordAuthAttempt("oidc", "failure"); failCallback(ctx, providerError); return; } Optional pending = loginStateService.consume(state); if (pending.isEmpty()) { + EventLensMetrics.recordAuthAttempt("oidc", "failure"); failCallback(ctx, "missing_state"); return; } String code = ctx.queryParam("code"); if (code == null || code.isBlank()) { + EventLensMetrics.recordAuthAttempt("oidc", "failure"); failCallback(ctx, "missing_code"); return; } @@ -118,6 +123,8 @@ public void callback(Context ctx) { } var session = sessionService.create(principal, attributes); + EventLensMetrics.recordAuthAttempt("oidc", "success"); + EventLensMetrics.recordSessionLifecycle("created"); setSessionCookie(ctx, session.sessionId()); auditLogger.log(SecurityContext.audit(ctx) @@ -130,6 +137,7 @@ public void callback(Context ctx) { ctx.redirect("/" + login.returnHash()); } catch (RuntimeException e) { + EventLensMetrics.recordAuthAttempt("oidc", "failure"); failCallback(ctx, "token_validation_failed"); } } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java index 2e29f17..ba8a31e 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/PiiRevealRoutes.java @@ -1,6 +1,7 @@ package io.eventlens.api.routes; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.api.security.RouteAuthorizer; import io.eventlens.api.source.SourceRegistry; import io.eventlens.core.InputValidator; @@ -33,6 +34,7 @@ public void revealEvent(Context ctx) { RevealRequest request = ctx.bodyAsClass(RevealRequest.class); String reason = request != null ? request.reason : null; if (reason == null || reason.isBlank()) { + EventLensMetrics.recordSensitiveAction("reveal_pii", "rejected"); ctx.status(400).json(Map.of("error", "reason_required")); return; } @@ -43,6 +45,7 @@ public void revealEvent(Context ctx) { .reduce((left, right) -> right) .orElse(null); if (event == null) { + EventLensMetrics.recordSensitiveAction("reveal_pii", "not_found"); ctx.status(404).json(Map.of("error", "not_found", "message", "Event not found")); return; } @@ -62,6 +65,7 @@ public void revealEvent(Context ctx) { "reason", reason )) .build()); + EventLensMetrics.recordSensitiveAction("reveal_pii", "success"); ctx.json(Map.of( "event", event, diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java b/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java index 131aed5..4df0e93 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java +++ b/eventlens-api/src/main/java/io/eventlens/api/security/ApiKeyAuthenticator.java @@ -1,5 +1,6 @@ package io.eventlens.api.security; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.core.metadata.ApiKeyRecord; import io.eventlens.core.security.ApiKeyService; import io.eventlens.core.security.Principal; @@ -31,6 +32,7 @@ public AuthenticationResult authenticate(Context ctx) { } ApiKeyRecord apiKey = record.get(); + EventLensMetrics.recordApiKeyLifecycle("used"); Principal principal = new Principal( apiKey.principalUserId(), apiKey.principalUserId(), diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java b/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java index 2ab3008..2fc2f1e 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/security/RouteAuthorizer.java @@ -1,6 +1,7 @@ package io.eventlens.api.security; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.core.security.AuthorizationDecision; import io.eventlens.core.security.AuthorizationDecisionReason; import io.eventlens.core.security.AuthorizationService; @@ -29,6 +30,8 @@ public boolean require(Context ctx, Permission permission, String sourceId, Stri return true; } + EventLensMetrics.recordAuthorizationDenied(permission.name(), decision.reason().name()); + Map payload = new LinkedHashMap<>(); payload.put("error", decision.reason() == AuthorizationDecisionReason.DENY_AUTH_REQUIRED ? "auth_required" : "forbidden"); payload.put("reason", decision.reason().name()); diff --git a/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java b/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java index a4f7dd0..b149836 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java +++ b/eventlens-api/src/main/java/io/eventlens/api/security/SessionAuthenticator.java @@ -1,6 +1,7 @@ package io.eventlens.api.security; import io.eventlens.api.http.SecurityContext; +import io.eventlens.api.metrics.EventLensMetrics; import io.eventlens.core.metadata.SessionRecord; import io.eventlens.core.security.Principal; import io.eventlens.core.security.SessionService; @@ -31,6 +32,7 @@ public AuthenticationResult authenticate(Context ctx) { Optional record = sessionService.touch(sessionId); if (record.isEmpty()) { + EventLensMetrics.recordSessionLifecycle("rejected"); return AuthenticationResult.failure(null, "invalid_session", null); } diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index 316a5b8..91f9ea2 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -265,7 +265,7 @@ void authorizationRejectsAuthenticatedUserWithoutPermission() throws Exception { var role = new EventLensConfig.RoleConfig(); role.setId("datasource-reader"); - role.setPermissions(List.of("VIEW_DATASOURCES")); + role.setPermissions(List.of("VIEW_DATASOURCES", "VIEW_METRICS")); cfg.getSecurity().getAuthorization().setRoles(List.of(role)); }); @@ -602,6 +602,66 @@ void revokedApiKeyCanNoLongerAuthenticate() throws Exception { assertThat(searchResponse.statusCode()).isEqualTo(401); } + @Test + void metricsExposeSecurityCountersAfterAuthAndAuthorizationFlows() throws Exception { + int port = freePort(); + server = startServer(port, true, null, cfg -> { + cfg.getSecurity().getAuthorization().setEnabled(true); + cfg.getSecurity().getAuthorization().setPrincipalRoles(Map.of("admin", List.of("datasource-reader"))); + + var role = new EventLensConfig.RoleConfig(); + role.setId("datasource-reader"); + role.setPermissions(List.of("VIEW_DATASOURCES")); + cfg.getSecurity().getAuthorization().setRoles(List.of(role)); + }); + + var client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + + var missingAuthRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .GET() + .build(); + HttpResponse missingAuthResponse = client.send(missingAuthRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(missingAuthResponse.statusCode()).isEqualTo(401); + + var forbiddenRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .GET() + .build(); + HttpResponse forbiddenResponse = client.send(forbiddenRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(forbiddenResponse.statusCode()).isEqualTo(403); + + var loginRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/login/basic".formatted(port))) + .header("Authorization", basicAuth("admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{\"returnHash\":\"#/timeline\"}")) + .build(); + HttpResponse loginResponse = client.send(loginRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(loginResponse.statusCode()).isEqualTo(200); + + server.stop(); + int metricsPort = freePort(); + server = startServer(metricsPort, false); + + var metricsRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/metrics".formatted(metricsPort))) + .GET() + .build(); + HttpResponse metricsResponse = client.send(metricsRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(metricsResponse.statusCode()).isEqualTo(200); + assertThat(metricsResponse.body()).contains("eventlens_security_auth_attempts_total"); + assertThat(metricsResponse.body()).contains("method=\"basic\""); + assertThat(metricsResponse.body()).contains("outcome=\"failure\""); + assertThat(metricsResponse.body()).contains("outcome=\"success\""); + assertThat(metricsResponse.body()).contains("eventlens_security_authz_denied_total"); + assertThat(metricsResponse.body()).contains("eventlens_security_sessions_total"); + } + private EventLensServer startServer(int port, boolean authEnabled) { return startServer(port, authEnabled, null, cfg -> {}); } diff --git a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java index 2158864..10dafcb 100644 --- a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java +++ b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java @@ -73,6 +73,10 @@ public void run() { if (dbPassword != null) config.getDatasource().setPassword(dbPassword); if (tableName != null) config.getDatasource().setTable(tableName); + var validationIssues = ConfigValidator.validate(config); + validationIssues.stream() + .filter(issue -> issue.severity() == ConfigValidator.ValidationError.Severity.WARNING) + .forEach(issue -> log.warn("Config warning {}: {}", issue.path(), issue.message())); ConfigValidator.validateOrThrow(config); MetadataDatabase metadataDatabase = MetadataDatabase.open( config.getSecurity() != null ? config.getSecurity().getMetadata() : null); diff --git a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java index f9f9927..173acb6 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java +++ b/eventlens-core/src/main/java/io/eventlens/core/ConfigValidator.java @@ -93,6 +93,7 @@ public static List validate(EventLensConfig config) { validateMetadata(config, issues); validateSecurityAuth(config, issues); validateAuthorization(config, issues); + validateProductionGuardrails(config, issues); validateLegacyDatasource(config, issues); validateDatasourceInstances(config.getDatasourcesOrLegacy(), issues); validateStreamInstances(config.getStreamsOrLegacy(), issues); @@ -254,6 +255,7 @@ private static void validateSecurityAuth(EventLensConfig config, List issues) { + var security = config.getSecurity(); + if (security == null || !security.isProductionMode()) { + return; + } + + var server = config.getServer(); + if (server != null) { + var origins = server.getAllowedOrigins(); + if (origins == null || origins.isEmpty()) { + issues.add(error("server.allowed-origins", + "Production mode requires an explicit browser origin allowlist")); + } else if (origins.contains("*")) { + issues.add(error("server.allowed-origins", + "Production mode does not allow wildcard origins")); + } + } + + var metadata = security.getMetadata(); + if (metadata != null && metadata.isEnabled() && "jdbc:sqlite::memory:".equalsIgnoreCase(metadata.getJdbcUrl())) { + issues.add(error("security.metadata.jdbc-url", + "Production mode does not allow in-memory metadata storage")); + } } private static void validateAuthorization(EventLensConfig config, List issues) { diff --git a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java index 0298ee6..8f67770 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java +++ b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java @@ -128,10 +128,13 @@ public static class SecurityConfig { } public static class SecurityFeaturesConfig { + private boolean productionMode = false; private MetadataConfig metadata = new MetadataConfig(); private AuthProviderConfig auth = new AuthProviderConfig(); private AuthorizationConfig authorization = new AuthorizationConfig(); + public boolean isProductionMode() { return productionMode; } + public void setProductionMode(boolean productionMode) { this.productionMode = productionMode; } public MetadataConfig getMetadata() { return metadata; } public void setMetadata(MetadataConfig metadata) { this.metadata = metadata; } public AuthProviderConfig getAuth() { return auth; } diff --git a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java index 819b317..c6b4675 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java +++ b/eventlens-core/src/main/java/io/eventlens/core/audit/AuditLogger.java @@ -31,14 +31,20 @@ public final class AuditLogger { private final ObjectMapper mapper; private final boolean enabled; private final AuditLogRepository auditLogRepository; + private final Runnable metadataFailureListener; public AuditLogger(boolean enabled) { - this(enabled, null); + this(enabled, null, null); } public AuditLogger(boolean enabled, AuditLogRepository auditLogRepository) { + this(enabled, auditLogRepository, null); + } + + public AuditLogger(boolean enabled, AuditLogRepository auditLogRepository, Runnable metadataFailureListener) { this.enabled = enabled; this.auditLogRepository = auditLogRepository; + this.metadataFailureListener = metadataFailureListener; this.mapper = new ObjectMapper() .registerModule(new JavaTimeModule()) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); @@ -62,6 +68,9 @@ public void log(AuditEvent event) { try { auditLogRepository.append(event, event.timestamp() != null ? event.timestamp() : Instant.now()); } catch (Exception ex) { + if (metadataFailureListener != null) { + metadataFailureListener.run(); + } auditLog.warn("Failed to persist audit event requestId={} action={}", event.requestId(), event.action(), ex); } } diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java index f209b54..f2e3d42 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/SessionRepository.java @@ -78,6 +78,21 @@ public int deleteExpired(Instant now) { } } + public int countActive(Instant now) { + try (var connection = dataSource.getConnection(); + PreparedStatement ps = connection.prepareStatement( + "SELECT COUNT(*) FROM sessions WHERE idle_expires_at > ? AND absolute_expires_at > ?")) { + String value = now.toString(); + ps.setString(1, value); + ps.setString(2, value); + try (ResultSet rs = ps.executeQuery()) { + return rs.next() ? rs.getInt(1) : 0; + } + } catch (Exception e) { + throw new IllegalStateException("Failed to count active sessions", e); + } + } + public void deleteById(String sessionId) { try (var connection = dataSource.getConnection(); PreparedStatement ps = connection.prepareStatement("DELETE FROM sessions WHERE session_id = ?")) { diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java b/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java index 0e36a6d..9c5c2c7 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java +++ b/eventlens-core/src/main/java/io/eventlens/core/security/SessionService.java @@ -97,6 +97,10 @@ public int deleteExpiredSessions() { return sessionRepository.deleteExpired(clock.instant()); } + public int activeSessionCount() { + return sessionRepository.countActive(clock.instant()); + } + private static boolean isExpired(SessionRecord session, Instant now) { return !session.idleExpiresAt().isAfter(now) || !session.absoluteExpiresAt().isAfter(now); } diff --git a/eventlens-core/src/main/resources/eventlens-example.yaml b/eventlens-core/src/main/resources/eventlens-example.yaml index 5469847..b472fec 100644 --- a/eventlens-core/src/main/resources/eventlens-example.yaml +++ b/eventlens-core/src/main/resources/eventlens-example.yaml @@ -87,6 +87,32 @@ ui: audit: enabled: true +security: + production-mode: false + metadata: + enabled: false + jdbc-url: jdbc:sqlite:./data/eventlens-metadata.db + wal-enabled: true + busy-timeout-ms: 5000 + foreign-keys-enabled: true + auth: + provider: disabled + session: + cookie-name: __Host-eventlens_session + idle-timeout-seconds: 1800 + absolute-timeout-seconds: 28800 + secure-cookie: true + same-site: Lax + api-keys: + enabled: false + header-name: X-API-Key + key-prefix: el + authorization: + enabled: false + default-roles: [] + principal-roles: {} + roles: [] + data-protection: pii: enabled: false @@ -109,4 +135,3 @@ export: max-concurrent: 2 max-events-per-export: 100000 expire-after-seconds: 3600 - diff --git a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java index 64b2ae1..8c00748 100644 --- a/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java +++ b/eventlens-core/src/test/java/io/eventlens/core/ConfigValidatorTest.java @@ -120,4 +120,35 @@ void authorizationRequiresKnownRolesAndPermissions() { assertThat(issues.stream().anyMatch(i -> i.path().equals("security.authorization.default-roles[0]") && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); } + + @Test + void productionModeRejectsWildcardOriginsAndMissingAuth() { + var cfg = new EventLensConfig(); + cfg.getSecurity().setProductionMode(true); + cfg.getServer().setAllowedOrigins(java.util.List.of("*")); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("server.allowed-origins") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.production-mode") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } + + @Test + void productionModeRejectsInMemoryMetadataAndAuditDisabledSecurityFeatures() { + var cfg = new EventLensConfig(); + cfg.getSecurity().setProductionMode(true); + cfg.getServer().setAllowedOrigins(java.util.List.of("https://eventlens.example")); + cfg.getSecurity().getAuth().setProvider("oidc"); + cfg.getSecurity().getAuth().getSession().setSecureCookie(true); + cfg.getSecurity().getMetadata().setEnabled(true); + cfg.getSecurity().getMetadata().setJdbcUrl("jdbc:sqlite::memory:"); + cfg.getAudit().setEnabled(false); + + var issues = ConfigValidator.validate(cfg); + assertThat(issues.stream().anyMatch(i -> i.path().equals("security.metadata.jdbc-url") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + assertThat(issues.stream().anyMatch(i -> i.path().equals("audit.enabled") + && i.severity() == ConfigValidator.ValidationError.Severity.ERROR)).isTrue(); + } } diff --git a/eventlens.yaml.example b/eventlens.yaml.example index c9a31ea..9728143 100644 --- a/eventlens.yaml.example +++ b/eventlens.yaml.example @@ -51,6 +51,24 @@ ui: audit: enabled: true # set to false to silence audit log entirely +security: + production-mode: false # set true for shared/prod deployments; blocks unsafe startup combinations + metadata: + enabled: false + jdbc-url: jdbc:sqlite:./data/eventlens-metadata.db + auth: + provider: disabled # disabled | basic | oidc + session: + cookie-name: __Host-eventlens_session + idle-timeout-seconds: 1800 + absolute-timeout-seconds: 28800 + secure-cookie: true + same-site: Lax + api-keys: + enabled: false + header-name: X-API-Key + key-prefix: el + # ── 1.9 PII Masking ─────────────────────────────────────────────────────────── # Scans event payloads in API responses for common PII patterns and replaces # matching text with the configured mask string. From b7f190dfa87d34edeff6bac71d4f90840b147650 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Tue, 31 Mar 2026 15:51:25 +0200 Subject: [PATCH 11/11] feat: add first-run security setup flow --- Dockerfile | 13 +- docker-compose.yml | 5 +- .../io/eventlens/api/EventLensServer.java | 121 ++++++++++-- .../io/eventlens/api/routes/AuthRoutes.java | 1 - .../eventlens/api/routes/OidcAuthRoutes.java | 2 +- .../io/eventlens/api/routes/SetupRoutes.java | 47 +++++ .../resources/web/assets/index-BU7xN9MK.css | 1 - .../resources/web/assets/index-BUz4IjMw.js | 14 ++ .../resources/web/assets/index-D5c7dwdI.css | 1 + .../resources/web/assets/index-kO-MJOqr.js | 14 -- .../src/main/resources/web/index.html | 4 +- .../api/AuthenticationIntegrationTest.java | 177 +++++++++++++++++- eventlens-app/build.gradle.kts | 2 + .../java/io/eventlens/cli/ServeCommand.java | 8 +- .../java/io/eventlens/core/ConfigLoader.java | 43 ++++- .../io/eventlens/core/EventLensConfig.java | 11 ++ .../main/java/io/eventlens/core/JsonUtil.java | 3 +- .../core/metadata/MetadataDatabase.java | 10 + .../core/security/ApiKeyService.java | 9 +- .../core/setup/InstanceSetupService.java | 140 ++++++++++++++ .../src/main/resources/eventlens-example.yaml | 6 + eventlens-ui/src/App.tsx | 64 ++++++- eventlens-ui/src/api/client.ts | 37 ++++ eventlens-ui/src/components/AdminConsole.tsx | 12 +- eventlens-ui/src/components/SetupWizard.tsx | 176 +++++++++++++++++ eventlens-ui/src/index.css | 69 +++++++ eventlens.yaml.example | 6 + scripts/reset-setup.py | 55 ++++++ 28 files changed, 988 insertions(+), 63 deletions(-) create mode 100644 eventlens-api/src/main/java/io/eventlens/api/routes/SetupRoutes.java delete mode 100644 eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css create mode 100644 eventlens-api/src/main/resources/web/assets/index-BUz4IjMw.js create mode 100644 eventlens-api/src/main/resources/web/assets/index-D5c7dwdI.css delete mode 100644 eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js create mode 100644 eventlens-core/src/main/java/io/eventlens/core/setup/InstanceSetupService.java create mode 100644 eventlens-ui/src/components/SetupWizard.tsx create mode 100644 scripts/reset-setup.py diff --git a/Dockerfile b/Dockerfile index de26d28..54bea98 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,13 +39,13 @@ RUN --mount=type=cache,target=/root/.gradle \ && chmod +x gradlew \ && ./gradlew :eventlens-app:shadowJar -x test --no-daemon -## 2) Runtime stage – slim multi-arch JRE image (7.1) -FROM docker.io/library/eclipse-temurin:21-jre-alpine AS base +## 2) Runtime stage – glibc-based JRE image for native-library compatibility +FROM docker.io/library/eclipse-temurin:21-jre-jammy AS base ARG TARGETARCH -RUN addgroup -g 1000 eventlens && \ - adduser -u 1000 -G eventlens -s /bin/sh -D eventlens +RUN groupadd --gid 1000 eventlens && \ + useradd --uid 1000 --gid eventlens --shell /bin/bash --create-home eventlens WORKDIR /app @@ -56,7 +56,9 @@ COPY --from=build --chown=eventlens:eventlens /workspace/eventlens-app/build/lib COPY --from=build --chown=eventlens:eventlens /workspace/eventlens.yaml /app/eventlens.yaml # Health check dependency -RUN apk add --no-cache curl +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl && \ + rm -rf /var/lib/apt/lists/* USER eventlens @@ -73,4 +75,3 @@ ENTRYPOINT ["java", \ "-XX:MaxRAMPercentage=75.0", \ "-Djava.security.egd=file:/dev/urandom", \ "-jar", "/app/eventlens.jar"] - diff --git a/docker-compose.yml b/docker-compose.yml index a2fd942..7b2b093 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,6 +45,7 @@ services: app: build: . container_name: eventlens-app + restart: on-failure depends_on: postgres: condition: service_healthy @@ -54,10 +55,11 @@ services: EVENTLENS_CONFIG: /app/eventlens.yaml volumes: - ./eventlens.yaml:/app/eventlens.yaml:ro + - eventlens_data:/app/data ports: - "9090:9090" healthcheck: - test: [ "CMD-SHELL", "curl -sf http://localhost:9090/api/health || exit 1" ] + test: [ "CMD-SHELL", "curl -sf http://localhost:9090/api/v1/health/ready || exit 1" ] interval: 10s timeout: 5s retries: 5 @@ -66,3 +68,4 @@ services: volumes: pg_data: kafka_data: + eventlens_data: diff --git a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java index c4eade2..2884687 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java +++ b/eventlens-api/src/main/java/io/eventlens/api/EventLensServer.java @@ -32,11 +32,13 @@ import io.eventlens.core.security.AuthorizationService; import io.eventlens.core.security.Principal; import io.eventlens.core.security.SessionService; +import io.eventlens.core.setup.InstanceSetupService; import io.eventlens.core.spi.EventStoreReader; import io.javalin.Javalin; import io.javalin.compression.CompressionStrategy; import io.javalin.compression.Gzip; import io.javalin.json.JavalinJackson; +import io.javalin.http.staticfiles.Location; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,9 +116,28 @@ public EventLensServer( DiffEngine diffEngine, Map sourceStreamBindings, MetadataDatabase metadataDatabase) { + this(config, reader, replayEngine, reducerRegistry, pluginManager, defaultSourceId, bisectEngine, anomalyDetector, exportEngine, diffEngine, sourceStreamBindings, metadataDatabase, null, true); + } + + public EventLensServer( + EventLensConfig config, + EventStoreReader reader, + ReplayEngine replayEngine, + ReducerRegistry reducerRegistry, + PluginManager pluginManager, + String defaultSourceId, + BisectEngine bisectEngine, + AnomalyDetector anomalyDetector, + ExportEngine exportEngine, + DiffEngine diffEngine, + Map sourceStreamBindings, + MetadataDatabase metadataDatabase, + String configPath, + boolean configPresentAtStartup) { this.port = config.getServer().getPort(); this.reader = reader; this.metadataDatabase = metadataDatabase == null ? MetadataDatabase.disabled() : metadataDatabase; + var setupService = new InstanceSetupService(config, configPath, configPresentAtStartup); // ── 1.8 Audit Logger ────────────────────────────────────────────── final AuditLogger auditLogger = new AuditLogger( @@ -215,11 +236,31 @@ public EventLensServer( auditLogger) : null; var apiKeyRoutes = apiKeyService != null ? new ApiKeyRoutes(apiKeyService, routeAuthorizer, auditLogger) : null; + var setupRoutes = new SetupRoutes(setupService); // ── Javalin 7: all routes + handlers inside cfg.routes ──────────── this.app = Javalin.create(cfg -> { - cfg.staticFiles.add("/web"); // Embedded React build - cfg.jsonMapper(new JavalinJackson()); + // Embedded React build — explicit MIME types prevent Javalin/Jetty + // from serving .css/.js as text/plain when running from a fat JAR, + // which would cause the browser to reject the stylesheets. + cfg.staticFiles.add(staticCfg -> { + staticCfg.hostedPath = "/"; + staticCfg.directory = "/web"; + staticCfg.location = Location.CLASSPATH; + // Explicitly set MIME types that Jetty sometimes mis-detects in JARs + staticCfg.mimeTypes.add("text/css", "css"); + staticCfg.mimeTypes.add("application/javascript", "js"); + staticCfg.mimeTypes.add("application/javascript", "mjs"); + staticCfg.mimeTypes.add("image/svg+xml", "svg"); + staticCfg.mimeTypes.add("font/woff", "woff"); + staticCfg.mimeTypes.add("font/woff2", "woff2"); + staticCfg.mimeTypes.add("image/x-icon", "ico"); + // Never cache HTML — ensures the browser fetches the latest index.html + // after every new build without requiring a manual cache clear. + staticCfg.headers.put("Cache-Control", "no-store"); + }); + // Use the shared mapper to ensure consistent date formatting (ISO-8601 instead of epoch) + cfg.jsonMapper(new JavalinJackson(io.eventlens.core.JsonUtil.mapper(), false)); cfg.http.compressionStrategy = new CompressionStrategy(null, new Gzip()); // ── Before filters ──────────────────────────────────────────── @@ -245,10 +286,10 @@ public EventLensServer( ctx.header("Content-Security-Policy", "default-src 'self'; " + "script-src 'self'; " + - "style-src 'self' 'unsafe-inline'; " + + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " + "img-src 'self' data:; " + "connect-src 'self' ws: wss:; " + - "font-src 'self'; " + + "font-src 'self' https://fonts.gstatic.com; " + "frame-ancestors 'none'"); // HSTS (only when TLS is detected — reverse proxy sets X-Forwarded-Proto) @@ -335,6 +376,28 @@ public EventLensServer( }); } + cfg.routes.before("/api/*", ctx -> { + var setupStatus = setupService.status(); + if (setupStatus.setupRequired()) { + if (isPublicSetupPath(ctx.path())) { + return; + } + ctx.status(503).json(Map.of( + "error", "setup_required", + "message", "Complete the first-run setup before using EventLens APIs." + )); + ctx.skipRemainingHandlers(); + return; + } + if (setupStatus.restartRequired() && !isPublicSetupPath(ctx.path())) { + ctx.status(503).json(Map.of( + "error", "restart_required", + "message", "Setup has been saved. Restart EventLens to apply the new security mode." + )); + ctx.skipRemainingHandlers(); + } + }); + if (authEnabled) { if ("changeme".equals(authConfig.getPassword())) { log.warn("Basic auth is enabled with default password 'changeme'. Change server.auth.password in production."); @@ -362,19 +425,20 @@ public EventLensServer( var authResult = authHolder.result(); if (!authResult.success()) { EventLensMetrics.recordAuthAttempt(authHolder.method(), "failure"); - // 1.8 - emit LOGIN_FAILED - auditLogger.log(SecurityContext.audit(ctx) - .action(AuditEvent.ACTION_LOGIN_FAILED) - .resourceType(AuditEvent.RT_AUTH) - .userId(authResult.attemptedUserId() != null ? authResult.attemptedUserId() : "anonymous") - .authMethod(authHolder.method()) - .details(Map.of("reason", authResult.failureReason(), "path", ctx.path())) - .build()); + // 1.8 - emit LOGIN_FAILED, but only if they actually attempted to login (not just an unauthenticated fetch) + if (!"missing_credentials".equals(authResult.failureReason())) { + auditLogger.log(SecurityContext.audit(ctx) + .action(AuditEvent.ACTION_LOGIN_FAILED) + .resourceType(AuditEvent.RT_AUTH) + .userId(authResult.attemptedUserId() != null ? authResult.attemptedUserId() : "anonymous") + .authMethod(authHolder.method()) + .details(Map.of("reason", authResult.failureReason(), "path", ctx.path())) + .build()); + } ctx.status(401); - if (authResult.challengeHeader() != null) { - ctx.header("WWW-Authenticate", authResult.challengeHeader()); - } + // We omit the WWW-Authenticate header here so the browser doesn't + // show its native basic-auth modal. The custom UI will handle the 401. ctx.json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { @@ -411,9 +475,8 @@ public EventLensServer( if (!authResult.success()) { EventLensMetrics.recordAuthAttempt(authHolder.method(), "failure"); ctx.status(401); - if (authResult.challengeHeader() != null) { - ctx.header("WWW-Authenticate", authResult.challengeHeader()); - } + // We omit the WWW-Authenticate header here so the browser doesn't + // show its native basic-auth modal. The custom UI will handle the 401. ctx.json(Map.of("error", "Unauthorized")); ctx.skipRemainingHandlers(); } else { @@ -436,6 +499,8 @@ public EventLensServer( // Metrics (4.1) cfg.routes.get("/api/v1/metrics", metricsRoutes::metrics); cfg.routes.get("/api/v1/audit", auditRoutes::recent); + cfg.routes.get("/api/v1/setup/status", setupRoutes::status); + cfg.routes.post("/api/v1/setup/apply", setupRoutes::apply); if (apiKeyRoutes != null) { cfg.routes.get("/api/v1/admin/api-keys", apiKeyRoutes::list); cfg.routes.post("/api/v1/admin/api-keys", apiKeyRoutes::create); @@ -449,6 +514,15 @@ public EventLensServer( cfg.routes.get("/api/v1/auth/session", authRoutes::session); cfg.routes.post("/api/v1/auth/logout", authRoutes::logout); cfg.routes.post("/api/v1/auth/login/basic", authRoutes::createBasicSession); + } else { + // Auth is disabled (no MetadataDatabase / no session provider). + // The UI always probes this endpoint on startup; return a minimal + // anonymous response so it can proceed without showing "Server unavailable". + cfg.routes.get("/api/v1/auth/session", ctx -> ctx.json(Map.of( + "authenticated", false, + "authEnabled", false, + "user", Map.of("id", "anonymous", "role", "admin") + ))); } if (oidcRoutes != null) { cfg.routes.get("/api/v1/auth/login/oidc", oidcRoutes::start); @@ -640,9 +714,20 @@ private static boolean isPublicAuthPath(String path, String oidcCallbackPath) { || "/api/v1/auth/logout".equals(path) || "/api/v1/auth/login/basic".equals(path) || "/api/v1/auth/login/oidc".equals(path) + || "/api/v1/setup/status".equals(path) + || "/api/v1/setup/apply".equals(path) || oidcCallbackPath.equals(path); } + private static boolean isPublicSetupPath(String path) { + return "/api/v1/setup/status".equals(path) + || "/api/v1/setup/apply".equals(path) + || "/api/v1/auth/session".equals(path) + || "/api/v1/health/live".equals(path) + || "/api/v1/health/ready".equals(path) + || "/api/health".equals(path); + } + private static boolean hasSessionCookie(io.javalin.http.Context ctx, String cookieName) { String value = ctx.cookie(cookieName); return value != null && !value.isBlank(); diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java index 63c983e..87b8021 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/AuthRoutes.java @@ -100,7 +100,6 @@ public void createBasicSession(Context ctx) { .build()); ctx.status(401) - .header("WWW-Authenticate", authResult.challengeHeader()) .json(Map.of("error", "Unauthorized")); return; } diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java index e4161ce..5943649 100644 --- a/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/OidcAuthRoutes.java @@ -22,7 +22,7 @@ public final class OidcAuthRoutes { - private static final String STATE_COOKIE = "__Host-eventlens_oidc_state"; + private static final String STATE_COOKIE = "eventlens_oidc_state"; private final SessionService sessionService; private final EventLensConfig.SessionConfig sessionConfig; diff --git a/eventlens-api/src/main/java/io/eventlens/api/routes/SetupRoutes.java b/eventlens-api/src/main/java/io/eventlens/api/routes/SetupRoutes.java new file mode 100644 index 0000000..fbe0bb0 --- /dev/null +++ b/eventlens-api/src/main/java/io/eventlens/api/routes/SetupRoutes.java @@ -0,0 +1,47 @@ +package io.eventlens.api.routes; + +import io.eventlens.core.exception.ConfigurationException; +import io.eventlens.core.setup.InstanceSetupService; +import io.javalin.http.Context; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * First-run instance setup endpoints. + */ +public final class SetupRoutes { + + private final InstanceSetupService setupService; + + public SetupRoutes(InstanceSetupService setupService) { + this.setupService = setupService; + } + + public void status(Context ctx) { + var status = setupService.status(); + ctx.json(Map.of( + "setupRequired", status.setupRequired(), + "restartRequired", status.restartRequired(), + "configPath", status.configPath() + )); + } + + public void apply(Context ctx) { + try { + var request = ctx.bodyAsClass(InstanceSetupService.SetupRequest.class); + var result = setupService.apply(request); + Map payload = new LinkedHashMap<>(); + payload.put("saved", result.saved()); + payload.put("restartRequired", result.restartRequired()); + payload.put("mode", result.mode()); + payload.put("configPath", result.configPath()); + ctx.json(payload); + } catch (IllegalArgumentException | IllegalStateException | ConfigurationException e) { + ctx.status(400).json(Map.of( + "error", "setup_invalid", + "message", e.getMessage() + )); + } + } +} diff --git a/eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css b/eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css deleted file mode 100644 index 26852b3..0000000 --- a/eventlens-api/src/main/resources/web/assets/index-BU7xN9MK.css +++ /dev/null @@ -1 +0,0 @@ -:root{--bg-base:#050508;--bg-surface:#0a0c14;--bg-raised:#0f1220;--bg-elevated:#161b2e;--bg-panel:linear-gradient(145deg, #0c0f1a 0%, #0a0d18 50%, #080b14 100%);--border:#1a2040;--border-muted:#121830;--border-glow:#00f0ff26;--text-primary:#e8eef8;--text-secondary:#94a3c0;--text-muted:#5a6a8a;--neon-cyan:#00f0ff;--neon-cyan-dim:#00f0ff14;--neon-cyan-mid:#00f0ff40;--neon-magenta:#ff00e5;--neon-magenta-dim:#ff00e514;--neon-green:#0f8;--neon-green-dim:#00ff881a;--neon-amber:#fa0;--neon-amber-dim:#ffaa001a;--neon-red:#f35;--neon-red-dim:#ff33551a;--neon-purple:#a855f7;--accent-blue:#4f9cf9;--accent-blue-dim:#4f9cf926;--accent-green:var(--neon-green);--accent-red:var(--neon-red);--accent-yellow:var(--neon-amber);--accent-purple:var(--neon-purple);--font-sans:"Inter", system-ui, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", monospace;--font-display:"Orbitron", var(--font-sans);--radius:6px;--radius-lg:10px;--shadow:0 4px 24px #0009;--shadow-neon:0 0 20px #00f0ff14, 0 0 60px #00f0ff08;--transition:.2s ease;--bottom-panel-scroll-height:280px}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{font-size:15px}body{font-family:var(--font-sans);background:var(--bg-base);color:var(--text-primary);-webkit-font-smoothing:antialiased;min-height:100vh;line-height:1.65}body:after{content:"";pointer-events:none;z-index:9999;background:repeating-linear-gradient(0deg,#0000,#0000 2px,#00f0ff04 2px 4px);position:fixed;inset:0}.app{flex-direction:column;min-height:100vh;display:flex}.auth-shell{background:radial-gradient(circle at 0 0,#00f0ff14,#0000 28%),radial-gradient(circle at 100% 0,#ff00e514,#0000 26%),linear-gradient(#060811 0%,#050508 100%)}.auth-screen{justify-content:center;align-items:center;min-height:100vh;padding:32px 20px;display:flex}.auth-card{background:linear-gradient(160deg,#0e1222f5,#080b14fa),radial-gradient(circle at top,#00f0ff0f,#0000 56%);border:1px solid #00f0ff24;border-radius:18px;width:min(460px,100%);padding:28px;box-shadow:0 18px 60px #0000008c,0 0 40px #00f0ff14}.auth-eyebrow{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-cyan);margin-bottom:10px;font-size:11px}.auth-title{font-family:var(--font-display);letter-spacing:1px;margin-bottom:10px;font-size:26px;line-height:1.2}.auth-copy{color:var(--text-secondary);margin-bottom:20px}.auth-form{gap:14px;display:grid}.auth-field{gap:6px;display:grid}.auth-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:11px}.auth-input{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0a0e1aeb;border:1px solid #ffffff24;border-radius:10px;padding:12px 14px;font-size:13px}.auth-input:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.auth-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);justify-content:space-between;gap:10px;display:flex}.auth-route{overflow-wrap:anywhere}.auth-error{color:#ff9caf;background:#ff335514;border:1px solid #ff33553d;border-radius:10px;padding:10px 12px;font-size:12px}.auth-submit{color:var(--text-primary);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff24,#4f9cf914);border:1px solid #00f0ff3d;border-radius:12px;padding:12px 16px;font-size:12px}.auth-submit:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff24}.auth-submit--secondary{width:100%;margin-bottom:14px}.auth-submit:disabled{opacity:.6;cursor:not-allowed;box-shadow:none;transform:none}.app-header{--header-pad-x:24px;--header-pad-right:48px;--header-control-h:34px;padding:0 var(--header-pad-right) 0 var(--header-pad-x);border-bottom:1px solid var(--border);z-index:100;background:linear-gradient(#0d1020 0%,#080b14 100%);grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;column-gap:clamp(12px,2vw,20px);height:64px;display:grid;position:sticky;top:0;box-shadow:0 2px 20px #00000080,inset 0 -1px #00f0ff0f}.brand{justify-self:start;align-items:center;gap:12px;min-width:0;display:flex}.brand-logo{justify-content:center;align-items:center;width:36px;height:36px;display:flex;position:relative}.brand-logo svg{width:36px;height:36px;filter:drop-shadow(0 0 6px var(--neon-cyan)) drop-shadow(0 0 12px #00f0ff4d)}.brand-name{font-family:var(--font-display);letter-spacing:1.5px;color:var(--text-primary);text-transform:uppercase;font-size:15px;font-weight:700}.brand-sub{color:var(--text-muted);letter-spacing:.5px;font-size:10px}.header-title{font-family:var(--font-display);letter-spacing:3px;text-transform:uppercase;background:linear-gradient(135deg, var(--neon-cyan), #4facfe, var(--neon-magenta));-webkit-text-fill-color:transparent;filter:drop-shadow(0 0 8px #00f0ff66);text-align:center;-webkit-background-clip:text;background-clip:text;flex-shrink:0;margin:0;font-size:20px;font-weight:800;line-height:1}.header-center{flex-flow:row;justify-content:center;justify-self:center;align-items:center;gap:clamp(10px,1.5vw,16px);min-width:0;display:flex}.header-demo-pill{box-sizing:border-box;height:var(--header-control-h);background:var(--neon-amber-dim);color:var(--neon-amber);font-family:var(--font-mono);white-space:nowrap;border:1px solid #ffaa0059;border-radius:999px;flex-shrink:0;justify-content:center;align-items:center;padding:0 12px;font-size:11px;line-height:1;display:inline-flex}.header-actions{flex-direction:row;justify-content:flex-end;justify-self:end;align-items:center;gap:clamp(10px,1.2vw,16px);min-width:0;display:flex}.header-user{background:#090d18e0;border:1px solid #00f0ff29;border-radius:999px;align-items:center;gap:12px;padding:6px 10px 6px 12px;display:inline-flex}.header-user-meta{flex-direction:column;align-items:flex-end;gap:2px;display:flex}.header-user-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:9px}.header-user-name{color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;max-width:180px;font-size:12px;font-weight:600;overflow:hidden}.header-user-action{color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;border:1px solid #ffffff1f;border-radius:999px;padding:6px 10px;font-size:11px}.header-user-action:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid);background:#00f0ff0f}.header-user-action:disabled{opacity:.6;cursor:not-allowed}.header-actions .conn-stats,.header-actions .header-status{flex-shrink:0}.workspace-datasource{flex-direction:column;gap:6px;min-width:0;margin:0;display:flex}.workspace-datasource-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:10px;line-height:1}.workspace-datasource-select{box-sizing:border-box;appearance:none;width:100%;height:34px;color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition);background-color:#0c1020f2;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3c0' d='M3 4.5 6 8l3-3.5'/%3E%3C/svg%3E");background-position:right 10px center;background-repeat:no-repeat;border:1px solid #ffffff24;border-radius:8px;padding:0 32px 0 12px;font-size:12px;line-height:1;box-shadow:inset 0 1px #00f0ff0f}.workspace-datasource-select:hover{border-color:#00f0ff40}.workspace-datasource-select:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.header-status{font-size:12px;font-family:var(--font-sans);letter-spacing:.3px;align-items:center;gap:8px;display:flex}.header-status .status-text{color:var(--neon-green);text-shadow:0 0 8px #00ff8880}.header-status .status-text.offline{color:var(--neon-red);text-shadow:0 0 8px #ff335580}.dot{border-radius:50%;width:8px;height:8px}.dot-green{background:var(--neon-green);box-shadow:0 0 6px var(--neon-green), 0 0 12px #0f86;animation:2s infinite pulse-neon}.dot-red{background:var(--neon-red);box-shadow:0 0 6px var(--neon-red)}.dot-yellow{background:var(--neon-amber);box-shadow:0 0 6px var(--neon-amber);animation:1.5s infinite pulse-neon}@keyframes pulse-neon{0%,to{opacity:1}50%{opacity:.4}}.app-main{flex-direction:column;flex:1;gap:16px;width:100%;max-width:1440px;margin:0 auto;padding:20px;display:flex}.card{background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow), var(--shadow-neon);padding:20px;position:relative;overflow:hidden}.card.card--dropdown-host{z-index:30;overflow:visible}.card:before{content:"";background:linear-gradient(90deg, transparent, var(--neon-cyan-mid), transparent);height:1px;position:absolute;top:0;left:0;right:0}.card:after{content:"";background:linear-gradient(90deg,#0000,#ff00e51a,#0000);height:1px;position:absolute;bottom:0;left:0;right:0}.card-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;text-shadow:0 0 6px #00f0ff33;align-items:center;gap:8px;margin-bottom:16px;font-size:13px;font-weight:600;display:flex}.control-ribbon{padding:14px 18px}.control-ribbon-top{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:14px;display:flex}.control-ribbon-title{margin-bottom:4px}.control-ribbon-subtitle{color:var(--text-muted);font-size:12px}.control-ribbon-nav{align-items:center;gap:12px;font-size:13px;display:inline-flex}.control-ribbon-nav a{text-decoration:none}.control-ribbon-nav a[aria-current=page]{text-underline-offset:2px;text-decoration:underline}.control-panel{gap:12px;padding:16px 18px;display:grid}.control-panel-grid{grid-template-columns:minmax(220px,360px) minmax(0,1fr);align-items:end;gap:12px;display:grid}.control-field,.control-field--search{min-width:0}.control-field-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-size:11px;display:block}.control-select{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0d1123eb;border:1px solid #ffffff24;border-radius:10px;outline:none;padding:10px 12px;font-size:12px}.control-select:focus{border-color:var(--neon-cyan-mid);box-shadow:0 0 14px #00f0ff26}.datasource-pills{flex-wrap:wrap;gap:8px;display:flex}.datasource-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:4px 8px}.selection-summary{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);margin-top:2px}.selection-clear-btn{color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);background:0 0;border:none;margin-left:12px}.selection-clear-btn:hover{color:var(--neon-cyan)}.event-agg-button,.anomaly-aggregate-link{cursor:pointer;background:0 0;border:none;padding:0}.event-agg-button{color:var(--text-secondary);font:inherit}.event-agg-button:hover,.event-agg-button:focus-visible{color:var(--neon-cyan);text-shadow:0 0 8px #00f0ff47}.anomaly-aggregate-link .anomaly-meta-value{color:var(--neon-cyan)}.anomaly-aggregate-link:hover .anomaly-meta-value,.anomaly-aggregate-link:focus-visible .anomaly-meta-value{text-shadow:0 0 8px #00f0ff4d}.workspace-dock{z-index:110;border:1px solid var(--border-muted);pointer-events:auto;border-right:none;border-radius:10px 0 0 10px;flex-direction:row;align-items:center;width:auto;height:auto;max-height:min(72vh,100vh - 112px);transition:box-shadow .2s;display:flex;position:fixed;inset:50% 0 auto auto;overflow:hidden;transform:translateY(-50%);box-shadow:-6px 4px 22px #0000006b}.workspace-dock--open{box-shadow:-8px 6px 28px #0000007a,0 0 0 1px #00f0ff14}.workspace-dock-handle{border:none;border-left:1px solid var(--border-muted);width:36px;height:36px;color:var(--neon-cyan);font-family:var(--font-mono);cursor:pointer;transition:background var(--transition), color var(--transition);background:linear-gradient(#0f1324 0%,#0a0e18 100%);flex-direction:row;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:flex}.workspace-dock-handle:hover{color:var(--text-primary);background:linear-gradient(#141a30 0%,#0d1220 100%)}.workspace-dock-handle:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:-2px}.workspace-dock-chevron{font-size:15px;font-weight:700;line-height:1}.workspace-dock-panel{border-left:1px solid var(--border);background:linear-gradient(145deg,#0c101c 0%,#080c14 100%);flex-direction:column;flex:0 auto;gap:8px;width:min(252px,100vw - 48px);min-width:0;max-height:min(72vh,100vh - 112px);padding:10px 12px 12px 14px;display:flex;overflow:hidden auto}.workspace-dock-panel[hidden]{display:none!important}.workspace-dock-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:12px;font-weight:600}.workspace-dock-scrim{z-index:109;cursor:pointer;background:#03050c73;border:none;margin:0;padding:0;position:fixed;inset:64px 0 36px}.workspace-sidebar-kpis{border:1px solid var(--border-muted);border-radius:var(--radius);background:#080b148c;gap:6px;padding:8px 10px;display:grid}.workspace-kpi-row{color:var(--text-muted);font-size:11px;font-family:var(--font-mono);justify-content:space-between;gap:8px;display:flex}.workspace-kpi-row strong{color:var(--text-primary)}.workspace-sidebar-links{color:var(--text-secondary);gap:6px;font-size:12px;display:grid}.workspace-content{gap:16px;display:grid}.search-panel{width:100%;min-width:0}.selection-clear-btn:focus-visible,.control-ribbon-nav a:focus-visible,.control-select:focus-visible,.workspace-datasource-select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.plugin-dashboard{gap:14px;display:grid}.plugin-cards-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.plugin-cards-grid--dense{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}.plugin-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(140deg,#101527f2,#0a0e1cf2);padding:12px 14px;box-shadow:inset 0 1px #ffffff05}.plugin-card--interactive{transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition)}.plugin-card--interactive:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 16px #00f0ff1a}.plugin-card--interactive:focus-within{border-color:var(--neon-cyan-mid);box-shadow:0 0 0 2px #00f0ff26}.plugin-card-head{justify-content:space-between;align-items:center;gap:10px;display:flex}.plugin-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:2px 8px}.plugin-card-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);overflow-wrap:anywhere;margin-top:8px}.plugin-card-detail{color:var(--text-secondary);margin-top:8px;font-size:12px;line-height:1.55}.search-wrapper{position:relative}.search-input{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);outline:none;padding:14px 16px 14px 44px;font-size:13px}.search-input:focus{border-color:var(--neon-cyan);box-shadow:0 0 14px #00f0ff2e}.search-input::placeholder{color:var(--text-muted)}.search-icon{color:var(--neon-cyan);pointer-events:none;filter:drop-shadow(0 0 4px #00f0ff80);font-size:16px;position:absolute;top:50%;left:14px;transform:translateY(-50%)}.search-results{background:var(--bg-raised);border:1px solid var(--neon-cyan-mid);border-radius:var(--radius);z-index:500;max-height:min(55vh,420px);position:absolute;top:calc(100% + 6px);left:0;right:0;overflow:hidden auto;box-shadow:0 12px 40px #000000a6,0 0 24px #00f0ff14}.search-result-item{cursor:pointer;transition:background var(--transition);font-family:var(--font-mono);color:var(--text-primary);text-align:left;background:0 0;border:none;align-items:center;gap:10px;width:100%;padding:10px 16px;font-size:13px;display:flex}.search-result-item:hover{background:var(--bg-elevated);box-shadow:inset 3px 0 0 var(--neon-cyan)}.search-result-item+.search-result-item{border-top:1px solid var(--border-muted)}.search-result-chevron{color:var(--text-muted);flex-shrink:0;padding-right:4px}.search-result-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:0 6px;min-width:0;display:flex}.search-result-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0;font-size:10px}.search-result-colon{color:var(--text-muted);flex-shrink:0;margin-right:2px}.search-result-value{overflow-wrap:anywhere;word-break:break-word;flex:1;min-width:0}.conn-stats{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:16px;font-size:11px;display:flex}.conn-stat{flex-direction:column;align-items:flex-end;gap:1px;display:flex}.conn-stat-label{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);font-size:9px}.conn-stat-value{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66;font-size:12px}.conn-stat-value.green{color:var(--neon-green);text-shadow:0 0 6px #0f86}.conn-stat-value.amber{color:var(--neon-amber);text-shadow:0 0 6px #fa06}.conn-stat--metric .conn-stat-value,.conn-stat-value--uptime{text-align:right;font-variant-numeric:tabular-nums;min-width:6.5ch;display:inline-block}.mini-wave{align-items:flex-end;gap:1px;height:24px;display:flex}.mini-wave-bar{background:var(--neon-cyan);border-radius:1px;width:3px;animation:1.2s ease-in-out infinite wave-pulse;box-shadow:0 0 4px #00f0ff4d}@keyframes wave-pulse{0%,to{transform:scaleY(.3)}50%{transform:scaleY(1)}}.timeline-count-pill{color:var(--accent-blue);font-family:var(--font-mono);background:var(--accent-blue-dim);border:1px solid #4f9cf940;border-radius:999px;padding:2px 10px;font-size:11px}.timeline-hint{color:var(--text-muted);max-width:52rem;margin:-8px 0 12px;font-size:12px;line-height:1.5}.timeline-rail{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-base);position:relative;box-shadow:inset 0 1px #00f0ff0a}.timeline-stepper{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);padding:12px 14px 14px;overflow:auto hidden;-webkit-mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000);mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}.timeline-stepper-track{flex-wrap:nowrap;align-items:center;gap:4px;width:max-content;min-height:88px;display:flex}.timeline-step-arrow{color:var(--text-muted);opacity:.7;flex-shrink:0;align-self:center;padding:0 2px;font-size:11px}.timeline-step-arrow-compact{padding:0 1px;font-size:9px}.timeline-step{border-radius:var(--radius);border:1px solid var(--border-muted);background:var(--bg-raised);cursor:pointer;text-align:left;min-width:118px;max-width:200px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-step-compact{gap:2px;min-width:96px;max-width:140px;padding:8px 10px}.timeline-step-compact .timeline-step-badge{font-size:8px}.timeline-step-compact .timeline-step-type{-webkit-line-clamp:1;font-size:10px}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-step-badge{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:10px;font-weight:600}.timeline-step-seq{color:var(--text-muted);font-size:10px;font-family:var(--font-mono)}.timeline-step-type{color:var(--text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}.timeline-step-created{border-left:3px solid var(--neon-green)}.timeline-step-deleted{border-left:3px solid var(--neon-red)}.timeline-step-completed{border-left:3px solid var(--neon-green)}.timeline-step-failed{border-left:3px solid var(--neon-red)}.timeline-step-transfer{border-left:3px solid var(--neon-amber)}.timeline-step-item{border-left:3px solid var(--neon-purple)}.timeline-step-progress{border-left:3px solid #38bdf8}.timeline-step-default{border-left:3px solid var(--neon-cyan)}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);cursor:pointer;text-align:left;min-width:140px;max-width:240px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-group-chip.expanded{border-color:#00f0ff59}.timeline-group-count{font-family:var(--font-display);letter-spacing:.5px;color:var(--neon-cyan);font-size:16px;font-weight:800;line-height:1}.timeline-group-chevron{color:var(--text-muted);font-size:10px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.timeline-group-range{color:var(--text-muted);font-size:10px;font-family:var(--font-mono);line-height:1.4}.timeline-expanded-deck{border-top:1px solid var(--border-muted);background:#00000040;padding:10px 14px 14px}.timeline-expanded-head{flex-wrap:wrap;align-items:center;gap:10px 16px;margin-bottom:10px;display:flex}.timeline-expanded-title{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:12px;font-weight:700}.timeline-expanded-meta{font-family:var(--font-mono);color:var(--text-muted);flex:1;font-size:11px}.timeline-expanded-close{border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-raised);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition);margin-left:auto;padding:4px 12px;font-size:11px}.timeline-expanded-close:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan)}.timeline-expanded-strip{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex-wrap:nowrap;align-items:center;gap:4px;width:100%;padding-bottom:6px;display:flex;overflow-x:auto}.timeline-slider{width:100%;accent-color:var(--neon-cyan);cursor:pointer;margin-top:4px}.timeline-info{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);grid-template-columns:minmax(0,1fr) minmax(0,2.2fr) minmax(0,1fr);align-items:start;gap:12px;margin-top:12px;display:grid}.timeline-info-edge{color:var(--text-muted);font-size:11px}.timeline-info-center{text-align:center;color:var(--text-secondary);line-height:1.55}.timeline-info-center strong{color:var(--neon-cyan);font-weight:600}.timeline-info-muted{color:var(--text-muted);font-weight:400}.timeline-info-type{color:var(--text-primary);font-size:11px}.state-grid{grid-template-columns:1fr 1fr;gap:16px;display:grid}.state-panel h4{font-family:var(--font-display);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:8px;font-size:10px;font-weight:600}.state-panel-before h4{color:var(--text-muted)}.state-panel-after h4{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.json-block{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);font-family:var(--font-mono);max-height:260px;color:var(--text-secondary);white-space:pre;padding:12px;font-size:12px;line-height:1.7;overflow:auto}.json-tree-root{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);max-height:320px;font-family:var(--font-mono);color:var(--text-secondary);padding:10px 12px;font-size:13px;line-height:1.75;overflow:auto}.json-tree-line{flex-wrap:wrap;align-items:baseline;gap:2px 0;min-height:1.5em;display:flex}.json-tree-toggle{width:22px;height:22px;color:var(--text-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;margin-right:4px;padding:0;font-size:10px;line-height:1}.json-tree-toggle:hover{color:var(--neon-cyan);background:var(--neon-cyan-dim)}.json-key{color:#7dd3fc}.json-string{color:#86efac}.json-number{color:#fcd34d}.json-boolean{color:#c4b5fd}.json-null{color:var(--text-muted);font-style:italic}.json-punct{color:var(--text-muted)}.json-ellipsis{color:var(--text-muted);font-size:11px;font-style:italic}.json-unknown{color:var(--neon-amber)}.diff-panel{border-top:1px solid var(--border-muted);margin-top:20px;padding-top:16px}.diff-toolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.diff-toolbar-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:13px;font-weight:600}.diff-view-toggle{border-radius:var(--radius);border:1px solid var(--border);font-family:var(--font-mono);font-size:11px;display:flex;overflow:hidden}.diff-view-toggle button{background:var(--bg-raised);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:6px 14px}.diff-view-toggle button+button{border-left:1px solid var(--border)}.diff-view-toggle button:hover{color:var(--text-secondary)}.diff-view-toggle button.active{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.diff-body{align-items:stretch;gap:10px;display:flex}.diff-minimap{border:1px solid var(--border-muted);background:var(--bg-base);border-radius:4px;flex-direction:column;flex-shrink:0;width:10px;max-height:280px;display:flex;overflow:hidden}.diff-minimap-chunk{background:var(--border);cursor:pointer;min-height:8px;transition:background var(--transition);border:none;flex:1;margin:0;padding:0}.diff-minimap-chunk:hover,.diff-minimap-chunk:focus-visible{background:var(--neon-cyan-mid);outline:none}.diff-scroll{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex:1;min-width:0;max-height:280px;overflow:auto}.diff-list{flex-direction:column;gap:0;display:flex}.diff-row{font-family:var(--font-mono);background:var(--bg-raised);border-left:3px solid var(--neon-cyan);border-bottom:1px solid var(--border-muted);border-radius:0;align-items:stretch;gap:0;padding:0;font-size:12px;line-height:1.65;display:flex}.diff-row:last-child{border-radius:0 0 var(--radius) var(--radius);border-bottom:none}.diff-row:first-child{border-radius:var(--radius) var(--radius) 0 0}.diff-line-no{text-align:right;width:36px;color:var(--text-muted);background:var(--bg-base);border-right:1px solid var(--border-muted);-webkit-user-select:none;user-select:none;flex-shrink:0;padding:10px 6px;font-size:10px}.diff-row-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:8px 12px;padding:10px 12px;display:flex}.diff-field{color:var(--text-primary);min-width:6rem;font-weight:700}.diff-values-inline{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.diff-old{color:var(--neon-red);font-weight:400;text-decoration:line-through}.diff-arrow{color:var(--text-muted);flex-shrink:0}.diff-new{color:var(--neon-green);text-shadow:0 0 4px #00ff8840;font-weight:400}.diff-split-head{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);border-bottom:1px solid var(--border);background:var(--bg-raised);z-index:1;grid-template-columns:1fr 1fr;gap:8px;padding:8px 12px 8px 48px;font-size:11px;display:grid;position:sticky;top:0}.diff-split-old-label{color:var(--neon-red)}.diff-split-new-label{color:var(--neon-green)}.diff-split-row{border-bottom:1px solid var(--border-muted);align-items:stretch;gap:0;display:flex}.diff-split-row:last-child{border-bottom:none}.diff-split-cells{flex:1;grid-template-columns:1fr 1fr;gap:8px;min-width:0;padding:8px 12px 8px 0;display:grid}.diff-split-cell{border-radius:var(--radius);background:var(--bg-base);border:1px solid var(--border-muted);min-width:0;padding:8px 10px}.diff-split-cell .diff-field{margin-bottom:6px;font-size:11px;display:block}.diff-cell-value{overflow-wrap:anywhere;word-break:break-word;font-size:12px;font-weight:400;line-height:1.6}.diff-split-old .diff-cell-value{color:var(--neon-red);opacity:.9}.diff-split-new .diff-cell-value{color:var(--neon-green)}.event-meta{color:var(--text-muted);grid-template-columns:repeat(2,1fr);gap:10px 16px;margin-top:20px;font-size:12px;display:grid}.event-meta-bar{background:var(--bg-raised);border:1px solid var(--border-muted);border-radius:var(--radius);padding:14px 16px;box-shadow:inset 0 1px #00f0ff0a}.event-meta-time{font-family:var(--font-mono);color:var(--text-secondary);font-size:12px;font-weight:500}.event-meta-id{font-family:var(--font-mono);color:var(--text-primary);overflow-wrap:anywhere;word-break:break-word;grid-column:1/-1;font-size:11px;line-height:1.5}.event-meta-extra{font-family:var(--font-mono);color:var(--text-secondary);font-size:11px}@media (width<=900px){.diff-split-cells{grid-template-columns:1fr}.diff-split-head{display:none}}.copy-btn{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-secondary);cursor:pointer;transition:all var(--transition);font-size:12px;font-family:var(--font-mono);margin-top:12px;padding:6px 14px}.copy-btn:hover{background:var(--bg-raised);color:var(--neon-cyan);border-color:var(--neon-cyan);box-shadow:0 0 10px #00f0ff1a}.live-header{justify-content:space-between;align-items:center;margin-bottom:12px;display:flex}.live-indicator{font-family:var(--font-sans);letter-spacing:.2px;align-items:center;gap:6px;font-size:11px;display:flex}.pause-btn{background:var(--bg-elevated);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);border-radius:4px;padding:5px 12px;font-size:11px}.pause-btn:hover{background:var(--bg-raised);border-color:var(--neon-cyan);color:var(--neon-cyan);box-shadow:0 0 8px #00f0ff1a}.event-stream{height:var(--bottom-panel-scroll-height);min-height:0;font-family:var(--font-mono);flex-direction:column;gap:3px;padding-right:4px;font-size:12px;display:flex;overflow-y:auto}.event-stream,.anomaly-scroll-region,.search-results{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base)}.event-stream::-webkit-scrollbar{width:6px}.anomaly-scroll-region::-webkit-scrollbar{width:6px}.search-results::-webkit-scrollbar{width:6px}.event-stream::-webkit-scrollbar-track{background:var(--bg-base)}.anomaly-scroll-region::-webkit-scrollbar-track{background:var(--bg-base)}.search-results::-webkit-scrollbar-track{background:var(--bg-base)}.event-stream::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.anomaly-scroll-region::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.search-results::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.event-stream::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.anomaly-scroll-region::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.search-results::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.event-row{transition:all var(--transition);cursor:default;background:#ffffff03;border-left:3px solid #0000;border-radius:4px;align-items:center;gap:12px;padding:6px 10px;display:flex}.event-row:hover{background:var(--bg-elevated)}.event-row.type-withdrawn{border-left-color:var(--neon-magenta)}.event-row.type-deposited{border-left-color:var(--neon-cyan)}.event-row.type-created{border-left-color:var(--neon-green)}.event-row.type-transfer{border-left-color:var(--neon-amber)}.event-row.type-deleted{border-left-color:var(--neon-red)}.event-row.type-completed{border-left-color:var(--neon-green)}.event-row.type-failed{border-left-color:var(--neon-red)}.event-row.type-default{border-left-color:var(--neon-purple)}.event-time{color:var(--text-muted);flex-shrink:0;width:75px;font-size:11px}.event-type{flex-shrink:0;width:180px;font-weight:500}.event-agg{color:var(--text-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.event-icon{text-align:center;flex-shrink:0;width:20px;font-size:14px}.type-created{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-deleted{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-completed{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-failed{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-transfer{color:var(--neon-amber);text-shadow:0 0 6px #ffaa004d}.type-default{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.type-withdrawn{color:var(--neon-magenta);text-shadow:0 0 6px #ff00e54d}.type-deposited{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.anomaly-panel-inner{position:relative}.anomaly-shield{background:radial-gradient(#00ff880f 0%,#0000 70%);border:1px solid #00ff8826;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;margin-bottom:20px;padding:30px 20px;display:flex}.shield-icon{justify-content:center;align-items:center;width:64px;height:64px;margin-bottom:12px;display:flex;position:relative}.shield-icon svg{filter:drop-shadow(0 0 12px #00ff8880)drop-shadow(0 0 24px #0f83);width:64px;height:64px}.shield-icon:after{content:"";border:1px solid #00ff8826;border-radius:50%;animation:3s ease-in-out infinite shield-pulse;position:absolute;inset:-8px}@keyframes shield-pulse{0%,to{opacity:.3;transform:scale(1)}50%{opacity:.8;transform:scale(1.15)}}.shield-text{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-green);text-shadow:0 0 8px #0f86;font-size:13px;font-weight:600}.gauge-row{grid-template-columns:1fr 1fr 1fr;gap:12px;display:grid}.gauge-card{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);text-align:center;padding:12px;position:relative;overflow:hidden}.gauge-card:before{content:"";height:2px;position:absolute;bottom:0;left:0;right:0}.gauge-card.optimal:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-card.baseline:before{background:var(--neon-cyan);box-shadow:0 0 8px #00f0ff66}.gauge-card.zero:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);margin-bottom:6px;font-size:10px;font-weight:500}.gauge-value{font-family:var(--font-display);letter-spacing:1px;font-size:13px;font-weight:700}.gauge-value.optimal{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-value.baseline{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66}.gauge-value.zero{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-wave{justify-content:center;align-items:flex-end;gap:2px;height:20px;margin-top:6px;display:flex}.gauge-wave-bar{border-radius:1px;width:2px;animation:1.5s ease-in-out infinite gauge-wave-anim}.gauge-wave-bar.green{background:var(--neon-green);box-shadow:0 0 3px #0f86}.gauge-wave-bar.cyan{background:var(--neon-cyan);box-shadow:0 0 3px #00f0ff66}@keyframes gauge-wave-anim{0%,to{height:4px}50%{height:16px}}.anomaly-scroll-region{box-sizing:border-box;height:var(--bottom-panel-scroll-height);max-height:var(--bottom-panel-scroll-height);z-index:1;min-height:0;padding-right:4px;position:relative;overflow:hidden auto}.anomaly-list-inner{flex-direction:column;gap:10px;display:flex}.anomaly-card-title-row{flex-wrap:wrap;width:100%}.anomaly-title-text{flex:1}.anomaly-header-count{min-width:28px;height:28px;font-family:var(--font-display);color:var(--bg-base);background:var(--neon-amber);border-radius:999px;justify-content:center;align-items:center;margin-left:auto;padding:0 10px;font-size:11px;font-weight:700;display:inline-flex;box-shadow:0 0 12px #ffaa0059}.anomaly-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-raised);transition:border-color var(--transition), box-shadow var(--transition);overflow:hidden}.anomaly-card.CRITICAL{border-left:4px solid #f44}.anomaly-card.HIGH{border-left:4px solid var(--neon-red)}.anomaly-card.MEDIUM{border-left:4px solid var(--neon-amber)}.anomaly-card.LOW{border-left:4px solid var(--neon-cyan)}.anomaly-card-summary{cursor:pointer;font-family:var(--font-sans);flex-wrap:wrap;align-items:center;gap:10px 12px;padding:14px 16px;list-style:none;display:flex}.anomaly-card-summary::-webkit-details-marker{display:none}.anomaly-severity-badge{font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:4px 10px;font-size:9px;font-weight:700}.anomaly-severity-badge.sev-critical{color:#f66;background:#f443;border:1px solid #ff444459}.anomaly-severity-badge.sev-high{background:var(--neon-red-dim);color:var(--neon-red);border:1px solid #ff335559}.anomaly-severity-badge.sev-medium{background:var(--neon-amber-dim);color:var(--neon-amber);border:1px solid #ffaa0059}.anomaly-severity-badge.sev-low{background:var(--neon-cyan-dim);color:var(--neon-cyan);border:1px solid #00f0ff40}.anomaly-card-title{min-width:0;color:var(--text-primary);flex:1;font-size:15px;font-weight:600;line-height:1.45}.anomaly-card-chevron{color:var(--text-muted);transition:transform var(--transition);flex-shrink:0;font-size:10px}.anomaly-card[open] .anomaly-card-chevron{transform:rotate(-180deg)}.anomaly-card-body{border-top:1px solid var(--border-muted);background:#0003;padding:0 16px 16px}.anomaly-card-meta{flex-wrap:wrap;align-items:baseline;gap:8px 12px;margin-top:12px;font-size:13px;line-height:1.5;display:flex}.anomaly-card-meta:first-child{margin-top:12px}.anomaly-meta-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);min-width:72px;font-size:10px}.anomaly-meta-value{font-family:var(--font-mono);color:var(--text-secondary);overflow-wrap:anywhere}.no-anomalies{color:var(--neon-green);align-items:center;gap:8px;padding:20px 0;font-size:13px;display:flex}.bottom-grid{grid-template-columns:1fr 1fr;align-items:start;gap:16px;display:grid}.bottom-grid>.card{flex-direction:column;align-items:stretch;min-width:0;min-height:0;display:flex}.bottom-grid>.card>.card-title,.bottom-grid>.card>.live-header,.bottom-grid .anomaly-scroll-region,.bottom-grid .event-stream{flex-shrink:0}.skeleton{background:linear-gradient(90deg, var(--bg-raised) 25%, var(--bg-elevated) 50%, var(--bg-raised) 75%);border-radius:var(--radius);background-size:200% 100%;animation:1.4s infinite shimmer}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.toast-container{z-index:999;flex-direction:column;gap:8px;display:flex;position:fixed;bottom:24px;right:24px}.toast{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);box-shadow:var(--shadow);border-left:3px solid var(--neon-amber);padding:10px 16px;font-size:13px;animation:.2s slideIn}.toast.error{border-left-color:var(--neon-red)}.toast.success{border-left-color:var(--neon-green)}@keyframes slideIn{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:var(--bg-base)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.demo-banner{font-size:13px;font-family:var(--font-mono);color:var(--neon-amber);background:var(--neon-amber-dim);border-radius:var(--radius);border:1px solid #ffaa0059;margin:0 0 16px;padding:10px 16px}.demo-banner code{color:var(--neon-cyan)}@media (width<=900px){.state-grid,.bottom-grid{grid-template-columns:1fr}.header-center .header-demo-pill{display:none}.workspace-dock-panel{width:min(240px,88vw)}.gauge-row,.control-panel-grid{grid-template-columns:1fr}.timeline-info{text-align:center;grid-template-columns:1fr}.timeline-info-edge{display:none}}.state-tabs{border-bottom:1px solid var(--border);gap:2px;margin-bottom:16px;padding-bottom:0;display:flex}.state-tab{color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:color var(--transition), border-color var(--transition);border-radius:var(--radius) var(--radius) 0 0;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-1px;padding:8px 16px;font-size:13px;font-weight:500;display:flex}.state-tab:hover{color:var(--text-secondary)}.state-tab.active{color:var(--neon-cyan);border-bottom-color:var(--neon-cyan);background:var(--neon-cyan-dim);text-shadow:0 0 8px #00f0ff33}.state-tab-emoji{font-size:14px}.state-tab-content{min-height:80px}.summary-tab{padding-top:4px}.summary-changes{flex-direction:column;gap:8px;display:flex}.summary-changes-header{color:var(--text-muted);border-bottom:1px solid var(--border-muted);margin-bottom:4px;padding-bottom:6px;font-size:12px;font-weight:600}.summary-change-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-amber);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.summary-change-row:hover{background:var(--bg-elevated)}.summary-change-field{color:var(--text-primary);min-width:5rem;font-weight:700;font-family:var(--font-sans)}.summary-change-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.summary-change-arrow{color:var(--text-muted);flex-shrink:0}.summary-change-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.summary-no-changes{color:var(--neon-green);align-items:center;gap:12px;padding:20px 0;font-size:14px;display:flex}.event-summary-bar{background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);border:1px solid var(--border);border-left:3px solid var(--neon-cyan);border-radius:var(--radius);justify-content:space-between;align-items:center;gap:12px;margin-bottom:4px;padding:10px 20px;display:flex;box-shadow:0 0 16px #00f0ff0d}.event-summary-left{flex-wrap:wrap;align-items:baseline;gap:6px 12px;display:flex}.event-summary-type{font-family:var(--font-sans);color:var(--text-primary);font-size:14px;font-weight:600}.event-summary-meta{font-family:var(--font-mono);color:var(--text-muted);font-size:12px}.event-summary-changes{color:var(--neon-amber);font-size:12px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;flex-shrink:0;padding:3px 10px;font-weight:600}.diff-count-badge{color:var(--neon-amber);font-size:11px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;align-items:center;margin-left:6px;padding:2px 8px;font-weight:500;display:inline-flex}.diff-summary-view{flex-direction:column;gap:6px;display:flex}.diff-summary-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-cyan);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.diff-summary-row:hover{background:var(--bg-elevated)}.diff-summary-field{color:var(--text-primary);min-width:6rem;font-weight:700;font-family:var(--font-sans)}.diff-summary-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.diff-summary-arrow{color:var(--text-muted);flex-shrink:0}.diff-summary-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.diff-jump-next{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;margin-left:auto;padding:2px 8px;font-size:10px}.diff-jump-next:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.json-tree-changed{border-radius:3px;background:#ffaa001a!important}.timeline-header-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.timeline-jump-group{border:1px solid var(--border);border-radius:var(--radius);align-items:center;gap:0;display:flex;overflow:hidden}.timeline-jump-input{background:var(--bg-raised);border:none;border-right:1px solid var(--border);width:100px;color:var(--text-primary);font-family:var(--font-mono);outline:none;padding:5px 8px;font-size:12px}.timeline-jump-input:focus{border-right-color:var(--neon-cyan);background:var(--bg-elevated)}.timeline-jump-input::placeholder{color:var(--text-muted)}.timeline-jump-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input[type=number]{-moz-appearance:textfield}.timeline-jump-btn{background:var(--bg-elevated);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:5px 10px;font-size:14px}.timeline-jump-btn:hover{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.timeline-filter-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.filter-chip{border:1px solid var(--border);background:var(--bg-raised);color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);white-space:nowrap;text-overflow:ellipsis;border-radius:999px;max-width:160px;padding:3px 10px;font-size:11px;overflow:hidden}.filter-chip:hover{border-color:var(--neon-cyan-mid);color:var(--text-secondary)}.filter-chip.active{background:var(--neon-cyan-dim);color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff33;border-color:#00f0ff66}.timeline-anomaly-marker{color:var(--neon-amber);text-shadow:0 0 4px #fa09;align-self:flex-start;font-size:7px;line-height:1}.keyboard-hints{z-index:200;border-top:1px solid var(--border);-webkit-backdrop-filter:blur(12px);background:#080b14eb;position:fixed;bottom:0;left:0;right:0}.keyboard-hints-bar{font-family:var(--font-mono);color:var(--text-muted);flex-wrap:wrap;justify-content:center;align-items:center;gap:8px;padding:6px 24px;font-size:11px;display:flex}.keyboard-hints-item{align-items:center;gap:5px;display:flex}.keyboard-hints-sep{color:var(--border);font-size:14px;line-height:1}.keyboard-hints-grid{flex-wrap:wrap;justify-content:center;gap:10px 24px;padding:14px 24px;display:flex}.keyboard-hint-row{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:10px;font-size:12px;display:flex}.keyboard-hint-desc{color:var(--text-muted)}.keyboard-key{border:1px solid var(--border);border-bottom:2px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:4px;align-items:center;padding:2px 8px;font-size:11px;display:inline-flex}.keyboard-key-mini{border:1px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:3px;align-items:center;padding:1px 5px;font-size:10px;display:inline-flex}.keyboard-hints-close{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;padding:4px 12px;font-size:11px}.keyboard-hints-close:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.app-main{padding-bottom:40px}.command-palette-backdrop{z-index:1000;background:#050508b3;justify-content:center;align-items:flex-start;padding-top:12vh;display:flex;position:fixed;inset:0}.command-palette{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius-lg);width:min(640px,100vw - 32px);box-shadow:var(--shadow);overflow:hidden}.command-palette-input{border:0;border-bottom:1px solid var(--border);width:100%;color:var(--text-primary);font:inherit;background:0 0;padding:16px}.command-palette-list{max-height:320px;list-style:none;overflow:auto}.command-palette-item{cursor:pointer;color:var(--text-primary);padding:12px 16px}.command-palette-item.active,.command-palette-item:hover{background:var(--neon-cyan-dim)}.command-palette-item.muted{color:var(--text-muted)}.statistics-toolbar,.stats-kpis,.stats-distribution{flex-wrap:wrap;gap:12px;display:flex}.statistics-panel-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px;display:flex}.statistics-back-btn{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff1a,#4f9cf912);border:1px solid #00f0ff33;padding:8px 14px;font-size:12px}.statistics-back-btn:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.statistics-back-btn:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.stats-kpis{margin:12px 0 16px}.stat-card{border:1px solid var(--border);border-radius:var(--radius);background:#ffffff05;flex-direction:column;gap:6px;min-width:140px;padding:14px;display:flex}.stat-card strong{font-size:24px}.stats-chart{gap:8px;margin-bottom:16px;display:grid}.stats-bar-row,.stats-list-row,.replay-toolbar{align-items:center;gap:10px;display:flex}.stats-bar-track{background:#ffffff0f;border-radius:999px;flex:1;height:10px;overflow:hidden}.stats-bar-fill{background:linear-gradient(90deg, var(--neon-cyan), var(--accent-blue));height:100%}.stats-distribution>div,.replay-state{flex:1;min-width:260px}.replay-debugger{border-color:#00f0ff29}.replay-toolbar{border:1px solid var(--border-muted);border-radius:var(--radius);background:linear-gradient(145deg,#0c0f1cf0,#080a12f5);justify-content:space-between;margin-bottom:12px;padding:12px 14px}.replay-toolbar button{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border:1px solid #00f0ff38;padding:8px 14px;font-size:12px}.replay-toolbar button:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.replay-toolbar button:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-toolbar button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;transform:none}.replay-controls-center{flex-direction:column;align-items:center;gap:10px;display:flex}.replay-auto-controls{flex-wrap:wrap;justify-content:center;align-items:center;gap:10px;display:flex}.replay-auto-toggle{min-width:160px}.replay-auto-toggle.active{background:linear-gradient(145deg,#00ff8826,#00cc6614);border-color:#00ff884d;box-shadow:0 0 14px #00ff8829}.replay-interval-picker{color:var(--text-secondary);font-family:var(--font-mono);align-items:center;gap:8px;font-size:12px;display:flex}.replay-interval-picker select{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);background:#080c14f2;border:1px solid #00f0ff33;padding:7px 10px;font-size:12px}.replay-interval-picker select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-interval-picker select:disabled{opacity:.6;cursor:not-allowed}.replay-compare-warning{border-radius:var(--radius);color:#ffe7b2;background:linear-gradient(145deg,#2a1d08db,#1c1204f0);border:1px solid #ffd1663d;justify-content:space-between;align-items:center;gap:14px;margin-bottom:12px;padding:12px 14px;display:flex}.replay-warning-hint{color:#ffe7b2c7;font-family:var(--font-mono);font-size:11px}.replay-compare-actions{flex-shrink:0;align-items:center;gap:10px;display:flex}.replay-warning-btn{border-radius:var(--radius);color:#ffe7b2;font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), background var(--transition), transform var(--transition);background:#ffd1660f;border:1px solid #ffd16638;padding:8px 12px;font-size:12px}.replay-warning-btn:hover{background:#ffd1661f;border-color:#ffd16675;transform:translateY(-1px)}.replay-warning-btn--primary{color:var(--text-primary);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border-color:#00f0ff47}.replay-warning-btn--primary:hover{border-color:var(--neon-cyan-mid);background:linear-gradient(145deg,#00f0ff2e,#4f9cf91a)}.replay-position{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;font-size:13px}.timeline-virtual-container{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(#080a12f5,#06080efa),radial-gradient(circle at top,#00f0ff0d,#0000 55%);margin-top:12px;box-shadow:inset 0 1px #00f0ff0d}.timeline-step{border:1px solid var(--border-muted);border-radius:var(--radius);min-width:136px;max-width:168px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#101422f5,#0a0c16fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;margin:0;padding:12px 14px;display:flex}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-step.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-step-badge{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:.8px;text-transform:uppercase;font-size:11px}.timeline-step-seq{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-type{color:var(--text-primary);letter-spacing:.2px;font-size:12px;font-weight:600}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);min-width:190px;max-width:220px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#141828f7,#0b0e18fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:6px;margin:0;padding:12px 16px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-group-chip.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-group-chip.expanded{background:linear-gradient(145deg,#181e30f7,#0c101cfc);border-color:#00f0ff59}.timeline-group-chip-top{justify-content:flex-start;align-items:center;gap:8px;width:100%;display:flex}.timeline-group-count{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;font-size:22px;line-height:1}.timeline-group-chevron{color:var(--text-muted);margin-left:auto;font-size:11px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);font-size:12px;font-weight:700;line-height:1.35}.timeline-group-range{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-compact{min-width:132px;max-width:156px;padding:10px 12px}.timeline-step-compact .timeline-step-type{font-size:11px}.timeline-step-compare{box-shadow:inset 0 0 0 1px var(--neon-magenta)}.timeline-track-item{align-items:center;gap:12px;display:flex}.timeline-track-item--group{align-items:flex-start}.timeline-expanded-head{justify-content:space-between}.timeline-collapse-btn{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;margin-left:auto;padding:6px 12px;font-size:12px}.timeline-collapse-btn:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan);background:#00f0ff0f}.diff-split-row--added .diff-split-new,.diff-split-row--changed .diff-split-new{background:#00ff880f}.diff-split-row--removed .diff-split-old,.diff-split-row--changed .diff-split-old{background:#ff33550f}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden} diff --git a/eventlens-api/src/main/resources/web/assets/index-BUz4IjMw.js b/eventlens-api/src/main/resources/web/assets/index-BUz4IjMw.js new file mode 100644 index 0000000..bb06dc3 --- /dev/null +++ b/eventlens-api/src/main/resources/web/assets/index-BUz4IjMw.js @@ -0,0 +1,14 @@ +var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},ee=Object.prototype.hasOwnProperty;function te(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ne(e,t){return te(e.type,t,e.props)}function T(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function re(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var ie=/\/+/g;function ae(e,t){return typeof e==`object`&&e&&e.key!=null?re(``+e.key):t.toString(36)}function oe(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function E(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,E(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+ae(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(ie,`$&/`)+`/`),E(o,r,i,``,function(e){return e})):o!=null&&(T(o)&&(o=ne(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(ie,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,T());else{var t=n(l);t!==null&&ae(x,t.startTime-e)}}var S=!1,C=-1,w=5,ee=-1;function te(){return g?!0:!(e.unstable_now()-eet&&te());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&ae(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?T():S=!1}}}var T;if(typeof y==`function`)T=function(){y(ne)};else if(typeof MessageChannel<`u`){var re=new MessageChannel,ie=re.port2;re.port1.onmessage=ne,T=function(){ie.postMessage(null)}}else T=function(){_(ne,0)};function ae(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,ae(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,T()))),r},e.unstable_shouldYield=te,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=m()})),g=o((e=>{var t=p(),n=d(),r=h();function i(e){var t=`https://react.dev/errors/`+e;if(1ue||(e.current=le[ue],le[ue]=null,ue--)}function j(e,t){ue++,le[ue]=e.current,e.current=t}var fe=de(null),pe=de(null),me=de(null),he=de(null);function ge(e,t){switch(j(me,t),j(pe,e),j(fe,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Vd(t),e=Hd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}A(fe),j(fe,e)}function _e(){A(fe),A(pe),A(me)}function ve(e){e.memoizedState!==null&&j(he,e);var t=fe.current,n=Hd(t,e.type);t!==n&&(j(pe,e),j(fe,n))}function ye(e){pe.current===e&&(A(fe),A(pe)),he.current===e&&(A(he),Qf._currentValue=ce)}var be,xe;function Se(e){if(be===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);be=t&&t[1]||``,xe=-1)`:-1i||c[r]!==l[i]){var u=` +`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{Ce=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?Se(n):``}function Te(e,t){switch(e.tag){case 26:case 27:case 5:return Se(e.type);case 16:return Se(`Lazy`);case 13:return e.child!==t&&t!==null?Se(`Suspense Fallback`):Se(`Suspense`);case 19:return Se(`SuspenseList`);case 0:case 15:return we(e.type,!1);case 11:return we(e.type.render,!1);case 1:return we(e.type,!0);case 31:return Se(`Activity`);default:return``}}function Ee(e){try{var t=``,n=null;do t+=Te(e,n),n=e,e=e.return;while(e);return t}catch(e){return` +Error generating stack: `+e.message+` +`+e.stack}}var De=Object.prototype.hasOwnProperty,Oe=t.unstable_scheduleCallback,ke=t.unstable_cancelCallback,Ae=t.unstable_shouldYield,je=t.unstable_requestPaint,Me=t.unstable_now,Ne=t.unstable_getCurrentPriorityLevel,Pe=t.unstable_ImmediatePriority,Fe=t.unstable_UserBlockingPriority,Ie=t.unstable_NormalPriority,Le=t.unstable_LowPriority,Re=t.unstable_IdlePriority,ze=t.log,Be=t.unstable_setDisableYieldValue,Ve=null,He=null;function Ue(e){if(typeof ze==`function`&&Be(e),He&&typeof He.setStrictMode==`function`)try{He.setStrictMode(Ve,e)}catch{}}var We=Math.clz32?Math.clz32:qe,Ge=Math.log,Ke=Math.LN2;function qe(e){return e>>>=0,e===0?32:31-(Ge(e)/Ke|0)|0}var Je=256,Ye=262144,Xe=4194304;function M(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function N(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=M(n))):i=M(o):i=M(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=M(n))):i=M(o)):i=M(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function Ze(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Qe(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function $e(){var e=Xe;return Xe<<=1,!(Xe&62914560)&&(Xe=4194304),e}function et(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function tt(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function nt(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),mn=!1;if(pn)try{var hn={};Object.defineProperty(hn,`passive`,{get:function(){mn=!0}}),window.addEventListener(`test`,hn,hn),window.removeEventListener(`test`,hn,hn)}catch{mn=!1}var gn=null,_n=null,vn=null;function yn(){if(vn)return vn;var e,t=_n,n=t.length,r,i=`value`in gn?gn.value:gn.textContent,a=i.length;for(e=0;e=Yn),Qn=` `,$n=!1;function er(e,t){switch(e){case`keyup`:return qn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function tr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var nr=!1;function rr(e,t){switch(e){case`compositionend`:return tr(t);case`keypress`:return t.which===32?($n=!0,Qn):null;case`textInput`:return e=t.data,e===Qn&&$n?null:e;default:return null}}function ir(e,t){if(nr)return e===`compositionend`||!Jn&&er(e,t)?(e=yn(),vn=_n=gn=null,nr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Er(n)}}function Or(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Or(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function kr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Bt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Bt(e.document)}return t}function Ar(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var jr=pn&&`documentMode`in document&&11>=document.documentMode,Mr=null,Nr=null,Pr=null,Fr=!1;function Ir(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Fr||Mr==null||Mr!==Bt(r)||(r=Mr,`selectionStart`in r&&Ar(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Pr&&Tr(Pr,r)||(Pr=r,r=Ed(Nr,`onSelect`),0>=o,i-=o,Di=1<<32-We(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),V&&z(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),V&&z(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return V&&z(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),V&&z(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===T&&Ea(l)===r.type){n(e,r.sibling),c=a(r,o.props),Na(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=mi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=pi(o.type,o.key,o.props,null,e.mode,c),Na(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=_i(o,e.mode,c),c.return=e,e=c}return s(e);case T:return o=Ea(o),b(e,r,o,c)}if(se(o))return h(e,r,o,c);if(oe(o)){if(l=oe(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Ma(o),c);if(o.$$typeof===C)return b(e,r,ea(e,o),c);Pa(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=hi(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{ja=0;var i=b(e,t,n,r);return Aa=null,i}catch(t){if(t===ba||t===Sa)throw t;var a=li(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Ia=Fa(!0),La=Fa(!1),Ra=!1;function za(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ba(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Va(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Ha(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,K&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=si(e),oi(e,null,n),t}return ri(e,r,t,n),si(e)}function Ua(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,it(e,n)}}function Wa(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Ga=!1;function Ka(){if(Ga){var e=da;if(e!==null)throw e}}function qa(e,t,n,r){Ga=!1;var i=e.updateQueue;Ra=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(Y&f)===f:(r&f)===f){f!==0&&f===ua&&(Ga=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=m({},d,f);break a;case 2:Ra=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Gl|=o,e.lanes=o,e.memoizedState=d}}function Ja(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Ya(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=O.T,s={};O.T=s,Ps(e,!1,t,n);try{var c=i(),l=O.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ns(e,t,ma(c,r),pu(e)):Ns(e,t,r,pu(e))}catch(n){Ns(e,t,{then:function(){},status:`rejected`,reason:n},pu())}finally{k.p=a,o!==null&&s.types!==null&&(o.types=s.types),O.T=o}}function Cs(){}function ws(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Ts(e).queue;Ss(e,a,t,ce,n===null?Cs:function(){return Es(e),n(r)})}function Ts(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:ce,baseState:ce,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Fo,lastRenderedState:ce},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Fo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Es(e){var t=Ts(e);t.next===null&&(t=e.alternate.memoizedState),Ns(e,t.next.queue,{},pu())}function Ds(){return $i(Qf)}function Os(){return Ao().memoizedState}function ks(){return Ao().memoizedState}function As(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=pu();e=Va(n);var r=Ha(t,e,n);r!==null&&(hu(r,t,n),Ua(r,t,n)),t={cache:oa()},e.payload=t;return}t=t.return}}function js(e,t,n){var r=pu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Fs(e)?Is(t,n):(n=ii(e,t,n,r),n!==null&&(hu(n,e,r),Ls(n,t,r)))}function Ms(e,t,n){Ns(e,t,n,pu())}function Ns(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Fs(e))Is(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,wr(s,o))return ri(e,t,i,0),q===null&&ni(),!1}catch{}if(n=ii(e,t,i,r),n!==null)return hu(n,e,r),Ls(n,t,r),!0}return!1}function Ps(e,t,n,r){if(r={lane:2,revertLane:dd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Fs(e)){if(t)throw Error(i(479))}else t=ii(e,n,r,2),t!==null&&hu(t,e,2)}function Fs(e){var t=e.alternate;return e===H||t!==null&&t===H}function Is(e,t){mo=po=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ls(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,it(e,n)}}var Rs={readContext:$i,use:No,useCallback:bo,useContext:bo,useEffect:bo,useImperativeHandle:bo,useLayoutEffect:bo,useInsertionEffect:bo,useMemo:bo,useReducer:bo,useRef:bo,useState:bo,useDebugValue:bo,useDeferredValue:bo,useTransition:bo,useSyncExternalStore:bo,useId:bo,useHostTransitionStatus:bo,useFormState:bo,useActionState:bo,useOptimistic:bo,useMemoCache:bo,useCacheRefresh:bo};Rs.useEffectEvent=bo;var zs={readContext:$i,use:No,useCallback:function(e,t){return ko().memoizedState=[e,t===void 0?null:t],e},useContext:$i,useEffect:ls,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),ss(4194308,4,hs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ss(4194308,4,e,t)},useInsertionEffect:function(e,t){ss(4,2,e,t)},useMemo:function(e,t){var n=ko();t=t===void 0?null:t;var r=e();if(ho){Ue(!0);try{e()}finally{Ue(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=ko();if(n!==void 0){var i=n(t);if(ho){Ue(!0);try{n(t)}finally{Ue(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=js.bind(null,H,e),[r.memoizedState,e]},useRef:function(e){var t=ko();return e={current:e},t.memoizedState=e},useState:function(e){e=Go(e);var t=e.queue,n=Ms.bind(null,H,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:_s,useDeferredValue:function(e,t){return bs(ko(),e,t)},useTransition:function(){var e=Go(!1);return e=Ss.bind(null,H,e.queue,!0,!1),ko().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=H,a=ko();if(V){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),q===null)throw Error(i(349));Y&127||Bo(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,ls(Ho.bind(null,r,o,e),[e]),r.flags|=2048,as(9,{destroy:void 0},Vo.bind(null,r,o,n,t),null),n},useId:function(){var e=ko(),t=q.identifierPrefix;if(V){var n=R,r=Di;n=(r&~(1<<32-We(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=go++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[dt]=t,o[ft]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Pd(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Nc(t)}}return W(t),Pc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Nc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=me.current,zi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Mi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[dt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Md(e.nodeValue,n)),e||Ii(t,!0)}else e=Bd(e).createTextNode(r),e[dt]=t,t.stateNode=e}return W(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=zi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[dt]=t}else Bi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;W(t),e=!1}else n=Vi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(so(t),t):(so(t),null);if(t.flags&128)throw Error(i(558))}return W(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=zi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[dt]=t}else Bi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;W(t),a=!1}else a=Vi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(so(t),t):(so(t),null)}return so(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Ic(t,t.updateQueue),W(t),null);case 4:return _e(),e===null&&Sd(t.stateNode.containerInfo),W(t),null;case 10:return qi(t.type),W(t),null;case 19:if(A(co),r=t.memoizedState,r===null)return W(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Lc(r,!1);else{if(Wl!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=lo(e),o!==null){for(t.flags|=128,Lc(r,!1),e=o.updateQueue,t.updateQueue=e,Ic(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)fi(n,e),n=n.sibling;return j(co,co.current&1|2),V&&z(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&Me()>tu&&(t.flags|=128,a=!0,Lc(r,!1),t.lanes=4194304)}else{if(!a)if(e=lo(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Ic(t,e),Lc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!V)return W(t),null}else 2*Me()-r.renderingStartTime>tu&&n!==536870912&&(t.flags|=128,a=!0,Lc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(W(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=Me(),e.sibling=null,n=co.current,j(co,a?n&1|2:n&1),V&&z(t,r.treeForkCount),e);case 22:case 23:return so(t),eo(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(W(t),t.subtreeFlags&6&&(t.flags|=8192)):W(t),n=t.updateQueue,n!==null&&Ic(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&A(ga),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),qi(aa),W(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function zc(e,t){switch(Ai(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return qi(aa),_e(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return ye(t),null;case 31:if(t.memoizedState!==null){if(so(t),t.alternate===null)throw Error(i(340));Bi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(so(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Bi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return A(co),null;case 4:return _e(),null;case 10:return qi(t.type),null;case 22:case 23:return so(t),eo(),e!==null&&A(ga),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return qi(aa),null;case 25:return null;default:return null}}function Bc(e,t){switch(Ai(t),t.tag){case 3:qi(aa),_e();break;case 26:case 27:case 5:ye(t);break;case 4:_e();break;case 31:t.memoizedState!==null&&so(t);break;case 13:so(t);break;case 19:A(co);break;case 10:qi(t.type);break;case 22:case 23:so(t),eo(),e!==null&&A(ga);break;case 24:qi(aa)}}function Vc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Z(t,t.return,e)}}function Hc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Z(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Z(t,t.return,e)}}function Uc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Ya(t,n)}catch(t){Z(e,e.return,t)}}}function Wc(e,t,n){n.props=Ks(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Z(e,t,n)}}function Gc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Z(e,t,n)}}function Kc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){Z(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Z(e,t,n)}else n.current=null}function qc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Z(e,e.return,t)}}function Jc(e,t,n){try{var r=e.stateNode;Fd(r,e.type,n,t),r[ft]=t}catch(t){Z(e,e.return,t)}}function Yc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Zd(e.type)||e.tag===4}function Xc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Yc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Zd(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Zc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=rn));else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Zc(e,t,n),e=e.sibling;e!==null;)Zc(e,t,n),e=e.sibling}function Qc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(Qc(e,t,n),e=e.sibling;e!==null;)Qc(e,t,n),e=e.sibling}function $c(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Pd(t,r,n),t[dt]=e,t[ft]=n}catch(t){Z(e,e.return,t)}}var el=!1,tl=!1,nl=!1,rl=typeof WeakSet==`function`?WeakSet:Set,il=null;function al(e,t){if(e=e.containerInfo,Rd=sp,e=kr(e),Ar(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(zd={focusedElem:e,selectionRange:n},sp=!1,il=t;il!==null;)if(t=il,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,il=e;else for(;il!==null;){switch(t=il,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Pd(o,r,n),o[dt]=e,P(o),r=o;break a;case`link`:var s=Vf(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Dr(s,h),v=Dr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,O.T=null,n=lu,lu=null;var o=au,s=su;if(iu=0,ou=au=null,su=0,K&6)throw Error(i(331));var c=K;if(K|=4,Pl(o.current),El(o,o.current,s,n),K=c,id(0,!1),He&&typeof He.onPostCommitFiberRoot==`function`)try{He.onPostCommitFiberRoot(Ve,o)}catch{}return!0}finally{k.p=a,O.T=r,Vu(e,t)}}function Wu(e,t,n){t=yi(n,t),t=Qs(e.stateNode,t,2),e=Ha(e,t,2),e!==null&&(tt(e,2),rd(e))}function Z(e,t,n){if(e.tag===3)Wu(e,e,n);else for(;t!==null;){if(t.tag===3){Wu(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(ru===null||!ru.has(r))){e=yi(n,e),n=$s(2),r=Ha(t,n,2),r!==null&&(ec(n,r,t,e),tt(r,2),rd(r));break}}t=t.return}}function Gu(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Rl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Hl=!0,i.add(n),e=Ku.bind(null,e,t,n),t.then(e,e))}function Ku(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,q===e&&(Y&n)===n&&(Wl===4||Wl===3&&(Y&62914560)===Y&&300>Me()-$l?!(K&2)&&Su(e,0):ql|=n,Yl===Y&&(Yl=0)),rd(e)}function qu(e,t){t===0&&(t=$e()),e=ai(e,t),e!==null&&(tt(e,t),rd(e))}function Ju(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),qu(e,n)}function Yu(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),qu(e,n)}function Xu(e,t){return Oe(e,t)}var Zu=null,Qu=null,$u=!1,ed=!1,td=!1,nd=0;function rd(e){e!==Qu&&e.next===null&&(Qu===null?Zu=Qu=e:Qu=Qu.next=e),ed=!0,$u||($u=!0,ud())}function id(e,t){if(!td&&ed){td=!0;do for(var n=!1,r=Zu;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-We(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,ld(r,a))}else a=Y,a=N(r,r===q?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||Ze(r,a)||(n=!0,ld(r,a));r=r.next}while(n);td=!1}}function ad(){od()}function od(){ed=$u=!1;var e=0;nd!==0&&Gd()&&(e=nd);for(var t=Me(),n=null,r=Zu;r!==null;){var i=r.next,a=sd(r,t);a===0?(r.next=null,n===null?Zu=i:n.next=i,i===null&&(Qu=n)):(n=r,(e!==0||a&3)&&(ed=!0)),r=i}iu!==0&&iu!==5||id(e,!1),nd!==0&&(nd=0)}function sd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Id(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function xf(e,t,n){var r=bf;if(r&&typeof t==`string`&&t){var i=Ht(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),hf.has(i)||(hf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Pd(t,`link`,e),P(t),r.head.appendChild(t)))}}function Sf(e){_f.D(e),xf(`dns-prefetch`,e,null)}function Cf(e,t){_f.C(e,t),xf(`preconnect`,e,t)}function wf(e,t,n){_f.L(e,t,n);var r=bf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Ht(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Ht(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Ht(n.imageSizes)+`"]`)):i+=`[href="`+Ht(e)+`"]`;var a=i;switch(t){case`style`:a=Af(e);break;case`script`:a=Pf(e)}mf.has(a)||(e=m({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),mf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(jf(a))||t===`script`&&r.querySelector(Ff(a))||(t=r.createElement(`link`),Pd(t,`link`,e),P(t),r.head.appendChild(t)))}}function Tf(e,t){_f.m(e,t);var n=bf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Ht(r)+`"][href="`+Ht(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Pf(e)}if(!mf.has(a)&&(e=m({rel:`modulepreload`,href:e},t),mf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Ff(a)))return}r=n.createElement(`link`),Pd(r,`link`,e),P(r),n.head.appendChild(r)}}}function Ef(e,t,n){_f.S(e,t,n);var r=bf;if(r&&e){var i=Ct(r).hoistableStyles,a=Af(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(jf(a)))s.loading=5;else{e=m({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=mf.get(a))&&Rf(e,n);var c=o=r.createElement(`link`);P(c),Pd(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Lf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Df(e,t){_f.X(e,t);var n=bf;if(n&&e){var r=Ct(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),P(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Of(e,t){_f.M(e,t);var n=bf;if(n&&e){var r=Ct(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0,type:`module`},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),P(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function kf(e,t,n,r){var a=(a=me.current)?gf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Af(n.href),n=Ct(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Af(n.href);var o=Ct(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(jf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),mf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},mf.set(e,n),o||Nf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Pf(n),n=Ct(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Af(e){return`href="`+Ht(e)+`"`}function jf(e){return`link[rel="stylesheet"][`+e+`]`}function Mf(e){return m({},e,{"data-precedence":e.precedence,precedence:null})}function Nf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Pd(t,`link`,n),P(t),e.head.appendChild(t))}function Pf(e){return`[src="`+Ht(e)+`"]`}function Ff(e){return`script[async]`+e}function If(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Ht(n.href)+`"]`);if(r)return t.instance=r,P(r),r;var a=m({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),P(r),Pd(r,`style`,a),Lf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Af(n.href);var o=e.querySelector(jf(a));if(o)return t.state.loading|=4,t.instance=o,P(o),o;r=Mf(n),(a=mf.get(a))&&Rf(r,a),o=(e.ownerDocument||e).createElement(`link`),P(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Pd(o,`link`,r),t.state.loading|=4,Lf(o,n.precedence,e),t.instance=o;case`script`:return o=Pf(n.src),(a=e.querySelector(Ff(o)))?(t.instance=a,P(a),a):(r=n,(a=mf.get(o))&&(r=m({},n),zf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),P(a),Pd(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Lf(r,n.precedence,e));return t.instance}function Lf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Uf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Wf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Gf(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Af(r.href),a=t.querySelector(jf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Jf.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,P(a);return}a=t.ownerDocument||t,r=Mf(r),(i=mf.get(i))&&Rf(r,i),a=a.createElement(`link`),P(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Pd(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Jf.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var Kf=0;function qf(e,t){return e.stylesheets&&e.count===0&&Xf(e,e.stylesheets),0Kf?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Jf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Xf(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Yf=null;function Xf(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Yf=new Map,t.forEach(Zf,e),Yf=null,Jf.call(e))}function Zf(e,t){if(!(t.state.loading&4)){var n=Yf.get(e);if(n)var r=n.get(null);else{n=new Map,Yf.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=g()})),v=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},y=new class extends v{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e();return window.addEventListener(`visibilitychange`,t,!1),()=>{window.removeEventListener(`visibilitychange`,t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(e=>{typeof e==`boolean`?this.setFocused(e):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){let e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e==`boolean`?this.#e:globalThis.document?.visibilityState!==`hidden`}},b={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},x=new class{#e=b;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}};function S(e){setTimeout(e,0)}var C=typeof window>`u`||`Deno`in globalThis;function w(){}function ee(e,t){return typeof e==`function`?e(t):e}function te(e){return typeof e==`number`&&e>=0&&e!==1/0}function ne(e,t){return Math.max(e+(t||0)-Date.now(),0)}function T(e,t){return typeof e==`function`?e(t):e}function re(e,t){return typeof e==`function`?e(t):e}function ie(e,t){let{type:n=`all`,exact:r,fetchStatus:i,predicate:a,queryKey:o,stale:s}=e;if(o){if(r){if(t.queryHash!==oe(o,t.options))return!1}else if(!D(t.queryKey,o))return!1}if(n!==`all`){let e=t.isActive();if(n===`active`&&!e||n===`inactive`&&e)return!1}return!(typeof s==`boolean`&&t.isStale()!==s||i&&i!==t.state.fetchStatus||a&&!a(t))}function ae(e,t){let{exact:n,status:r,predicate:i,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(E(t.options.mutationKey)!==E(a))return!1}else if(!D(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function oe(e,t){return(t?.queryKeyHashFn||E)(e)}function E(e){return JSON.stringify(e,(e,t)=>le(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}function D(e,t){return e===t?!0:typeof e==typeof t&&e&&t&&typeof e==`object`&&typeof t==`object`?Object.keys(t).every(n=>D(e[n],t[n])):!1}var se=Object.prototype.hasOwnProperty;function O(e,t,n=0){if(e===t)return e;if(n>500)return t;let r=ce(e)&&ce(t);if(!r&&!(le(e)&&le(t)))return t;let i=(r?e:Object.keys(e)).length,a=r?t:Object.keys(t),o=a.length,s=r?Array(o):{},c=0;for(let l=0;l{x.setTimeout(t,e)})}function A(e,t,n){return typeof n.structuralSharing==`function`?n.structuralSharing(e,t):n.structuralSharing===!1?t:O(e,t)}function j(e,t,n=0){let r=[...e,t];return n&&r.length>n?r.slice(1):r}function fe(e,t,n=0){let r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var pe=Symbol();function me(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===pe?()=>Promise.reject(Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function he(e,t){return typeof e==`function`?e(...t):!!e}function ge(e,t,n){let r=!1,i;return Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(i??=t(),r?i:(r=!0,i.aborted?n():i.addEventListener(`abort`,n,{once:!0}),i))}),e}var _e=(()=>{let e=()=>C;return{isServer(){return e()},setIsServer(t){e=t}}})();function ve(){let e,t,n=new Promise((n,r)=>{e=n,t=r});n.status=`pending`,n.catch(()=>{});function r(e){Object.assign(n,e),delete n.resolve,delete n.reject}return n.resolve=t=>{r({status:`fulfilled`,value:t}),e(t)},n.reject=e=>{r({status:`rejected`,reason:e}),t(e)},n}var ye=S;function be(){let e=[],t=0,n=e=>{e()},r=e=>{e()},i=ye,a=r=>{t?e.push(r):i(()=>{n(r)})},o=()=>{let t=e;e=[],t.length&&i(()=>{r(()=>{t.forEach(e=>{n(e)})})})};return{batch:e=>{let n;t++;try{n=e()}finally{t--,t||o()}return n},batchCalls:e=>(...t)=>{a(()=>{e(...t)})},schedule:a,setNotifyFunction:e=>{n=e},setBatchNotifyFunction:e=>{r=e},setScheduler:e=>{i=e}}}var xe=be(),Se=new class extends v{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e(!0),n=()=>e(!1);return window.addEventListener(`online`,t,!1),window.addEventListener(`offline`,n,!1),()=>{window.removeEventListener(`online`,t),window.removeEventListener(`offline`,n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(t=>{t(e)}))}isOnline(){return this.#e}};function Ce(e){return Math.min(1e3*2**e,3e4)}function we(e){return(e??`online`)===`online`?Se.isOnline():!0}var Te=class extends Error{constructor(e){super(`CancelledError`),this.revert=e?.revert,this.silent=e?.silent}};function Ee(e){let t=!1,n=0,r,i=ve(),a=()=>i.status!==`pending`,o=t=>{if(!a()){let n=new Te(t);f(n),e.onCancel?.(n)}},s=()=>{t=!0},c=()=>{t=!1},l=()=>y.isFocused()&&(e.networkMode===`always`||Se.isOnline())&&e.canRun(),u=()=>we(e.networkMode)&&e.canRun(),d=e=>{a()||(r?.(),i.resolve(e))},f=e=>{a()||(r?.(),i.reject(e))},p=()=>new Promise(t=>{r=e=>{(a()||l())&&t(e)},e.onPause?.()}).then(()=>{r=void 0,a()||e.onContinue?.()}),m=()=>{if(a())return;let r,i=n===0?e.initialPromise:void 0;try{r=i??e.fn()}catch(e){r=Promise.reject(e)}Promise.resolve(r).then(d).catch(r=>{if(a())return;let i=e.retry??(_e.isServer()?0:3),o=e.retryDelay??Ce,s=typeof o==`function`?o(n,r):o,c=i===!0||typeof i==`number`&&nl()?void 0:p()).then(()=>{t?f(r):m()})})};return{promise:i,status:()=>i.status,cancel:o,continue:()=>(r?.(),i),cancelRetry:s,continueRetry:c,canStart:u,start:()=>(u()?m():p().then(m),i)}}var De=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),te(this.gcTime)&&(this.#e=x.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(_e.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e&&=(x.clearTimeout(this.#e),void 0)}},Oe=class extends De{#e;#t;#n;#r;#i;#a;#o;constructor(e){super(),this.#o=!1,this.#a=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#r=e.client,this.#n=this.#r.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#e=je(this.options),this.state=e.state??this.#e,this.scheduleGc()}get meta(){return this.options.meta}get promise(){return this.#i?.promise}setOptions(e){if(this.options={...this.#a,...e},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){let e=je(this.options);e.data!==void 0&&(this.setState(Ae(e.data,e.dataUpdatedAt)),this.#e=e)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus===`idle`&&this.#n.remove(this)}setData(e,t){let n=A(this.state.data,e,this.options);return this.#c({data:n,type:`success`,dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e,t){this.#c({type:`setState`,state:e,setStateOptions:t})}cancel(e){let t=this.#i?.promise;return this.#i?.cancel(e),t?t.then(w).catch(w):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#e}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>re(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===pe||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>T(e.options.staleTime,this)===`static`):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e===`static`?!1:this.state.isInvalidated?!0:!ne(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(e=>e.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#i?.continue()}onOnline(){this.observers.find(e=>e.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#i?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#i&&(this.#o||this.#s()?this.#i.cancel({revert:!0}):this.#i.cancelRetry()),this.scheduleGc()),this.#n.notify({type:`observerRemoved`,query:this,observer:e}))}getObserversCount(){return this.observers.length}#s(){return this.state.fetchStatus===`paused`&&this.state.status===`pending`}invalidate(){this.state.isInvalidated||this.#c({type:`invalidate`})}async fetch(e,t){if(this.state.fetchStatus!==`idle`&&this.#i?.status()!==`rejected`){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#i)return this.#i.continueRetry(),this.#i.promise}if(e&&this.setOptions(e),!this.options.queryFn){let e=this.observers.find(e=>e.options.queryFn);e&&this.setOptions(e.options)}let n=new AbortController,r=e=>{Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(this.#o=!0,n.signal)})},i=()=>{let e=me(this.options,t),n=(()=>{let e={client:this.#r,queryKey:this.queryKey,meta:this.meta};return r(e),e})();return this.#o=!1,this.options.persister?this.options.persister(e,n,this):e(n)},a=(()=>{let e={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#r,state:this.state,fetchFn:i};return r(e),e})();this.options.behavior?.onFetch(a,this),this.#t=this.state,(this.state.fetchStatus===`idle`||this.state.fetchMeta!==a.fetchOptions?.meta)&&this.#c({type:`fetch`,meta:a.fetchOptions?.meta}),this.#i=Ee({initialPromise:t?.initialPromise,fn:a.fetchFn,onCancel:e=>{e instanceof Te&&e.revert&&this.setState({...this.#t,fetchStatus:`idle`}),n.abort()},onFail:(e,t)=>{this.#c({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#c({type:`pause`})},onContinue:()=>{this.#c({type:`continue`})},retry:a.options.retry,retryDelay:a.options.retryDelay,networkMode:a.options.networkMode,canRun:()=>!0});try{let e=await this.#i.start();if(e===void 0)throw Error(`${this.queryHash} data is undefined`);return this.setData(e),this.#n.config.onSuccess?.(e,this),this.#n.config.onSettled?.(e,this.state.error,this),e}catch(e){if(e instanceof Te){if(e.silent)return this.#i.promise;if(e.revert){if(this.state.data===void 0)throw e;return this.state.data}}throw this.#c({type:`error`,error:e}),this.#n.config.onError?.(e,this),this.#n.config.onSettled?.(this.state.data,e,this),e}finally{this.scheduleGc()}}#c(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case`pause`:return{...t,fetchStatus:`paused`};case`continue`:return{...t,fetchStatus:`fetching`};case`fetch`:return{...t,...ke(t.data,this.options),fetchMeta:e.meta??null};case`success`:let n={...t,...Ae(e.data,e.dataUpdatedAt),dataUpdateCount:t.dataUpdateCount+1,...!e.manual&&{fetchStatus:`idle`,fetchFailureCount:0,fetchFailureReason:null}};return this.#t=e.manual?n:void 0,n;case`error`:let r=e.error;return{...t,error:r,errorUpdateCount:t.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:t.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:`idle`,status:`error`,isInvalidated:!0};case`invalidate`:return{...t,isInvalidated:!0};case`setState`:return{...t,...e.state}}})(this.state),xe.batch(()=>{this.observers.forEach(e=>{e.onQueryUpdate()}),this.#n.notify({query:this,type:`updated`,action:e})})}};function ke(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:we(t.networkMode)?`fetching`:`paused`,...e===void 0&&{error:null,status:`pending`}}}function Ae(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:`success`}}function je(e){let t=typeof e.initialData==`function`?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt==`function`?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?`success`:`pending`,fetchStatus:`idle`}}var Me=class extends v{constructor(e,t){super(),this.options=t,this.#e=e,this.#s=null,this.#o=ve(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#i;#a;#o;#s;#c;#l;#u;#d;#f;#p;#m=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),Pe(this.#t,this.options)?this.#h():this.updateResult(),this.#y())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return Fe(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return Fe(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#b(),this.#x(),this.#t.removeObserver(this)}setOptions(e){let t=this.options,n=this.#t;if(this.options=this.#e.defaultQueryOptions(e),this.options.enabled!==void 0&&typeof this.options.enabled!=`boolean`&&typeof this.options.enabled!=`function`&&typeof re(this.options.enabled,this.#t)!=`boolean`)throw Error(`Expected enabled to be a boolean or a callback that returns a boolean`);this.#S(),this.#t.setOptions(this.options),t._defaulted&&!k(this.options,t)&&this.#e.getQueryCache().notify({type:`observerOptionsUpdated`,query:this.#t,observer:this});let r=this.hasListeners();r&&Ie(this.#t,n,this.options,t)&&this.#h(),this.updateResult(),r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||T(this.options.staleTime,this.#t)!==T(t.staleTime,this.#t))&&this.#g();let i=this.#_();r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||i!==this.#p)&&this.#v(i)}getOptimisticResult(e){let t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return Re(this,n)&&(this.#r=n,this.#a=this.options,this.#i=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(e,n)=>(this.trackProp(n),t?.(n),n===`promise`&&(this.trackProp(`data`),!this.options.experimental_prefetchInRender&&this.#o.status===`pending`&&this.#o.reject(Error(`experimental_prefetchInRender feature flag is not enabled`))),Reflect.get(e,n))})}trackProp(e){this.#m.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#h({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#h(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(w)),t}#g(){this.#b();let e=T(this.options.staleTime,this.#t);if(_e.isServer()||this.#r.isStale||!te(e))return;let t=ne(this.#r.dataUpdatedAt,e)+1;this.#d=x.setTimeout(()=>{this.#r.isStale||this.updateResult()},t)}#_(){return(typeof this.options.refetchInterval==`function`?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#v(e){this.#x(),this.#p=e,!(_e.isServer()||re(this.options.enabled,this.#t)===!1||!te(this.#p)||this.#p===0)&&(this.#f=x.setInterval(()=>{(this.options.refetchIntervalInBackground||y.isFocused())&&this.#h()},this.#p))}#y(){this.#g(),this.#v(this.#_())}#b(){this.#d&&=(x.clearTimeout(this.#d),void 0)}#x(){this.#f&&=(x.clearInterval(this.#f),void 0)}createResult(e,t){let n=this.#t,r=this.options,i=this.#r,a=this.#i,o=this.#a,s=e===n?this.#n:e.state,{state:c}=e,l={...c},u=!1,d;if(t._optimisticResults){let i=this.hasListeners(),a=!i&&Pe(e,t),o=i&&Ie(e,n,t,r);(a||o)&&(l={...l,...ke(c.data,e.options)}),t._optimisticResults===`isRestoring`&&(l.fetchStatus=`idle`)}let{error:f,errorUpdatedAt:p,status:m}=l;d=l.data;let h=!1;if(t.placeholderData!==void 0&&d===void 0&&m===`pending`){let e;i?.isPlaceholderData&&t.placeholderData===o?.placeholderData?(e=i.data,h=!0):e=typeof t.placeholderData==`function`?t.placeholderData(this.#u?.state.data,this.#u):t.placeholderData,e!==void 0&&(m=`success`,d=A(i?.data,e,t),u=!0)}if(t.select&&d!==void 0&&!h)if(i&&d===a?.data&&t.select===this.#c)d=this.#l;else try{this.#c=t.select,d=t.select(d),d=A(i?.data,d,t),this.#l=d,this.#s=null}catch(e){this.#s=e}this.#s&&(f=this.#s,d=this.#l,p=Date.now(),m=`error`);let g=l.fetchStatus===`fetching`,_=m===`pending`,v=m===`error`,y=_&&g,b=d!==void 0,x={status:m,fetchStatus:l.fetchStatus,isPending:_,isSuccess:m===`success`,isError:v,isInitialLoading:y,isLoading:y,data:d,dataUpdatedAt:l.dataUpdatedAt,error:f,errorUpdatedAt:p,failureCount:l.fetchFailureCount,failureReason:l.fetchFailureReason,errorUpdateCount:l.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:l.dataUpdateCount>s.dataUpdateCount||l.errorUpdateCount>s.errorUpdateCount,isFetching:g,isRefetching:g&&!_,isLoadingError:v&&!b,isPaused:l.fetchStatus===`paused`,isPlaceholderData:u,isRefetchError:v&&b,isStale:Le(e,t),refetch:this.refetch,promise:this.#o,isEnabled:re(t.enabled,e)!==!1};if(this.options.experimental_prefetchInRender){let t=x.data!==void 0,r=x.status===`error`&&!t,i=e=>{r?e.reject(x.error):t&&e.resolve(x.data)},a=()=>{i(this.#o=x.promise=ve())},o=this.#o;switch(o.status){case`pending`:e.queryHash===n.queryHash&&i(o);break;case`fulfilled`:(r||x.data!==o.value)&&a();break;case`rejected`:(!r||x.error!==o.reason)&&a();break}}return x}updateResult(){let e=this.#r,t=this.createResult(this.#t,this.options);this.#i=this.#t.state,this.#a=this.options,this.#i.data!==void 0&&(this.#u=this.#t),!k(t,e)&&(this.#r=t,this.#C({listeners:(()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,n=typeof t==`function`?t():t;if(n===`all`||!n&&!this.#m.size)return!0;let r=new Set(n??this.#m);return this.options.throwOnError&&r.add(`error`),Object.keys(this.#r).some(t=>{let n=t;return this.#r[n]!==e[n]&&r.has(n)})})()}))}#S(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;let t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#y()}#C(e){xe.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:`observerResultsUpdated`})})}};function Ne(e,t){return re(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status===`error`&&t.retryOnMount===!1)}function Pe(e,t){return Ne(e,t)||e.state.data!==void 0&&Fe(e,t,t.refetchOnMount)}function Fe(e,t,n){if(re(t.enabled,e)!==!1&&T(t.staleTime,e)!==`static`){let r=typeof n==`function`?n(e):n;return r===`always`||r!==!1&&Le(e,t)}return!1}function Ie(e,t,n,r){return(e!==t||re(r.enabled,e)===!1)&&(!n.suspense||e.state.status!==`error`)&&Le(e,n)}function Le(e,t){return re(t.enabled,e)!==!1&&e.isStaleByTime(T(t.staleTime,e))}function Re(e,t){return!k(e.getCurrentResult(),t)}function ze(e){return{onFetch:(t,n)=>{let r=t.options,i=t.fetchOptions?.meta?.fetchMore?.direction,a=t.state.data?.pages||[],o=t.state.data?.pageParams||[],s={pages:[],pageParams:[]},c=0,l=async()=>{let n=!1,l=e=>{ge(e,()=>t.signal,()=>n=!0)},u=me(t.options,t.fetchOptions),d=async(e,r,i)=>{if(n)return Promise.reject();if(r==null&&e.pages.length)return Promise.resolve(e);let a=await u((()=>{let e={client:t.client,queryKey:t.queryKey,pageParam:r,direction:i?`backward`:`forward`,meta:t.options.meta};return l(e),e})()),{maxPages:o}=t.options,s=i?fe:j;return{pages:s(e.pages,a,o),pageParams:s(e.pageParams,r,o)}};if(i&&a.length){let e=i===`backward`,t=e?Ve:Be,n={pages:a,pageParams:o};s=await d(n,t(r,n),e)}else{let t=e??a.length;do{let e=c===0?o[0]??r.initialPageParam:Be(r,s);if(c>0&&e==null)break;s=await d(s,e),c++}while(ct.options.persister?.(l,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=l}}}function Be(e,{pages:t,pageParams:n}){let r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function Ve(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var He=class extends De{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||Ue(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){let t=()=>{this.#i({type:`continue`})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=Ee({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});let r=this.state.status===`pending`,i=!this.#r.canStart();try{if(r)t();else{this.#i({type:`pending`,variables:e,isPaused:i}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);let t=await this.options.onMutate?.(e,n);t!==this.state.context&&this.#i({type:`pending`,context:t,variables:e,isPaused:i})}let a=await this.#r.start();return await this.#n.config.onSuccess?.(a,e,this.state.context,this,n),await this.options.onSuccess?.(a,e,this.state.context,n),await this.#n.config.onSettled?.(a,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(a,null,e,this.state.context,n),this.#i({type:`success`,data:a}),a}catch(t){try{await this.#n.config.onError?.(t,e,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onError?.(t,e,this.state.context,n)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,t,this.state.variables,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,t,e,this.state.context,n)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:t}),t}finally{this.#n.runNext(this)}}#i(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}})(this.state),xe.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function Ue(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var We=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new He({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=Ge(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=Ge(e);if(typeof t==`string`){let n=this.#t.get(t);if(n)if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=Ge(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}else return!0}runNext(e){let t=Ge(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){xe.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>ae(t,e))}findAll(e={}){return this.getAll().filter(t=>ae(e,t))}notify(e){xe.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return xe.batch(()=>Promise.all(e.map(e=>e.continue().catch(w))))}};function Ge(e){return e.options.scope?.id}var Ke=class extends v{#e;#t=void 0;#n;#r;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#i()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),k(this.options,t)||this.#e.getMutationCache().notify({type:`observerOptionsUpdated`,mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&E(t.mutationKey)!==E(this.options.mutationKey)?this.reset():this.#n?.state.status===`pending`&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#i(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#i(),this.#a()}mutate(e,t){return this.#r=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#i(){let e=this.#n?.state??Ue();this.#t={...e,isPending:e.status===`pending`,isSuccess:e.status===`success`,isError:e.status===`error`,isIdle:e.status===`idle`,mutate:this.mutate,reset:this.reset}}#a(e){xe.batch(()=>{if(this.#r&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type===`success`){try{this.#r.onSuccess?.(e.data,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(e.data,null,t,n,r)}catch(e){Promise.reject(e)}}else if(e?.type===`error`){try{this.#r.onError?.(e.error,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(void 0,e.error,t,n,r)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}},qe=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,i=t.queryHash??oe(r,t),a=this.get(i);return a||(a=new Oe({client:e,queryKey:r,queryHash:i,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(a)),a}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){xe.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>ie(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>ie(e,t)):t}notify(e){xe.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){xe.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){xe.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},Je=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new qe,this.#t=e.mutationCache||new We,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=y.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=Se.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(T(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,t,n){let r=this.defaultQueryOptions({queryKey:e}),i=this.#e.get(r.queryHash)?.state.data,a=ee(t,i);if(a!==void 0)return this.#e.build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return xe.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;xe.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return xe.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=xe.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(w).catch(w)}invalidateQueries(e,t={}){return xe.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=xe.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(w)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(w)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(T(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(w).catch(w)}fetchInfiniteQuery(e){return e.behavior=ze(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(w).catch(w)}ensureInfiniteQueryData(e){return e.behavior=ze(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return Se.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(E(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{D(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(E(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{D(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=oe(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===pe&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},Ye=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),Xe=o(((e,t)=>{t.exports=Ye()})),M=l(d(),1),N=Xe(),Ze=M.createContext(void 0),Qe=e=>{let t=M.useContext(Ze);if(e)return e;if(!t)throw Error(`No QueryClient set, use QueryClientProvider to set one`);return t},$e=({client:e,children:t})=>(M.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),(0,N.jsx)(Ze.Provider,{value:e,children:t})),et=M.createContext(!1),tt=()=>M.useContext(et);et.Provider;function nt(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var rt=M.createContext(nt()),it=()=>M.useContext(rt),at=(e,t,n)=>{let r=n?.state.error&&typeof e.throwOnError==`function`?he(e.throwOnError,[n.state.error,n]):e.throwOnError;(e.suspense||e.experimental_prefetchInRender||r)&&(t.isReset()||(e.retryOnMount=!1))},ot=e=>{M.useEffect(()=>{e.clearReset()},[e])},st=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||he(n,[e.error,r])),ct=e=>{if(e.suspense){let t=1e3,n=e=>e===`static`?e:Math.max(e??t,t),r=e.staleTime;e.staleTime=typeof r==`function`?(...e)=>n(r(...e)):n(r),typeof e.gcTime==`number`&&(e.gcTime=Math.max(e.gcTime,t))}},lt=(e,t)=>e.isLoading&&e.isFetching&&!t,ut=(e,t)=>e?.suspense&&t.isPending,dt=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function ft(e,t,n){let r=tt(),i=it(),a=Qe(n),o=a.defaultQueryOptions(e);a.getDefaultOptions().queries?._experimental_beforeQuery?.(o);let s=a.getQueryCache().get(o.queryHash);o._optimisticResults=r?`isRestoring`:`optimistic`,ct(o),at(o,i,s),ot(i);let c=!a.getQueryCache().get(o.queryHash),[l]=M.useState(()=>new t(a,o)),u=l.getOptimisticResult(o),d=!r&&e.subscribed!==!1;if(M.useSyncExternalStore(M.useCallback(e=>{let t=d?l.subscribe(xe.batchCalls(e)):w;return l.updateResult(),t},[l,d]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),M.useEffect(()=>{l.setOptions(o)},[o,l]),ut(o,u))throw dt(o,l,i);if(st({result:u,errorResetBoundary:i,throwOnError:o.throwOnError,query:s,suspense:o.suspense}))throw u.error;return a.getDefaultOptions().queries?._experimental_afterQuery?.(o,u),o.experimental_prefetchInRender&&!_e.isServer()&<(u,r)&&(c?dt(o,l,i):s?.promise)?.catch(w).finally(()=>{l.updateResult()}),o.notifyOnChangeProps?u:l.trackResult(u)}function pt(e,t){return ft(e,Me,t)}function mt(e,t){let n=Qe(t),[r]=M.useState(()=>new Ke(n,e));M.useEffect(()=>{r.setOptions(e)},[r,e]);let i=M.useSyncExternalStore(M.useCallback(e=>r.subscribe(xe.batchCalls(e)),[r]),()=>r.getCurrentResult(),()=>r.getCurrentResult()),a=M.useCallback((e,t)=>{r.mutate(e,t).catch(w)},[r]);if(i.error&&he(r.options.throwOnError,[i.error]))throw i.error;return{...i,mutate:a,mutateAsync:i.mutate}}function ht(e,t){return function(){return e.apply(t,arguments)}}var{toString:gt}=Object.prototype,{getPrototypeOf:_t}=Object,{iterator:vt,toStringTag:yt}=Symbol,bt=(e=>t=>{let n=gt.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),xt=e=>(e=e.toLowerCase(),t=>bt(t)===e),St=e=>t=>typeof t===e,{isArray:Ct}=Array,P=St(`undefined`);function wt(e){return e!==null&&!P(e)&&e.constructor!==null&&!P(e.constructor)&&Ot(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var Tt=xt(`ArrayBuffer`);function Et(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&Tt(e.buffer),t}var Dt=St(`string`),Ot=St(`function`),kt=St(`number`),At=e=>typeof e==`object`&&!!e,jt=e=>e===!0||e===!1,Mt=e=>{if(bt(e)!==`object`)return!1;let t=_t(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(yt in e)&&!(vt in e)},Nt=e=>{if(!At(e)||wt(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Pt=xt(`Date`),Ft=xt(`File`),It=e=>!!(e&&e.uri!==void 0),Lt=e=>e&&e.getParts!==void 0,Rt=xt(`Blob`),zt=xt(`FileList`),Bt=e=>At(e)&&Ot(e.pipe);function Vt(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var Ht=Vt(),Ut=Ht.FormData===void 0?void 0:Ht.FormData,Wt=e=>{let t;return e&&(Ut&&e instanceof Ut||Ot(e.append)&&((t=bt(e))===`formdata`||t===`object`&&Ot(e.toString)&&e.toString()===`[object FormData]`))},Gt=xt(`URLSearchParams`),[Kt,qt,Jt,Yt]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(xt),Xt=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function Zt(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),Ct(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var $t=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,en=e=>!P(e)&&e!==$t;function tn(){let{caseless:e,skipUndefined:t}=en(this)&&this||{},n={},r=(r,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=e&&Qt(n,i)||i;Mt(n[a])&&Mt(r)?n[a]=tn(n[a],r):Mt(r)?n[a]=tn({},r):Ct(r)?n[a]=r.slice():(!t||!P(r))&&(n[a]=r)};for(let e=0,t=arguments.length;e(Zt(t,(t,r)=>{n&&Ot(t)?Object.defineProperty(e,r,{value:ht(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),rn=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),an=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,`constructor`,{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,`super`,{value:t.prototype}),n&&Object.assign(e.prototype,n)},on=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-- >0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&_t(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},sn=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},cn=e=>{if(!e)return null;if(Ct(e))return e;let t=e.length;if(!kt(t))return null;let n=Array(t);for(;t-- >0;)n[t]=e[t];return n},ln=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&_t(Uint8Array)),un=(e,t)=>{let n=(e&&e[vt]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},dn=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},fn=xt(`HTMLFormElement`),pn=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),mn=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),hn=xt(`RegExp`),gn=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};Zt(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},_n=e=>{gn(e,(t,n)=>{if(Ot(e)&&[`arguments`,`caller`,`callee`].indexOf(n)!==-1)return!1;let r=e[n];if(Ot(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},vn=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return Ct(e)?r(e):r(String(e).split(t)),n},yn=()=>{},bn=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function xn(e){return!!(e&&Ot(e.append)&&e[yt]===`FormData`&&e[vt])}var Sn=e=>{let t=Array(10),n=(e,r)=>{if(At(e)){if(t.indexOf(e)>=0)return;if(wt(e))return e;if(!(`toJSON`in e)){t[r]=e;let i=Ct(e)?[]:{};return Zt(e,(e,t)=>{let a=n(e,r+1);!P(a)&&(i[t]=a)}),t[r]=void 0,i}}return e};return n(e,0)},Cn=xt(`AsyncFunction`),wn=e=>e&&(At(e)||Ot(e))&&Ot(e.then)&&Ot(e.catch),Tn=((e,t)=>e?setImmediate:t?((e,t)=>($t.addEventListener(`message`,({source:n,data:r})=>{n===$t&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),$t.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,Ot($t.postMessage)),F={isArray:Ct,isArrayBuffer:Tt,isBuffer:wt,isFormData:Wt,isArrayBufferView:Et,isString:Dt,isNumber:kt,isBoolean:jt,isObject:At,isPlainObject:Mt,isEmptyObject:Nt,isReadableStream:Kt,isRequest:qt,isResponse:Jt,isHeaders:Yt,isUndefined:P,isDate:Pt,isFile:Ft,isReactNativeBlob:It,isReactNative:Lt,isBlob:Rt,isRegExp:hn,isFunction:Ot,isStream:Bt,isURLSearchParams:Gt,isTypedArray:ln,isFileList:zt,forEach:Zt,merge:tn,extend:nn,trim:Xt,stripBOM:rn,inherits:an,toFlatObject:on,kindOf:bt,kindOfTest:xt,endsWith:sn,toArray:cn,forEachEntry:un,matchAll:dn,isHTMLForm:fn,hasOwnProperty:mn,hasOwnProp:mn,reduceDescriptors:gn,freezeMethods:_n,toObjectSet:vn,toCamelCase:pn,noop:yn,toFiniteNumber:bn,findKey:Qt,global:$t,isContextDefined:en,isSpecCompliantForm:xn,toJSONObject:Sn,isAsyncFn:Cn,isThenable:wn,setImmediate:Tn,asap:typeof queueMicrotask<`u`?queueMicrotask.bind($t):typeof process<`u`&&process.nextTick||Tn,isIterable:e=>e!=null&&Ot(e[vt])},I=class e extends Error{static from(t,n,r,i,a,o){let s=new e(t.message,n||t.code,r,i,a);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),o&&Object.assign(s,o),s}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,`message`,{value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:F.toJSONObject(this.config),code:this.code,status:this.status}}};I.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,I.ERR_BAD_OPTION=`ERR_BAD_OPTION`,I.ECONNABORTED=`ECONNABORTED`,I.ETIMEDOUT=`ETIMEDOUT`,I.ERR_NETWORK=`ERR_NETWORK`,I.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,I.ERR_DEPRECATED=`ERR_DEPRECATED`,I.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,I.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,I.ERR_CANCELED=`ERR_CANCELED`,I.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,I.ERR_INVALID_URL=`ERR_INVALID_URL`;function En(e){return F.isPlainObject(e)||F.isArray(e)}function Dn(e){return F.endsWith(e,`[]`)?e.slice(0,-2):e}function On(e,t,n){return e?e.concat(t).map(function(e,t){return e=Dn(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function kn(e){return F.isArray(e)&&!e.some(En)}var An=F.toFlatObject(F,{},null,function(e){return/^is[A-Z]/.test(e)});function jn(e,t,n){if(!F.isObject(e))throw TypeError(`target must be an object`);t||=new FormData,n=F.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!F.isUndefined(t[e])});let r=n.metaTokens,i=n.visitor||l,a=n.dots,o=n.indexes,s=(n.Blob||typeof Blob<`u`&&Blob)&&F.isSpecCompliantForm(t);if(!F.isFunction(i))throw TypeError(`visitor must be a function`);function c(e){if(e===null)return``;if(F.isDate(e))return e.toISOString();if(F.isBoolean(e))return e.toString();if(!s&&F.isBlob(e))throw new I(`Blob is not supported. Use a Buffer instead.`);return F.isArrayBuffer(e)||F.isTypedArray(e)?s&&typeof Blob==`function`?new Blob([e]):Buffer.from(e):e}function l(e,n,i){let s=e;if(F.isReactNative(t)&&F.isReactNativeBlob(e))return t.append(On(i,n,a),c(e)),!1;if(e&&!i&&typeof e==`object`){if(F.endsWith(n,`{}`))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(F.isArray(e)&&kn(e)||(F.isFileList(e)||F.endsWith(n,`[]`))&&(s=F.toArray(e)))return n=Dn(n),s.forEach(function(e,r){!(F.isUndefined(e)||e===null)&&t.append(o===!0?On([n],r,a):o===null?n:n+`[]`,c(e))}),!1}return En(e)?!0:(t.append(On(i,n,a),c(e)),!1)}let u=[],d=Object.assign(An,{defaultVisitor:l,convertValue:c,isVisitable:En});function f(e,n){if(!F.isUndefined(e)){if(u.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));u.push(e),F.forEach(e,function(e,r){(!(F.isUndefined(e)||e===null)&&i.call(t,e,F.isString(r)?r.trim():r,n,d))===!0&&f(e,n?n.concat(r):[r])}),u.pop()}}if(!F.isObject(e))throw TypeError(`data must be an object`);return f(e),t}function Mn(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`,"%00":`\0`};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function Nn(e,t){this._pairs=[],e&&jn(e,this,t)}var Pn=Nn.prototype;Pn.append=function(e,t){this._pairs.push([e,t])},Pn.toString=function(e){let t=e?function(t){return e.call(this,t,Mn)}:Mn;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Fn(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function In(e,t,n){if(!t)return e;let r=n&&n.encode||Fn,i=F.isFunction(n)?{serialize:n}:n,a=i&&i.serialize,o;if(o=a?a(t,i):F.isURLSearchParams(t)?t.toString():new Nn(t,i).toString(r),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var Ln=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&=[]}forEach(e){F.forEach(this.handlers,function(t){t!==null&&e(t)})}},Rn={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},zn={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Nn,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},Bn=s({hasBrowserEnv:()=>Vn,hasStandardBrowserEnv:()=>Un,hasStandardBrowserWebWorkerEnv:()=>Wn,navigator:()=>Hn,origin:()=>Gn}),Vn=typeof window<`u`&&typeof document<`u`,Hn=typeof navigator==`object`&&navigator||void 0,Un=Vn&&(!Hn||[`ReactNative`,`NativeScript`,`NS`].indexOf(Hn.product)<0),Wn=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Gn=Vn&&window.location.href||`http://localhost`,Kn={...Bn,...zn};function qn(e,t){return jn(e,new Kn.classes.URLSearchParams,{visitor:function(e,t,n,r){return Kn.isNode&&F.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}function Jn(e){return F.matchAll(/\w+|\[(\w*)]/g,e).map(e=>e[0]===`[]`?``:e[1]||e[0])}function Yn(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&F.isArray(r)?r.length:a,s?(F.hasOwnProp(r,a)?r[a]=[r[a],n]:r[a]=n,!o):((!r[a]||!F.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&F.isArray(r[a])&&(r[a]=Yn(r[a])),!o)}if(F.isFormData(e)&&F.isFunction(e.entries)){let n={};return F.forEachEntry(e,(e,r)=>{t(Jn(e),r,n,0)}),n}return null}function Zn(e,t,n){if(F.isString(e))try{return(t||JSON.parse)(e),F.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Qn={transitional:Rn,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=F.isObject(e);if(i&&F.isHTMLForm(e)&&(e=new FormData(e)),F.isFormData(e))return r?JSON.stringify(Xn(e)):e;if(F.isArrayBuffer(e)||F.isBuffer(e)||F.isStream(e)||F.isFile(e)||F.isBlob(e)||F.isReadableStream(e))return e;if(F.isArrayBufferView(e))return e.buffer;if(F.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return qn(e,this.formSerializer).toString();if((a=F.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let t=this.env&&this.env.FormData;return jn(a?{"files[]":e}:e,t&&new t,this.formSerializer)}}return i||r?(t.setContentType(`application/json`,!1),Zn(e)):e}],transformResponse:[function(e){let t=this.transitional||Qn.transitional,n=t&&t.forcedJSONParsing,r=this.responseType===`json`;if(F.isResponse(e)||F.isReadableStream(e))return e;if(e&&F.isString(e)&&(n&&!this.responseType||r)){let n=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(n)throw e.name===`SyntaxError`?I.from(e,I.ERR_BAD_RESPONSE,this,null,this.response):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Kn.classes.FormData,Blob:Kn.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};F.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`],e=>{Qn.headers[e]={}});var $n=F.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),er=e=>{let t={},n,r,i;return e&&e.split(` +`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim(),!(!n||t[n]&&$n[n])&&(n===`set-cookie`?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+`, `+r:r)}),t},tr=Symbol(`internals`);function nr(e){return e&&String(e).trim().toLowerCase()}function rr(e){return e===!1||e==null?e:F.isArray(e)?e.map(rr):String(e)}function ir(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var ar=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function or(e,t,n,r,i){if(F.isFunction(r))return r.call(this,t,n);if(i&&(t=n),F.isString(t)){if(F.isString(r))return t.indexOf(r)!==-1;if(F.isRegExp(r))return r.test(t)}}function sr(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function cr(e,t){let n=F.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var lr=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=nr(t);if(!i)throw Error(`header name must be a non-empty string`);let a=F.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=rr(e))}let a=(e,t)=>F.forEach(e,(e,n)=>i(e,n,t));if(F.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(F.isString(e)&&(e=e.trim())&&!ar(e))a(er(e),t);else if(F.isObject(e)&&F.isIterable(e)){let n={},r,i;for(let t of e){if(!F.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);n[i=t[0]]=(r=n[i])?F.isArray(r)?[...r,t[1]]:[r,t[1]]:t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=nr(e),e){let n=F.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return ir(e);if(F.isFunction(t))return t.call(this,e,n);if(F.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=nr(e),e){let n=F.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||or(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=nr(e),e){let i=F.findKey(n,e);i&&(!t||or(n,n[i],i,t))&&(delete n[i],r=!0)}}return F.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||or(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return F.forEach(this,(r,i)=>{let a=F.findKey(n,i);if(a){t[a]=rr(r),delete t[i];return}let o=e?sr(i):String(i).trim();o!==i&&delete t[i],t[o]=rr(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return F.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&F.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` +`)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[tr]=this[tr]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=nr(e);t[r]||(cr(n,e),t[r]=!0)}return F.isArray(e)?e.forEach(r):r(e),this}};lr.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),F.reduceDescriptors(lr.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),F.freezeMethods(lr);function ur(e,t){let n=this||Qn,r=t||n,i=lr.from(r.headers),a=r.data;return F.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function dr(e){return!!(e&&e.__CANCEL__)}var fr=class extends I{constructor(e,t,n){super(e??`canceled`,I.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function pr(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new I(`Request failed with status code `+n.status,[I.ERR_BAD_REQUEST,I.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function mr(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function hr(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var _r=(e,t,n=3)=>{let r=0,i=hr(50,250);return gr(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},vr=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},yr=e=>(...t)=>F.asap(()=>e(...t)),br=Kn.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Kn.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Kn.origin),Kn.navigator&&/(msie|trident)/i.test(Kn.navigator.userAgent)):()=>!0,xr=Kn.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];F.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),F.isString(r)&&s.push(`path=${r}`),F.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),F.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function Sr(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function Cr(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function wr(e,t,n){let r=!Sr(t);return e&&(r||n==0)?Cr(e,t):t}var Tr=e=>e instanceof lr?{...e}:e;function Er(e,t){t||={};let n={};function r(e,t,n,r){return F.isPlainObject(e)&&F.isPlainObject(t)?F.merge.call({caseless:r},e,t):F.isPlainObject(t)?F.merge({},t):F.isArray(t)?t.slice():t}function i(e,t,n,i){if(!F.isUndefined(t))return r(e,t,n,i);if(!F.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!F.isUndefined(t))return r(void 0,t)}function o(e,t){if(!F.isUndefined(t))return r(void 0,t);if(!F.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(Tr(e),Tr(t),n,!0)};return F.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=F.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);F.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Dr=e=>{let t=Er({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=lr.from(o),t.url=In(wr(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),F.isFormData(n)){if(Kn.hasStandardBrowserEnv||Kn.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(F.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(Kn.hasStandardBrowserEnv&&(r&&F.isFunction(r)&&(r=r(t)),r||r!==!1&&br(t.url))){let e=i&&a&&xr.read(a);e&&o.set(i,e)}return t},Or=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Dr(e),i=r.data,a=lr.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=lr.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());pr(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new I(`Request aborted`,I.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new I(t&&t.message?t.message:`Network Error`,I.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Rn;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new I(t,i.clarifyTimeoutError?I.ETIMEDOUT:I.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&F.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),F.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=_r(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=_r(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new fr(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=mr(r.url);if(_&&Kn.protocols.indexOf(_)===-1){n(new I(`Unsupported protocol `+_+`:`,I.ERR_BAD_REQUEST,e));return}h.send(i||null)})},kr=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof I?t:new fr(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new I(`timeout of ${t}ms exceeded`,I.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>F.asap(o),s}},Ar=function*(e,t){let n=e.byteLength;if(!t||n{let i=jr(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Pr=64*1024,{isFunction:Fr}=F,Ir=(({Request:e,Response:t})=>({Request:e,Response:t}))(F.global),{ReadableStream:Lr,TextEncoder:Rr}=F.global,zr=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Br=e=>{e=F.merge.call({skipUndefined:!0},Ir,e);let{fetch:t,Request:n,Response:r}=e,i=t?Fr(t):typeof fetch==`function`,a=Fr(n),o=Fr(r);if(!i)return!1;let s=i&&Fr(Lr),c=i&&(typeof Rr==`function`?(e=>t=>e.encode(t))(new Rr):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&zr(()=>{let e=!1,t=new n(Kn.origin,{body:new Lr,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return e&&!t}),u=o&&s&&zr(()=>F.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new I(`Response type '${e}' is not supported`,I.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(F.isBlob(e))return e.size;if(F.isSpecCompliantForm(e))return(await new n(Kn.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(F.isArrayBufferView(e)||F.isArrayBuffer(e))return e.byteLength;if(F.isURLSearchParams(e)&&(e+=``),F.isString(e))return(await c(e)).byteLength},p=async(e,t)=>F.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Dr(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=kr([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),ee;try{if(g&&l&&o!==`get`&&o!==`head`&&(ee=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(F.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=vr(ee,_r(yr(g)));s=Nr(e.body,Pr,t,n)}}F.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=F.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&vr(t,_r(yr(h),!0))||[];f=new r(Nr(f.body,Pr,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let te=await d[F.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{pr(t,n,{data:te,headers:lr.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new I(`Network Error`,I.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):I.from(t,t&&t.code,e,C,t&&t.response)}}},Vr=new Map,Hr=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Vr;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:Br(t)),l=c;return c};Hr();var Ur={http:null,xhr:Or,fetch:{get:Hr}};F.forEach(Ur,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Wr=e=>`- ${e}`,Gr=e=>F.isFunction(e)||e===null||e===!1;function Kr(e,t){e=F.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new I(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : +`+e.map(Wr).join(` +`):` `+Wr(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var qr={getAdapter:Kr,adapters:Ur};function Jr(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new fr(null,e)}function Yr(e){return Jr(e),e.headers=lr.from(e.headers),e.data=ur.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),qr.getAdapter(e.adapter||Qn.adapter,e)(e).then(function(t){return Jr(e),t.data=ur.call(e,e.transformResponse,t),t.headers=lr.from(t.headers),t},function(t){return dr(t)||(Jr(e),t&&t.response&&(t.response.data=ur.call(e,e.transformResponse,t.response),t.response.headers=lr.from(t.response.headers))),Promise.reject(t)})}var Xr=`1.13.6`,Zr={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{Zr[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var Qr={};Zr.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Xr+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new I(r(i,` has been removed`+(t?` in `+t:``)),I.ERR_DEPRECATED);return t&&!Qr[i]&&(Qr[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},Zr.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function $r(e,t,n){if(typeof e!=`object`)throw new I(`options must be an object`,I.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new I(`option `+a+` must be `+n,I.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new I(`Unknown option `+a,I.ERR_BAD_OPTION)}}var ei={assertOptions:$r,validators:Zr},ti=ei.validators,ni=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Ln,response:new Ln}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` +`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Er(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&ei.assertOptions(n,{silentJSONParsing:ti.transitional(ti.boolean),forcedJSONParsing:ti.transitional(ti.boolean),clarifyTimeoutError:ti.transitional(ti.boolean),legacyInterceptorReqResOrdering:ti.transitional(ti.boolean)},!1),r!=null&&(F.isFunction(r)?t.paramsSerializer={serialize:r}:ei.assertOptions(r,{encode:ti.function,serialize:ti.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),ei.assertOptions(t,{baseUrl:ti.spelling(`baseURL`),withXsrfToken:ti.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&F.merge(i.common,i[t.method]);i&&F.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=lr.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Rn;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[Yr.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new fr(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ii(e){return function(t){return e.apply(null,t)}}function ai(e){return F.isObject(e)&&e.isAxiosError===!0}var oi={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(oi).forEach(([e,t])=>{oi[t]=e});function si(e){let t=new ni(e),n=ht(ni.prototype.request,t);return F.extend(n,ni.prototype,t,{allOwnKeys:!0}),F.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return si(Er(e,t))},n}var L=si(Qn);L.Axios=ni,L.CanceledError=fr,L.CancelToken=ri,L.isCancel=dr,L.VERSION=Xr,L.toFormData=jn,L.AxiosError=I,L.Cancel=L.CanceledError,L.all=function(e){return Promise.all(e)},L.spread=ii,L.isAxiosError=ai,L.mergeConfig=Er,L.AxiosHeaders=lr,L.formToJSON=e=>Xn(F.isHTMLForm(e)?new FormData(e):e),L.getAdapter=qr.getAdapter,L.HttpStatusCode=oi,L.default=L;var ci=l(_()),li=`order-demo-001`;function ui(e,t,n,r,i,a){return{eventId:e,aggregateId:li,aggregateType:`ORDER`,sequenceNumber:t,eventType:n,payload:a,metadata:JSON.stringify({source:`demo`,correlationId:`corr-demo-${t}`}),timestamp:r,globalPosition:i}}function di(){let e=[],t=Date.parse(`2025-01-15T08:00:00.000Z`);for(let n=1;n<=100;n++){let r=new Date(t+n*45e3).toISOString(),i=5e4+n,a,o;if(n===1)a=`ORDER_PLACED`,o={customerId:`cust-77`,channel:`web`,status:`PENDING`,totalCents:0,itemCount:0};else if(n>=2&&n<=48){a=`LINE_ITEM_ADDED`;let e=350+n*73%1200;o={sku:`SKU-${String(1e4+n*17).slice(-4)}`,qty:n%4+1,lineTotalCents:e,lineIndex:n-1}}else if(n>=49&&n<=58)a=`PAYMENT_PROGRESS`,o={paymentId:`pay-chunk-${n}`,amountCents:1500+n*120,balanceCents:Math.max(0,48e3-n*700)};else if(n>=59&&n<=72){let e=[`inventory`,`fraud_check`,`address_verify`,`manual_review`,`carrier_delay`];a=`FULFILLMENT_BLOCKED`,o={reason:e[n%e.length],caseId:`CASE-${n}`,retryAfterMinutes:15+n%45}}else n>=73&&n<=88?(a=`SHIPMENT_EVENT`,o={leg:n-72,carrier:n%3==0?`FAST`:n%3==1?`ECONOMY`:`OVERNIGHT`,status:`IN_TRANSIT`,trackingToken:`trk-${n}${(n*7919).toString(36)}`}):n>=89&&n<=99?(a=`NOTE_APPENDED`,o={author:`agent-${n%6+1}`,noteId:`n-${n}`,preview:`Ops note #${n}: SLA watch / customer ping`}):(a=`REFUND_ISSUED`,o={refundCents:88e3,balanceCents:-12500,reason:`bulk_settlement_adjustment`});e.push(ui(`evt-demo-${n}`,n,a,r,i,JSON.stringify(o)))}return e}var fi=di();function pi(e){return[{code:`NEGATIVE_BALANCE`,severity:`HIGH`,description:`Ledger balance dropped below zero after refund batch`},{code:`REFUND_EXCEEDS_CAPTURE`,severity:`CRITICAL`,description:`Cumulative refunds exceed captured payments for this aggregate`},{code:`DUPLICATE_PAYMENT_CHUNK`,severity:`MEDIUM`,description:`Two payment chunks share the same window and amount fingerprint`},{code:`LINE_ITEM_PRICE_OUTLIER`,severity:`LOW`,description:`Line total deviates >3σ from cohort for this SKU family`},{code:`FULFILLMENT_STALL`,severity:`HIGH`,description:`Order blocked in fulfillment longer than SLA for channel`},{code:`CARRIER_MISMATCH`,severity:`MEDIUM`,description:`Shipment leg carrier differs from preferred routing profile`},{code:`MANUAL_REVIEW_BACKLOG`,severity:`LOW`,description:`Case reopened multiple times without resolution`},{code:`VELOCITY_SPIKE`,severity:`HIGH`,description:`Event rate on this aggregate exceeded rolling baseline`},{code:`ADDRESS_VERIFY_LOOP`,severity:`MEDIUM`,description:`Address verification failed three times with same payload hash`},{code:`INVENTORY_HOLD`,severity:`MEDIUM`,description:`Inventory hold exceeded expected release window`},{code:`FRAUD_SCORE_EDGE`,severity:`LOW`,description:`Fraud score landed in manual-review gray band`},{code:`DISCOUNT_STACK`,severity:`LOW`,description:`Multiple discount signals present without explicit approval event`},{code:`SHIPMENT_GAP`,severity:`HIGH`,description:`Missing scan between expected hub handoffs`},{code:`NOTE_SPAM`,severity:`LOW`,description:`Unusually high operator notes density in short interval`},{code:`PAYMENT_PARTIAL_CLUSTER`,severity:`MEDIUM`,description:`Several partial captures without closing settlement event`},{code:`SKU_QUANTITY_ANOMALY`,severity:`MEDIUM`,description:`Quantity pattern inconsistent with historical order curve`},{code:`CASE_ESCALATION`,severity:`HIGH`,description:`Support case escalated without prior tier-1 closure`},{code:`TRACKING_TOKEN_REUSE`,severity:`CRITICAL`,description:`Tracking token collision across two concurrent legs`},{code:`SLA_BREACH_RISK`,severity:`HIGH`,description:`Projected delivery crosses committed SLA if delay persists`},{code:`SETTLEMENT_BATCH_DRIFT`,severity:`CRITICAL`,description:`Settlement batch totals diverge from summed payment chunks`}].map((t,n)=>{let r=Math.min(100,5+n*5),i=e.find(e=>e.sequenceNumber===r)??e[e.length-1];return{code:t.code,description:t.description,severity:t.severity,aggregateId:li,atSequence:r,triggeringEventType:i.eventType,timestamp:i.timestamp,stateAtAnomaly:{demoIndex:n+1,atSequence:r,code:t.code}}})}var mi=pi(fi);function hi(e,t){let n={...e},r={};try{r=JSON.parse(t.payload||`{}`)}catch{}n._version=t.sequenceNumber,n._lastEventType=t.eventType,n._lastUpdated=t.timestamp;let i=t.eventType.toLowerCase();return i.includes(`created`)||i.includes(`opened`)||i.includes(`placed`)||i.includes(`submitted`)||(i.includes(`deleted`)||i.includes(`closed`)||i.includes(`cancelled`)||i.includes(`rejected`))&&(n.status=`DELETED`),Object.assign(n,r),n}function gi(e,t){let n={};for(let r of Object.keys(t)){let i=e[r],a=t[r];JSON.stringify(i)!==JSON.stringify(a)&&(n[r]={oldValue:i,newValue:a})}for(let r of Object.keys(e))r in t||(n[r]={oldValue:e[r],newValue:void 0});return n}function _i(e){let t=[],n={};for(let r of e){let e={...n};n=hi(n,r);let i={...n};t.push({event:r,stateBefore:e,stateAfter:i,diff:gi(e,i)})}return t}var vi=_i(fi);function yi(e){let t=e.trim().toLowerCase();return t.length<2?!1:t.includes(`demo`)||`order-demo-001`.includes(t)}function bi(e){return yi(e)?[li]:[]}function xi(e){let t=Math.min(Math.max(e,1),500);return[...fi].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,t)}function Si(e,t,n){if(e!==`order-demo-001`)return{events:[],totalEvents:0};let r=fi.length,i=Math.max(0,n),a=Math.min(Math.max(t,1),1e3);return i>=r?{events:[],totalEvents:r}:{events:fi.slice(i,i+a),totalEvents:r}}function Ci(e){return e===`order-demo-001`?vi:[]}function wi(e){let t=Math.min(Math.max(e,1),500);return mi.slice(0,t)}function Ti(){return[...fi].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,40)}function Ei(){return{status:`UP`,version:`demo`,demo:!0}}function Di(e=1,t=24){let n=Math.max(e,1)*60*60*1e3,r=[...fi].sort((e,t)=>Date.parse(e.timestamp)-Date.parse(t.timestamp)),i=new Map,a=new Map,o=new Map;for(let e of r){let t=new Date(Math.floor(Date.parse(e.timestamp)/n)*n).toISOString();i.set(t,(i.get(t)??0)+1),a.set(e.eventType,(a.get(e.eventType)??0)+1),o.set(e.aggregateType,(o.get(e.aggregateType)??0)+1)}return{totalEvents:fi.length,distinctAggregates:1,eventTypes:[...a.entries()].map(([e,t])=>({type:e,count:t})),aggregateTypes:[...o.entries()].map(([e,t])=>({type:e,count:t})),throughput:[...i.entries()].slice(-Math.max(t,1)).map(([e,t])=>({bucket:e,count:t})),available:!0,message:null}}function R(){return!1}var z=L.create({baseURL:`/api`,withCredentials:!0}),Oi=null;z.interceptors.request.use(e=>{let t=(e.method??`get`).toUpperCase();return Oi&&![`GET`,`HEAD`,`OPTIONS`].includes(t)&&(e.headers=e.headers??{},e.headers[`X-CSRF-Token`]=Oi),e});function ki(e){return new Promise(t=>{setTimeout(t,e)})}function Ai(e,t){return t?`${e}${e.includes(`?`)?`&`:`?`}source=${encodeURIComponent(t)}`:e}function ji(e,t){let n=`${e}:${t}`,r=new TextEncoder().encode(n),i=``;return r.forEach(e=>{i+=String.fromCharCode(e)}),btoa(i)}var Mi=async(e,t=20,n)=>{let r=Ai(`/aggregates/search?q=${encodeURIComponent(e)}&limit=${t}`,n);if(R()){await ki(40);let n=bi(e);try{let e=await z.get(r);return[...new Set([...n,...e.data])].slice(0,t)}catch{return n}}return z.get(r).then(e=>e.data)},B=async(e,t=500,n=0,r,i=`full`)=>{if(R()&&e===`order-demo-001`)return await ki(50),Si(e,t,n);let a=Ai(`/aggregates/${e}/timeline?limit=${t}&offset=${n}&fields=${i}`,r);return z.get(a).then(e=>e.data)},V=async(e,t)=>R()&&e===`order-demo-001`?(await ki(50),Ci(e)):z.get(Ai(`/aggregates/${e}/transitions`,t)).then(e=>e.data),Ni=async(e=100,t)=>R()?(await ki(45),wi(e)):z.get(Ai(`/anomalies/recent?limit=${e}`,t)).then(e=>e.data),Pi=async(e=50,t)=>R()?(await ki(35),xi(e)):z.get(Ai(`/events/recent?limit=${e}`,t)).then(e=>e.data),Fi=async(e,t=1,n=24)=>{if(R())return await ki(30),Di(t,n);let r=Ai(`/v1/statistics?bucketHours=${t}&maxBuckets=${n}`,e);return z.get(r).then(e=>e.data)},Ii=async()=>R()?(await ki(20),Ei()):z.get(`/health`).then(e=>e.data),Li=async()=>R()?{authenticated:!0,principal:{userId:`demo-user`,displayName:`Demo User`,authMethod:`demo`,roles:[`demo`]}}:z.get(`/v1/auth/session`).then(e=>e.data),Ri=async()=>R()?{setupRequired:!1,restartRequired:!1,configPath:`eventlens.yaml`}:z.get(`/v1/setup/status`).then(e=>e.data),zi=async e=>z.post(`/v1/setup/apply`,e).then(e=>e.data),Bi=async(e=25)=>z.get(`/v1/audit?limit=${e}`).then(e=>e.data),Vi=async()=>z.get(`/v1/admin/api-keys`).then(e=>e.data),Hi=async e=>z.post(`/v1/admin/api-keys`,e).then(e=>e.data),Ui=async e=>z.post(`/v1/admin/api-keys/${encodeURIComponent(e)}/revoke`).then(e=>e.data),Wi=async(e,t,n)=>z.post(`/v1/auth/login/basic`,{returnHash:n},{headers:{Authorization:`Basic ${ji(e,t)}`}}).then(e=>e.data),Gi=async()=>R()?{authenticated:!1}:z.post(`/v1/auth/logout`).then(e=>e.data),Ki=e=>`/api/v1/auth/login/oidc?returnHash=${encodeURIComponent(e)}`,qi=e=>{Oi=e},Ji=async()=>R()?(await ki(20),[{id:`demo-primary`,displayName:`Demo Primary`,status:`ready`,healthMessage:`Frontend demo datasource`,capabilities:[`timeline`,`replay`,`statistics`]}]):z.get(`/v1/datasources`).then(e=>e.data),Yi=async()=>R()?(await ki(20),[{instanceId:`demo-source`,typeId:`demo`,displayName:`Demo Source Plugin`,pluginType:`EVENT_SOURCE`,lifecycle:`ready`,health:{state:`up`,message:`Frontend demo plugin`},lastHealthCheck:new Date().toISOString(),failureReason:null}]):z.get(`/v1/plugins`).then(e=>e.data);function Xi(e,t){let[n,r]=(0,M.useState)(e);return(0,M.useEffect)(()=>{let n=setTimeout(()=>r(e),t);return()=>clearTimeout(n)},[e,t]),n}function Zi({onSelect:e,source:t,selectedValue:n}){let[r,i]=(0,M.useState)(``),[a,o]=(0,M.useState)(!1),s=(0,M.useRef)(null),c=Xi(r,300),{data:l=[]}=pt({queryKey:[`search`,c,t??`default`],queryFn:()=>Mi(c,20,t),enabled:c.length>=2,staleTime:5e3});(0,M.useEffect)(()=>{i(n??``)},[n]),(0,M.useEffect)(()=>{let e=e=>{s.current&&!s.current.contains(e.target)&&o(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[]);let u=(0,M.useRef)(null),d=(0,M.useCallback)(()=>{u.current?.focus(),u.current?.select()},[]);(0,M.useEffect)(()=>{let e=document.getElementById(`aggregate-search`);return e?.addEventListener(`focus`,d),()=>e?.removeEventListener(`focus`,d)},[d]);let f=t=>{i(t),o(!1),e(t)};return(0,N.jsxs)(`div`,{className:`search-wrapper`,ref:s,children:[(0,N.jsx)(`span`,{className:`search-icon`,children:`??`}),(0,N.jsx)(`input`,{id:`aggregate-search`,ref:u,type:`text`,className:`search-input`,placeholder:`Search by aggregate ID (e.g. UUID or stream key)`,value:r,onChange:e=>{i(e.target.value),o(!0)},onFocus:()=>r.length>=2&&o(!0),onKeyDown:e=>{e.key===`Enter`&&r.trim()&&f(r.trim()),e.key===`Escape`&&o(!1)},autoComplete:`off`,"aria-autocomplete":`list`}),a&&l.length>0&&(0,N.jsx)(`div`,{className:`search-results`,role:`listbox`,id:`aggregate-search-results`,children:l.map(e=>(0,N.jsxs)(`button`,{type:`button`,className:`search-result-item`,onClick:()=>f(e),role:`option`,children:[(0,N.jsx)(`span`,{className:`search-result-chevron`,"aria-hidden":!0,children:`?`}),(0,N.jsxs)(`span`,{className:`search-result-body`,children:[(0,N.jsx)(`span`,{className:`search-result-label`,children:`ID`}),(0,N.jsx)(`span`,{className:`search-result-colon`,children:`:`}),(0,N.jsx)(`span`,{className:`search-result-value`,children:e})]})]},e))})]})}function Qi(e,t){return pt({queryKey:[`timeline`,e,t??`default`,`metadata`],queryFn:()=>B(e,500,0,t,`metadata`),enabled:!!e})}function $i(e){if(typeof e==`number`)return Number.isNaN(e)?new Date:e<0xe8d4a51000?new Date(e*1e3):new Date(e);let t=String(e).trim();if(!t)return new Date;if(t.includes(`T`)||/^\d{4}-\d{2}-\d{2}/.test(t)){let e=Date.parse(t);if(!Number.isNaN(e))return new Date(e)}let n=parseFloat(t);return Number.isNaN(n)?new Date:n<0xe8d4a51000?new Date(n*1e3):new Date(n)}function ea(e){let t=[],n=0;for(;n=4)t.push({kind:`group`,eventType:r,items:e.slice(n,i),startIndex:n});else for(let r=n;re.sequenceNumber===n),o=t===e;r.push({kind:`group`,key:`group-${e}`,eventType:i.eventType,items:i.items,startIndex:i.startIndex,expanded:o,containsSelection:a}),o&&r.push(...i.items.map((t,n)=>({kind:`group-item`,key:`group-item-${t.sequenceNumber}`,event:t,stepNumber:i.startIndex+n+1,parentKey:e})))}return r}function ra(e,t){return e===t?null:t}var ia=[1,6,24,168];function aa(e){let t=e.toLowerCase();return t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`created`:t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`deleted`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`completed`:t.includes(`failed`)||t.includes(`error`)||t.includes(`blocked`)?`failed`:t.includes(`transfer`)?`transfer`:`default`}function oa(e){switch(e.kind){case`single`:case`group-item`:return e.event.sequenceNumber;case`group`:return e.items[0]?.sequenceNumber??0}}function sa(e,t){return`Event ${e}, sequence ${t.sequenceNumber}, ${t.eventType}`}function ca(e,t){return t==null?!1:e.kind===`single`?e.event.sequenceNumber===t:e.items.some(e=>e.sequenceNumber===t)}function la(e,t,n,r,i){let a=e.event,o=t===a.sequenceNumber,s=n===a.sequenceNumber,c=e.kind===`group-item`?` timeline-step-compact`:``;return(0,N.jsxs)(`button`,{type:`button`,className:`timeline-step timeline-step-${aa(a.eventType)} ${o?`active`:``} ${s?`timeline-step-compare`:``}${c}`,onClick:e=>{e.shiftKey&&i?i(ra(n,a.sequenceNumber)):r(a.sequenceNumber)},"aria-current":o?`step`:void 0,"aria-label":sa(e.stepNumber,a),children:[(0,N.jsxs)(`span`,{className:`timeline-step-badge`,children:[`Event `,e.stepNumber]}),(0,N.jsxs)(`span`,{className:`timeline-step-seq`,children:[`seq #`,a.sequenceNumber]}),(0,N.jsx)(`span`,{className:`timeline-step-type`,children:a.eventType}),(0,N.jsx)(`span`,{className:`timeline-step-seq`,children:$i(a.timestamp).toLocaleTimeString()})]},e.key)}function ua({aggregateId:e,selectedSequence:t,compareSequence:n,onSelectEvent:r,onSelectCompare:i,source:a}){let{data:o,isLoading:s}=Qi(e,a),[c,l]=(0,M.useState)(``),[u,d]=(0,M.useState)(``),[f,p]=(0,M.useState)(`all`),[m,h]=(0,M.useState)(null),g=(0,M.useRef)(null),_=o?.events??[],v=o?.totalEvents??0,y=(0,M.useMemo)(()=>{let e=_;if(c&&(e=e.filter(e=>e.eventType===c)),f!==`all`&&e.length>0){let t=$i(e[e.length-1].timestamp).getTime()-f*60*60*1e3;e=e.filter(e=>$i(e.timestamp).getTime()>=t)}return e},[_,c,f]),b=(0,M.useMemo)(()=>ea(y),[y]),x=(0,M.useMemo)(()=>[...new Set(_.map(e=>e.eventType))].sort(),[_]);(0,M.useEffect)(()=>{if(t!=null){for(let e of b)if(e.kind===`group`&&e.items.some(e=>e.sequenceNumber===t)){h(ta(e.startIndex,e.items.length));return}h(null)}},[b,t]);let S=(0,M.useMemo)(()=>na(b,m,t),[m,b,t]),C=(0,M.useMemo)(()=>t==null?-1:S.findIndex(e=>e.kind===`group`?e.items.some(e=>e.sequenceNumber===t):e.event.sequenceNumber===t),[S,t]);if((0,M.useEffect)(()=>{let e=e=>{let t=e.detail;if(S.length&&(t<0||t>0)){let e=C>=0?C:0;r(oa(S[Math.max(0,Math.min(S.length-1,e+t))]))}};return window.addEventListener(`eventlens:timeline-step`,e),()=>window.removeEventListener(`eventlens:timeline-step`,e)},[r,S,C]),(0,M.useEffect)(()=>{let e=e=>{let n=e.target;if(!(n&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(n.tagName)||!S.length)&&(e.key===`ArrowLeft`||e.key===`ArrowRight`)&&e.shiftKey){e.preventDefault();let n=e.key===`ArrowLeft`?-1:1,i=S.filter(e=>e.kind===`group`);if(!i.length)return;let a=i.findIndex(e=>t!=null&&e.items.some(e=>e.sequenceNumber===t));r(i[a>=0?Math.max(0,Math.min(i.length-1,a+n)):n>0?0:i.length-1].items[0].sequenceNumber)}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r,S,t]),(0,M.useEffect)(()=>{if(!g.current||t==null)return;let e=g.current.querySelector(`[aria-current="step"]`);if(!e)return;let n=g.current.getBoundingClientRect(),r=e.getBoundingClientRect(),i=g.current.scrollLeft,a=i+(r.left-n.left),o=a+r.width,s=i,c=i+g.current.clientWidth;ac-24&&g.current.scrollTo({left:o-g.current.clientWidth+24,behavior:`smooth`})},[m,t]),s)return(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,N.jsx)(`div`,{className:`skeleton`,style:{height:160}})]});if(!_.length)return(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,N.jsx)(`p`,{style:{color:`var(--text-muted)`},children:`No events found.`})]});let w=y[0]?.sequenceNumber??0,ee=y[y.length-1]?.sequenceNumber??0;return(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsxs)(`div`,{className:`timeline-header-row`,children:[(0,N.jsxs)(`div`,{className:`card-title`,style:{marginBottom:0},children:[`Event Sequence`,(0,N.jsxs)(`span`,{className:`timeline-count-pill`,children:[y.length,` / `,v,` events`]})]}),(0,N.jsx)(`div`,{className:`timeline-jump-group`,children:(0,N.jsx)(`input`,{className:`timeline-jump-input`,type:`number`,placeholder:`Jump to seq`,value:u,onChange:e=>d(e.target.value),onKeyDown:e=>{if(e.key===`Enter`){let e=Number(u);y.some(t=>t.sequenceNumber===e)&&(r(e),d(``))}},"aria-label":`Jump to sequence number`})})]}),(0,N.jsx)(`p`,{className:`timeline-hint`,children:`Scroll the rail horizontally from left to right. Repeated event runs collapse into grouped cards; click a group to open the full strip below.`}),(0,N.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Filter by event type`,children:[(0,N.jsx)(`button`,{type:`button`,className:`filter-chip ${c?``:`active`}`,onClick:()=>l(``),children:`All`}),x.map(e=>(0,N.jsx)(`button`,{type:`button`,className:`filter-chip ${c===e?`active`:``}`,onClick:()=>l(t=>t===e?``:e),children:e},e))]}),(0,N.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Zoom range`,children:[(0,N.jsx)(`button`,{type:`button`,className:`filter-chip ${f===`all`?`active`:``}`,onClick:()=>p(`all`),children:`All`}),ia.map(e=>(0,N.jsx)(`button`,{type:`button`,className:`filter-chip ${f===e?`active`:``}`,onClick:()=>p(e),children:e>=24?`${e/24}d`:`${e}h`},e))]}),(0,N.jsx)(`div`,{className:`timeline-rail`,children:(0,N.jsx)(`div`,{ref:g,className:`timeline-stepper`,children:(0,N.jsx)(`div`,{className:`timeline-stepper-track`,children:b.map((e,a)=>{let o=ah(e=>e===s?null:s),"aria-expanded":c,children:[(0,N.jsxs)(`span`,{className:`timeline-group-chip-top`,children:[(0,N.jsxs)(`span`,{className:`timeline-group-count`,children:[`x`,e.items.length]}),(0,N.jsx)(`span`,{className:`timeline-group-chevron`,"aria-hidden":!0,children:c?`v`:`>`})]}),(0,N.jsx)(`span`,{className:`timeline-group-type`,children:e.eventType}),(0,N.jsxs)(`span`,{className:`timeline-group-range`,children:[`steps `,e.startIndex+1,`-`,e.startIndex+e.items.length,` seq #`,e.items[0].sequenceNumber,`-#`,e.items[e.items.length-1].sequenceNumber]})]}),o,c&&(0,N.jsxs)(`div`,{className:`timeline-expanded-deck`,children:[(0,N.jsxs)(`div`,{className:`timeline-expanded-head`,children:[(0,N.jsx)(`div`,{className:`timeline-expanded-title`,children:e.eventType}),(0,N.jsxs)(`div`,{className:`timeline-group-range`,children:[`Expanded run of `,e.items.length,` events`]}),(0,N.jsx)(`button`,{type:`button`,className:`timeline-collapse-btn`,onClick:()=>h(null),children:`Collapse`})]}),(0,N.jsx)(`div`,{className:`timeline-stepper-track`,children:e.items.map((a,o)=>{let c={kind:`group-item`,key:`group-item-${a.sequenceNumber}`,event:a,stepNumber:e.startIndex+o+1,parentKey:s};return(0,N.jsxs)(`div`,{className:`timeline-track-item`,children:[la(c,t,n,r,i),or(Number(e.target.value)),"aria-label":`Scrub event sequence`}),(0,N.jsxs)(`div`,{className:`timeline-info`,children:[(0,N.jsxs)(`span`,{className:`timeline-info-edge`,children:[`First seq #`,w]}),(0,N.jsx)(`span`,{className:`timeline-info-center`,children:t==null?`Select an event`:(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`strong`,{children:[`Selected seq #`,t]}),n!=null&&` compared with #${n}`,(0,N.jsxs)(`span`,{className:`timeline-info-muted`,children:[` across `,b.length,` visible segments`]})]})}),(0,N.jsxs)(`span`,{className:`timeline-info-edge`,children:[`Last seq #`,ee]})]})]})}function da(e,t,n=!0){return pt({queryKey:[`transitions`,e,t??`default`],queryFn:()=>V(e,t),enabled:n&&!!e})}function fa(e,t,n=!0){let[r,i]=(0,M.useState)([]),[a,o]=(0,M.useState)(0),[s,c]=(0,M.useState)(!1),l=(0,M.useMemo)(()=>`${Date.now()}-${Math.random()}`,[e,t]);return(0,M.useEffect)(()=>{if(!n){i([]),o(0),c(!1);return}let r=new Worker(new URL(`/assets/json-diff.worker-DTKMBXSt.js`,``+import.meta.url),{type:`module`});c(!0);let a=e=>{e.data.requestId===l&&(i(e.data.patches),o(e.data.durationMs),c(!1))};return r.addEventListener(`message`,a),r.postMessage({left:e,right:t,requestId:l}),()=>{r.removeEventListener(`message`,a),r.terminate()}},[n,e,t,l]),{patches:r,durationMs:a,loading:s}}function pa({diff:e,patches:t,title:n=`Changes`}){let r=t&&t.length>0?t.map(e=>({field:e.path,oldValue:e.oldValue,newValue:e.newValue,kind:e.type})):Object.entries(e??{}).map(([e,t])=>({field:e,oldValue:t.oldValue,newValue:t.newValue,kind:`changed`}));return r.length?(0,N.jsxs)(`div`,{className:`diff-panel`,children:[(0,N.jsx)(`div`,{className:`diff-toolbar`,children:(0,N.jsxs)(`div`,{className:`diff-toolbar-title`,children:[n,(0,N.jsxs)(`span`,{className:`diff-count-badge`,"aria-live":`polite`,children:[r.length,` `,r.length===1?`change`:`changes`]})]})}),(0,N.jsx)(`div`,{className:`diff-body`,children:(0,N.jsx)(`div`,{className:`diff-scroll`,children:(0,N.jsxs)(`div`,{className:`diff-list diff-list-split`,children:[(0,N.jsxs)(`div`,{className:`diff-split-head`,children:[(0,N.jsx)(`span`,{className:`diff-split-label diff-split-old-label`,children:`Before`}),(0,N.jsx)(`span`,{className:`diff-split-label diff-split-new-label`,children:`After`})]}),r.map((e,t)=>(0,N.jsxs)(`div`,{className:`diff-split-row diff-split-row--${e.kind}`,children:[(0,N.jsx)(`span`,{className:`diff-line-no`,"aria-hidden":!0,children:t+1}),(0,N.jsxs)(`div`,{className:`diff-split-cells`,children:[(0,N.jsxs)(`div`,{className:`diff-split-cell diff-split-old`,children:[(0,N.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,N.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.oldValue)})]}),(0,N.jsxs)(`div`,{className:`diff-split-cell diff-split-new`,children:[(0,N.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,N.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.newValue)})]})]})]},`${e.field}-${t}`))]})})})]}):null}function ma({open:e,onToggle:t}){return(0,N.jsx)(`button`,{type:`button`,className:`json-tree-toggle`,onClick:e=>{e.stopPropagation(),t()},"aria-expanded":e,"aria-label":e?`Collapse`:`Expand`,children:e?`-`:`+`})}function ha({value:e}){return e===null?(0,N.jsx)(`span`,{className:`json-null`,children:`null`}):typeof e==`boolean`?(0,N.jsx)(`span`,{className:`json-boolean`,children:String(e)}):typeof e==`number`?(0,N.jsx)(`span`,{className:`json-number`,children:e}):(0,N.jsx)(`span`,{className:`json-string`,children:JSON.stringify(e)})}function ga({value:e,changedKeys:t}){return(0,N.jsx)(`div`,{className:`json-tree json-tree-root`,children:(0,N.jsx)(_a,{value:e,depth:0,changedKeys:t,keyPath:``})})}function _a({value:e,depth:t,propertyKey:n,changedKeys:r,keyPath:i=``}){let a=r&&n!==void 0&&r.has(n),o=r&&i&&[...r].some(e=>e.startsWith(i+`.`)),[s,c]=(0,M.useState)(r?t<3||!!a||!!o:t<3),l={paddingLeft:Math.min(t,12)*14},u=a?{background:`rgba(255, 170, 0, 0.12)`,borderRadius:3}:{};if(e===null||typeof e==`boolean`||typeof e==`number`||typeof e==`string`)return(0,N.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[n!==void 0&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,N.jsx)(ha,{value:e})]});let d=e=>i?`${i}.${e}`:e;if(Array.isArray(e))return e.length===0?(0,N.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`[]`})]}):(0,N.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,N.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,N.jsx)(ma,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`[`}),!s&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,e.length,` items `]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`]`})]})]}),s&&(0,N.jsxs)(N.Fragment,{children:[e.map((e,n)=>(0,N.jsx)(_a,{value:e,depth:t+1,changedKeys:r,keyPath:d(String(n))},n)),(0,N.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,N.jsx)(`span`,{className:`json-punct`,children:`]`})})]})]});if(typeof e==`object`){let i=Object.entries(e);return i.length===0?(0,N.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`{}`})]}):(0,N.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,N.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,N.jsx)(ma,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`{`}),!s&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,i.length,` keys `]}),(0,N.jsx)(`span`,{className:`json-punct`,children:`}`})]})]}),s&&(0,N.jsxs)(N.Fragment,{children:[i.map(([e,n])=>(0,N.jsx)(_a,{value:n,depth:t+1,propertyKey:e,changedKeys:r,keyPath:d(e)},e)),(0,N.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,N.jsx)(`span`,{className:`json-punct`,children:`}`})})]})]})}return(0,N.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,N.jsx)(`span`,{className:`json-unknown`,children:String(e)})})}var va=[{id:`changes`,label:`Changes`},{id:`before-after`,label:`Before / After`},{id:`raw`,label:`Raw JSON`}];function ya({aggregateId:e,sequence:t,compareSequence:n,activeTab:r,onTabChange:i,active:a=!1,onActivate:o,source:s}){let{data:c=[],isLoading:l}=da(e,s),[u,d]=(0,M.useState)(`changes`),f=r??u,p=e=>{d(e),i?.(e)},m=c.find(e=>e.event.sequenceNumber===t)??null,h=n==null?null:c.find(e=>e.event.sequenceNumber===n)??null,g=m!=null&&h!=null&&m.event.sequenceNumber!==h.event.sequenceNumber,_=g?h.stateAfter:m?.stateBefore,v=m?.stateAfter,{patches:y,loading:b,durationMs:x}=fa(_,v,g&&!!_&&!!v),S=(0,M.useMemo)(()=>g?new Set(y.map(e=>e.path.replace(/^\$\./,``).split(`.`)[0])):new Set(Object.keys(m?.diff??{})),[g,y,m?.diff]);return l?(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`State`}),(0,N.jsx)(`div`,{className:`skeleton`,style:{height:160}})]}):m?(0,N.jsxs)(`section`,{className:`card`,role:`region`,"aria-label":`State viewer`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,N.jsxs)(`div`,{className:`card-title`,children:[`State at Event #`,m.event.sequenceNumber,(0,N.jsx)(`span`,{className:`diff-count-badge`,children:m.event.eventType}),g&&(0,N.jsxs)(`span`,{className:`diff-count-badge`,children:[`Compared with #`,h?.event.sequenceNumber]}),g&&!b&&(0,N.jsxs)(`span`,{className:`diff-count-badge`,children:[`Worker `,x.toFixed(1),`ms`]})]}),(0,N.jsx)(`div`,{className:`state-tabs`,role:`tablist`,children:va.map(e=>(0,N.jsx)(`button`,{type:`button`,role:`tab`,"aria-selected":f===e.id,className:`state-tab ${f===e.id?`active`:``}`,onClick:()=>p(e.id),children:e.label},e.id))}),(0,N.jsxs)(`div`,{className:`state-tab-content`,role:`tabpanel`,children:[f===`changes`&&(0,N.jsx)(`div`,{children:g?b?(0,N.jsx)(`div`,{className:`skeleton`,style:{height:120}}):(0,N.jsx)(pa,{patches:y,title:`Structural diff`}):m.diff&&Object.keys(m.diff).length>0?(0,N.jsx)(pa,{diff:m.diff}):(0,N.jsx)(`p`,{style:{color:`var(--text-muted)`,marginTop:12,fontSize:13},children:`No field changes at this event.`})}),f===`before-after`&&(0,N.jsxs)(`div`,{className:`state-grid`,style:{marginTop:12},children:[(0,N.jsxs)(`div`,{className:`state-panel state-panel-before`,children:[(0,N.jsx)(`h4`,{children:g?`Event #${h?.event.sequenceNumber}`:`Before`}),(0,N.jsx)(ga,{value:_??{},changedKeys:S})]}),(0,N.jsxs)(`div`,{className:`state-panel state-panel-after`,children:[(0,N.jsx)(`h4`,{children:g?`Event #${m.event.sequenceNumber}`:`After`}),(0,N.jsx)(ga,{value:v??{},changedKeys:S})]})]}),f===`raw`&&(0,N.jsx)(`div`,{style:{marginTop:12},children:(0,N.jsx)(`div`,{className:`json-block`,style:{maxHeight:340},children:JSON.stringify(g?{leftEvent:h?.event,rightEvent:m.event,leftState:_,rightState:v}:m.event,null,2)})})]})]}):null}var ba=1e3,xa=3e4;function Sa(e,t,n){let r=n?.enabled??!0,[i,a]=(0,M.useState)(()=>r?`connecting`:`connected`),o=(0,M.useRef)(null),s=(0,M.useRef)(t),c=(0,M.useRef)(ba);return s.current=t,(0,M.useEffect)(()=>{if(!r)return a(`connected`),()=>{};let t=!1,n,i=()=>{if(t)return;let r=new WebSocket(`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.host}${e}`);o.current=r,r.onopen=()=>{c.current=ba,a(`connected`)},r.onclose=()=>{if(a(`disconnected`),!t){let e=c.current;n=setTimeout(()=>{c.current=Math.min(e*2,xa),i()},e)}},r.onerror=()=>a(`disconnected`),r.onmessage=e=>{try{let t=JSON.parse(e.data);s.current(t)}catch{}}};return i(),()=>{t=!0,clearTimeout(n),o.current?.close()}},[e,r]),i}var Ca=(0,M.createContext)(void 0);function wa({children:e}){let[t,n]=(0,M.useState)([]),r=(0,M.useCallback)(e=>{let t=Date.now();n(n=>[...n,{id:t,message:e}]),setTimeout(()=>{n(e=>e.filter(e=>e.id!==t))},4e3)},[]);return(0,N.jsxs)(Ca.Provider,{value:{notify:r},children:[e,(0,N.jsx)(`div`,{className:`toast-container`,children:t.map(e=>(0,N.jsx)(`div`,{className:`toast`,children:e.message},e.id))})]})}function Ta(){let e=(0,M.useContext)(Ca);if(!e)throw Error(`useToast must be used within ToastProvider`);return e}function Ea(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`type-deleted`:t.includes(`withdrawn`)||t.includes(`debit`)?`type-withdrawn`:t.includes(`deposited`)||t.includes(`credit`)?`type-deposited`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`type-created`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`type-completed`:t.includes(`failed`)||t.includes(`error`)?`type-failed`:t.includes(`transfer`)?`type-transfer`:`type-default`}function Da(e){return Ea(e)}var Oa=100;function ka(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`✖`:t.includes(`withdrawn`)||t.includes(`debit`)?`↩`:t.includes(`deposited`)||t.includes(`credit`)?`↪`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`✦`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)?`✔`:t.includes(`failed`)||t.includes(`error`)?`⚠`:t.includes(`transfer`)?`⇄`:`◆`}function Aa({source:e,onSelectAggregate:t}){return(0,N.jsx)(Na,{source:e,onSelectAggregate:t})}function ja(e){return`type`in e&&e.type===`NO_LIVE_STREAM`}function Ma(e){return e?`/ws/live?source=${encodeURIComponent(e)}`:`/ws/live`}function Na({source:e,onSelectAggregate:t}){let n=R(),[r,i]=(0,M.useState)(()=>n?Ti():[]),[a,o]=(0,M.useState)(!1),[s,c]=(0,M.useState)(null),l=(0,M.useRef)(null),u=(0,M.useRef)(a);u.current=a;let{notify:d}=Ta();(0,M.useEffect)(()=>{c(null),i(n?Ti():[])},[e,n]);let f=Sa(Ma(e),e=>{if(ja(e)){c(e.source),i([]);return}c(null),!u.current&&i(t=>[...t.slice(-(Oa-1)),e])},{enabled:!n}),p=(0,M.useRef)(d);p.current=d;let m=(0,M.useRef)(0);return(0,M.useEffect)(()=>{n||(f===`disconnected`?(m.current++,m.current<=1&&p.current(`Live stream disconnected. Retrying…`)):f===`connected`&&(m.current=0))},[f,n]),(0,M.useEffect)(()=>{!a&&l.current&&(l.current.scrollTop=l.current.scrollHeight)},[r,a]),(0,M.useEffect)(()=>{let e=()=>o(e=>!e);return window.addEventListener(`eventlens:togglestream`,e),()=>window.removeEventListener(`eventlens:togglestream`,e)},[]),(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsxs)(`div`,{className:`live-header`,children:[(0,N.jsx)(`div`,{className:`card-title`,style:{marginBottom:0},children:`📡 Live Event Stream`}),(0,N.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,N.jsxs)(`div`,{className:`live-indicator`,children:[(0,N.jsx)(`span`,{className:`dot ${f===`connected`?`dot-green`:f===`connecting`?`dot-yellow`:`dot-red`}`}),(0,N.jsx)(`span`,{style:{color:f===`connected`?`var(--neon-green)`:`var(--text-muted)`,fontSize:11},children:f===`connected`?a?`Paused`:`Live`:f})]}),(0,N.jsx)(`button`,{className:`pause-btn`,onClick:()=>o(!a),children:a?`▶ Resume`:`⏸ Pause`})]})]}),(0,N.jsxs)(`div`,{className:`event-stream`,ref:l,children:[s&&(0,N.jsxs)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:[`Live stream not available for this source`,e?` (${e})`:s?` (${s})`:``,`.`]}),r.length===0&&(0,N.jsx)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:s?null:n?`Demo stream (static sample events)`:`Waiting for events…`}),r.map(e=>(0,N.jsxs)(`div`,{className:`event-row ${Da(e.eventType)}`,children:[(0,N.jsx)(`span`,{className:`event-icon`,children:ka(e.eventType)}),(0,N.jsx)(`span`,{className:`event-time`,children:$i(e.timestamp).toLocaleTimeString()}),(0,N.jsx)(`span`,{className:`event-type ${Ea(e.eventType)}`,children:e.eventType}),t?(0,N.jsx)(`button`,{type:`button`,className:`event-agg event-agg-button`,onClick:()=>t(e.aggregateId),children:e.aggregateId}):(0,N.jsx)(`span`,{className:`event-agg`,children:e.aggregateId})]},e.eventId))]})]})}function Pa(e){switch(e){case`CRITICAL`:return`sev-critical`;case`HIGH`:return`sev-high`;case`MEDIUM`:return`sev-medium`;case`LOW`:return`sev-low`;default:return`sev-low`}}function Fa(e){switch(e){case`CRITICAL`:return`Critical`;case`HIGH`:return`High`;case`MEDIUM`:return`Warning`;case`LOW`:return`Info`;default:return e}}function Ia(){return(0,N.jsxs)(`svg`,{viewBox:`0 0 64 64`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,N.jsx)(`defs`,{children:(0,N.jsxs)(`linearGradient`,{id:`shield-grad`,x1:`16`,y1:`8`,x2:`48`,y2:`56`,children:[(0,N.jsx)(`stop`,{offset:`0%`,stopColor:`#00ff88`,stopOpacity:`0.9`}),(0,N.jsx)(`stop`,{offset:`100%`,stopColor:`#00cc66`,stopOpacity:`0.6`})]})}),(0,N.jsx)(`path`,{d:`M32 4 L52 14 L52 32 C52 46 32 58 32 58 C32 58 12 46 12 32 L12 14 Z`,stroke:`url(#shield-grad)`,strokeWidth:`2`,fill:`rgba(0, 255, 136, 0.06)`}),(0,N.jsx)(`path`,{d:`M32 10 L48 18 L48 32 C48 43 32 53 32 53 C32 53 16 43 16 32 L16 18 Z`,stroke:`rgba(0, 255, 136, 0.2)`,strokeWidth:`1`,fill:`none`}),(0,N.jsx)(`polyline`,{points:`22,32 29,40 42,24`,stroke:`#00ff88`,strokeWidth:`3`,strokeLinecap:`round`,strokeLinejoin:`round`,fill:`none`})]})}function La({color:e}){return(0,N.jsx)(`div`,{className:`gauge-wave`,children:Array.from({length:12},(e,t)=>t).map(t=>(0,N.jsx)(`div`,{className:`gauge-wave-bar ${e}`,style:{animationDelay:`${t*.12}s`}},t))})}function Ra({source:e,onSelectAggregate:t}){let{data:n,isLoading:r}=pt({queryKey:[`anomalies`,e??`default`],queryFn:()=>Ni(100,e),refetchInterval:3e4}),i=n&&n.length>0;return(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsxs)(`div`,{className:`card-title anomaly-card-title-row`,children:[(0,N.jsx)(`span`,{className:`anomaly-title-text`,children:`⚠️ Anomaly Detection`}),!r&&i&&(0,N.jsx)(`span`,{className:`anomaly-header-count`,"aria-label":`${n.length} anomalies`,children:n.length})]}),r&&(0,N.jsx)(`div`,{className:`skeleton`,style:{height:120}}),!r&&!i&&(0,N.jsxs)(`div`,{className:`anomaly-panel-inner`,children:[(0,N.jsxs)(`div`,{className:`anomaly-shield`,children:[(0,N.jsx)(`div`,{className:`shield-icon`,children:(0,N.jsx)(Ia,{})}),(0,N.jsx)(`div`,{className:`shield-text`,children:`No anomalies detected`})]}),(0,N.jsxs)(`div`,{className:`gauge-row`,children:[(0,N.jsxs)(`div`,{className:`gauge-card optimal`,children:[(0,N.jsx)(`div`,{className:`gauge-label`,children:`Data Integrity`}),(0,N.jsx)(`div`,{className:`gauge-value optimal`,children:`OPTIMAL`}),(0,N.jsx)(La,{color:`green`})]}),(0,N.jsxs)(`div`,{className:`gauge-card baseline`,children:[(0,N.jsx)(`div`,{className:`gauge-label`,children:`Pattern Scan`}),(0,N.jsx)(`div`,{className:`gauge-value baseline`,children:`BASELINE`}),(0,N.jsx)(La,{color:`cyan`})]}),(0,N.jsxs)(`div`,{className:`gauge-card zero`,children:[(0,N.jsx)(`div`,{className:`gauge-label`,children:`Threat Level`}),(0,N.jsx)(`div`,{className:`gauge-value zero`,children:`ZERO`}),(0,N.jsx)(La,{color:`green`})]})]})]}),!r&&i&&(0,N.jsx)(`div`,{className:`anomaly-scroll-region`,children:(0,N.jsx)(`div`,{className:`anomaly-list-inner`,children:n.map((e,n)=>(0,N.jsxs)(`details`,{className:`anomaly-card ${e.severity}`,children:[(0,N.jsxs)(`summary`,{className:`anomaly-card-summary`,children:[(0,N.jsx)(`span`,{className:`anomaly-severity-badge ${Pa(e.severity)}`,children:Fa(e.severity)}),(0,N.jsx)(`span`,{className:`anomaly-card-title`,children:e.description}),(0,N.jsx)(`span`,{className:`anomaly-card-chevron`,"aria-hidden":!0,children:`▼`})]}),(0,N.jsxs)(`div`,{className:`anomaly-card-body`,children:[(0,N.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,N.jsx)(`span`,{className:`anomaly-meta-label`,children:`Aggregate`}),t?(0,N.jsx)(`button`,{type:`button`,className:`anomaly-aggregate-link`,onClick:()=>t(e.aggregateId),children:(0,N.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})}):(0,N.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})]}),(0,N.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,N.jsx)(`span`,{className:`anomaly-meta-label`,children:`Sequence`}),(0,N.jsxs)(`span`,{className:`anomaly-meta-value`,children:[`#`,e.atSequence]})]}),(0,N.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,N.jsx)(`span`,{className:`anomaly-meta-label`,children:`Event type`}),(0,N.jsx)(`span`,{className:`anomaly-meta-value`,children:e.triggeringEventType})]}),(0,N.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,N.jsx)(`span`,{className:`anomaly-meta-label`,children:`When`}),(0,N.jsx)(`span`,{className:`anomaly-meta-value`,children:$i(e.timestamp).toLocaleString()})]}),(0,N.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,N.jsx)(`span`,{className:`anomaly-meta-label`,children:`Code`}),(0,N.jsx)(`code`,{className:`anomaly-meta-value`,children:e.code})]})]})]},`${e.aggregateId}-${e.atSequence}-${n}`))})})]})}var za=[{keys:`j / k`,desc:`Move selection through the timeline`},{keys:`Shift+Click`,desc:`Pick a compare event for diff mode`},{keys:`1 - 3`,desc:`Switch state tabs`},{keys:`Ctrl/Cmd+K`,desc:`Open command palette`},{keys:`/`,desc:`Focus aggregate search`},{keys:`Space`,desc:`Pause or resume live stream`},{keys:`?`,desc:`Toggle shortcut help`}];function Ba(){let[e,t]=(0,M.useState)(!1);return(0,M.useEffect)(()=>{let e=e=>{e.target.tagName!==`INPUT`&&e.key===`?`&&(e.preventDefault(),t(e=>!e))};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]),(0,N.jsx)(`div`,{className:`keyboard-hints ${e?`keyboard-hints--expanded`:``}`,"aria-label":`Keyboard shortcuts`,children:e?(0,N.jsxs)(`div`,{className:`keyboard-hints-grid`,children:[za.map(e=>(0,N.jsxs)(`div`,{className:`keyboard-hint-row`,children:[(0,N.jsx)(`kbd`,{className:`keyboard-key`,children:e.keys}),(0,N.jsx)(`span`,{className:`keyboard-hint-desc`,children:e.desc})]},e.keys)),(0,N.jsx)(`button`,{type:`button`,className:`keyboard-hints-close`,onClick:()=>t(!1),"aria-label":`Close shortcuts`,children:`Close`})]}):(0,N.jsxs)(`div`,{className:`keyboard-hints-bar`,children:[(0,N.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,N.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`j/k`}),` Navigate`]}),(0,N.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,N.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,N.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Shift+Click`}),` Compare`]}),(0,N.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,N.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,N.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Ctrl/Cmd+K`}),` Palette`]}),(0,N.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,N.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,N.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`?`}),` All shortcuts`]})]})})}var Va=[2,3,5,10];function Ha({transitions:e,selectedSequence:t,compareSequence:n,onSelectSequence:r,onClearCompare:i,active:a=!1,onActivate:o}){let[s,c]=(0,M.useState)(null),[l,u]=(0,M.useState)(3),[d,f]=(0,M.useState)(!1);if((0,M.useEffect)(()=>{n==null&&s!=null&&(s.type===`autoplay`&&f(!0),r(s.sequence),c(null))},[n,r,s]),!e.length)return null;let p=e.find(e=>e.event.sequenceNumber===t)??e[0],m=e.findIndex(e=>e.event.sequenceNumber===p.event.sequenceNumber),h=n!=null&&n!==p.event.sequenceNumber,g=m>=e.length-1,_=m<=0;(0,M.useEffect)(()=>{h&&d&&f(!1)},[h,d]),(0,M.useEffect)(()=>{if(!d||g){g&&d&&f(!1);return}let t=window.setInterval(()=>{let t=e[Math.min(m+1,e.length-1)];if(!t||t.event.sequenceNumber===p.event.sequenceNumber){f(!1);return}r(t.event.sequenceNumber)},l*1e3);return()=>window.clearInterval(t)},[g,l,p.event.sequenceNumber,m,d,r,e]);let v=e=>{if(e!==p.event.sequenceNumber){if(h&&i){c({type:`select`,sequence:e});return}f(!1),r(e)}},y=()=>{if(d){f(!1);return}if(g)return;let t=e[Math.min(m+1,e.length-1)].event.sequenceNumber;if(h&&i){c({type:`autoplay`,sequence:t});return}f(!0)};return(0,N.jsxs)(`section`,{className:`card replay-debugger`,tabIndex:0,role:`region`,"aria-label":`Replay debugger`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Replay Debugger`}),(0,N.jsxs)(`div`,{className:`replay-toolbar`,children:[(0,N.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.max(m-1,0)].event.sequenceNumber),title:`Step backward`,"aria-label":`Replay previous event`,disabled:_,children:`Previous`}),(0,N.jsxs)(`div`,{className:`replay-controls-center`,children:[(0,N.jsxs)(`div`,{className:`replay-position`,children:[`Event `,m+1,` of `,e.length]}),(0,N.jsxs)(`div`,{className:`replay-auto-controls`,children:[(0,N.jsx)(`button`,{type:`button`,className:`replay-auto-toggle ${d?`active`:``}`,onClick:y,disabled:g,"aria-pressed":d,children:d?`Pause Auto Replay`:`Start Auto Replay`}),(0,N.jsxs)(`label`,{className:`replay-interval-picker`,children:[(0,N.jsx)(`span`,{children:`Speed`}),(0,N.jsx)(`select`,{value:l,onChange:e=>u(Number(e.target.value)),disabled:d,"aria-label":`Auto replay interval`,children:Va.map(e=>(0,N.jsxs)(`option`,{value:e,children:[e,`s / event`]},e))})]})]})]}),(0,N.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.min(m+1,e.length-1)].event.sequenceNumber),title:`Step forward`,"aria-label":`Replay next event`,disabled:g,children:`Next`})]}),h&&(0,N.jsxs)(`div`,{className:`replay-compare-warning`,role:`alert`,"aria-live":`polite`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`strong`,{children:`Compare mode is active.`}),` Replay stepping or auto replay will replace the comparison and continue from the selected event.`]}),s==null?(0,N.jsx)(`div`,{className:`replay-warning-hint`,children:`Use Previous, Next, the slider, or auto replay. We’ll ask before clearing compare mode.`}):(0,N.jsxs)(`div`,{className:`replay-compare-actions`,children:[(0,N.jsx)(`button`,{type:`button`,className:`replay-warning-btn replay-warning-btn--primary`,onClick:()=>i?.(),children:s.type===`autoplay`?`Start replay and replace compare`:`Continue and replace compare`}),(0,N.jsx)(`button`,{type:`button`,className:`replay-warning-btn`,onClick:()=>c(null),children:`Keep compare`})]})]}),(0,N.jsx)(`input`,{type:`range`,min:0,max:Math.max(e.length-1,0),value:m,onChange:t=>v(e[Number(t.target.value)].event.sequenceNumber),"aria-label":`Replay position`}),(0,N.jsxs)(`div`,{className:`state-panel replay-state`,children:[(0,N.jsx)(`h4`,{children:`Current State`}),(0,N.jsx)(ga,{value:p.stateAfter,changedKeys:new Set(Object.keys(p.diff))})]})]})}function Ua(e,t=1,n=24){return pt({queryKey:[`statistics`,e??`default`,t,n],queryFn:()=>Fi(e,t,n),staleTime:3e4})}function Wa({source:e,onBack:t}){let[n,r]=(0,M.useState)(24),{data:i,isLoading:a}=Ua(e,1,n);return(0,N.jsxs)(`section`,{className:`card statistics-panel`,role:`region`,"aria-label":`Statistics panel`,"aria-busy":a,children:[(0,N.jsxs)(`div`,{className:`statistics-panel-header`,children:[(0,N.jsxs)(`div`,{className:`card-title`,children:[`Statistics `,e?`- ${e}`:``]}),(0,N.jsx)(`button`,{type:`button`,className:`statistics-back-btn`,onClick:t,children:`Back to workspace`})]}),(0,N.jsx)(`div`,{className:`statistics-toolbar`,children:[6,24,72].map(e=>(0,N.jsx)(`button`,{type:`button`,className:`filter-chip ${n===e?`active`:``}`,onClick:()=>r(e),children:e===72?`3d`:`${e}h`},e))}),a&&(0,N.jsx)(`div`,{className:`skeleton`,style:{height:140}}),!a&&i&&!i.available&&(0,N.jsx)(`p`,{style:{color:`var(--text-muted)`},children:i.message??`Statistics not available.`}),!a&&i?.available&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`div`,{className:`stats-kpis`,children:[(0,N.jsxs)(`div`,{className:`stat-card`,children:[(0,N.jsx)(`strong`,{children:i.totalEvents}),(0,N.jsx)(`span`,{children:`Total events`})]}),(0,N.jsxs)(`div`,{className:`stat-card`,children:[(0,N.jsx)(`strong`,{children:i.distinctAggregates}),(0,N.jsx)(`span`,{children:`Aggregates`})]}),(0,N.jsxs)(`div`,{className:`stat-card`,children:[(0,N.jsx)(`strong`,{children:i.eventTypes.length}),(0,N.jsx)(`span`,{children:`Event types`})]})]}),(0,N.jsx)(`div`,{className:`stats-chart`,children:i.throughput.map(e=>(0,N.jsxs)(`div`,{className:`stats-bar-row`,children:[(0,N.jsx)(`span`,{children:e.bucket.slice(11,16)}),(0,N.jsx)(`div`,{className:`stats-bar-track`,children:(0,N.jsx)(`div`,{className:`stats-bar-fill`,style:{width:`${Math.max(8,e.count/Math.max(...i.throughput.map(e=>e.count),1)*100)}%`}})}),(0,N.jsx)(`strong`,{children:e.count})]},e.bucket))}),(0,N.jsxs)(`div`,{className:`stats-distribution`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h4`,{children:`Event Types`}),i.eventTypes.map(e=>(0,N.jsxs)(`div`,{className:`stats-list-row`,children:[(0,N.jsx)(`span`,{children:e.type}),(0,N.jsx)(`strong`,{children:e.count})]},e.type))]}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h4`,{children:`Aggregate Types`}),i.aggregateTypes.map(e=>(0,N.jsxs)(`div`,{className:`stats-list-row`,children:[(0,N.jsx)(`span`,{children:e.type}),(0,N.jsx)(`strong`,{children:e.count})]},e.type))]})]})]})]})}function Ga({open:e,selectedSource:t,onClose:n,onSelectAggregate:r,onOpenHome:i,onOpenStats:a,onOpenAdmin:o}){let[s,c]=(0,M.useState)(``),[l,u]=(0,M.useState)(0),[d,f]=(0,M.useState)(null),{data:p=[]}=pt({queryKey:[`palette-search`,s,t??`default`],queryFn:()=>Mi(s,8,t),enabled:e&&s.trim().length>=2,staleTime:5e3});(0,M.useEffect)(()=>{e?(u(0),f(document.activeElement instanceof HTMLElement?document.activeElement:null)):(c(``),d?.focus())},[e,d]);let m=(0,M.useMemo)(()=>{let e=[{id:`home`,label:`Go to main page`,action:i},{id:`stats`,label:`Go to statistics panel`,action:a},{id:`admin`,label:`Open security administration`,action:o}],t=p.map(e=>({id:`agg-${e}`,label:`Open aggregate ${e}`,action:()=>r(e)}));return s.trim()&&p.length===0&&e.unshift({id:`direct`,label:`Open aggregate ${s.trim()}`,action:()=>r(s.trim())}),[...t,...e]},[p,o,i,a,r,s]);return(0,M.useEffect)(()=>{if(!e)return;let t=e=>{e.key===`ArrowDown`?(e.preventDefault(),u(e=>Math.min(e+1,Math.max(m.length-1,0)))):e.key===`ArrowUp`?(e.preventDefault(),u(e=>Math.max(e-1,0))):e.key===`Enter`&&(e.preventDefault(),m[l]?.action(),n())};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[m,n,e,l]),e?(0,N.jsx)(`div`,{className:`command-palette-backdrop`,onClick:n,children:(0,N.jsxs)(`div`,{className:`command-palette`,role:`dialog`,"aria-modal":`true`,"aria-label":`Command palette`,"aria-describedby":`command-palette-help`,onClick:e=>e.stopPropagation(),children:[(0,N.jsx)(`p`,{id:`command-palette-help`,className:`sr-only`,children:`Search aggregates, return to the main page, open statistics, or open security administration. Use arrow keys to move and Enter to confirm.`}),(0,N.jsx)(`input`,{autoFocus:!0,className:`command-palette-input`,placeholder:`Search aggregates or jump to a panel`,value:s,onChange:e=>c(e.target.value),"aria-label":`Command palette search`}),(0,N.jsxs)(`ul`,{className:`command-palette-list`,role:`listbox`,"aria-label":`Command results`,children:[m.map((e,t)=>(0,N.jsx)(`li`,{className:`command-palette-item ${t===l?`active`:``}`,role:`option`,"aria-selected":t===l,onMouseEnter:()=>u(t),onClick:()=>{e.action(),n()},children:e.label},e.id)),m.length===0&&(0,N.jsx)(`li`,{className:`command-palette-item muted`,children:`Type at least two characters to search.`})]})]})}):null}function Ka({paletteOpen:e,onOpenPalette:t,onClosePalette:n}){return(0,M.useEffect)(()=>{let r=r=>{let i=r.target,a=i&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(i.tagName);if((r.metaKey||r.ctrlKey)&&r.key.toLowerCase()===`k`){r.preventDefault(),t();return}if(r.key===`/`&&!a){r.preventDefault(),document.getElementById(`aggregate-search`)?.focus();return}if(r.key===`Escape`&&e){r.preventDefault(),n();return}!a&&(r.key===`j`||r.key===`k`)&&window.dispatchEvent(new CustomEvent(`eventlens:timeline-step`,{detail:r.key===`j`?1:-1}))};return window.addEventListener(`keydown`,r),()=>window.removeEventListener(`keydown`,r)},[e,n,t]),null}function qa(e){if(L.isAxiosError(e)){let t=e.response?.status,n=e.response?.data??{};return{status:t,error:typeof n.error==`string`?n.error:void 0,reason:typeof n.reason==`string`?n.reason:void 0,permission:typeof n.permission==`string`?n.permission:void 0,message:typeof n.message==`string`?n.message:typeof n.error==`string`?n.error:e.message||`Request failed`}}return e instanceof Error?{message:e.message}:{message:`Request failed`}}function Ja({title:e,detail:t,variant:n=`error`}){return(0,N.jsxs)(`div`,{className:`admin-state admin-state--${n}`,role:`alert`,children:[(0,N.jsx)(`strong`,{children:e}),(0,N.jsx)(`p`,{children:t})]})}function Ya(){let e=Qe(),{notify:t}=Ta(),[n,r]=(0,M.useState)(``),[i,a]=(0,M.useState)(`api-reader`),[o,s]=(0,M.useState)(``),[c,l]=(0,M.useState)(``),[u,d]=(0,M.useState)(null),f=pt({queryKey:[`admin-audit`],queryFn:()=>Bi(25),retry:!1,refetchOnWindowFocus:!1}),p=pt({queryKey:[`admin-api-keys`],queryFn:Vi,retry:!1,refetchOnWindowFocus:!1}),m=mt({mutationFn:Hi,onSuccess:async n=>{d({apiKey:n.apiKey,keyPrefix:n.keyPrefix}),r(``),a(`api-reader`),s(``),l(``),t(`Created API key ${n.keyPrefix}`),await e.invalidateQueries({queryKey:[`admin-api-keys`]}),await e.invalidateQueries({queryKey:[`admin-audit`]})}}),h=mt({mutationFn:Ui,onSuccess:async n=>{t(`Revoked API key ${n.apiKeyId}`),await e.invalidateQueries({queryKey:[`admin-api-keys`]}),await e.invalidateQueries({queryKey:[`admin-audit`]})}}),g=async e=>{e.preventDefault(),d(null),await m.mutateAsync({principalUserId:n.trim(),roles:i.split(`,`).map(e=>e.trim()).filter(Boolean),description:o.trim()||void 0,expiresAt:c.trim()?new Date(c).toISOString():void 0})},_=f.error?qa(f.error):null,v=p.error?qa(p.error):null,y=m.error?qa(m.error):null;return(0,N.jsxs)(`section`,{className:`admin-console`,"aria-label":`Security administration`,children:[(0,N.jsxs)(`div`,{className:`card admin-hero`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Security Administration`}),(0,N.jsx)(`p`,{className:`admin-hero-copy`,children:`Review recent security activity, issue machine credentials, and revoke keys without leaving the current EventLens workspace.`})]}),(0,N.jsxs)(`div`,{className:`admin-grid`,children:[(0,N.jsxs)(`div`,{className:`card admin-card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Recent Audit Activity`}),f.isLoading&&(0,N.jsx)(`p`,{className:`admin-muted`,children:`Loading the latest audit entries from metadata-backed storage.`}),_?.status===403&&(0,N.jsx)(Ja,{title:`Audit access denied`,detail:`This session cannot view the audit log${_.permission?` (${_.permission})`:``}.`,variant:`warning`}),_&&_.status!==403&&(0,N.jsx)(Ja,{title:`Audit unavailable`,detail:_.message}),!f.isLoading&&!_&&(0,N.jsx)(`div`,{className:`admin-table-wrap`,children:(0,N.jsxs)(`table`,{className:`admin-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`When`}),(0,N.jsx)(`th`,{children:`Action`}),(0,N.jsx)(`th`,{children:`Actor`}),(0,N.jsx)(`th`,{children:`Auth`}),(0,N.jsx)(`th`,{children:`Resource`})]})}),(0,N.jsx)(`tbody`,{children:f.data?.entries.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:new Date(e.createdAt).toLocaleString()}),(0,N.jsx)(`td`,{children:e.action}),(0,N.jsx)(`td`,{children:e.userId}),(0,N.jsx)(`td`,{children:e.authMethod}),(0,N.jsxs)(`td`,{children:[e.resourceType,e.resourceId?`:${e.resourceId}`:``]})]},`${e.auditId}-${e.requestId??e.createdAt}`))})]})})]}),(0,N.jsxs)(`div`,{className:`card admin-card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`API Key Management`}),(0,N.jsx)(`p`,{className:`admin-muted`,children:`Issue one-time machine credentials tied to existing RBAC roles. Raw keys are shown once and never listed again.`}),v?.status===403?(0,N.jsx)(Ja,{title:`API key management denied`,detail:`This session cannot manage API keys${v.permission?` (${v.permission})`:``}.`,variant:`warning`}):v?(0,N.jsx)(Ja,{title:`API key management unavailable`,detail:v.message}):(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`form`,{className:`admin-form`,onSubmit:g,children:[(0,N.jsxs)(`label`,{className:`admin-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Principal User Id`}),(0,N.jsx)(`input`,{className:`auth-input`,value:n,onChange:e=>r(e.target.value),required:!0})]}),(0,N.jsxs)(`label`,{className:`admin-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Roles`}),(0,N.jsx)(`input`,{className:`auth-input`,value:i,onChange:e=>a(e.target.value),required:!0})]}),(0,N.jsxs)(`label`,{className:`admin-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Description`}),(0,N.jsx)(`input`,{className:`auth-input`,value:o,onChange:e=>s(e.target.value)})]}),(0,N.jsxs)(`label`,{className:`admin-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Expires At (Local Time)`}),(0,N.jsx)(`input`,{type:`datetime-local`,className:`auth-input`,value:c,onChange:e=>l(e.target.value),min:new Date(Date.now()-new Date().getTimezoneOffset()*6e4).toISOString().slice(0,16)})]}),y&&(0,N.jsx)(Ja,{title:`Key creation failed`,detail:y.message}),(0,N.jsx)(`button`,{className:`auth-submit`,type:`submit`,disabled:m.isPending,children:m.isPending?`Issuing key...`:`Issue API key`})]}),u&&(0,N.jsxs)(`div`,{className:`admin-issued-key`,role:`status`,children:[(0,N.jsx)(`strong`,{children:`Copy this key now.`}),(0,N.jsx)(`p`,{children:u.apiKey}),(0,N.jsxs)(`span`,{children:[`Stored prefix: `,u.keyPrefix]})]}),(0,N.jsx)(`div`,{className:`admin-table-wrap`,children:(0,N.jsxs)(`table`,{className:`admin-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Prefix`}),(0,N.jsx)(`th`,{children:`Principal`}),(0,N.jsx)(`th`,{children:`Roles`}),(0,N.jsx)(`th`,{children:`Last used`}),(0,N.jsx)(`th`,{children:`Status`}),(0,N.jsx)(`th`,{})]})}),(0,N.jsx)(`tbody`,{children:p.data?.entries.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:e.keyPrefix}),(0,N.jsx)(`td`,{children:e.principalUserId}),(0,N.jsx)(`td`,{children:e.roles.join(`, `)}),(0,N.jsx)(`td`,{children:e.lastUsedAt?new Date(e.lastUsedAt).toLocaleString():`Never`}),(0,N.jsx)(`td`,{children:e.revokedAt?`Revoked`:e.expiresAt?`Expires ${new Date(e.expiresAt).toLocaleString()}`:`Active`}),(0,N.jsx)(`td`,{children:!e.revokedAt&&(0,N.jsx)(`button`,{className:`admin-inline-action`,type:`button`,disabled:h.isPending,onClick:()=>h.mutate(e.apiKeyId),children:`Revoke`})})]},e.apiKeyId))})]})})]})]})]})]})}function Xa({status:e,onApplied:t}){let[n,r]=(0,M.useState)(`basic`),[i,a]=(0,M.useState)(`admin`),[o,s]=(0,M.useState)(``),[c,l]=(0,M.useState)(``),[u,d]=(0,M.useState)(``),[f,p]=(0,M.useState)(``),[m,h]=(0,M.useState)(null),[g,_]=(0,M.useState)(!1),v=(0,M.useMemo)(()=>{switch(n){case`basic`:return`Use a local username/password and enable the v5 browser-session flow on top of it.`;case`oidc`:return`Use your OpenID Connect provider and keep EventLens sessions server-side.`;case`disabled`:return`Skip authentication for local-only debugging. This is not safe for shared environments.`}},[n]),y=async e=>{e.preventDefault(),h(null),_(!0);try{t(await zi({mode:n,username:i,password:o,issuer:c,clientId:u,clientSecret:f}))}catch(e){h(e instanceof Error?e.message:`Setup failed. Review the values and try again.`)}finally{_(!1)}};return(0,N.jsx)(`div`,{className:`app auth-shell`,children:(0,N.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Instance setup`,children:(0,N.jsxs)(`section`,{className:`auth-card setup-card`,children:[(0,N.jsx)(`div`,{className:`auth-eyebrow`,children:`EventLens Setup`}),(0,N.jsx)(`h1`,{className:`auth-title`,children:`Secure this instance`}),(0,N.jsxs)(`p`,{className:`auth-copy`,children:[`This instance has not been configured yet. Choose one security mode now, save it to`,(0,N.jsxs)(`span`,{className:`setup-path`,children:[` `,e.configPath]}),`, then restart EventLens once.`]}),(0,N.jsxs)(`div`,{className:`setup-mode-grid`,role:`radiogroup`,"aria-label":`Security mode`,children:[(0,N.jsxs)(`button`,{type:`button`,className:`setup-mode-card ${n===`basic`?`setup-mode-card--active`:``}`,onClick:()=>r(`basic`),children:[(0,N.jsx)(`strong`,{children:`Basic Auth`}),(0,N.jsx)(`span`,{children:`Fastest protected path for a small team or local container.`})]}),(0,N.jsxs)(`button`,{type:`button`,className:`setup-mode-card ${n===`oidc`?`setup-mode-card--active`:``}`,onClick:()=>r(`oidc`),children:[(0,N.jsx)(`strong`,{children:`OIDC / SSO`}),(0,N.jsx)(`span`,{children:`Use your identity provider and keep browser sessions server-side.`})]}),(0,N.jsxs)(`button`,{type:`button`,className:`setup-mode-card ${n===`disabled`?`setup-mode-card--active`:``}`,onClick:()=>r(`disabled`),children:[(0,N.jsx)(`strong`,{children:`Local Dev Only`}),(0,N.jsx)(`span`,{children:`No authentication. Good only for a private local sandbox.`})]})]}),(0,N.jsx)(`p`,{className:`setup-mode-copy`,children:v}),(0,N.jsxs)(`form`,{className:`auth-form`,onSubmit:y,children:[n===`basic`&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Username`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`text`,value:i,onChange:e=>a(e.target.value),disabled:g,required:!0})]}),(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Password`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`password`,value:o,onChange:e=>s(e.target.value),disabled:g,required:!0})]})]}),n===`oidc`&&(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Issuer`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`url`,placeholder:`https://id.example.com/realms/eventlens`,value:c,onChange:e=>l(e.target.value),disabled:g,required:!0})]}),(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Client ID`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`text`,value:u,onChange:e=>d(e.target.value),disabled:g,required:!0})]}),(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Client Secret`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`password`,value:f,onChange:e=>p(e.target.value),disabled:g,required:!0})]})]}),n===`disabled`&&(0,N.jsx)(`div`,{className:`setup-warning`,children:`EventLens will save a local no-auth configuration. Use this only for a private developer workstation.`}),m&&(0,N.jsx)(`div`,{className:`auth-error`,role:`alert`,children:m}),(0,N.jsx)(`button`,{className:`auth-submit`,type:`submit`,disabled:g,children:g?`Saving...`:`Save setup`})]})]})})})}function Za(e){let t=e.toLowerCase();return t===`ready`||t===`up`}function Qa(e){return e.toLowerCase()===`ready`}function $a(){let e=[6,12,8,16,10,14,7,11,15,9];return(0,N.jsx)(`div`,{className:`mini-wave`,"aria-hidden":!0,children:e.map((e,t)=>(0,N.jsx)(`div`,{className:`mini-wave-bar`,style:{height:e,animationDelay:`${t*.1}s`}},t))})}function eo({isUp:e,selectedSource:t,fallbackCount:n}){let[r,i]=(0,M.useState)(0),[a,o]=(0,M.useState)(n),s=(0,M.useRef)(null);(0,M.useEffect)(()=>{let e=Date.now();return s.current=setInterval(()=>{i(Math.floor((Date.now()-e)/1e3))},1e3),()=>{s.current&&clearInterval(s.current)}},[]),(0,M.useEffect)(()=>{o(n)},[n]),(0,M.useEffect)(()=>{let e=!1,r=()=>{Pi(500,t||null).then(t=>{e||o(t.length)}).catch(()=>{!e&&n!=null&&o(n)})};r();let i=setInterval(r,15e3);return()=>{e=!0,clearInterval(i)}},[n,t]);let c=e=>{let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60;return t>0?`${t}h ${n}m`:n>0?`${n}m ${r}s`:`${r}s`};return(0,N.jsxs)(`div`,{className:`conn-stats`,"aria-label":`Connection metrics`,children:[(0,N.jsx)($a,{}),(0,N.jsxs)(`div`,{className:`conn-stat`,children:[(0,N.jsx)(`span`,{className:`conn-stat-label`,children:`API`}),(0,N.jsx)(`span`,{className:`conn-stat-value ${e?`green`:`amber`}`,children:e?`Healthy`:`Down`})]}),(0,N.jsxs)(`div`,{className:`conn-stat conn-stat--metric`,children:[(0,N.jsx)(`span`,{className:`conn-stat-label`,children:`Events`}),(0,N.jsx)(`span`,{className:`conn-stat-value`,children:a??`...`})]}),(0,N.jsxs)(`div`,{className:`conn-stat`,children:[(0,N.jsx)(`span`,{className:`conn-stat-label`,children:`Uptime`}),(0,N.jsx)(`span`,{className:`conn-stat-value green conn-stat-value--uptime`,children:c(r)})]})]})}function to(){let e=Qe(),[t,n]=(0,M.useState)(`state`),[r,i]=(0,M.useState)(null),[a,o]=(0,M.useState)(null),[s,c]=(0,M.useState)(null),[l,u]=(0,M.useState)(`changes`),[d,f]=(0,M.useState)(``),[p,m]=(0,M.useState)(window.location.hash||``),[h,g]=(0,M.useState)(!1),[_,v]=(0,M.useState)(!1),[y,b]=(0,M.useState)(``),[x,S]=(0,M.useState)(``),[C,w]=(0,M.useState)(null),[ee,te]=(0,M.useState)(!1),[ne,T]=(0,M.useState)(!1),[re,ie]=(0,M.useState)(null),[ae,oe]=(0,M.useState)(null);(0,M.useEffect)(()=>{let e=()=>m(window.location.hash||``);return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,M.useEffect)(()=>{p===`#/plugins`&&(window.location.hash=`#/timeline`)},[p]),(0,M.useEffect)(()=>{let e=e=>{let t=e.detail;t&&u(t)};return window.addEventListener(`eventlens:switchtab`,e),()=>window.removeEventListener(`eventlens:switchtab`,e)},[]),(0,M.useEffect)(()=>{let e=new URLSearchParams(window.location.search),t=e.get(`authError`),r=e.get(`aggregateId`),a=e.get(`seq`),s=e.get(`compare`),l=e.get(`tab`),d=e.get(`panel`),p=e.get(`source`);if(t){ie(t.replace(/_/g,` `)),e.delete(`authError`);let n=e.toString(),r=`${window.location.pathname}${n?`?${n}`:``}${window.location.hash||`#/timeline`}`;window.history.replaceState(null,``,r)}r&&i(r),a&&o(Number(a)),s&&c(Number(s)),l&&[`changes`,`before-after`,`raw`].includes(l)&&u(l),(d===`replay`||d===`state`)&&n(d),p&&f(p)},[]),(0,M.useEffect)(()=>{let e=new URLSearchParams(window.location.search);r?e.set(`aggregateId`,r):e.delete(`aggregateId`),a==null?e.delete(`seq`):e.set(`seq`,String(a)),s==null?e.delete(`compare`):e.set(`compare`,String(s)),e.set(`tab`,l),e.set(`panel`,t),d?e.set(`source`,d):e.delete(`source`);let n=e.toString(),i=`${window.location.pathname}${n?`?${n}`:``}${window.location.hash}`;window.history.replaceState(null,``,i)},[t,l,s,r,a,d]);let E=pt({queryKey:[`setup-status`],queryFn:Ri,retry:!1,refetchOnWindowFocus:!1}),D=pt({queryKey:[`auth-session`],queryFn:Li,retry:!1,refetchOnWindowFocus:!1,enabled:R()||E.isSuccess&&!E.data?.setupRequired&&!E.data?.restartRequired}),se=R()||D.data?.authenticated===!0,O=R()||E.isSuccess||E.isError,k=R()||D.isSuccess||D.isError||!!E.data?.setupRequired||!!E.data?.restartRequired;(0,M.useEffect)(()=>{if(R()){qi(null);return}qi(D.data?.authenticated?D.data.csrfToken??null:null)},[D.data]);let{data:ce}=pt({queryKey:[`health`],queryFn:Ii,refetchInterval:3e4,enabled:se}),{data:le=[]}=pt({queryKey:[`datasources`],queryFn:Ji,staleTime:1e4,enabled:se}),{data:ue=[]}=pt({queryKey:[`plugins`],queryFn:Yi,staleTime:1e4,enabled:se}),{data:de=[]}=da(r??``,d||null,se),{data:A}=pt({queryKey:[`timeline-summary`,r,d||`default`],queryFn:()=>B(r,500,0,d||null,`metadata`),enabled:se&&!!r,staleTime:3e4}),j=ce?.status===`UP`,fe=p===`#/stats`,pe=p===`#/admin`,me=le.filter(e=>Za(e.status)).length,he=ue.filter(e=>Za(e.lifecycle)).length,ge=le.length-me+(ue.length-he),_e=e=>{i(e),o(null),c(null),n(`state`),window.location.hash=`#/timeline`},ve=()=>{window.location.hash=`#/timeline`},ye=()=>{window.location.hash=`#/admin`},be=async t=>{t.preventDefault(),w(null),ie(null),te(!0);try{let t=await Wi(y,x,p||`#/timeline`);S(``),e.setQueryData([`auth-session`],t),await e.invalidateQueries(),window.location.hash=t.returnHash||p||`#/timeline`}catch{w(`Login failed. Check your credentials and try again.`)}finally{te(!1)}},xe=async()=>{T(!0),w(null),ie(null);try{await Gi(),e.setQueryData([`auth-session`],{authenticated:!1}),await e.invalidateQueries(),S(``),window.location.hash=`#/timeline`}finally{T(!1)}};if(!O)return(0,N.jsx)(`div`,{className:`app auth-shell`,children:(0,N.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Setup status`,children:(0,N.jsxs)(`section`,{className:`auth-card`,children:[(0,N.jsx)(`div`,{className:`auth-eyebrow`,children:`EventLens Setup`}),(0,N.jsx)(`h1`,{className:`auth-title`,children:`Checking instance state`}),(0,N.jsx)(`p`,{className:`auth-copy`,children:`We are checking whether this EventLens instance still needs first-run security setup.`})]})})});if(E.data?.setupRequired&&!ae)return(0,N.jsx)(Xa,{status:E.data,onApplied:oe});if(ae||E.data?.restartRequired){let e=ae??{saved:!0,restartRequired:!0,mode:`saved`,configPath:E.data?.configPath??`eventlens.yaml`};return(0,N.jsx)(`div`,{className:`app auth-shell`,children:(0,N.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Restart required`,children:(0,N.jsxs)(`section`,{className:`auth-card`,children:[(0,N.jsx)(`div`,{className:`auth-eyebrow`,children:`EventLens Setup`}),(0,N.jsx)(`h1`,{className:`auth-title`,children:`Restart required`}),(0,N.jsxs)(`p`,{className:`auth-copy`,children:[`We saved the new security mode to `,(0,N.jsx)(`span`,{className:`setup-path`,children:e.configPath}),`. Restart EventLens once so the new auth pipeline comes up cleanly.`]}),(0,N.jsxs)(`div`,{className:`setup-warning`,children:[`Saved mode: `,(0,N.jsx)(`strong`,{children:e.mode}),`. After restart, reload this page and continue with the normal sign-in flow.`]})]})})})}if(!k)return(0,N.jsx)(`div`,{className:`app auth-shell`,children:(0,N.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Session status`,children:(0,N.jsxs)(`section`,{className:`auth-card`,children:[(0,N.jsx)(`div`,{className:`auth-eyebrow`,children:`Security Session`}),(0,N.jsx)(`h1`,{className:`auth-title`,children:`Checking active session`}),(0,N.jsx)(`p`,{className:`auth-copy`,children:`We are verifying whether this browser already has an active EventLens session.`})]})})});if(!se){let e=D.data?.provider??`basic`,t=D.data?.basicLoginEnabled??e!==`oidc`;return(0,N.jsx)(`div`,{className:`app auth-shell`,children:(0,N.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Login`,children:(0,N.jsxs)(`section`,{className:`auth-card`,children:[(0,N.jsx)(`div`,{className:`auth-eyebrow`,children:`EventLens Security`}),(0,N.jsx)(`h1`,{className:`auth-title`,children:D.isError?`Server unavailable`:`Sign in to continue`}),(0,N.jsx)(`p`,{className:`auth-copy`,children:D.isError?`We could not reach the EventLens API to check session state. Start the server or restore connectivity, then refresh this page.`:e===`oidc`?`This deployment expects OpenID Connect sign-in and then creates a server-side browser session for EventLens.`:`This v5 transition uses a server-side browser session. After login, we return you to the current workspace route.`}),re&&(0,N.jsxs)(`div`,{className:`auth-error`,role:`alert`,children:[`OIDC sign-in failed: `,re]}),!D.isError&&e===`oidc`&&(0,N.jsx)(`button`,{className:`auth-submit auth-submit--secondary`,type:`button`,onClick:()=>window.location.assign(Ki(p||`#/timeline`)),children:`Sign in with OpenID Connect`}),!D.isError&&t&&(0,N.jsxs)(`form`,{className:`auth-form`,onSubmit:be,children:[(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Username`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`text`,autoComplete:`username`,value:y,onChange:e=>b(e.target.value),disabled:ee,required:!0})]}),(0,N.jsxs)(`label`,{className:`auth-field`,children:[(0,N.jsx)(`span`,{className:`auth-label`,children:`Password`}),(0,N.jsx)(`input`,{className:`auth-input`,type:`password`,autoComplete:`current-password`,value:x,onChange:e=>S(e.target.value),disabled:ee,required:!0})]}),(0,N.jsx)(`div`,{className:`auth-meta`,children:(0,N.jsxs)(`span`,{className:`auth-route`,children:[`Return route: `,p||`#/timeline`]})}),C&&(0,N.jsx)(`div`,{className:`auth-error`,role:`alert`,children:C}),(0,N.jsx)(`button`,{className:`auth-submit`,type:`submit`,disabled:ee,children:ee?`Signing in...`:`Sign in`})]}),D.isError&&(0,N.jsx)(`div`,{className:`auth-error`,role:`alert`,children:`Session check failed. Refresh after the API is available again.`})]})})})}return(0,N.jsxs)(`div`,{className:`app`,children:[(0,N.jsx)(Ka,{paletteOpen:_,onOpenPalette:()=>v(!0),onClosePalette:()=>v(!1)}),(0,N.jsx)(Ga,{open:_,selectedSource:d||null,onClose:()=>v(!1),onSelectAggregate:_e,onOpenHome:ve,onOpenStats:()=>{window.location.hash=`#/stats`},onOpenAdmin:ye}),(0,N.jsxs)(`header`,{className:`app-header`,children:[(0,N.jsx)(`div`,{className:`brand`,children:(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`div`,{className:`brand-name`,children:`EventLens`}),(0,N.jsx)(`div`,{className:`brand-sub`,children:`Event Store Visual Debugger`})]})}),(0,N.jsxs)(`div`,{className:`header-center`,children:[R()&&(0,N.jsx)(`div`,{className:`header-demo-pill`,role:`status`,children:`Demo mode`}),(0,N.jsx)(`div`,{className:`header-title`,children:`EventLens`})]}),(0,N.jsxs)(`div`,{className:`header-actions`,children:[D.data?.principal&&(0,N.jsxs)(`div`,{className:`header-user`,"aria-label":`Authenticated user`,children:[(0,N.jsxs)(`div`,{className:`header-user-meta`,children:[(0,N.jsx)(`span`,{className:`header-user-label`,children:`Signed in`}),(0,N.jsx)(`strong`,{className:`header-user-name`,children:D.data.principal.displayName})]}),(0,N.jsx)(`button`,{className:`header-user-action ${pe?`header-user-action--active`:``}`,type:`button`,onClick:ye,children:`Admin`}),(0,N.jsx)(`button`,{className:`header-user-action`,type:`button`,onClick:xe,disabled:ne,children:ne?`Signing out...`:`Sign out`})]}),(0,N.jsx)(eo,{isUp:j,selectedSource:d,fallbackCount:A?.totalEvents??null}),(0,N.jsxs)(`div`,{className:`header-status`,children:[(0,N.jsx)(`span`,{className:`dot ${j?`dot-green`:`dot-red`}`}),(0,N.jsx)(`span`,{className:`status-text ${j?``:`offline`}`,children:j?`Connected`:ce?.status??`Connecting`})]})]})]}),(0,N.jsxs)(`aside`,{className:`workspace-dock${h?` workspace-dock--open`:``}`,"aria-label":`Workspace`,children:[(0,N.jsxs)(`div`,{className:`workspace-dock-panel`,id:`workspace-dock-panel`,hidden:!h,children:[(0,N.jsx)(`div`,{className:`workspace-dock-title`,children:`Workspace`}),(0,N.jsxs)(`label`,{className:`workspace-datasource`,children:[(0,N.jsx)(`span`,{className:`workspace-datasource-label`,children:`Datasource`}),(0,N.jsxs)(`select`,{id:`workspace-datasource-select`,className:`workspace-datasource-select`,value:d,onChange:e=>{f(e.target.value),o(null),c(null)},children:[(0,N.jsx)(`option`,{value:``,children:`Auto (primary datasource)`}),le.map(e=>(0,N.jsxs)(`option`,{value:e.id,disabled:!Qa(e.status),children:[e.id,` [`,e.status,`]`]},e.id))]})]}),(0,N.jsxs)(`div`,{className:`workspace-sidebar-kpis`,children:[(0,N.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,N.jsx)(`span`,{children:`Datasources Healthy`}),(0,N.jsxs)(`strong`,{children:[me,`/`,le.length||0]})]}),(0,N.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,N.jsx)(`span`,{children:`Plugins Healthy`}),(0,N.jsxs)(`strong`,{children:[he,`/`,ue.length||0]})]}),(0,N.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,N.jsx)(`span`,{children:`Issues`}),(0,N.jsx)(`strong`,{children:ge})]})]})]}),(0,N.jsx)(`button`,{type:`button`,className:`workspace-dock-handle`,onClick:()=>g(e=>!e),"aria-expanded":h,"aria-controls":`workspace-dock-panel`,"aria-label":h?`Collapse workspace panel`:`Expand workspace panel`,children:(0,N.jsx)(`span`,{className:`workspace-dock-chevron`,"aria-hidden":!0,children:h?`>`:`<`})})]}),(0,N.jsx)(`main`,{className:`app-main`,role:`main`,"aria-label":`EventLens workspace`,children:(0,N.jsxs)(`div`,{className:`workspace-content`,children:[!fe&&!pe&&(0,N.jsxs)(`div`,{className:`card search-panel card--dropdown-host`,children:[(0,N.jsx)(`label`,{className:`control-field-label`,htmlFor:`aggregate-search`,children:`Search Aggregates`}),(0,N.jsx)(Zi,{onSelect:_e,source:d||null,selectedValue:r}),r&&(0,N.jsxs)(`div`,{className:`selection-summary`,children:[`Viewing: `,(0,N.jsx)(`span`,{style:{color:`var(--neon-cyan)`},children:r}),d?(0,N.jsxs)(`span`,{children:[` on `,d]}):(0,N.jsx)(`span`,{children:` on primary datasource`}),s!=null&&(0,N.jsxs)(`span`,{children:[` comparing with seq #`,s]}),(0,N.jsxs)(`span`,{className:`sr-only`,"aria-live":`polite`,children:[`Current panel `,t]}),(0,N.jsx)(`button`,{className:`selection-clear-btn`,onClick:()=>{i(null),o(null),c(null)},children:`× clear`})]})]}),fe?(0,N.jsx)(Wa,{source:d||null,onBack:ve}):pe?(0,N.jsx)(Ya,{}):(0,N.jsxs)(N.Fragment,{children:[r&&(0,N.jsx)(ua,{aggregateId:r,selectedSequence:a,compareSequence:s,onSelectEvent:e=>{o(e),n(`state`)},onSelectCompare:c,source:d||null}),r&&a!==null&&(0,N.jsx)(ya,{aggregateId:r,sequence:a,compareSequence:s,activeTab:l,onTabChange:e=>{u(e),n(`state`)},active:t===`state`,onActivate:()=>n(`state`),source:d||null}),r&&de.length>0&&(0,N.jsx)(Ha,{transitions:de,selectedSequence:a,compareSequence:s,onSelectSequence:e=>{o(e),n(`replay`)},onClearCompare:()=>c(null),active:t===`replay`,onActivate:()=>n(`replay`)}),(0,N.jsxs)(`div`,{className:`bottom-grid`,children:[(0,N.jsx)(Aa,{source:d||null,onSelectAggregate:_e}),(0,N.jsx)(Ra,{source:d||null,onSelectAggregate:_e})]}),r&&A&&(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Selection Summary`}),(0,N.jsxs)(`p`,{style:{color:`var(--text-secondary)`},children:[A.totalEvents,` events available for this aggregate. Use Shift+Click in the timeline to compare two points in time, or open the command palette with Ctrl/Cmd+K.`]})]})]})]})}),(0,N.jsx)(Ba,{})]})}var no=class extends M.Component{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e,t){console.error(`UI error boundary caught:`,e,t)}render(){return this.state.hasError?(0,N.jsx)(`div`,{className:`app`,children:(0,N.jsx)(`main`,{className:`app-main`,children:(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`div`,{className:`card-title`,children:`Something went wrong`}),(0,N.jsx)(`p`,{style:{color:`var(--text-muted)`,fontSize:13},children:`The UI hit an unexpected error. Try refreshing the page. If the problem persists, check the browser console and server logs.`})]})})}):this.props.children}},ro=new Je({defaultOptions:{queries:{staleTime:3e4,retry:2}}});ci.createRoot(document.getElementById(`root`)).render((0,N.jsx)(M.StrictMode,{children:(0,N.jsx)($e,{client:ro,children:(0,N.jsx)(wa,{children:(0,N.jsx)(no,{children:(0,N.jsx)(to,{})})})})})); \ No newline at end of file diff --git a/eventlens-api/src/main/resources/web/assets/index-D5c7dwdI.css b/eventlens-api/src/main/resources/web/assets/index-D5c7dwdI.css new file mode 100644 index 0000000..939669b --- /dev/null +++ b/eventlens-api/src/main/resources/web/assets/index-D5c7dwdI.css @@ -0,0 +1 @@ +:root{--bg-base:#050508;--bg-surface:#0a0c14;--bg-raised:#0f1220;--bg-elevated:#161b2e;--bg-panel:linear-gradient(145deg, #0c0f1a 0%, #0a0d18 50%, #080b14 100%);--border:#1a2040;--border-muted:#121830;--border-glow:#00f0ff26;--text-primary:#e8eef8;--text-secondary:#94a3c0;--text-muted:#5a6a8a;--neon-cyan:#00f0ff;--neon-cyan-dim:#00f0ff14;--neon-cyan-mid:#00f0ff40;--neon-magenta:#ff00e5;--neon-magenta-dim:#ff00e514;--neon-green:#0f8;--neon-green-dim:#00ff881a;--neon-amber:#fa0;--neon-amber-dim:#ffaa001a;--neon-red:#f35;--neon-red-dim:#ff33551a;--neon-purple:#a855f7;--accent-blue:#4f9cf9;--accent-blue-dim:#4f9cf926;--accent-green:var(--neon-green);--accent-red:var(--neon-red);--accent-yellow:var(--neon-amber);--accent-purple:var(--neon-purple);--font-sans:"Inter", system-ui, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", monospace;--font-display:"Orbitron", var(--font-sans);--radius:6px;--radius-lg:10px;--shadow:0 4px 24px #0009;--shadow-neon:0 0 20px #00f0ff14, 0 0 60px #00f0ff08;--transition:.2s ease;--bottom-panel-scroll-height:280px}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{font-size:15px}body{font-family:var(--font-sans);background:var(--bg-base);color:var(--text-primary);-webkit-font-smoothing:antialiased;min-height:100vh;line-height:1.65}body:after{content:"";pointer-events:none;z-index:9999;background:repeating-linear-gradient(0deg,#0000,#0000 2px,#00f0ff04 2px 4px);position:fixed;inset:0}.app{flex-direction:column;min-height:100vh;display:flex}.auth-shell{background:radial-gradient(circle at 0 0,#00f0ff14,#0000 28%),radial-gradient(circle at 100% 0,#ff00e514,#0000 26%),linear-gradient(#060811 0%,#050508 100%)}.auth-screen{justify-content:center;align-items:center;min-height:100vh;padding:32px 20px;display:flex}.auth-card{background:linear-gradient(160deg,#0e1222f5,#080b14fa),radial-gradient(circle at top,#00f0ff0f,#0000 56%);border:1px solid #00f0ff24;border-radius:18px;width:min(460px,100%);padding:28px;box-shadow:0 18px 60px #0000008c,0 0 40px #00f0ff14}.auth-eyebrow{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-cyan);margin-bottom:10px;font-size:11px}.auth-title{font-family:var(--font-display);letter-spacing:1px;margin-bottom:10px;font-size:26px;line-height:1.2}.auth-copy{color:var(--text-secondary);margin-bottom:20px}.auth-form{gap:14px;display:grid}.auth-field{gap:6px;display:grid}.auth-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:11px}.auth-input{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0a0e1aeb;border:1px solid #ffffff24;border-radius:10px;padding:12px 14px;font-size:13px}.auth-input:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.auth-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);justify-content:space-between;gap:10px;display:flex}.auth-route{overflow-wrap:anywhere}.auth-error{color:#ff9caf;background:#ff335514;border:1px solid #ff33553d;border-radius:10px;padding:10px 12px;font-size:12px}.auth-submit{color:var(--text-primary);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff24,#4f9cf914);border:1px solid #00f0ff3d;border-radius:12px;padding:12px 16px;font-size:12px}.auth-submit:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff24}.auth-submit--secondary{width:100%;margin-bottom:14px}.auth-submit:disabled{opacity:.6;cursor:not-allowed;box-shadow:none;transform:none}.setup-card{width:min(760px,100%)}.setup-path{color:var(--neon-cyan);font-family:var(--font-mono)}.setup-mode-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-bottom:16px;display:grid}.setup-mode-card{text-align:left;color:var(--text-primary);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:#0a0e1ae6;border:1px solid #ffffff1f;border-radius:14px;gap:8px;padding:14px;display:grid}.setup-mode-card strong{font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;font-size:12px}.setup-mode-card span{color:var(--text-secondary);font-size:13px;line-height:1.5}.setup-mode-card:hover,.setup-mode-card--active{border-color:#00f0ff47;transform:translateY(-1px);box-shadow:0 0 24px #00f0ff14}.setup-mode-copy{color:var(--text-secondary);margin-bottom:18px}.setup-warning{color:#ffd27a;background:#ffaa0014;border:1px solid #ffaa003d;border-radius:12px;padding:12px 14px;font-size:13px}@media (width<=760px){.setup-mode-grid{grid-template-columns:1fr}}.app-header{--header-pad-x:24px;--header-pad-right:48px;--header-control-h:34px;padding:0 var(--header-pad-right) 0 var(--header-pad-x);border-bottom:1px solid var(--border);z-index:100;background:linear-gradient(#0d1020 0%,#080b14 100%);grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;column-gap:clamp(12px,2vw,20px);height:64px;display:grid;position:sticky;top:0;box-shadow:0 2px 20px #00000080,inset 0 -1px #00f0ff0f}.brand{justify-self:start;align-items:center;gap:12px;min-width:0;display:flex}.brand-logo{justify-content:center;align-items:center;width:36px;height:36px;display:flex;position:relative}.brand-logo svg{width:36px;height:36px;filter:drop-shadow(0 0 6px var(--neon-cyan)) drop-shadow(0 0 12px #00f0ff4d)}.brand-name{font-family:var(--font-display);letter-spacing:1.5px;color:var(--text-primary);text-transform:uppercase;font-size:15px;font-weight:700}.brand-sub{color:var(--text-muted);letter-spacing:.5px;font-size:10px}.header-title{font-family:var(--font-display);letter-spacing:3px;text-transform:uppercase;background:linear-gradient(135deg, var(--neon-cyan), #4facfe, var(--neon-magenta));-webkit-text-fill-color:transparent;filter:drop-shadow(0 0 8px #00f0ff66);text-align:center;-webkit-background-clip:text;background-clip:text;flex-shrink:0;margin:0;font-size:20px;font-weight:800;line-height:1}.header-center{flex-flow:row;justify-content:center;justify-self:center;align-items:center;gap:clamp(10px,1.5vw,16px);min-width:0;display:flex}.header-demo-pill{box-sizing:border-box;height:var(--header-control-h);background:var(--neon-amber-dim);color:var(--neon-amber);font-family:var(--font-mono);white-space:nowrap;border:1px solid #ffaa0059;border-radius:999px;flex-shrink:0;justify-content:center;align-items:center;padding:0 12px;font-size:11px;line-height:1;display:inline-flex}.header-actions{flex-direction:row;justify-content:flex-end;justify-self:end;align-items:center;gap:clamp(10px,1.2vw,16px);min-width:0;display:flex}.header-user{background:#090d18e0;border:1px solid #00f0ff29;border-radius:999px;align-items:center;gap:12px;padding:6px 10px 6px 12px;display:inline-flex}.header-user-meta{flex-direction:column;align-items:flex-end;gap:2px;display:flex}.header-user-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:9px}.header-user-name{color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;max-width:180px;font-size:12px;font-weight:600;overflow:hidden}.header-user-action{color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;border:1px solid #ffffff1f;border-radius:999px;padding:6px 10px;font-size:11px}.header-user-action:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid);background:#00f0ff0f}.header-user-action--active{color:var(--neon-cyan);border-color:var(--neon-cyan-mid);background:#00f0ff14}.header-user-action:disabled{opacity:.6;cursor:not-allowed}.header-actions .conn-stats,.header-actions .header-status{flex-shrink:0}.workspace-datasource{flex-direction:column;gap:6px;min-width:0;margin:0;display:flex}.workspace-datasource-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;font-size:10px;line-height:1}.workspace-datasource-select{box-sizing:border-box;appearance:none;width:100%;height:34px;color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition);background-color:#0c1020f2;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3c0' d='M3 4.5 6 8l3-3.5'/%3E%3C/svg%3E");background-position:right 10px center;background-repeat:no-repeat;border:1px solid #ffffff24;border-radius:8px;padding:0 32px 0 12px;font-size:12px;line-height:1;box-shadow:inset 0 1px #00f0ff0f}.workspace-datasource-select:hover{border-color:#00f0ff40}.workspace-datasource-select:focus{border-color:var(--neon-cyan-mid);outline:none;box-shadow:0 0 0 1px #00f0ff33}.header-status{font-size:12px;font-family:var(--font-sans);letter-spacing:.3px;align-items:center;gap:8px;display:flex}.header-status .status-text{color:var(--neon-green);text-shadow:0 0 8px #00ff8880}.header-status .status-text.offline{color:var(--neon-red);text-shadow:0 0 8px #ff335580}.dot{border-radius:50%;width:8px;height:8px}.dot-green{background:var(--neon-green);box-shadow:0 0 6px var(--neon-green), 0 0 12px #0f86;animation:2s infinite pulse-neon}.dot-red{background:var(--neon-red);box-shadow:0 0 6px var(--neon-red)}.dot-yellow{background:var(--neon-amber);box-shadow:0 0 6px var(--neon-amber);animation:1.5s infinite pulse-neon}@keyframes pulse-neon{0%,to{opacity:1}50%{opacity:.4}}.app-main{flex-direction:column;flex:1;gap:16px;width:100%;max-width:1440px;margin:0 auto;padding:20px;display:flex}.card{background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow), var(--shadow-neon);padding:20px;position:relative;overflow:hidden}.card.card--dropdown-host{z-index:30;overflow:visible}.card:before{content:"";background:linear-gradient(90deg, transparent, var(--neon-cyan-mid), transparent);height:1px;position:absolute;top:0;left:0;right:0}.card:after{content:"";background:linear-gradient(90deg,#0000,#ff00e51a,#0000);height:1px;position:absolute;bottom:0;left:0;right:0}.card-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;text-shadow:0 0 6px #00f0ff33;align-items:center;gap:8px;margin-bottom:16px;font-size:13px;font-weight:600;display:flex}.control-ribbon{padding:14px 18px}.control-ribbon-top{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:14px;display:flex}.control-ribbon-title{margin-bottom:4px}.control-ribbon-subtitle{color:var(--text-muted);font-size:12px}.control-ribbon-nav{align-items:center;gap:12px;font-size:13px;display:inline-flex}.control-ribbon-nav a{text-decoration:none}.control-ribbon-nav a[aria-current=page]{text-underline-offset:2px;text-decoration:underline}.control-panel{gap:12px;padding:16px 18px;display:grid}.control-panel-grid{grid-template-columns:minmax(220px,360px) minmax(0,1fr);align-items:end;gap:12px;display:grid}.control-field,.control-field--search{min-width:0}.control-field-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-size:11px;display:block}.control-select{width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);background:#0d1123eb;border:1px solid #ffffff24;border-radius:10px;outline:none;padding:10px 12px;font-size:12px}.control-select:focus{border-color:var(--neon-cyan-mid);box-shadow:0 0 14px #00f0ff26}.datasource-pills{flex-wrap:wrap;gap:8px;display:flex}.datasource-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:4px 8px}.selection-summary{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);margin-top:2px}.selection-clear-btn{color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);background:0 0;border:none;margin-left:12px}.selection-clear-btn:hover{color:var(--neon-cyan)}.event-agg-button,.anomaly-aggregate-link{cursor:pointer;background:0 0;border:none;padding:0}.event-agg-button{color:var(--text-secondary);font:inherit}.event-agg-button:hover,.event-agg-button:focus-visible{color:var(--neon-cyan);text-shadow:0 0 8px #00f0ff47}.anomaly-aggregate-link .anomaly-meta-value{color:var(--neon-cyan)}.anomaly-aggregate-link:hover .anomaly-meta-value,.anomaly-aggregate-link:focus-visible .anomaly-meta-value{text-shadow:0 0 8px #00f0ff4d}.workspace-dock{z-index:110;border:1px solid var(--border-muted);pointer-events:auto;border-right:none;border-radius:10px 0 0 10px;flex-direction:row;align-items:center;width:auto;height:auto;max-height:min(72vh,100vh - 112px);transition:box-shadow .2s;display:flex;position:fixed;inset:50% 0 auto auto;overflow:hidden;transform:translateY(-50%);box-shadow:-6px 4px 22px #0000006b}.workspace-dock--open{box-shadow:-8px 6px 28px #0000007a,0 0 0 1px #00f0ff14}.workspace-dock-handle{border:none;border-left:1px solid var(--border-muted);width:36px;height:36px;color:var(--neon-cyan);font-family:var(--font-mono);cursor:pointer;transition:background var(--transition), color var(--transition);background:linear-gradient(#0f1324 0%,#0a0e18 100%);flex-direction:row;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:flex}.workspace-dock-handle:hover{color:var(--text-primary);background:linear-gradient(#141a30 0%,#0d1220 100%)}.workspace-dock-handle:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:-2px}.workspace-dock-chevron{font-size:15px;font-weight:700;line-height:1}.workspace-dock-panel{border-left:1px solid var(--border);background:linear-gradient(145deg,#0c101c 0%,#080c14 100%);flex-direction:column;flex:0 auto;gap:8px;width:min(252px,100vw - 48px);min-width:0;max-height:min(72vh,100vh - 112px);padding:10px 12px 12px 14px;display:flex;overflow:hidden auto}.workspace-dock-panel[hidden]{display:none!important}.workspace-dock-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:12px;font-weight:600}.workspace-dock-scrim{z-index:109;cursor:pointer;background:#03050c73;border:none;margin:0;padding:0;position:fixed;inset:64px 0 36px}.workspace-sidebar-kpis{border:1px solid var(--border-muted);border-radius:var(--radius);background:#080b148c;gap:6px;padding:8px 10px;display:grid}.workspace-kpi-row{color:var(--text-muted);font-size:11px;font-family:var(--font-mono);justify-content:space-between;gap:8px;display:flex}.workspace-kpi-row strong{color:var(--text-primary)}.workspace-sidebar-links{color:var(--text-secondary);gap:6px;font-size:12px;display:grid}.workspace-content,.admin-console{gap:16px;display:grid}.admin-hero{padding:18px 20px}.admin-hero-copy{color:var(--text-secondary);max-width:72ch}.admin-grid{grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);align-items:start;gap:16px;display:grid}.admin-card{gap:14px;display:grid}.admin-muted{color:var(--text-muted);font-size:12px}.admin-form{border:1px solid var(--border-muted);background:#080c18b8;border-radius:12px;gap:12px;padding:14px;display:grid}.admin-field{gap:6px;display:grid}.admin-table-wrap{background:#060912d1;border:1px solid #ffffff14;border-radius:12px;overflow-x:auto}.admin-table{border-collapse:collapse;width:100%;min-width:560px;font-size:12px}.admin-table th,.admin-table td{text-align:left;vertical-align:top;border-bottom:1px solid #ffffff0f;padding:10px 12px}.admin-table th{color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;background:#0c101eeb;font-size:11px}.admin-table td{color:var(--text-secondary)}.admin-issued-key{background:#00ff8814;border:1px solid #00ff8838;border-radius:12px;gap:8px;padding:14px 16px;display:grid}.admin-issued-key strong{color:var(--neon-green)}.admin-issued-key p{color:var(--text-primary);font-family:var(--font-mono);overflow-wrap:anywhere}.admin-issued-key span{color:var(--text-muted);font-size:12px}.admin-inline-action{color:#ff9caf;font-family:var(--font-mono);cursor:pointer;background:#ff335514;border:1px solid #ff33553d;border-radius:999px;padding:6px 10px;font-size:11px}.admin-inline-action:disabled{opacity:.6;cursor:not-allowed}.admin-state{border-radius:12px;gap:6px;padding:12px 14px;display:grid}.admin-state strong{font-size:13px}.admin-state p{color:inherit;font-size:12px}.admin-state--error{color:#ff9caf;background:#ff335514;border:1px solid #ff33553d}.admin-state--warning{color:var(--neon-amber);background:#ffaa0014;border:1px solid #ffaa003d}.search-panel{width:100%;min-width:0}.selection-clear-btn:focus-visible,.control-ribbon-nav a:focus-visible,.control-select:focus-visible,.workspace-datasource-select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.plugin-dashboard{gap:14px;display:grid}.plugin-cards-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.plugin-cards-grid--dense{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}.plugin-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(140deg,#101527f2,#0a0e1cf2);padding:12px 14px;box-shadow:inset 0 1px #ffffff05}.plugin-card--interactive{transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition)}.plugin-card--interactive:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 16px #00f0ff1a}.plugin-card--interactive:focus-within{border-color:var(--neon-cyan-mid);box-shadow:0 0 0 2px #00f0ff26}.plugin-card-head{justify-content:space-between;align-items:center;gap:10px;display:flex}.plugin-pill{border:1px solid var(--border);font-size:11px;font-family:var(--font-mono);border-radius:999px;padding:2px 8px}.plugin-card-meta{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);overflow-wrap:anywhere;margin-top:8px}.plugin-card-detail{color:var(--text-secondary);margin-top:8px;font-size:12px;line-height:1.55}.search-wrapper{position:relative}.search-input{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text-primary);font-family:var(--font-mono);transition:border-color var(--transition), box-shadow var(--transition);outline:none;padding:14px 16px 14px 44px;font-size:13px}.search-input:focus{border-color:var(--neon-cyan);box-shadow:0 0 14px #00f0ff2e}.search-input::placeholder{color:var(--text-muted)}.search-icon{color:var(--neon-cyan);pointer-events:none;filter:drop-shadow(0 0 4px #00f0ff80);font-size:16px;position:absolute;top:50%;left:14px;transform:translateY(-50%)}.search-results{background:var(--bg-raised);border:1px solid var(--neon-cyan-mid);border-radius:var(--radius);z-index:500;max-height:min(55vh,420px);position:absolute;top:calc(100% + 6px);left:0;right:0;overflow:hidden auto;box-shadow:0 12px 40px #000000a6,0 0 24px #00f0ff14}.search-result-item{cursor:pointer;transition:background var(--transition);font-family:var(--font-mono);color:var(--text-primary);text-align:left;background:0 0;border:none;align-items:center;gap:10px;width:100%;padding:10px 16px;font-size:13px;display:flex}.search-result-item:hover{background:var(--bg-elevated);box-shadow:inset 3px 0 0 var(--neon-cyan)}.search-result-item+.search-result-item{border-top:1px solid var(--border-muted)}.search-result-chevron{color:var(--text-muted);flex-shrink:0;padding-right:4px}.search-result-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:0 6px;min-width:0;display:flex}.search-result-label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;flex-shrink:0;font-size:10px}.search-result-colon{color:var(--text-muted);flex-shrink:0;margin-right:2px}.search-result-value{overflow-wrap:anywhere;word-break:break-word;flex:1;min-width:0}.conn-stats{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:16px;font-size:11px;display:flex}.conn-stat{flex-direction:column;align-items:flex-end;gap:1px;display:flex}.conn-stat-label{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);font-size:9px}.conn-stat-value{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66;font-size:12px}.conn-stat-value.green{color:var(--neon-green);text-shadow:0 0 6px #0f86}.conn-stat-value.amber{color:var(--neon-amber);text-shadow:0 0 6px #fa06}.conn-stat--metric .conn-stat-value,.conn-stat-value--uptime{text-align:right;font-variant-numeric:tabular-nums;min-width:6.5ch;display:inline-block}.mini-wave{align-items:flex-end;gap:1px;height:24px;display:flex}.mini-wave-bar{background:var(--neon-cyan);border-radius:1px;width:3px;animation:1.2s ease-in-out infinite wave-pulse;box-shadow:0 0 4px #00f0ff4d}@keyframes wave-pulse{0%,to{transform:scaleY(.3)}50%{transform:scaleY(1)}}.timeline-count-pill{color:var(--accent-blue);font-family:var(--font-mono);background:var(--accent-blue-dim);border:1px solid #4f9cf940;border-radius:999px;padding:2px 10px;font-size:11px}.timeline-hint{color:var(--text-muted);max-width:52rem;margin:-8px 0 12px;font-size:12px;line-height:1.5}.timeline-rail{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-base);position:relative;box-shadow:inset 0 1px #00f0ff0a}.timeline-stepper{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);padding:12px 14px 14px;overflow:auto hidden;-webkit-mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000);mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}.timeline-stepper-track{flex-wrap:nowrap;align-items:center;gap:4px;width:max-content;min-height:88px;display:flex}.timeline-step-arrow{color:var(--text-muted);opacity:.7;flex-shrink:0;align-self:center;padding:0 2px;font-size:11px}.timeline-step-arrow-compact{padding:0 1px;font-size:9px}.timeline-step{border-radius:var(--radius);border:1px solid var(--border-muted);background:var(--bg-raised);cursor:pointer;text-align:left;min-width:118px;max-width:200px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-step-compact{gap:2px;min-width:96px;max-width:140px;padding:8px 10px}.timeline-step-compact .timeline-step-badge{font-size:8px}.timeline-step-compact .timeline-step-type{-webkit-line-clamp:1;font-size:10px}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-step-badge{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:10px;font-weight:600}.timeline-step-seq{color:var(--text-muted);font-size:10px;font-family:var(--font-mono)}.timeline-step-type{color:var(--text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}.timeline-step-created{border-left:3px solid var(--neon-green)}.timeline-step-deleted{border-left:3px solid var(--neon-red)}.timeline-step-completed{border-left:3px solid var(--neon-green)}.timeline-step-failed{border-left:3px solid var(--neon-red)}.timeline-step-transfer{border-left:3px solid var(--neon-amber)}.timeline-step-item{border-left:3px solid var(--neon-purple)}.timeline-step-progress{border-left:3px solid #38bdf8}.timeline-step-default{border-left:3px solid var(--neon-cyan)}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);cursor:pointer;text-align:left;min-width:140px;max-width:240px;font-family:var(--font-sans);transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;padding:10px 14px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px)}.timeline-group-chip.expanded{border-color:#00f0ff59}.timeline-group-count{font-family:var(--font-display);letter-spacing:.5px;color:var(--neon-cyan);font-size:16px;font-weight:800;line-height:1}.timeline-group-chevron{color:var(--text-muted);font-size:10px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.timeline-group-range{color:var(--text-muted);font-size:10px;font-family:var(--font-mono);line-height:1.4}.timeline-expanded-deck{border-top:1px solid var(--border-muted);background:#00000040;padding:10px 14px 14px}.timeline-expanded-head{flex-wrap:wrap;align-items:center;gap:10px 16px;margin-bottom:10px;display:flex}.timeline-expanded-title{font-family:var(--font-sans);letter-spacing:.3px;color:var(--neon-cyan);font-size:12px;font-weight:700}.timeline-expanded-meta{font-family:var(--font-mono);color:var(--text-muted);flex:1;font-size:11px}.timeline-expanded-close{border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-raised);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition);margin-left:auto;padding:4px 12px;font-size:11px}.timeline-expanded-close:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan)}.timeline-expanded-strip{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex-wrap:nowrap;align-items:center;gap:4px;width:100%;padding-bottom:6px;display:flex;overflow-x:auto}.timeline-slider{width:100%;accent-color:var(--neon-cyan);cursor:pointer;margin-top:4px}.timeline-info{color:var(--text-muted);font-size:12px;font-family:var(--font-mono);grid-template-columns:minmax(0,1fr) minmax(0,2.2fr) minmax(0,1fr);align-items:start;gap:12px;margin-top:12px;display:grid}.timeline-info-edge{color:var(--text-muted);font-size:11px}.timeline-info-center{text-align:center;color:var(--text-secondary);line-height:1.55}.timeline-info-center strong{color:var(--neon-cyan);font-weight:600}.timeline-info-muted{color:var(--text-muted);font-weight:400}.timeline-info-type{color:var(--text-primary);font-size:11px}.state-grid{grid-template-columns:1fr 1fr;gap:16px;display:grid}.state-panel h4{font-family:var(--font-display);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:8px;font-size:10px;font-weight:600}.state-panel-before h4{color:var(--text-muted)}.state-panel-after h4{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.json-block{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);font-family:var(--font-mono);max-height:260px;color:var(--text-secondary);white-space:pre;padding:12px;font-size:12px;line-height:1.7;overflow:auto}.json-tree-root{background:var(--bg-base);border:1px solid var(--border-muted);border-radius:var(--radius);max-height:320px;font-family:var(--font-mono);color:var(--text-secondary);padding:10px 12px;font-size:13px;line-height:1.75;overflow:auto}.json-tree-line{flex-wrap:wrap;align-items:baseline;gap:2px 0;min-height:1.5em;display:flex}.json-tree-toggle{width:22px;height:22px;color:var(--text-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;margin-right:4px;padding:0;font-size:10px;line-height:1}.json-tree-toggle:hover{color:var(--neon-cyan);background:var(--neon-cyan-dim)}.json-key{color:#7dd3fc}.json-string{color:#86efac}.json-number{color:#fcd34d}.json-boolean{color:#c4b5fd}.json-null{color:var(--text-muted);font-style:italic}.json-punct{color:var(--text-muted)}.json-ellipsis{color:var(--text-muted);font-size:11px;font-style:italic}.json-unknown{color:var(--neon-amber)}.diff-panel{border-top:1px solid var(--border-muted);margin-top:20px;padding-top:16px}.diff-toolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.diff-toolbar-title{font-family:var(--font-sans);color:var(--neon-cyan);letter-spacing:.2px;font-size:13px;font-weight:600}.diff-view-toggle{border-radius:var(--radius);border:1px solid var(--border);font-family:var(--font-mono);font-size:11px;display:flex;overflow:hidden}.diff-view-toggle button{background:var(--bg-raised);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:6px 14px}.diff-view-toggle button+button{border-left:1px solid var(--border)}.diff-view-toggle button:hover{color:var(--text-secondary)}.diff-view-toggle button.active{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.diff-body{align-items:stretch;gap:10px;display:flex}.diff-minimap{border:1px solid var(--border-muted);background:var(--bg-base);border-radius:4px;flex-direction:column;flex-shrink:0;width:10px;max-height:280px;display:flex;overflow:hidden}.diff-minimap-chunk{background:var(--border);cursor:pointer;min-height:8px;transition:background var(--transition);border:none;flex:1;margin:0;padding:0}.diff-minimap-chunk:hover,.diff-minimap-chunk:focus-visible{background:var(--neon-cyan-mid);outline:none}.diff-scroll{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base);flex:1;min-width:0;max-height:280px;overflow:auto}.diff-list{flex-direction:column;gap:0;display:flex}.diff-row{font-family:var(--font-mono);background:var(--bg-raised);border-left:3px solid var(--neon-cyan);border-bottom:1px solid var(--border-muted);border-radius:0;align-items:stretch;gap:0;padding:0;font-size:12px;line-height:1.65;display:flex}.diff-row:last-child{border-radius:0 0 var(--radius) var(--radius);border-bottom:none}.diff-row:first-child{border-radius:var(--radius) var(--radius) 0 0}.diff-line-no{text-align:right;width:36px;color:var(--text-muted);background:var(--bg-base);border-right:1px solid var(--border-muted);-webkit-user-select:none;user-select:none;flex-shrink:0;padding:10px 6px;font-size:10px}.diff-row-body{flex-wrap:wrap;flex:1;align-items:baseline;gap:8px 12px;padding:10px 12px;display:flex}.diff-field{color:var(--text-primary);min-width:6rem;font-weight:700}.diff-values-inline{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.diff-old{color:var(--neon-red);font-weight:400;text-decoration:line-through}.diff-arrow{color:var(--text-muted);flex-shrink:0}.diff-new{color:var(--neon-green);text-shadow:0 0 4px #00ff8840;font-weight:400}.diff-split-head{font-family:var(--font-sans);letter-spacing:.3px;color:var(--text-muted);border-bottom:1px solid var(--border);background:var(--bg-raised);z-index:1;grid-template-columns:1fr 1fr;gap:8px;padding:8px 12px 8px 48px;font-size:11px;display:grid;position:sticky;top:0}.diff-split-old-label{color:var(--neon-red)}.diff-split-new-label{color:var(--neon-green)}.diff-split-row{border-bottom:1px solid var(--border-muted);align-items:stretch;gap:0;display:flex}.diff-split-row:last-child{border-bottom:none}.diff-split-cells{flex:1;grid-template-columns:1fr 1fr;gap:8px;min-width:0;padding:8px 12px 8px 0;display:grid}.diff-split-cell{border-radius:var(--radius);background:var(--bg-base);border:1px solid var(--border-muted);min-width:0;padding:8px 10px}.diff-split-cell .diff-field{margin-bottom:6px;font-size:11px;display:block}.diff-cell-value{overflow-wrap:anywhere;word-break:break-word;font-size:12px;font-weight:400;line-height:1.6}.diff-split-old .diff-cell-value{color:var(--neon-red);opacity:.9}.diff-split-new .diff-cell-value{color:var(--neon-green)}.event-meta{color:var(--text-muted);grid-template-columns:repeat(2,1fr);gap:10px 16px;margin-top:20px;font-size:12px;display:grid}.event-meta-bar{background:var(--bg-raised);border:1px solid var(--border-muted);border-radius:var(--radius);padding:14px 16px;box-shadow:inset 0 1px #00f0ff0a}.event-meta-time{font-family:var(--font-mono);color:var(--text-secondary);font-size:12px;font-weight:500}.event-meta-id{font-family:var(--font-mono);color:var(--text-primary);overflow-wrap:anywhere;word-break:break-word;grid-column:1/-1;font-size:11px;line-height:1.5}.event-meta-extra{font-family:var(--font-mono);color:var(--text-secondary);font-size:11px}@media (width<=900px){.diff-split-cells{grid-template-columns:1fr}.diff-split-head{display:none}}.copy-btn{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-secondary);cursor:pointer;transition:all var(--transition);font-size:12px;font-family:var(--font-mono);margin-top:12px;padding:6px 14px}.copy-btn:hover{background:var(--bg-raised);color:var(--neon-cyan);border-color:var(--neon-cyan);box-shadow:0 0 10px #00f0ff1a}.live-header{justify-content:space-between;align-items:center;margin-bottom:12px;display:flex}.live-indicator{font-family:var(--font-sans);letter-spacing:.2px;align-items:center;gap:6px;font-size:11px;display:flex}.pause-btn{background:var(--bg-elevated);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);border-radius:4px;padding:5px 12px;font-size:11px}.pause-btn:hover{background:var(--bg-raised);border-color:var(--neon-cyan);color:var(--neon-cyan);box-shadow:0 0 8px #00f0ff1a}.event-stream{height:var(--bottom-panel-scroll-height);min-height:0;font-family:var(--font-mono);flex-direction:column;gap:3px;padding-right:4px;font-size:12px;display:flex;overflow-y:auto}.event-stream,.anomaly-scroll-region,.search-results{scrollbar-width:thin;scrollbar-color:var(--border) var(--bg-base)}.event-stream::-webkit-scrollbar{width:6px}.anomaly-scroll-region::-webkit-scrollbar{width:6px}.search-results::-webkit-scrollbar{width:6px}.event-stream::-webkit-scrollbar-track{background:var(--bg-base)}.anomaly-scroll-region::-webkit-scrollbar-track{background:var(--bg-base)}.search-results::-webkit-scrollbar-track{background:var(--bg-base)}.event-stream::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.anomaly-scroll-region::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.search-results::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.event-stream::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.anomaly-scroll-region::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.search-results::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.event-row{transition:all var(--transition);cursor:default;background:#ffffff03;border-left:3px solid #0000;border-radius:4px;align-items:center;gap:12px;padding:6px 10px;display:flex}.event-row:hover{background:var(--bg-elevated)}.event-row.type-withdrawn{border-left-color:var(--neon-magenta)}.event-row.type-deposited{border-left-color:var(--neon-cyan)}.event-row.type-created{border-left-color:var(--neon-green)}.event-row.type-transfer{border-left-color:var(--neon-amber)}.event-row.type-deleted{border-left-color:var(--neon-red)}.event-row.type-completed{border-left-color:var(--neon-green)}.event-row.type-failed{border-left-color:var(--neon-red)}.event-row.type-default{border-left-color:var(--neon-purple)}.event-time{color:var(--text-muted);flex-shrink:0;width:75px;font-size:11px}.event-type{flex-shrink:0;width:180px;font-weight:500}.event-agg{color:var(--text-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.event-icon{text-align:center;flex-shrink:0;width:20px;font-size:14px}.type-created{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-deleted{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-completed{color:var(--neon-green);text-shadow:0 0 6px #00ff884d}.type-failed{color:var(--neon-red);text-shadow:0 0 6px #ff33554d}.type-transfer{color:var(--neon-amber);text-shadow:0 0 6px #ffaa004d}.type-default{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.type-withdrawn{color:var(--neon-magenta);text-shadow:0 0 6px #ff00e54d}.type-deposited{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff4d}.anomaly-panel-inner{position:relative}.anomaly-shield{background:radial-gradient(#00ff880f 0%,#0000 70%);border:1px solid #00ff8826;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;margin-bottom:20px;padding:30px 20px;display:flex}.shield-icon{justify-content:center;align-items:center;width:64px;height:64px;margin-bottom:12px;display:flex;position:relative}.shield-icon svg{filter:drop-shadow(0 0 12px #00ff8880)drop-shadow(0 0 24px #0f83);width:64px;height:64px}.shield-icon:after{content:"";border:1px solid #00ff8826;border-radius:50%;animation:3s ease-in-out infinite shield-pulse;position:absolute;inset:-8px}@keyframes shield-pulse{0%,to{opacity:.3;transform:scale(1)}50%{opacity:.8;transform:scale(1.15)}}.shield-text{font-family:var(--font-display);letter-spacing:2px;text-transform:uppercase;color:var(--neon-green);text-shadow:0 0 8px #0f86;font-size:13px;font-weight:600}.gauge-row{grid-template-columns:1fr 1fr 1fr;gap:12px;display:grid}.gauge-card{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);text-align:center;padding:12px;position:relative;overflow:hidden}.gauge-card:before{content:"";height:2px;position:absolute;bottom:0;left:0;right:0}.gauge-card.optimal:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-card.baseline:before{background:var(--neon-cyan);box-shadow:0 0 8px #00f0ff66}.gauge-card.zero:before{background:var(--neon-green);box-shadow:0 0 8px #0f86}.gauge-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);margin-bottom:6px;font-size:10px;font-weight:500}.gauge-value{font-family:var(--font-display);letter-spacing:1px;font-size:13px;font-weight:700}.gauge-value.optimal{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-value.baseline{color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff66}.gauge-value.zero{color:var(--neon-green);text-shadow:0 0 6px #0f86}.gauge-wave{justify-content:center;align-items:flex-end;gap:2px;height:20px;margin-top:6px;display:flex}.gauge-wave-bar{border-radius:1px;width:2px;animation:1.5s ease-in-out infinite gauge-wave-anim}.gauge-wave-bar.green{background:var(--neon-green);box-shadow:0 0 3px #0f86}.gauge-wave-bar.cyan{background:var(--neon-cyan);box-shadow:0 0 3px #00f0ff66}@keyframes gauge-wave-anim{0%,to{height:4px}50%{height:16px}}.anomaly-scroll-region{box-sizing:border-box;height:var(--bottom-panel-scroll-height);max-height:var(--bottom-panel-scroll-height);z-index:1;min-height:0;padding-right:4px;position:relative;overflow:hidden auto}.anomaly-list-inner{flex-direction:column;gap:10px;display:flex}.anomaly-card-title-row{flex-wrap:wrap;width:100%}.anomaly-title-text{flex:1}.anomaly-header-count{min-width:28px;height:28px;font-family:var(--font-display);color:var(--bg-base);background:var(--neon-amber);border-radius:999px;justify-content:center;align-items:center;margin-left:auto;padding:0 10px;font-size:11px;font-weight:700;display:inline-flex;box-shadow:0 0 12px #ffaa0059}.anomaly-card{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:var(--bg-raised);transition:border-color var(--transition), box-shadow var(--transition);overflow:hidden}.anomaly-card.CRITICAL{border-left:4px solid #f44}.anomaly-card.HIGH{border-left:4px solid var(--neon-red)}.anomaly-card.MEDIUM{border-left:4px solid var(--neon-amber)}.anomaly-card.LOW{border-left:4px solid var(--neon-cyan)}.anomaly-card-summary{cursor:pointer;font-family:var(--font-sans);flex-wrap:wrap;align-items:center;gap:10px 12px;padding:14px 16px;list-style:none;display:flex}.anomaly-card-summary::-webkit-details-marker{display:none}.anomaly-severity-badge{font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;border-radius:4px;flex-shrink:0;padding:4px 10px;font-size:9px;font-weight:700}.anomaly-severity-badge.sev-critical{color:#f66;background:#f443;border:1px solid #ff444459}.anomaly-severity-badge.sev-high{background:var(--neon-red-dim);color:var(--neon-red);border:1px solid #ff335559}.anomaly-severity-badge.sev-medium{background:var(--neon-amber-dim);color:var(--neon-amber);border:1px solid #ffaa0059}.anomaly-severity-badge.sev-low{background:var(--neon-cyan-dim);color:var(--neon-cyan);border:1px solid #00f0ff40}.anomaly-card-title{min-width:0;color:var(--text-primary);flex:1;font-size:15px;font-weight:600;line-height:1.45}.anomaly-card-chevron{color:var(--text-muted);transition:transform var(--transition);flex-shrink:0;font-size:10px}.anomaly-card[open] .anomaly-card-chevron{transform:rotate(-180deg)}.anomaly-card-body{border-top:1px solid var(--border-muted);background:#0003;padding:0 16px 16px}.anomaly-card-meta{flex-wrap:wrap;align-items:baseline;gap:8px 12px;margin-top:12px;font-size:13px;line-height:1.5;display:flex}.anomaly-card-meta:first-child{margin-top:12px}.anomaly-meta-label{font-family:var(--font-sans);letter-spacing:.2px;color:var(--text-muted);min-width:72px;font-size:10px}.anomaly-meta-value{font-family:var(--font-mono);color:var(--text-secondary);overflow-wrap:anywhere}.no-anomalies{color:var(--neon-green);align-items:center;gap:8px;padding:20px 0;font-size:13px;display:flex}.bottom-grid{grid-template-columns:1fr 1fr;align-items:start;gap:16px;display:grid}.bottom-grid>.card{flex-direction:column;align-items:stretch;min-width:0;min-height:0;display:flex}.bottom-grid>.card>.card-title,.bottom-grid>.card>.live-header,.bottom-grid .anomaly-scroll-region,.bottom-grid .event-stream{flex-shrink:0}.skeleton{background:linear-gradient(90deg, var(--bg-raised) 25%, var(--bg-elevated) 50%, var(--bg-raised) 75%);border-radius:var(--radius);background-size:200% 100%;animation:1.4s infinite shimmer}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.toast-container{z-index:999;flex-direction:column;gap:8px;display:flex;position:fixed;bottom:24px;right:24px}.toast{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-primary);box-shadow:var(--shadow);border-left:3px solid var(--neon-amber);padding:10px 16px;font-size:13px;animation:.2s slideIn}.toast.error{border-left-color:var(--neon-red)}.toast.success{border-left-color:var(--neon-green)}@keyframes slideIn{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:var(--bg-base)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#00f0ff4d}.demo-banner{font-size:13px;font-family:var(--font-mono);color:var(--neon-amber);background:var(--neon-amber-dim);border-radius:var(--radius);border:1px solid #ffaa0059;margin:0 0 16px;padding:10px 16px}.demo-banner code{color:var(--neon-cyan)}@media (width<=900px){.state-grid,.bottom-grid,.admin-grid{grid-template-columns:1fr}.header-center .header-demo-pill{display:none}.workspace-dock-panel{width:min(240px,88vw)}.gauge-row,.control-panel-grid{grid-template-columns:1fr}.timeline-info{text-align:center;grid-template-columns:1fr}.timeline-info-edge{display:none}}.state-tabs{border-bottom:1px solid var(--border);gap:2px;margin-bottom:16px;padding-bottom:0;display:flex}.state-tab{color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:color var(--transition), border-color var(--transition);border-radius:var(--radius) var(--radius) 0 0;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-1px;padding:8px 16px;font-size:13px;font-weight:500;display:flex}.state-tab:hover{color:var(--text-secondary)}.state-tab.active{color:var(--neon-cyan);border-bottom-color:var(--neon-cyan);background:var(--neon-cyan-dim);text-shadow:0 0 8px #00f0ff33}.state-tab-emoji{font-size:14px}.state-tab-content{min-height:80px}.summary-tab{padding-top:4px}.summary-changes{flex-direction:column;gap:8px;display:flex}.summary-changes-header{color:var(--text-muted);border-bottom:1px solid var(--border-muted);margin-bottom:4px;padding-bottom:6px;font-size:12px;font-weight:600}.summary-change-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-amber);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.summary-change-row:hover{background:var(--bg-elevated)}.summary-change-field{color:var(--text-primary);min-width:5rem;font-weight:700;font-family:var(--font-sans)}.summary-change-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.summary-change-arrow{color:var(--text-muted);flex-shrink:0}.summary-change-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.summary-no-changes{color:var(--neon-green);align-items:center;gap:12px;padding:20px 0;font-size:14px;display:flex}.event-summary-bar{background:linear-gradient(145deg, var(--bg-elevated) 0%, var(--bg-raised) 100%);border:1px solid var(--border);border-left:3px solid var(--neon-cyan);border-radius:var(--radius);justify-content:space-between;align-items:center;gap:12px;margin-bottom:4px;padding:10px 20px;display:flex;box-shadow:0 0 16px #00f0ff0d}.event-summary-left{flex-wrap:wrap;align-items:baseline;gap:6px 12px;display:flex}.event-summary-type{font-family:var(--font-sans);color:var(--text-primary);font-size:14px;font-weight:600}.event-summary-meta{font-family:var(--font-mono);color:var(--text-muted);font-size:12px}.event-summary-changes{color:var(--neon-amber);font-size:12px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;flex-shrink:0;padding:3px 10px;font-weight:600}.diff-count-badge{color:var(--neon-amber);font-size:11px;font-family:var(--font-sans);background:#ffaa001f;border:1px solid #ffaa004d;border-radius:999px;align-items:center;margin-left:6px;padding:2px 8px;font-weight:500;display:inline-flex}.diff-summary-view{flex-direction:column;gap:6px;display:flex}.diff-summary-row{border-radius:var(--radius);background:var(--bg-raised);border:1px solid var(--border-muted);border-left:3px solid var(--neon-cyan);font-family:var(--font-mono);transition:background var(--transition);flex-wrap:wrap;align-items:baseline;gap:6px 10px;padding:9px 12px;font-size:13px;display:flex}.diff-summary-row:hover{background:var(--bg-elevated)}.diff-summary-field{color:var(--text-primary);min-width:6rem;font-weight:700;font-family:var(--font-sans)}.diff-summary-old{color:var(--neon-red);opacity:.9;text-decoration:line-through}.diff-summary-arrow{color:var(--text-muted);flex-shrink:0}.diff-summary-new{color:var(--neon-green);text-shadow:0 0 4px #0f83;font-weight:600}.diff-jump-next{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;margin-left:auto;padding:2px 8px;font-size:10px}.diff-jump-next:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.json-tree-changed{border-radius:3px;background:#ffaa001a!important}.timeline-header-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.timeline-jump-group{border:1px solid var(--border);border-radius:var(--radius);align-items:center;gap:0;display:flex;overflow:hidden}.timeline-jump-input{background:var(--bg-raised);border:none;border-right:1px solid var(--border);width:100px;color:var(--text-primary);font-family:var(--font-mono);outline:none;padding:5px 8px;font-size:12px}.timeline-jump-input:focus{border-right-color:var(--neon-cyan);background:var(--bg-elevated)}.timeline-jump-input::placeholder{color:var(--text-muted)}.timeline-jump-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.timeline-jump-input[type=number]{-moz-appearance:textfield}.timeline-jump-btn{background:var(--bg-elevated);color:var(--text-muted);cursor:pointer;transition:background var(--transition), color var(--transition);border:none;padding:5px 10px;font-size:14px}.timeline-jump-btn:hover{background:var(--neon-cyan-dim);color:var(--neon-cyan)}.timeline-filter-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.filter-chip{border:1px solid var(--border);background:var(--bg-raised);color:var(--text-muted);font-family:var(--font-sans);cursor:pointer;transition:all var(--transition);white-space:nowrap;text-overflow:ellipsis;border-radius:999px;max-width:160px;padding:3px 10px;font-size:11px;overflow:hidden}.filter-chip:hover{border-color:var(--neon-cyan-mid);color:var(--text-secondary)}.filter-chip.active{background:var(--neon-cyan-dim);color:var(--neon-cyan);text-shadow:0 0 6px #00f0ff33;border-color:#00f0ff66}.timeline-anomaly-marker{color:var(--neon-amber);text-shadow:0 0 4px #fa09;align-self:flex-start;font-size:7px;line-height:1}.keyboard-hints{z-index:200;border-top:1px solid var(--border);-webkit-backdrop-filter:blur(12px);background:#080b14eb;position:fixed;bottom:0;left:0;right:0}.keyboard-hints-bar{font-family:var(--font-mono);color:var(--text-muted);flex-wrap:wrap;justify-content:center;align-items:center;gap:8px;padding:6px 24px;font-size:11px;display:flex}.keyboard-hints-item{align-items:center;gap:5px;display:flex}.keyboard-hints-sep{color:var(--border);font-size:14px;line-height:1}.keyboard-hints-grid{flex-wrap:wrap;justify-content:center;gap:10px 24px;padding:14px 24px;display:flex}.keyboard-hint-row{font-family:var(--font-mono);color:var(--text-secondary);align-items:center;gap:10px;font-size:12px;display:flex}.keyboard-hint-desc{color:var(--text-muted)}.keyboard-key{border:1px solid var(--border);border-bottom:2px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:4px;align-items:center;padding:2px 8px;font-size:11px;display:inline-flex}.keyboard-key-mini{border:1px solid var(--border);background:var(--bg-elevated);color:var(--neon-cyan);font-family:var(--font-mono);white-space:nowrap;border-radius:3px;align-items:center;padding:1px 5px;font-size:10px;display:inline-flex}.keyboard-hints-close{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-muted);font-family:var(--font-mono);cursor:pointer;transition:color var(--transition), border-color var(--transition);background:0 0;padding:4px 12px;font-size:11px}.keyboard-hints-close:hover{color:var(--neon-cyan);border-color:var(--neon-cyan-mid)}.app-main{padding-bottom:40px}.command-palette-backdrop{z-index:1000;background:#050508b3;justify-content:center;align-items:flex-start;padding-top:12vh;display:flex;position:fixed;inset:0}.command-palette{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius-lg);width:min(640px,100vw - 32px);box-shadow:var(--shadow);overflow:hidden}.command-palette-input{border:0;border-bottom:1px solid var(--border);width:100%;color:var(--text-primary);font:inherit;background:0 0;padding:16px}.command-palette-list{max-height:320px;list-style:none;overflow:auto}.command-palette-item{cursor:pointer;color:var(--text-primary);padding:12px 16px}.command-palette-item.active,.command-palette-item:hover{background:var(--neon-cyan-dim)}.command-palette-item.muted{color:var(--text-muted)}.statistics-toolbar,.stats-kpis,.stats-distribution{flex-wrap:wrap;gap:12px;display:flex}.statistics-panel-header{justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px;display:flex}.statistics-back-btn{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition);background:linear-gradient(145deg,#00f0ff1a,#4f9cf912);border:1px solid #00f0ff33;padding:8px 14px;font-size:12px}.statistics-back-btn:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.statistics-back-btn:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.stats-kpis{margin:12px 0 16px}.stat-card{border:1px solid var(--border);border-radius:var(--radius);background:#ffffff05;flex-direction:column;gap:6px;min-width:140px;padding:14px;display:flex}.stat-card strong{font-size:24px}.stats-chart{gap:8px;margin-bottom:16px;display:grid}.stats-bar-row,.stats-list-row,.replay-toolbar{align-items:center;gap:10px;display:flex}.stats-bar-track{background:#ffffff0f;border-radius:999px;flex:1;height:10px;overflow:hidden}.stats-bar-fill{background:linear-gradient(90deg, var(--neon-cyan), var(--accent-blue));height:100%}.stats-distribution>div,.replay-state{flex:1;min-width:260px}.replay-debugger{border-color:#00f0ff29}.replay-toolbar{border:1px solid var(--border-muted);border-radius:var(--radius);background:linear-gradient(145deg,#0c0f1cf0,#080a12f5);justify-content:space-between;margin-bottom:12px;padding:12px 14px}.replay-toolbar button{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border:1px solid #00f0ff38;padding:8px 14px;font-size:12px}.replay-toolbar button:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 12px #00f0ff24}.replay-toolbar button:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-toolbar button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;transform:none}.replay-controls-center{flex-direction:column;align-items:center;gap:10px;display:flex}.replay-auto-controls{flex-wrap:wrap;justify-content:center;align-items:center;gap:10px;display:flex}.replay-auto-toggle{min-width:160px}.replay-auto-toggle.active{background:linear-gradient(145deg,#00ff8826,#00cc6614);border-color:#00ff884d;box-shadow:0 0 14px #00ff8829}.replay-interval-picker{color:var(--text-secondary);font-family:var(--font-mono);align-items:center;gap:8px;font-size:12px;display:flex}.replay-interval-picker select{border-radius:var(--radius);color:var(--text-primary);font-family:var(--font-mono);background:#080c14f2;border:1px solid #00f0ff33;padding:7px 10px;font-size:12px}.replay-interval-picker select:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px}.replay-interval-picker select:disabled{opacity:.6;cursor:not-allowed}.replay-compare-warning{border-radius:var(--radius);color:#ffe7b2;background:linear-gradient(145deg,#2a1d08db,#1c1204f0);border:1px solid #ffd1663d;justify-content:space-between;align-items:center;gap:14px;margin-bottom:12px;padding:12px 14px;display:flex}.replay-warning-hint{color:#ffe7b2c7;font-family:var(--font-mono);font-size:11px}.replay-compare-actions{flex-shrink:0;align-items:center;gap:10px;display:flex}.replay-warning-btn{border-radius:var(--radius);color:#ffe7b2;font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), background var(--transition), transform var(--transition);background:#ffd1660f;border:1px solid #ffd16638;padding:8px 12px;font-size:12px}.replay-warning-btn:hover{background:#ffd1661f;border-color:#ffd16675;transform:translateY(-1px)}.replay-warning-btn--primary{color:var(--text-primary);background:linear-gradient(145deg,#00f0ff1f,#4f9cf914);border-color:#00f0ff47}.replay-warning-btn--primary:hover{border-color:var(--neon-cyan-mid);background:linear-gradient(145deg,#00f0ff2e,#4f9cf91a)}.replay-position{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;text-transform:uppercase;font-size:13px}.timeline-virtual-container{border:1px solid var(--border-muted);border-radius:var(--radius-lg);background:linear-gradient(#080a12f5,#06080efa),radial-gradient(circle at top,#00f0ff0d,#0000 55%);margin-top:12px;box-shadow:inset 0 1px #00f0ff0d}.timeline-step{border:1px solid var(--border-muted);border-radius:var(--radius);min-width:136px;max-width:168px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#101422f5,#0a0c16fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:4px;margin:0;padding:12px 14px;display:flex}.timeline-step:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-step.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-step-badge{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:.8px;text-transform:uppercase;font-size:11px}.timeline-step-seq{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-type{color:var(--text-primary);letter-spacing:.2px;font-size:12px;font-weight:600}.timeline-group-chip{border-radius:var(--radius);border:1px solid var(--border-muted);min-width:190px;max-width:220px;color:var(--text-primary);text-align:left;cursor:pointer;transition:border-color var(--transition), box-shadow var(--transition), transform var(--transition), background var(--transition);background:linear-gradient(145deg,#141828f7,#0b0e18fa);flex-direction:column;flex-shrink:0;align-items:flex-start;gap:6px;margin:0;padding:12px 16px;display:flex}.timeline-group-chip:hover{border-color:var(--neon-cyan-mid);transform:translateY(-1px);box-shadow:0 0 14px #00f0ff14}.timeline-group-chip.active{box-shadow:0 0 0 2px var(--neon-cyan-dim), 0 0 18px #00f0ff24;border-color:#00f0ff8c}.timeline-group-chip.expanded{background:linear-gradient(145deg,#181e30f7,#0c101cfc);border-color:#00f0ff59}.timeline-group-chip-top{justify-content:flex-start;align-items:center;gap:8px;width:100%;display:flex}.timeline-group-count{color:var(--neon-cyan);font-family:var(--font-display);letter-spacing:1px;font-size:22px;line-height:1}.timeline-group-chevron{color:var(--text-muted);margin-left:auto;font-size:11px}.timeline-group-chip .timeline-group-type{color:var(--text-primary);font-size:12px;font-weight:700;line-height:1.35}.timeline-group-range{color:var(--text-muted);font-family:var(--font-mono);font-size:11px}.timeline-step-compact{min-width:132px;max-width:156px;padding:10px 12px}.timeline-step-compact .timeline-step-type{font-size:11px}.timeline-step-compare{box-shadow:inset 0 0 0 1px var(--neon-magenta)}.timeline-track-item{align-items:center;gap:12px;display:flex}.timeline-track-item--group{align-items:flex-start}.timeline-expanded-head{justify-content:space-between}.timeline-collapse-btn{border-radius:var(--radius);border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-mono);cursor:pointer;transition:border-color var(--transition), color var(--transition), background var(--transition);background:0 0;margin-left:auto;padding:6px 12px;font-size:12px}.timeline-collapse-btn:hover{border-color:var(--neon-cyan-mid);color:var(--neon-cyan);background:#00f0ff0f}.diff-split-row--added .diff-split-new,.diff-split-row--changed .diff-split-new{background:#00ff880f}.diff-split-row--removed .diff-split-old,.diff-split-row--changed .diff-split-old{background:#ff33550f}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden} diff --git a/eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js b/eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js deleted file mode 100644 index 5dd2a5b..0000000 --- a/eventlens-api/src/main/resources/web/assets/index-kO-MJOqr.js +++ /dev/null @@ -1,14 +0,0 @@ -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},ee=Object.prototype.hasOwnProperty;function te(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ne(e,t){return te(e.type,t,e.props)}function T(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function re(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var ie=/\/+/g;function E(e,t){return typeof e==`object`&&e&&e.key!=null?re(``+e.key):t.toString(36)}function ae(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function oe(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,oe(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+E(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(ie,`$&/`)+`/`),oe(o,r,i,``,function(e){return e})):o!=null&&(T(o)&&(o=ne(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(ie,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,T());else{var t=n(l);t!==null&&E(x,t.startTime-e)}}var S=!1,C=-1,w=5,ee=-1;function te(){return g?!0:!(e.unstable_now()-eet&&te());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&E(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?T():S=!1}}}var T;if(typeof y==`function`)T=function(){y(ne)};else if(typeof MessageChannel<`u`){var re=new MessageChannel,ie=re.port2;re.port1.onmessage=ne,T=function(){ie.postMessage(null)}}else T=function(){_(ne,0)};function E(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,E(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,T()))),r},e.unstable_shouldYield=te,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=m()})),g=o((e=>{var t=p(),n=d(),r=h();function i(e){var t=`https://react.dev/errors/`+e;if(1de||(e.current=ue[de],ue[de]=null,de--)}function A(e,t){de++,ue[de]=e.current,e.current=t}var pe=fe(null),me=fe(null),he=fe(null),ge=fe(null);function _e(e,t){switch(A(he,t),A(me,e),A(pe,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Vd(t),e=Hd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}k(pe),A(pe,e)}function ve(){k(pe),k(me),k(he)}function ye(e){e.memoizedState!==null&&A(ge,e);var t=pe.current,n=Hd(t,e.type);t!==n&&(A(me,e),A(pe,n))}function be(e){me.current===e&&(k(pe),k(me)),ge.current===e&&(k(ge),Qf._currentValue=le)}var xe,Se;function Ce(e){if(xe===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);xe=t&&t[1]||``,Se=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{we=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?Ce(n):``}function Ee(e,t){switch(e.tag){case 26:case 27:case 5:return Ce(e.type);case 16:return Ce(`Lazy`);case 13:return e.child!==t&&t!==null?Ce(`Suspense Fallback`):Ce(`Suspense`);case 19:return Ce(`SuspenseList`);case 0:case 15:return Te(e.type,!1);case 11:return Te(e.type.render,!1);case 1:return Te(e.type,!0);case 31:return Ce(`Activity`);default:return``}}function De(e){try{var t=``,n=null;do t+=Ee(e,n),n=e,e=e.return;while(e);return t}catch(e){return` -Error generating stack: `+e.message+` -`+e.stack}}var Oe=Object.prototype.hasOwnProperty,ke=t.unstable_scheduleCallback,Ae=t.unstable_cancelCallback,je=t.unstable_shouldYield,Me=t.unstable_requestPaint,Ne=t.unstable_now,Pe=t.unstable_getCurrentPriorityLevel,Fe=t.unstable_ImmediatePriority,Ie=t.unstable_UserBlockingPriority,Le=t.unstable_NormalPriority,Re=t.unstable_LowPriority,ze=t.unstable_IdlePriority,Be=t.log,Ve=t.unstable_setDisableYieldValue,He=null,Ue=null;function We(e){if(typeof Be==`function`&&Ve(e),Ue&&typeof Ue.setStrictMode==`function`)try{Ue.setStrictMode(He,e)}catch{}}var Ge=Math.clz32?Math.clz32:Je,Ke=Math.log,qe=Math.LN2;function Je(e){return e>>>=0,e===0?32:31-(Ke(e)/qe|0)|0}var Ye=256,Xe=262144,j=4194304;function M(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ze(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=M(n))):i=M(o):i=M(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=M(n))):i=M(o)):i=M(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function Qe(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function $e(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function et(){var e=j;return j<<=1,!(j&62914560)&&(j=4194304),e}function tt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function nt(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function rt(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),hn=!1;if(mn)try{var gn={};Object.defineProperty(gn,`passive`,{get:function(){hn=!0}}),window.addEventListener(`test`,gn,gn),window.removeEventListener(`test`,gn,gn)}catch{hn=!1}var _n=null,vn=null,yn=null;function bn(){if(yn)return yn;var e,t=vn,n=t.length,r,i=`value`in _n?_n.value:_n.textContent,a=i.length;for(e=0;e=Xn),$n=` `,er=!1;function tr(e,t){switch(e){case`keyup`:return Jn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function nr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var rr=!1;function ir(e,t){switch(e){case`compositionend`:return nr(t);case`keypress`:return t.which===32?(er=!0,$n):null;case`textInput`:return e=t.data,e===$n&&er?null:e;default:return null}}function ar(e,t){if(rr)return e===`compositionend`||!Yn&&tr(e,t)?(e=bn(),yn=vn=_n=null,rr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Dr(n)}}function kr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?kr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Ar(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Vt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Vt(e.document)}return t}function jr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Mr=mn&&`documentMode`in document&&11>=document.documentMode,Nr=null,Pr=null,Fr=null,Ir=!1;function Lr(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ir||Nr==null||Nr!==Vt(r)||(r=Nr,`selectionStart`in r&&jr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Fr&&Er(Fr,r)||(Fr=r,r=Ed(Pr,`onSelect`),0>=o,i-=o,z=1<<32-Ge(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),V&&Di(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),V&&Di(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return V&&Di(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),V&&Di(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===T&&Ea(l)===r.type){n(e,r.sibling),c=a(r,o.props),Na(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=mi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=pi(o.type,o.key,o.props,null,e.mode,c),Na(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=_i(o,e.mode,c),c.return=e,e=c}return s(e);case T:return o=Ea(o),b(e,r,o,c)}if(ce(o))return h(e,r,o,c);if(ae(o)){if(l=ae(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Ma(o),c);if(o.$$typeof===C)return b(e,r,ea(e,o),c);Pa(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=hi(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{ja=0;var i=b(e,t,n,r);return Aa=null,i}catch(t){if(t===ba||t===Sa)throw t;var a=li(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Ia=Fa(!0),La=Fa(!1),Ra=!1;function za(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ba(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Va(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Ha(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,K&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=oi(e),L(e,null,n),t}return ri(e,r,t,n),oi(e)}function Ua(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,at(e,n)}}function Wa(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Ga=!1;function Ka(){if(Ga){var e=da;if(e!==null)throw e}}function qa(e,t,n,r){Ga=!1;var i=e.updateQueue;Ra=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(Y&f)===f:(r&f)===f){f!==0&&f===ua&&(Ga=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=m({},d,f);break a;case 2:Ra=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Gl|=o,e.lanes=o,e.memoizedState=d}}function Ja(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Ya(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=D.T,s={};D.T=s,Ps(e,!1,t,n);try{var c=i(),l=D.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ns(e,t,ma(c,r),pu(e)):Ns(e,t,r,pu(e))}catch(n){Ns(e,t,{then:function(){},status:`rejected`,reason:n},pu())}finally{O.p=a,o!==null&&s.types!==null&&(o.types=s.types),D.T=o}}function Cs(){}function ws(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Ts(e).queue;Ss(e,a,t,le,n===null?Cs:function(){return Es(e),n(r)})}function Ts(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:le,baseState:le,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Fo,lastRenderedState:le},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Fo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Es(e){var t=Ts(e);t.next===null&&(t=e.alternate.memoizedState),Ns(e,t.next.queue,{},pu())}function Ds(){return $i(Qf)}function Os(){return Ao().memoizedState}function ks(){return Ao().memoizedState}function As(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=pu();e=Va(n);var r=Ha(t,e,n);r!==null&&(hu(r,t,n),Ua(r,t,n)),t={cache:oa()},e.payload=t;return}t=t.return}}function js(e,t,n){var r=pu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Fs(e)?Is(t,n):(n=ii(e,t,n,r),n!==null&&(hu(n,e,r),Ls(n,t,r)))}function Ms(e,t,n){Ns(e,t,n,pu())}function Ns(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Fs(e))Is(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,Tr(s,o))return ri(e,t,i,0),q===null&&ni(),!1}catch{}if(n=ii(e,t,i,r),n!==null)return hu(n,e,r),Ls(n,t,r),!0}return!1}function Ps(e,t,n,r){if(r={lane:2,revertLane:dd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Fs(e)){if(t)throw Error(i(479))}else t=ii(e,n,r,2),t!==null&&hu(t,e,2)}function Fs(e){var t=e.alternate;return e===H||t!==null&&t===H}function Is(e,t){mo=po=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ls(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,at(e,n)}}var Rs={readContext:$i,use:No,useCallback:bo,useContext:bo,useEffect:bo,useImperativeHandle:bo,useLayoutEffect:bo,useInsertionEffect:bo,useMemo:bo,useReducer:bo,useRef:bo,useState:bo,useDebugValue:bo,useDeferredValue:bo,useTransition:bo,useSyncExternalStore:bo,useId:bo,useHostTransitionStatus:bo,useFormState:bo,useActionState:bo,useOptimistic:bo,useMemoCache:bo,useCacheRefresh:bo};Rs.useEffectEvent=bo;var zs={readContext:$i,use:No,useCallback:function(e,t){return ko().memoizedState=[e,t===void 0?null:t],e},useContext:$i,useEffect:ls,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),ss(4194308,4,hs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ss(4194308,4,e,t)},useInsertionEffect:function(e,t){ss(4,2,e,t)},useMemo:function(e,t){var n=ko();t=t===void 0?null:t;var r=e();if(ho){We(!0);try{e()}finally{We(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=ko();if(n!==void 0){var i=n(t);if(ho){We(!0);try{n(t)}finally{We(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=js.bind(null,H,e),[r.memoizedState,e]},useRef:function(e){var t=ko();return e={current:e},t.memoizedState=e},useState:function(e){e=Go(e);var t=e.queue,n=Ms.bind(null,H,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:_s,useDeferredValue:function(e,t){return bs(ko(),e,t)},useTransition:function(){var e=Go(!1);return e=Ss.bind(null,H,e.queue,!0,!1),ko().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=H,a=ko();if(V){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),q===null)throw Error(i(349));Y&127||Bo(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,ls(Ho.bind(null,r,o,e),[e]),r.flags|=2048,as(9,{destroy:void 0},Vo.bind(null,r,o,n,t),null),n},useId:function(){var e=ko(),t=q.identifierPrefix;if(V){var n=Ei,r=z;n=(r&~(1<<32-Ge(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=go++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[ft]=t,o[N]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Pd(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Nc(t)}}return W(t),Pc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Nc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=he.current,zi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Mi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[ft]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Md(e.nodeValue,n)),e||Ii(t,!0)}else e=Bd(e).createTextNode(r),e[ft]=t,t.stateNode=e}return W(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=zi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[ft]=t}else Bi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;W(t),e=!1}else n=Vi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(so(t),t):(so(t),null);if(t.flags&128)throw Error(i(558))}return W(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=zi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[ft]=t}else Bi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;W(t),a=!1}else a=Vi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(so(t),t):(so(t),null)}return so(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Ic(t,t.updateQueue),W(t),null);case 4:return ve(),e===null&&Sd(t.stateNode.containerInfo),W(t),null;case 10:return qi(t.type),W(t),null;case 19:if(k(co),r=t.memoizedState,r===null)return W(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Lc(r,!1);else{if(Wl!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=lo(e),o!==null){for(t.flags|=128,Lc(r,!1),e=o.updateQueue,t.updateQueue=e,Ic(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)fi(n,e),n=n.sibling;return A(co,co.current&1|2),V&&Di(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&Ne()>tu&&(t.flags|=128,a=!0,Lc(r,!1),t.lanes=4194304)}else{if(!a)if(e=lo(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Ic(t,e),Lc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!V)return W(t),null}else 2*Ne()-r.renderingStartTime>tu&&n!==536870912&&(t.flags|=128,a=!0,Lc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(W(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=Ne(),e.sibling=null,n=co.current,A(co,a?n&1|2:n&1),V&&Di(t,r.treeForkCount),e);case 22:case 23:return so(t),eo(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(W(t),t.subtreeFlags&6&&(t.flags|=8192)):W(t),n=t.updateQueue,n!==null&&Ic(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&k(ga),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),qi(aa),W(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function zc(e,t){switch(Ai(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return qi(aa),ve(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return be(t),null;case 31:if(t.memoizedState!==null){if(so(t),t.alternate===null)throw Error(i(340));Bi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(so(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Bi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return k(co),null;case 4:return ve(),null;case 10:return qi(t.type),null;case 22:case 23:return so(t),eo(),e!==null&&k(ga),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return qi(aa),null;case 25:return null;default:return null}}function Bc(e,t){switch(Ai(t),t.tag){case 3:qi(aa),ve();break;case 26:case 27:case 5:be(t);break;case 4:ve();break;case 31:t.memoizedState!==null&&so(t);break;case 13:so(t);break;case 19:k(co);break;case 10:qi(t.type);break;case 22:case 23:so(t),eo(),e!==null&&k(ga);break;case 24:qi(aa)}}function Vc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Z(t,t.return,e)}}function Hc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Z(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Z(t,t.return,e)}}function Uc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Ya(t,n)}catch(t){Z(e,e.return,t)}}}function Wc(e,t,n){n.props=Ks(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Z(e,t,n)}}function Gc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Z(e,t,n)}}function Kc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){Z(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Z(e,t,n)}else n.current=null}function qc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Z(e,e.return,t)}}function Jc(e,t,n){try{var r=e.stateNode;Fd(r,e.type,n,t),r[N]=t}catch(t){Z(e,e.return,t)}}function Yc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Zd(e.type)||e.tag===4}function Xc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Yc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Zd(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Zc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=an));else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Zc(e,t,n),e=e.sibling;e!==null;)Zc(e,t,n),e=e.sibling}function Qc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&Zd(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(Qc(e,t,n),e=e.sibling;e!==null;)Qc(e,t,n),e=e.sibling}function $c(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Pd(t,r,n),t[ft]=e,t[N]=n}catch(t){Z(e,e.return,t)}}var el=!1,tl=!1,nl=!1,rl=typeof WeakSet==`function`?WeakSet:Set,il=null;function al(e,t){if(e=e.containerInfo,Rd=sp,e=Ar(e),jr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(zd={focusedElem:e,selectionRange:n},sp=!1,il=t;il!==null;)if(t=il,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,il=e;else for(;il!==null;){switch(t=il,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Pd(o,r,n),o[ft]=e,wt(o),r=o;break a;case`link`:var s=Vf(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Or(s,h),v=Or(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,D.T=null,n=lu,lu=null;var o=au,s=su;if(iu=0,ou=au=null,su=0,K&6)throw Error(i(331));var c=K;if(K|=4,Pl(o.current),El(o,o.current,s,n),K=c,id(0,!1),Ue&&typeof Ue.onPostCommitFiberRoot==`function`)try{Ue.onPostCommitFiberRoot(He,o)}catch{}return!0}finally{O.p=a,D.T=r,Vu(e,t)}}function Wu(e,t,n){t=yi(n,t),t=Qs(e.stateNode,t,2),e=Ha(e,t,2),e!==null&&(nt(e,2),rd(e))}function Z(e,t,n){if(e.tag===3)Wu(e,e,n);else for(;t!==null;){if(t.tag===3){Wu(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(ru===null||!ru.has(r))){e=yi(n,e),n=$s(2),r=Ha(t,n,2),r!==null&&(ec(n,r,t,e),nt(r,2),rd(r));break}}t=t.return}}function Gu(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Rl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Hl=!0,i.add(n),e=Ku.bind(null,e,t,n),t.then(e,e))}function Ku(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,q===e&&(Y&n)===n&&(Wl===4||Wl===3&&(Y&62914560)===Y&&300>Ne()-$l?!(K&2)&&Su(e,0):ql|=n,Yl===Y&&(Yl=0)),rd(e)}function qu(e,t){t===0&&(t=et()),e=ai(e,t),e!==null&&(nt(e,t),rd(e))}function Ju(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),qu(e,n)}function Yu(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),qu(e,n)}function Xu(e,t){return ke(e,t)}var Zu=null,Qu=null,$u=!1,ed=!1,td=!1,nd=0;function rd(e){e!==Qu&&e.next===null&&(Qu===null?Zu=Qu=e:Qu=Qu.next=e),ed=!0,$u||($u=!0,ud())}function id(e,t){if(!td&&ed){td=!0;do for(var n=!1,r=Zu;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ge(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,ld(r,a))}else a=Y,a=Ze(r,r===q?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||Qe(r,a)||(n=!0,ld(r,a));r=r.next}while(n);td=!1}}function ad(){od()}function od(){ed=$u=!1;var e=0;nd!==0&&Gd()&&(e=nd);for(var t=Ne(),n=null,r=Zu;r!==null;){var i=r.next,a=sd(r,t);a===0?(r.next=null,n===null?Zu=i:n.next=i,i===null&&(Qu=n)):(n=r,(e!==0||a&3)&&(ed=!0)),r=i}iu!==0&&iu!==5||id(e,!1),nd!==0&&(nd=0)}function sd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Id(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function xf(e,t,n){var r=bf;if(r&&typeof t==`string`&&t){var i=Ut(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),hf.has(i)||(hf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Pd(t,`link`,e),wt(t),r.head.appendChild(t)))}}function Sf(e){_f.D(e),xf(`dns-prefetch`,e,null)}function Cf(e,t){_f.C(e,t),xf(`preconnect`,e,t)}function wf(e,t,n){_f.L(e,t,n);var r=bf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Ut(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Ut(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Ut(n.imageSizes)+`"]`)):i+=`[href="`+Ut(e)+`"]`;var a=i;switch(t){case`style`:a=Af(e);break;case`script`:a=Pf(e)}mf.has(a)||(e=m({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),mf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(jf(a))||t===`script`&&r.querySelector(Ff(a))||(t=r.createElement(`link`),Pd(t,`link`,e),wt(t),r.head.appendChild(t)))}}function Tf(e,t){_f.m(e,t);var n=bf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Ut(r)+`"][href="`+Ut(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Pf(e)}if(!mf.has(a)&&(e=m({rel:`modulepreload`,href:e},t),mf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Ff(a)))return}r=n.createElement(`link`),Pd(r,`link`,e),wt(r),n.head.appendChild(r)}}}function Ef(e,t,n){_f.S(e,t,n);var r=bf;if(r&&e){var i=Ct(r).hoistableStyles,a=Af(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(jf(a)))s.loading=5;else{e=m({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=mf.get(a))&&Rf(e,n);var c=o=r.createElement(`link`);wt(c),Pd(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Lf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Df(e,t){_f.X(e,t);var n=bf;if(n&&e){var r=Ct(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),wt(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Of(e,t){_f.M(e,t);var n=bf;if(n&&e){var r=Ct(n).hoistableScripts,i=Pf(e),a=r.get(i);a||(a=n.querySelector(Ff(i)),a||(e=m({src:e,async:!0,type:`module`},t),(t=mf.get(i))&&zf(e,t),a=n.createElement(`script`),wt(a),Pd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function kf(e,t,n,r){var a=(a=he.current)?gf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Af(n.href),n=Ct(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Af(n.href);var o=Ct(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(jf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),mf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},mf.set(e,n),o||Nf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Pf(n),n=Ct(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Af(e){return`href="`+Ut(e)+`"`}function jf(e){return`link[rel="stylesheet"][`+e+`]`}function Mf(e){return m({},e,{"data-precedence":e.precedence,precedence:null})}function Nf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Pd(t,`link`,n),wt(t),e.head.appendChild(t))}function Pf(e){return`[src="`+Ut(e)+`"]`}function Ff(e){return`script[async]`+e}function If(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Ut(n.href)+`"]`);if(r)return t.instance=r,wt(r),r;var a=m({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),wt(r),Pd(r,`style`,a),Lf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Af(n.href);var o=e.querySelector(jf(a));if(o)return t.state.loading|=4,t.instance=o,wt(o),o;r=Mf(n),(a=mf.get(a))&&Rf(r,a),o=(e.ownerDocument||e).createElement(`link`),wt(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Pd(o,`link`,r),t.state.loading|=4,Lf(o,n.precedence,e),t.instance=o;case`script`:return o=Pf(n.src),(a=e.querySelector(Ff(o)))?(t.instance=a,wt(a),a):(r=n,(a=mf.get(o))&&(r=m({},n),zf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),wt(a),Pd(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Lf(r,n.precedence,e));return t.instance}function Lf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Uf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Wf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Gf(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Af(r.href),a=t.querySelector(jf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Jf.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,wt(a);return}a=t.ownerDocument||t,r=Mf(r),(i=mf.get(i))&&Rf(r,i),a=a.createElement(`link`),wt(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Pd(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Jf.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var Kf=0;function qf(e,t){return e.stylesheets&&e.count===0&&Xf(e,e.stylesheets),0Kf?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Jf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Xf(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Yf=null;function Xf(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Yf=new Map,t.forEach(Zf,e),Yf=null,Jf.call(e))}function Zf(e,t){if(!(t.state.loading&4)){var n=Yf.get(e);if(n)var r=n.get(null);else{n=new Map,Yf.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=g()})),v=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},y=new class extends v{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e();return window.addEventListener(`visibilitychange`,t,!1),()=>{window.removeEventListener(`visibilitychange`,t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(e=>{typeof e==`boolean`?this.setFocused(e):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){let e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e==`boolean`?this.#e:globalThis.document?.visibilityState!==`hidden`}},b={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},x=new class{#e=b;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}};function S(e){setTimeout(e,0)}var C=typeof window>`u`||`Deno`in globalThis;function w(){}function ee(e,t){return typeof e==`function`?e(t):e}function te(e){return typeof e==`number`&&e>=0&&e!==1/0}function ne(e,t){return Math.max(e+(t||0)-Date.now(),0)}function T(e,t){return typeof e==`function`?e(t):e}function re(e,t){return typeof e==`function`?e(t):e}function ie(e,t){let{type:n=`all`,exact:r,fetchStatus:i,predicate:a,queryKey:o,stale:s}=e;if(o){if(r){if(t.queryHash!==ae(o,t.options))return!1}else if(!se(t.queryKey,o))return!1}if(n!==`all`){let e=t.isActive();if(n===`active`&&!e||n===`inactive`&&e)return!1}return!(typeof s==`boolean`&&t.isStale()!==s||i&&i!==t.state.fetchStatus||a&&!a(t))}function E(e,t){let{exact:n,status:r,predicate:i,mutationKey:a}=e;if(a){if(!t.options.mutationKey)return!1;if(n){if(oe(t.options.mutationKey)!==oe(a))return!1}else if(!se(t.options.mutationKey,a))return!1}return!(r&&t.state.status!==r||i&&!i(t))}function ae(e,t){return(t?.queryKeyHashFn||oe)(e)}function oe(e){return JSON.stringify(e,(e,t)=>ue(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}function se(e,t){return e===t?!0:typeof e==typeof t&&e&&t&&typeof e==`object`&&typeof t==`object`?Object.keys(t).every(n=>se(e[n],t[n])):!1}var ce=Object.prototype.hasOwnProperty;function D(e,t,n=0){if(e===t)return e;if(n>500)return t;let r=le(e)&&le(t);if(!r&&!(ue(e)&&ue(t)))return t;let i=(r?e:Object.keys(e)).length,a=r?t:Object.keys(t),o=a.length,s=r?Array(o):{},c=0;for(let l=0;l{x.setTimeout(t,e)})}function k(e,t,n){return typeof n.structuralSharing==`function`?n.structuralSharing(e,t):n.structuralSharing===!1?t:D(e,t)}function A(e,t,n=0){let r=[...e,t];return n&&r.length>n?r.slice(1):r}function pe(e,t,n=0){let r=[t,...e];return n&&r.length>n?r.slice(0,-1):r}var me=Symbol();function he(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===me?()=>Promise.reject(Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function ge(e,t){return typeof e==`function`?e(...t):!!e}function _e(e,t,n){let r=!1,i;return Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(i??=t(),r?i:(r=!0,i.aborted?n():i.addEventListener(`abort`,n,{once:!0}),i))}),e}var ve=(()=>{let e=()=>C;return{isServer(){return e()},setIsServer(t){e=t}}})();function ye(){let e,t,n=new Promise((n,r)=>{e=n,t=r});n.status=`pending`,n.catch(()=>{});function r(e){Object.assign(n,e),delete n.resolve,delete n.reject}return n.resolve=t=>{r({status:`fulfilled`,value:t}),e(t)},n.reject=e=>{r({status:`rejected`,reason:e}),t(e)},n}var be=S;function xe(){let e=[],t=0,n=e=>{e()},r=e=>{e()},i=be,a=r=>{t?e.push(r):i(()=>{n(r)})},o=()=>{let t=e;e=[],t.length&&i(()=>{r(()=>{t.forEach(e=>{n(e)})})})};return{batch:e=>{let n;t++;try{n=e()}finally{t--,t||o()}return n},batchCalls:e=>(...t)=>{a(()=>{e(...t)})},schedule:a,setNotifyFunction:e=>{n=e},setBatchNotifyFunction:e=>{r=e},setScheduler:e=>{i=e}}}var Se=xe(),Ce=new class extends v{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<`u`&&window.addEventListener){let t=()=>e(!0),n=()=>e(!1);return window.addEventListener(`online`,t,!1),window.addEventListener(`offline`,n,!1),()=>{window.removeEventListener(`online`,t),window.removeEventListener(`offline`,n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(t=>{t(e)}))}isOnline(){return this.#e}};function we(e){return Math.min(1e3*2**e,3e4)}function Te(e){return(e??`online`)===`online`?Ce.isOnline():!0}var Ee=class extends Error{constructor(e){super(`CancelledError`),this.revert=e?.revert,this.silent=e?.silent}};function De(e){let t=!1,n=0,r,i=ye(),a=()=>i.status!==`pending`,o=t=>{if(!a()){let n=new Ee(t);f(n),e.onCancel?.(n)}},s=()=>{t=!0},c=()=>{t=!1},l=()=>y.isFocused()&&(e.networkMode===`always`||Ce.isOnline())&&e.canRun(),u=()=>Te(e.networkMode)&&e.canRun(),d=e=>{a()||(r?.(),i.resolve(e))},f=e=>{a()||(r?.(),i.reject(e))},p=()=>new Promise(t=>{r=e=>{(a()||l())&&t(e)},e.onPause?.()}).then(()=>{r=void 0,a()||e.onContinue?.()}),m=()=>{if(a())return;let r,i=n===0?e.initialPromise:void 0;try{r=i??e.fn()}catch(e){r=Promise.reject(e)}Promise.resolve(r).then(d).catch(r=>{if(a())return;let i=e.retry??(ve.isServer()?0:3),o=e.retryDelay??we,s=typeof o==`function`?o(n,r):o,c=i===!0||typeof i==`number`&&nl()?void 0:p()).then(()=>{t?f(r):m()})})};return{promise:i,status:()=>i.status,cancel:o,continue:()=>(r?.(),i),cancelRetry:s,continueRetry:c,canStart:u,start:()=>(u()?m():p().then(m),i)}}var Oe=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),te(this.gcTime)&&(this.#e=x.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(ve.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e&&=(x.clearTimeout(this.#e),void 0)}},ke=class extends Oe{#e;#t;#n;#r;#i;#a;#o;constructor(e){super(),this.#o=!1,this.#a=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#r=e.client,this.#n=this.#r.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#e=Me(this.options),this.state=e.state??this.#e,this.scheduleGc()}get meta(){return this.options.meta}get promise(){return this.#i?.promise}setOptions(e){if(this.options={...this.#a,...e},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){let e=Me(this.options);e.data!==void 0&&(this.setState(je(e.data,e.dataUpdatedAt)),this.#e=e)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus===`idle`&&this.#n.remove(this)}setData(e,t){let n=k(this.state.data,e,this.options);return this.#c({data:n,type:`success`,dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e,t){this.#c({type:`setState`,state:e,setStateOptions:t})}cancel(e){let t=this.#i?.promise;return this.#i?.cancel(e),t?t.then(w).catch(w):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#e}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>re(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===me||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>T(e.options.staleTime,this)===`static`):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e===`static`?!1:this.state.isInvalidated?!0:!ne(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(e=>e.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#i?.continue()}onOnline(){this.observers.find(e=>e.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#i?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#i&&(this.#o||this.#s()?this.#i.cancel({revert:!0}):this.#i.cancelRetry()),this.scheduleGc()),this.#n.notify({type:`observerRemoved`,query:this,observer:e}))}getObserversCount(){return this.observers.length}#s(){return this.state.fetchStatus===`paused`&&this.state.status===`pending`}invalidate(){this.state.isInvalidated||this.#c({type:`invalidate`})}async fetch(e,t){if(this.state.fetchStatus!==`idle`&&this.#i?.status()!==`rejected`){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#i)return this.#i.continueRetry(),this.#i.promise}if(e&&this.setOptions(e),!this.options.queryFn){let e=this.observers.find(e=>e.options.queryFn);e&&this.setOptions(e.options)}let n=new AbortController,r=e=>{Object.defineProperty(e,`signal`,{enumerable:!0,get:()=>(this.#o=!0,n.signal)})},i=()=>{let e=he(this.options,t),n=(()=>{let e={client:this.#r,queryKey:this.queryKey,meta:this.meta};return r(e),e})();return this.#o=!1,this.options.persister?this.options.persister(e,n,this):e(n)},a=(()=>{let e={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#r,state:this.state,fetchFn:i};return r(e),e})();this.options.behavior?.onFetch(a,this),this.#t=this.state,(this.state.fetchStatus===`idle`||this.state.fetchMeta!==a.fetchOptions?.meta)&&this.#c({type:`fetch`,meta:a.fetchOptions?.meta}),this.#i=De({initialPromise:t?.initialPromise,fn:a.fetchFn,onCancel:e=>{e instanceof Ee&&e.revert&&this.setState({...this.#t,fetchStatus:`idle`}),n.abort()},onFail:(e,t)=>{this.#c({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#c({type:`pause`})},onContinue:()=>{this.#c({type:`continue`})},retry:a.options.retry,retryDelay:a.options.retryDelay,networkMode:a.options.networkMode,canRun:()=>!0});try{let e=await this.#i.start();if(e===void 0)throw Error(`${this.queryHash} data is undefined`);return this.setData(e),this.#n.config.onSuccess?.(e,this),this.#n.config.onSettled?.(e,this.state.error,this),e}catch(e){if(e instanceof Ee){if(e.silent)return this.#i.promise;if(e.revert){if(this.state.data===void 0)throw e;return this.state.data}}throw this.#c({type:`error`,error:e}),this.#n.config.onError?.(e,this),this.#n.config.onSettled?.(this.state.data,e,this),e}finally{this.scheduleGc()}}#c(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case`pause`:return{...t,fetchStatus:`paused`};case`continue`:return{...t,fetchStatus:`fetching`};case`fetch`:return{...t,...Ae(t.data,this.options),fetchMeta:e.meta??null};case`success`:let n={...t,...je(e.data,e.dataUpdatedAt),dataUpdateCount:t.dataUpdateCount+1,...!e.manual&&{fetchStatus:`idle`,fetchFailureCount:0,fetchFailureReason:null}};return this.#t=e.manual?n:void 0,n;case`error`:let r=e.error;return{...t,error:r,errorUpdateCount:t.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:t.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:`idle`,status:`error`,isInvalidated:!0};case`invalidate`:return{...t,isInvalidated:!0};case`setState`:return{...t,...e.state}}})(this.state),Se.batch(()=>{this.observers.forEach(e=>{e.onQueryUpdate()}),this.#n.notify({query:this,type:`updated`,action:e})})}};function Ae(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:Te(t.networkMode)?`fetching`:`paused`,...e===void 0&&{error:null,status:`pending`}}}function je(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:`success`}}function Me(e){let t=typeof e.initialData==`function`?e.initialData():e.initialData,n=t!==void 0,r=n?typeof e.initialDataUpdatedAt==`function`?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?r??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?`success`:`pending`,fetchStatus:`idle`}}var Ne=class extends v{constructor(e,t){super(),this.options=t,this.#e=e,this.#s=null,this.#o=ye(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#i;#a;#o;#s;#c;#l;#u;#d;#f;#p;#m=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),Fe(this.#t,this.options)?this.#h():this.updateResult(),this.#y())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return Ie(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return Ie(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#b(),this.#x(),this.#t.removeObserver(this)}setOptions(e){let t=this.options,n=this.#t;if(this.options=this.#e.defaultQueryOptions(e),this.options.enabled!==void 0&&typeof this.options.enabled!=`boolean`&&typeof this.options.enabled!=`function`&&typeof re(this.options.enabled,this.#t)!=`boolean`)throw Error(`Expected enabled to be a boolean or a callback that returns a boolean`);this.#S(),this.#t.setOptions(this.options),t._defaulted&&!O(this.options,t)&&this.#e.getQueryCache().notify({type:`observerOptionsUpdated`,query:this.#t,observer:this});let r=this.hasListeners();r&&Le(this.#t,n,this.options,t)&&this.#h(),this.updateResult(),r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||T(this.options.staleTime,this.#t)!==T(t.staleTime,this.#t))&&this.#g();let i=this.#_();r&&(this.#t!==n||re(this.options.enabled,this.#t)!==re(t.enabled,this.#t)||i!==this.#p)&&this.#v(i)}getOptimisticResult(e){let t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return ze(this,n)&&(this.#r=n,this.#a=this.options,this.#i=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(e,n)=>(this.trackProp(n),t?.(n),n===`promise`&&(this.trackProp(`data`),!this.options.experimental_prefetchInRender&&this.#o.status===`pending`&&this.#o.reject(Error(`experimental_prefetchInRender feature flag is not enabled`))),Reflect.get(e,n))})}trackProp(e){this.#m.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){let t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#h({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#h(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(w)),t}#g(){this.#b();let e=T(this.options.staleTime,this.#t);if(ve.isServer()||this.#r.isStale||!te(e))return;let t=ne(this.#r.dataUpdatedAt,e)+1;this.#d=x.setTimeout(()=>{this.#r.isStale||this.updateResult()},t)}#_(){return(typeof this.options.refetchInterval==`function`?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#v(e){this.#x(),this.#p=e,!(ve.isServer()||re(this.options.enabled,this.#t)===!1||!te(this.#p)||this.#p===0)&&(this.#f=x.setInterval(()=>{(this.options.refetchIntervalInBackground||y.isFocused())&&this.#h()},this.#p))}#y(){this.#g(),this.#v(this.#_())}#b(){this.#d&&=(x.clearTimeout(this.#d),void 0)}#x(){this.#f&&=(x.clearInterval(this.#f),void 0)}createResult(e,t){let n=this.#t,r=this.options,i=this.#r,a=this.#i,o=this.#a,s=e===n?this.#n:e.state,{state:c}=e,l={...c},u=!1,d;if(t._optimisticResults){let i=this.hasListeners(),a=!i&&Fe(e,t),o=i&&Le(e,n,t,r);(a||o)&&(l={...l,...Ae(c.data,e.options)}),t._optimisticResults===`isRestoring`&&(l.fetchStatus=`idle`)}let{error:f,errorUpdatedAt:p,status:m}=l;d=l.data;let h=!1;if(t.placeholderData!==void 0&&d===void 0&&m===`pending`){let e;i?.isPlaceholderData&&t.placeholderData===o?.placeholderData?(e=i.data,h=!0):e=typeof t.placeholderData==`function`?t.placeholderData(this.#u?.state.data,this.#u):t.placeholderData,e!==void 0&&(m=`success`,d=k(i?.data,e,t),u=!0)}if(t.select&&d!==void 0&&!h)if(i&&d===a?.data&&t.select===this.#c)d=this.#l;else try{this.#c=t.select,d=t.select(d),d=k(i?.data,d,t),this.#l=d,this.#s=null}catch(e){this.#s=e}this.#s&&(f=this.#s,d=this.#l,p=Date.now(),m=`error`);let g=l.fetchStatus===`fetching`,_=m===`pending`,v=m===`error`,y=_&&g,b=d!==void 0,x={status:m,fetchStatus:l.fetchStatus,isPending:_,isSuccess:m===`success`,isError:v,isInitialLoading:y,isLoading:y,data:d,dataUpdatedAt:l.dataUpdatedAt,error:f,errorUpdatedAt:p,failureCount:l.fetchFailureCount,failureReason:l.fetchFailureReason,errorUpdateCount:l.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:l.dataUpdateCount>s.dataUpdateCount||l.errorUpdateCount>s.errorUpdateCount,isFetching:g,isRefetching:g&&!_,isLoadingError:v&&!b,isPaused:l.fetchStatus===`paused`,isPlaceholderData:u,isRefetchError:v&&b,isStale:Re(e,t),refetch:this.refetch,promise:this.#o,isEnabled:re(t.enabled,e)!==!1};if(this.options.experimental_prefetchInRender){let t=x.data!==void 0,r=x.status===`error`&&!t,i=e=>{r?e.reject(x.error):t&&e.resolve(x.data)},a=()=>{i(this.#o=x.promise=ye())},o=this.#o;switch(o.status){case`pending`:e.queryHash===n.queryHash&&i(o);break;case`fulfilled`:(r||x.data!==o.value)&&a();break;case`rejected`:(!r||x.error!==o.reason)&&a();break}}return x}updateResult(){let e=this.#r,t=this.createResult(this.#t,this.options);this.#i=this.#t.state,this.#a=this.options,this.#i.data!==void 0&&(this.#u=this.#t),!O(t,e)&&(this.#r=t,this.#C({listeners:(()=>{if(!e)return!0;let{notifyOnChangeProps:t}=this.options,n=typeof t==`function`?t():t;if(n===`all`||!n&&!this.#m.size)return!0;let r=new Set(n??this.#m);return this.options.throwOnError&&r.add(`error`),Object.keys(this.#r).some(t=>{let n=t;return this.#r[n]!==e[n]&&r.has(n)})})()}))}#S(){let e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;let t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#y()}#C(e){Se.batch(()=>{e.listeners&&this.listeners.forEach(e=>{e(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:`observerResultsUpdated`})})}};function Pe(e,t){return re(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status===`error`&&t.retryOnMount===!1)}function Fe(e,t){return Pe(e,t)||e.state.data!==void 0&&Ie(e,t,t.refetchOnMount)}function Ie(e,t,n){if(re(t.enabled,e)!==!1&&T(t.staleTime,e)!==`static`){let r=typeof n==`function`?n(e):n;return r===`always`||r!==!1&&Re(e,t)}return!1}function Le(e,t,n,r){return(e!==t||re(r.enabled,e)===!1)&&(!n.suspense||e.state.status!==`error`)&&Re(e,n)}function Re(e,t){return re(t.enabled,e)!==!1&&e.isStaleByTime(T(t.staleTime,e))}function ze(e,t){return!O(e.getCurrentResult(),t)}function Be(e){return{onFetch:(t,n)=>{let r=t.options,i=t.fetchOptions?.meta?.fetchMore?.direction,a=t.state.data?.pages||[],o=t.state.data?.pageParams||[],s={pages:[],pageParams:[]},c=0,l=async()=>{let n=!1,l=e=>{_e(e,()=>t.signal,()=>n=!0)},u=he(t.options,t.fetchOptions),d=async(e,r,i)=>{if(n)return Promise.reject();if(r==null&&e.pages.length)return Promise.resolve(e);let a=await u((()=>{let e={client:t.client,queryKey:t.queryKey,pageParam:r,direction:i?`backward`:`forward`,meta:t.options.meta};return l(e),e})()),{maxPages:o}=t.options,s=i?pe:A;return{pages:s(e.pages,a,o),pageParams:s(e.pageParams,r,o)}};if(i&&a.length){let e=i===`backward`,t=e?He:Ve,n={pages:a,pageParams:o};s=await d(n,t(r,n),e)}else{let t=e??a.length;do{let e=c===0?o[0]??r.initialPageParam:Ve(r,s);if(c>0&&e==null)break;s=await d(s,e),c++}while(ct.options.persister?.(l,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=l}}}function Ve(e,{pages:t,pageParams:n}){let r=t.length-1;return t.length>0?e.getNextPageParam(t[r],t,n[r],n):void 0}function He(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var Ue=class extends Oe{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||We(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:`observerAdded`,mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:`observerRemoved`,mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status===`pending`?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){let t=()=>{this.#i({type:`continue`})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=De({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(Error(`No mutationFn found`)),onFail:(e,t)=>{this.#i({type:`failed`,failureCount:e,error:t})},onPause:()=>{this.#i({type:`pause`})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});let r=this.state.status===`pending`,i=!this.#r.canStart();try{if(r)t();else{this.#i({type:`pending`,variables:e,isPaused:i}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);let t=await this.options.onMutate?.(e,n);t!==this.state.context&&this.#i({type:`pending`,context:t,variables:e,isPaused:i})}let a=await this.#r.start();return await this.#n.config.onSuccess?.(a,e,this.state.context,this,n),await this.options.onSuccess?.(a,e,this.state.context,n),await this.#n.config.onSettled?.(a,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(a,null,e,this.state.context,n),this.#i({type:`success`,data:a}),a}catch(t){try{await this.#n.config.onError?.(t,e,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onError?.(t,e,this.state.context,n)}catch(e){Promise.reject(e)}try{await this.#n.config.onSettled?.(void 0,t,this.state.variables,this.state.context,this,n)}catch(e){Promise.reject(e)}try{await this.options.onSettled?.(void 0,t,e,this.state.context,n)}catch(e){Promise.reject(e)}throw this.#i({type:`error`,error:t}),t}finally{this.#n.runNext(this)}}#i(e){this.state=(t=>{switch(e.type){case`failed`:return{...t,failureCount:e.failureCount,failureReason:e.error};case`pause`:return{...t,isPaused:!0};case`continue`:return{...t,isPaused:!1};case`pending`:return{...t,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:`pending`,variables:e.variables,submittedAt:Date.now()};case`success`:return{...t,data:e.data,failureCount:0,failureReason:null,error:null,status:`success`,isPaused:!1};case`error`:return{...t,data:void 0,error:e.error,failureCount:t.failureCount+1,failureReason:e.error,isPaused:!1,status:`error`}}})(this.state),Se.batch(()=>{this.#t.forEach(t=>{t.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:`updated`,action:e})})}};function We(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:`idle`,variables:void 0,submittedAt:0}}var Ge=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){let r=new Ue({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(r),r}add(e){this.#e.add(e);let t=Ke(e);if(typeof t==`string`){let n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:`added`,mutation:e})}remove(e){if(this.#e.delete(e)){let t=Ke(e);if(typeof t==`string`){let n=this.#t.get(t);if(n)if(n.length>1){let t=n.indexOf(e);t!==-1&&n.splice(t,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:`removed`,mutation:e})}canRun(e){let t=Ke(e);if(typeof t==`string`){let n=this.#t.get(t)?.find(e=>e.state.status===`pending`);return!n||n===e}else return!0}runNext(e){let t=Ke(e);return typeof t==`string`?(this.#t.get(t)?.find(t=>t!==e&&t.state.isPaused))?.continue()??Promise.resolve():Promise.resolve()}clear(){Se.batch(()=>{this.#e.forEach(e=>{this.notify({type:`removed`,mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){let t={exact:!0,...e};return this.getAll().find(e=>E(t,e))}findAll(e={}){return this.getAll().filter(t=>E(e,t))}notify(e){Se.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){let e=this.getAll().filter(e=>e.state.isPaused);return Se.batch(()=>Promise.all(e.map(e=>e.continue().catch(w))))}};function Ke(e){return e.options.scope?.id}var qe=class extends v{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){let r=t.queryKey,i=t.queryHash??ae(r,t),a=this.get(i);return a||(a=new ke({client:e,queryKey:r,queryHash:i,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(r)}),this.add(a)),a}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:`added`,query:e}))}remove(e){let t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:`removed`,query:e}))}clear(){Se.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){let t={exact:!0,...e};return this.getAll().find(e=>ie(t,e))}findAll(e={}){let t=this.getAll();return Object.keys(e).length>0?t.filter(t=>ie(e,t)):t}notify(e){Se.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){Se.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){Se.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},Je=class{#e;#t;#n;#r;#i;#a;#o;#s;constructor(e={}){this.#e=e.queryCache||new qe,this.#t=e.mutationCache||new Ge,this.#n=e.defaultOptions||{},this.#r=new Map,this.#i=new Map,this.#a=0}mount(){this.#a++,this.#a===1&&(this.#o=y.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#s=Ce.subscribe(async e=>{e&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#a--,this.#a===0&&(this.#o?.(),this.#o=void 0,this.#s?.(),this.#s=void 0)}isFetching(e){return this.#e.findAll({...e,fetchStatus:`fetching`}).length}isMutating(e){return this.#t.findAll({...e,status:`pending`}).length}getQueryData(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state.data}ensureQueryData(e){let t=this.defaultQueryOptions(e),n=this.#e.build(this,t),r=n.state.data;return r===void 0?this.fetchQuery(e):(e.revalidateIfStale&&n.isStaleByTime(T(t.staleTime,n))&&this.prefetchQuery(t),Promise.resolve(r))}getQueriesData(e){return this.#e.findAll(e).map(({queryKey:e,state:t})=>[e,t.data])}setQueryData(e,t,n){let r=this.defaultQueryOptions({queryKey:e}),i=this.#e.get(r.queryHash)?.state.data,a=ee(t,i);if(a!==void 0)return this.#e.build(this,r).setData(a,{...n,manual:!0})}setQueriesData(e,t,n){return Se.batch(()=>this.#e.findAll(e).map(({queryKey:e})=>[e,this.setQueryData(e,t,n)]))}getQueryState(e){let t=this.defaultQueryOptions({queryKey:e});return this.#e.get(t.queryHash)?.state}removeQueries(e){let t=this.#e;Se.batch(()=>{t.findAll(e).forEach(e=>{t.remove(e)})})}resetQueries(e,t){let n=this.#e;return Se.batch(()=>(n.findAll(e).forEach(e=>{e.reset()}),this.refetchQueries({type:`active`,...e},t)))}cancelQueries(e,t={}){let n={revert:!0,...t},r=Se.batch(()=>this.#e.findAll(e).map(e=>e.cancel(n)));return Promise.all(r).then(w).catch(w)}invalidateQueries(e,t={}){return Se.batch(()=>(this.#e.findAll(e).forEach(e=>{e.invalidate()}),e?.refetchType===`none`?Promise.resolve():this.refetchQueries({...e,type:e?.refetchType??e?.type??`active`},t)))}refetchQueries(e,t={}){let n={...t,cancelRefetch:t.cancelRefetch??!0},r=Se.batch(()=>this.#e.findAll(e).filter(e=>!e.isDisabled()&&!e.isStatic()).map(e=>{let t=e.fetch(void 0,n);return n.throwOnError||(t=t.catch(w)),e.state.fetchStatus===`paused`?Promise.resolve():t}));return Promise.all(r).then(w)}fetchQuery(e){let t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);let n=this.#e.build(this,t);return n.isStaleByTime(T(t.staleTime,n))?n.fetch(t):Promise.resolve(n.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(w).catch(w)}fetchInfiniteQuery(e){return e.behavior=Be(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(w).catch(w)}ensureInfiniteQueryData(e){return e.behavior=Be(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return Ce.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(e){this.#n=e}setQueryDefaults(e,t){this.#r.set(oe(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){let t=[...this.#r.values()],n={};return t.forEach(t=>{se(e,t.queryKey)&&Object.assign(n,t.defaultOptions)}),n}setMutationDefaults(e,t){this.#i.set(oe(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){let t=[...this.#i.values()],n={};return t.forEach(t=>{se(e,t.mutationKey)&&Object.assign(n,t.defaultOptions)}),n}defaultQueryOptions(e){if(e._defaulted)return e;let t={...this.#n.queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||=ae(t.queryKey,t),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!==`always`),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode=`offlineFirst`),t.queryFn===me&&(t.enabled=!1),t}defaultMutationOptions(e){return e?._defaulted?e:{...this.#n.mutations,...e?.mutationKey&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}},Ye=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),Xe=o(((e,t)=>{t.exports=Ye()})),j=l(d(),1),M=Xe(),Ze=j.createContext(void 0),Qe=e=>{let t=j.useContext(Ze);if(e)return e;if(!t)throw Error(`No QueryClient set, use QueryClientProvider to set one`);return t},$e=({client:e,children:t})=>(j.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),(0,M.jsx)(Ze.Provider,{value:e,children:t})),et=j.createContext(!1),tt=()=>j.useContext(et);et.Provider;function nt(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var rt=j.createContext(nt()),it=()=>j.useContext(rt),at=(e,t,n)=>{let r=n?.state.error&&typeof e.throwOnError==`function`?ge(e.throwOnError,[n.state.error,n]):e.throwOnError;(e.suspense||e.experimental_prefetchInRender||r)&&(t.isReset()||(e.retryOnMount=!1))},ot=e=>{j.useEffect(()=>{e.clearReset()},[e])},st=({result:e,errorResetBoundary:t,throwOnError:n,query:r,suspense:i})=>e.isError&&!t.isReset()&&!e.isFetching&&r&&(i&&e.data===void 0||ge(n,[e.error,r])),ct=e=>{if(e.suspense){let t=1e3,n=e=>e===`static`?e:Math.max(e??t,t),r=e.staleTime;e.staleTime=typeof r==`function`?(...e)=>n(r(...e)):n(r),typeof e.gcTime==`number`&&(e.gcTime=Math.max(e.gcTime,t))}},lt=(e,t)=>e.isLoading&&e.isFetching&&!t,ut=(e,t)=>e?.suspense&&t.isPending,dt=(e,t,n)=>t.fetchOptimistic(e).catch(()=>{n.clearReset()});function ft(e,t,n){let r=tt(),i=it(),a=Qe(n),o=a.defaultQueryOptions(e);a.getDefaultOptions().queries?._experimental_beforeQuery?.(o);let s=a.getQueryCache().get(o.queryHash);o._optimisticResults=r?`isRestoring`:`optimistic`,ct(o),at(o,i,s),ot(i);let c=!a.getQueryCache().get(o.queryHash),[l]=j.useState(()=>new t(a,o)),u=l.getOptimisticResult(o),d=!r&&e.subscribed!==!1;if(j.useSyncExternalStore(j.useCallback(e=>{let t=d?l.subscribe(Se.batchCalls(e)):w;return l.updateResult(),t},[l,d]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),j.useEffect(()=>{l.setOptions(o)},[o,l]),ut(o,u))throw dt(o,l,i);if(st({result:u,errorResetBoundary:i,throwOnError:o.throwOnError,query:s,suspense:o.suspense}))throw u.error;return a.getDefaultOptions().queries?._experimental_afterQuery?.(o,u),o.experimental_prefetchInRender&&!ve.isServer()&<(u,r)&&(c?dt(o,l,i):s?.promise)?.catch(w).finally(()=>{l.updateResult()}),o.notifyOnChangeProps?u:l.trackResult(u)}function N(e,t){return ft(e,Ne,t)}function pt(e,t){return function(){return e.apply(t,arguments)}}var{toString:mt}=Object.prototype,{getPrototypeOf:ht}=Object,{iterator:gt,toStringTag:_t}=Symbol,vt=(e=>t=>{let n=mt.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),yt=e=>(e=e.toLowerCase(),t=>vt(t)===e),bt=e=>t=>typeof t===e,{isArray:xt}=Array,St=bt(`undefined`);function Ct(e){return e!==null&&!St(e)&&e.constructor!==null&&!St(e.constructor)&&Dt(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var wt=yt(`ArrayBuffer`);function Tt(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&wt(e.buffer),t}var Et=bt(`string`),Dt=bt(`function`),Ot=bt(`number`),kt=e=>typeof e==`object`&&!!e,At=e=>e===!0||e===!1,jt=e=>{if(vt(e)!==`object`)return!1;let t=ht(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(_t in e)&&!(gt in e)},Mt=e=>{if(!kt(e)||Ct(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Nt=yt(`Date`),Pt=yt(`File`),Ft=e=>!!(e&&e.uri!==void 0),It=e=>e&&e.getParts!==void 0,Lt=yt(`Blob`),Rt=yt(`FileList`),zt=e=>kt(e)&&Dt(e.pipe);function Bt(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var Vt=Bt(),Ht=Vt.FormData===void 0?void 0:Vt.FormData,Ut=e=>{let t;return e&&(Ht&&e instanceof Ht||Dt(e.append)&&((t=vt(e))===`formdata`||t===`object`&&Dt(e.toString)&&e.toString()===`[object FormData]`))},Wt=yt(`URLSearchParams`),[Gt,Kt,qt,Jt]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(yt),Yt=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function Xt(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),xt(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var Qt=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,$t=e=>!St(e)&&e!==Qt;function en(){let{caseless:e,skipUndefined:t}=$t(this)&&this||{},n={},r=(r,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=e&&Zt(n,i)||i;jt(n[a])&&jt(r)?n[a]=en(n[a],r):jt(r)?n[a]=en({},r):xt(r)?n[a]=r.slice():(!t||!St(r))&&(n[a]=r)};for(let e=0,t=arguments.length;e(Xt(t,(t,r)=>{n&&Dt(t)?Object.defineProperty(e,r,{value:pt(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),nn=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),rn=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,`constructor`,{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,`super`,{value:t.prototype}),n&&Object.assign(e.prototype,n)},an=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-- >0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&ht(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},on=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},sn=e=>{if(!e)return null;if(xt(e))return e;let t=e.length;if(!Ot(t))return null;let n=Array(t);for(;t-- >0;)n[t]=e[t];return n},cn=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&ht(Uint8Array)),ln=(e,t)=>{let n=(e&&e[gt]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},un=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},dn=yt(`HTMLFormElement`),fn=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),pn=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),mn=yt(`RegExp`),hn=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};Xt(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},gn=e=>{hn(e,(t,n)=>{if(Dt(e)&&[`arguments`,`caller`,`callee`].indexOf(n)!==-1)return!1;let r=e[n];if(Dt(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},_n=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return xt(e)?r(e):r(String(e).split(t)),n},vn=()=>{},yn=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function bn(e){return!!(e&&Dt(e.append)&&e[_t]===`FormData`&&e[gt])}var xn=e=>{let t=Array(10),n=(e,r)=>{if(kt(e)){if(t.indexOf(e)>=0)return;if(Ct(e))return e;if(!(`toJSON`in e)){t[r]=e;let i=xt(e)?[]:{};return Xt(e,(e,t)=>{let a=n(e,r+1);!St(a)&&(i[t]=a)}),t[r]=void 0,i}}return e};return n(e,0)},Sn=yt(`AsyncFunction`),Cn=e=>e&&(kt(e)||Dt(e))&&Dt(e.then)&&Dt(e.catch),wn=((e,t)=>e?setImmediate:t?((e,t)=>(Qt.addEventListener(`message`,({source:n,data:r})=>{n===Qt&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),Qt.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,Dt(Qt.postMessage)),P={isArray:xt,isArrayBuffer:wt,isBuffer:Ct,isFormData:Ut,isArrayBufferView:Tt,isString:Et,isNumber:Ot,isBoolean:At,isObject:kt,isPlainObject:jt,isEmptyObject:Mt,isReadableStream:Gt,isRequest:Kt,isResponse:qt,isHeaders:Jt,isUndefined:St,isDate:Nt,isFile:Pt,isReactNativeBlob:Ft,isReactNative:It,isBlob:Lt,isRegExp:mn,isFunction:Dt,isStream:zt,isURLSearchParams:Wt,isTypedArray:cn,isFileList:Rt,forEach:Xt,merge:en,extend:tn,trim:Yt,stripBOM:nn,inherits:rn,toFlatObject:an,kindOf:vt,kindOfTest:yt,endsWith:on,toArray:sn,forEachEntry:ln,matchAll:un,isHTMLForm:dn,hasOwnProperty:pn,hasOwnProp:pn,reduceDescriptors:hn,freezeMethods:gn,toObjectSet:_n,toCamelCase:fn,noop:vn,toFiniteNumber:yn,findKey:Zt,global:Qt,isContextDefined:$t,isSpecCompliantForm:bn,toJSONObject:xn,isAsyncFn:Sn,isThenable:Cn,setImmediate:wn,asap:typeof queueMicrotask<`u`?queueMicrotask.bind(Qt):typeof process<`u`&&process.nextTick||wn,isIterable:e=>e!=null&&Dt(e[gt])},F=class e extends Error{static from(t,n,r,i,a,o){let s=new e(t.message,n||t.code,r,i,a);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),o&&Object.assign(s,o),s}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,`message`,{value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:P.toJSONObject(this.config),code:this.code,status:this.status}}};F.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,F.ERR_BAD_OPTION=`ERR_BAD_OPTION`,F.ECONNABORTED=`ECONNABORTED`,F.ETIMEDOUT=`ETIMEDOUT`,F.ERR_NETWORK=`ERR_NETWORK`,F.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,F.ERR_DEPRECATED=`ERR_DEPRECATED`,F.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,F.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,F.ERR_CANCELED=`ERR_CANCELED`,F.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,F.ERR_INVALID_URL=`ERR_INVALID_URL`;function Tn(e){return P.isPlainObject(e)||P.isArray(e)}function En(e){return P.endsWith(e,`[]`)?e.slice(0,-2):e}function Dn(e,t,n){return e?e.concat(t).map(function(e,t){return e=En(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function On(e){return P.isArray(e)&&!e.some(Tn)}var kn=P.toFlatObject(P,{},null,function(e){return/^is[A-Z]/.test(e)});function An(e,t,n){if(!P.isObject(e))throw TypeError(`target must be an object`);t||=new FormData,n=P.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!P.isUndefined(t[e])});let r=n.metaTokens,i=n.visitor||l,a=n.dots,o=n.indexes,s=(n.Blob||typeof Blob<`u`&&Blob)&&P.isSpecCompliantForm(t);if(!P.isFunction(i))throw TypeError(`visitor must be a function`);function c(e){if(e===null)return``;if(P.isDate(e))return e.toISOString();if(P.isBoolean(e))return e.toString();if(!s&&P.isBlob(e))throw new F(`Blob is not supported. Use a Buffer instead.`);return P.isArrayBuffer(e)||P.isTypedArray(e)?s&&typeof Blob==`function`?new Blob([e]):Buffer.from(e):e}function l(e,n,i){let s=e;if(P.isReactNative(t)&&P.isReactNativeBlob(e))return t.append(Dn(i,n,a),c(e)),!1;if(e&&!i&&typeof e==`object`){if(P.endsWith(n,`{}`))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(P.isArray(e)&&On(e)||(P.isFileList(e)||P.endsWith(n,`[]`))&&(s=P.toArray(e)))return n=En(n),s.forEach(function(e,r){!(P.isUndefined(e)||e===null)&&t.append(o===!0?Dn([n],r,a):o===null?n:n+`[]`,c(e))}),!1}return Tn(e)?!0:(t.append(Dn(i,n,a),c(e)),!1)}let u=[],d=Object.assign(kn,{defaultVisitor:l,convertValue:c,isVisitable:Tn});function f(e,n){if(!P.isUndefined(e)){if(u.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));u.push(e),P.forEach(e,function(e,r){(!(P.isUndefined(e)||e===null)&&i.call(t,e,P.isString(r)?r.trim():r,n,d))===!0&&f(e,n?n.concat(r):[r])}),u.pop()}}if(!P.isObject(e))throw TypeError(`data must be an object`);return f(e),t}function jn(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`,"%00":`\0`};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function Mn(e,t){this._pairs=[],e&&An(e,this,t)}var Nn=Mn.prototype;Nn.append=function(e,t){this._pairs.push([e,t])},Nn.toString=function(e){let t=e?function(t){return e.call(this,t,jn)}:jn;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Pn(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function Fn(e,t,n){if(!t)return e;let r=n&&n.encode||Pn,i=P.isFunction(n)?{serialize:n}:n,a=i&&i.serialize,o;if(o=a?a(t,i):P.isURLSearchParams(t)?t.toString():new Mn(t,i).toString(r),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var In=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&=[]}forEach(e){P.forEach(this.handlers,function(t){t!==null&&e(t)})}},Ln={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},Rn={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Mn,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},zn=s({hasBrowserEnv:()=>Bn,hasStandardBrowserEnv:()=>Hn,hasStandardBrowserWebWorkerEnv:()=>Un,navigator:()=>Vn,origin:()=>Wn}),Bn=typeof window<`u`&&typeof document<`u`,Vn=typeof navigator==`object`&&navigator||void 0,Hn=Bn&&(!Vn||[`ReactNative`,`NativeScript`,`NS`].indexOf(Vn.product)<0),Un=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Wn=Bn&&window.location.href||`http://localhost`,Gn={...zn,...Rn};function Kn(e,t){return An(e,new Gn.classes.URLSearchParams,{visitor:function(e,t,n,r){return Gn.isNode&&P.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}function qn(e){return P.matchAll(/\w+|\[(\w*)]/g,e).map(e=>e[0]===`[]`?``:e[1]||e[0])}function Jn(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&P.isArray(r)?r.length:a,s?(P.hasOwnProp(r,a)?r[a]=[r[a],n]:r[a]=n,!o):((!r[a]||!P.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&P.isArray(r[a])&&(r[a]=Jn(r[a])),!o)}if(P.isFormData(e)&&P.isFunction(e.entries)){let n={};return P.forEachEntry(e,(e,r)=>{t(qn(e),r,n,0)}),n}return null}function Xn(e,t,n){if(P.isString(e))try{return(t||JSON.parse)(e),P.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Zn={transitional:Ln,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=P.isObject(e);if(i&&P.isHTMLForm(e)&&(e=new FormData(e)),P.isFormData(e))return r?JSON.stringify(Yn(e)):e;if(P.isArrayBuffer(e)||P.isBuffer(e)||P.isStream(e)||P.isFile(e)||P.isBlob(e)||P.isReadableStream(e))return e;if(P.isArrayBufferView(e))return e.buffer;if(P.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return Kn(e,this.formSerializer).toString();if((a=P.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let t=this.env&&this.env.FormData;return An(a?{"files[]":e}:e,t&&new t,this.formSerializer)}}return i||r?(t.setContentType(`application/json`,!1),Xn(e)):e}],transformResponse:[function(e){let t=this.transitional||Zn.transitional,n=t&&t.forcedJSONParsing,r=this.responseType===`json`;if(P.isResponse(e)||P.isReadableStream(e))return e;if(e&&P.isString(e)&&(n&&!this.responseType||r)){let n=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(n)throw e.name===`SyntaxError`?F.from(e,F.ERR_BAD_RESPONSE,this,null,this.response):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Gn.classes.FormData,Blob:Gn.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};P.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`],e=>{Zn.headers[e]={}});var Qn=P.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),$n=e=>{let t={},n,r,i;return e&&e.split(` -`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim(),!(!n||t[n]&&Qn[n])&&(n===`set-cookie`?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+`, `+r:r)}),t},er=Symbol(`internals`);function tr(e){return e&&String(e).trim().toLowerCase()}function nr(e){return e===!1||e==null?e:P.isArray(e)?e.map(nr):String(e)}function rr(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var ir=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function ar(e,t,n,r,i){if(P.isFunction(r))return r.call(this,t,n);if(i&&(t=n),P.isString(t)){if(P.isString(r))return t.indexOf(r)!==-1;if(P.isRegExp(r))return r.test(t)}}function or(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function sr(e,t){let n=P.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var cr=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=tr(t);if(!i)throw Error(`header name must be a non-empty string`);let a=P.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=nr(e))}let a=(e,t)=>P.forEach(e,(e,n)=>i(e,n,t));if(P.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(P.isString(e)&&(e=e.trim())&&!ir(e))a($n(e),t);else if(P.isObject(e)&&P.isIterable(e)){let n={},r,i;for(let t of e){if(!P.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);n[i=t[0]]=(r=n[i])?P.isArray(r)?[...r,t[1]]:[r,t[1]]:t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=tr(e),e){let n=P.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return rr(e);if(P.isFunction(t))return t.call(this,e,n);if(P.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=tr(e),e){let n=P.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||ar(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=tr(e),e){let i=P.findKey(n,e);i&&(!t||ar(n,n[i],i,t))&&(delete n[i],r=!0)}}return P.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||ar(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return P.forEach(this,(r,i)=>{let a=P.findKey(n,i);if(a){t[a]=nr(r),delete t[i];return}let o=e?or(i):String(i).trim();o!==i&&delete t[i],t[o]=nr(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return P.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&P.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` -`)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[er]=this[er]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=tr(e);t[r]||(sr(n,e),t[r]=!0)}return P.isArray(e)?e.forEach(r):r(e),this}};cr.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),P.reduceDescriptors(cr.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),P.freezeMethods(cr);function lr(e,t){let n=this||Zn,r=t||n,i=cr.from(r.headers),a=r.data;return P.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function ur(e){return!!(e&&e.__CANCEL__)}var dr=class extends F{constructor(e,t,n){super(e??`canceled`,F.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function fr(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new F(`Request failed with status code `+n.status,[F.ERR_BAD_REQUEST,F.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function pr(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function mr(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var gr=(e,t,n=3)=>{let r=0,i=mr(50,250);return hr(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},_r=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},vr=e=>(...t)=>P.asap(()=>e(...t)),yr=Gn.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Gn.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Gn.origin),Gn.navigator&&/(msie|trident)/i.test(Gn.navigator.userAgent)):()=>!0,br=Gn.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];P.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),P.isString(r)&&s.push(`path=${r}`),P.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),P.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function xr(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function Sr(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function Cr(e,t,n){let r=!xr(t);return e&&(r||n==0)?Sr(e,t):t}var wr=e=>e instanceof cr?{...e}:e;function Tr(e,t){t||={};let n={};function r(e,t,n,r){return P.isPlainObject(e)&&P.isPlainObject(t)?P.merge.call({caseless:r},e,t):P.isPlainObject(t)?P.merge({},t):P.isArray(t)?t.slice():t}function i(e,t,n,i){if(!P.isUndefined(t))return r(e,t,n,i);if(!P.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!P.isUndefined(t))return r(void 0,t)}function o(e,t){if(!P.isUndefined(t))return r(void 0,t);if(!P.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(wr(e),wr(t),n,!0)};return P.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=P.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);P.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Er=e=>{let t=Tr({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=cr.from(o),t.url=Fn(Cr(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),P.isFormData(n)){if(Gn.hasStandardBrowserEnv||Gn.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(P.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(Gn.hasStandardBrowserEnv&&(r&&P.isFunction(r)&&(r=r(t)),r||r!==!1&&yr(t.url))){let e=i&&a&&br.read(a);e&&o.set(i,e)}return t},Dr=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Er(e),i=r.data,a=cr.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=cr.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());fr(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new F(`Request aborted`,F.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new F(t&&t.message?t.message:`Network Error`,F.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Ln;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new F(t,i.clarifyTimeoutError?F.ETIMEDOUT:F.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&P.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),P.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=gr(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=gr(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new dr(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=pr(r.url);if(_&&Gn.protocols.indexOf(_)===-1){n(new F(`Unsupported protocol `+_+`:`,F.ERR_BAD_REQUEST,e));return}h.send(i||null)})},Or=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof F?t:new dr(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new F(`timeout of ${t}ms exceeded`,F.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>P.asap(o),s}},kr=function*(e,t){let n=e.byteLength;if(!t||n{let i=Ar(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Nr=64*1024,{isFunction:Pr}=P,Fr=(({Request:e,Response:t})=>({Request:e,Response:t}))(P.global),{ReadableStream:Ir,TextEncoder:Lr}=P.global,Rr=(e,...t)=>{try{return!!e(...t)}catch{return!1}},zr=e=>{e=P.merge.call({skipUndefined:!0},Fr,e);let{fetch:t,Request:n,Response:r}=e,i=t?Pr(t):typeof fetch==`function`,a=Pr(n),o=Pr(r);if(!i)return!1;let s=i&&Pr(Ir),c=i&&(typeof Lr==`function`?(e=>t=>e.encode(t))(new Lr):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&Rr(()=>{let e=!1,t=new n(Gn.origin,{body:new Ir,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return e&&!t}),u=o&&s&&Rr(()=>P.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new F(`Response type '${e}' is not supported`,F.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(P.isBlob(e))return e.size;if(P.isSpecCompliantForm(e))return(await new n(Gn.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(P.isArrayBufferView(e)||P.isArrayBuffer(e))return e.byteLength;if(P.isURLSearchParams(e)&&(e+=``),P.isString(e))return(await c(e)).byteLength},p=async(e,t)=>P.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Er(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=Or([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),ee;try{if(g&&l&&o!==`get`&&o!==`head`&&(ee=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(P.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=_r(ee,gr(vr(g)));s=Mr(e.body,Nr,t,n)}}P.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=P.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&_r(t,gr(vr(h),!0))||[];f=new r(Mr(f.body,Nr,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let te=await d[P.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{fr(t,n,{data:te,headers:cr.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new F(`Network Error`,F.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):F.from(t,t&&t.code,e,C,t&&t.response)}}},Br=new Map,Vr=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Br;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:zr(t)),l=c;return c};Vr();var Hr={http:null,xhr:Dr,fetch:{get:Vr}};P.forEach(Hr,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Ur=e=>`- ${e}`,Wr=e=>P.isFunction(e)||e===null||e===!1;function Gr(e,t){e=P.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new F(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : -`+e.map(Ur).join(` -`):` `+Ur(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var Kr={getAdapter:Gr,adapters:Hr};function qr(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new dr(null,e)}function Jr(e){return qr(e),e.headers=cr.from(e.headers),e.data=lr.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),Kr.getAdapter(e.adapter||Zn.adapter,e)(e).then(function(t){return qr(e),t.data=lr.call(e,e.transformResponse,t),t.headers=cr.from(t.headers),t},function(t){return ur(t)||(qr(e),t&&t.response&&(t.response.data=lr.call(e,e.transformResponse,t.response),t.response.headers=cr.from(t.response.headers))),Promise.reject(t)})}var Yr=`1.13.6`,Xr={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{Xr[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var Zr={};Xr.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Yr+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new F(r(i,` has been removed`+(t?` in `+t:``)),F.ERR_DEPRECATED);return t&&!Zr[i]&&(Zr[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},Xr.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function Qr(e,t,n){if(typeof e!=`object`)throw new F(`options must be an object`,F.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new F(`option `+a+` must be `+n,F.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new F(`Unknown option `+a,F.ERR_BAD_OPTION)}}var $r={assertOptions:Qr,validators:Xr},I=$r.validators,ei=class{constructor(e){this.defaults=e||{},this.interceptors={request:new In,response:new In}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` -`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Tr(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&$r.assertOptions(n,{silentJSONParsing:I.transitional(I.boolean),forcedJSONParsing:I.transitional(I.boolean),clarifyTimeoutError:I.transitional(I.boolean),legacyInterceptorReqResOrdering:I.transitional(I.boolean)},!1),r!=null&&(P.isFunction(r)?t.paramsSerializer={serialize:r}:$r.assertOptions(r,{encode:I.function,serialize:I.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),$r.assertOptions(t,{baseUrl:I.spelling(`baseURL`),withXsrfToken:I.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&P.merge(i.common,i[t.method]);i&&P.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=cr.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Ln;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[Jr.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new dr(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ni(e){return function(t){return e.apply(null,t)}}function ri(e){return P.isObject(e)&&e.isAxiosError===!0}var ii={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(ii).forEach(([e,t])=>{ii[t]=e});function ai(e){let t=new ei(e),n=pt(ei.prototype.request,t);return P.extend(n,ei.prototype,t,{allOwnKeys:!0}),P.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return ai(Tr(e,t))},n}var L=ai(Zn);L.Axios=ei,L.CanceledError=dr,L.CancelToken=ti,L.isCancel=ur,L.VERSION=Yr,L.toFormData=An,L.AxiosError=F,L.Cancel=L.CanceledError,L.all=function(e){return Promise.all(e)},L.spread=ni,L.isAxiosError=ri,L.mergeConfig=Tr,L.AxiosHeaders=cr,L.formToJSON=e=>Yn(P.isHTMLForm(e)?new FormData(e):e),L.getAdapter=Kr.getAdapter,L.HttpStatusCode=ii,L.default=L;var oi=l(_()),si=`order-demo-001`;function ci(e,t,n,r,i,a){return{eventId:e,aggregateId:si,aggregateType:`ORDER`,sequenceNumber:t,eventType:n,payload:a,metadata:JSON.stringify({source:`demo`,correlationId:`corr-demo-${t}`}),timestamp:r,globalPosition:i}}function li(){let e=[],t=Date.parse(`2025-01-15T08:00:00.000Z`);for(let n=1;n<=100;n++){let r=new Date(t+n*45e3).toISOString(),i=5e4+n,a,o;if(n===1)a=`ORDER_PLACED`,o={customerId:`cust-77`,channel:`web`,status:`PENDING`,totalCents:0,itemCount:0};else if(n>=2&&n<=48){a=`LINE_ITEM_ADDED`;let e=350+n*73%1200;o={sku:`SKU-${String(1e4+n*17).slice(-4)}`,qty:n%4+1,lineTotalCents:e,lineIndex:n-1}}else if(n>=49&&n<=58)a=`PAYMENT_PROGRESS`,o={paymentId:`pay-chunk-${n}`,amountCents:1500+n*120,balanceCents:Math.max(0,48e3-n*700)};else if(n>=59&&n<=72){let e=[`inventory`,`fraud_check`,`address_verify`,`manual_review`,`carrier_delay`];a=`FULFILLMENT_BLOCKED`,o={reason:e[n%e.length],caseId:`CASE-${n}`,retryAfterMinutes:15+n%45}}else n>=73&&n<=88?(a=`SHIPMENT_EVENT`,o={leg:n-72,carrier:n%3==0?`FAST`:n%3==1?`ECONOMY`:`OVERNIGHT`,status:`IN_TRANSIT`,trackingToken:`trk-${n}${(n*7919).toString(36)}`}):n>=89&&n<=99?(a=`NOTE_APPENDED`,o={author:`agent-${n%6+1}`,noteId:`n-${n}`,preview:`Ops note #${n}: SLA watch / customer ping`}):(a=`REFUND_ISSUED`,o={refundCents:88e3,balanceCents:-12500,reason:`bulk_settlement_adjustment`});e.push(ci(`evt-demo-${n}`,n,a,r,i,JSON.stringify(o)))}return e}var ui=li();function di(e){return[{code:`NEGATIVE_BALANCE`,severity:`HIGH`,description:`Ledger balance dropped below zero after refund batch`},{code:`REFUND_EXCEEDS_CAPTURE`,severity:`CRITICAL`,description:`Cumulative refunds exceed captured payments for this aggregate`},{code:`DUPLICATE_PAYMENT_CHUNK`,severity:`MEDIUM`,description:`Two payment chunks share the same window and amount fingerprint`},{code:`LINE_ITEM_PRICE_OUTLIER`,severity:`LOW`,description:`Line total deviates >3σ from cohort for this SKU family`},{code:`FULFILLMENT_STALL`,severity:`HIGH`,description:`Order blocked in fulfillment longer than SLA for channel`},{code:`CARRIER_MISMATCH`,severity:`MEDIUM`,description:`Shipment leg carrier differs from preferred routing profile`},{code:`MANUAL_REVIEW_BACKLOG`,severity:`LOW`,description:`Case reopened multiple times without resolution`},{code:`VELOCITY_SPIKE`,severity:`HIGH`,description:`Event rate on this aggregate exceeded rolling baseline`},{code:`ADDRESS_VERIFY_LOOP`,severity:`MEDIUM`,description:`Address verification failed three times with same payload hash`},{code:`INVENTORY_HOLD`,severity:`MEDIUM`,description:`Inventory hold exceeded expected release window`},{code:`FRAUD_SCORE_EDGE`,severity:`LOW`,description:`Fraud score landed in manual-review gray band`},{code:`DISCOUNT_STACK`,severity:`LOW`,description:`Multiple discount signals present without explicit approval event`},{code:`SHIPMENT_GAP`,severity:`HIGH`,description:`Missing scan between expected hub handoffs`},{code:`NOTE_SPAM`,severity:`LOW`,description:`Unusually high operator notes density in short interval`},{code:`PAYMENT_PARTIAL_CLUSTER`,severity:`MEDIUM`,description:`Several partial captures without closing settlement event`},{code:`SKU_QUANTITY_ANOMALY`,severity:`MEDIUM`,description:`Quantity pattern inconsistent with historical order curve`},{code:`CASE_ESCALATION`,severity:`HIGH`,description:`Support case escalated without prior tier-1 closure`},{code:`TRACKING_TOKEN_REUSE`,severity:`CRITICAL`,description:`Tracking token collision across two concurrent legs`},{code:`SLA_BREACH_RISK`,severity:`HIGH`,description:`Projected delivery crosses committed SLA if delay persists`},{code:`SETTLEMENT_BATCH_DRIFT`,severity:`CRITICAL`,description:`Settlement batch totals diverge from summed payment chunks`}].map((t,n)=>{let r=Math.min(100,5+n*5),i=e.find(e=>e.sequenceNumber===r)??e[e.length-1];return{code:t.code,description:t.description,severity:t.severity,aggregateId:si,atSequence:r,triggeringEventType:i.eventType,timestamp:i.timestamp,stateAtAnomaly:{demoIndex:n+1,atSequence:r,code:t.code}}})}var fi=di(ui);function pi(e,t){let n={...e},r={};try{r=JSON.parse(t.payload||`{}`)}catch{}n._version=t.sequenceNumber,n._lastEventType=t.eventType,n._lastUpdated=t.timestamp;let i=t.eventType.toLowerCase();return i.includes(`created`)||i.includes(`opened`)||i.includes(`placed`)||i.includes(`submitted`)||(i.includes(`deleted`)||i.includes(`closed`)||i.includes(`cancelled`)||i.includes(`rejected`))&&(n.status=`DELETED`),Object.assign(n,r),n}function mi(e,t){let n={};for(let r of Object.keys(t)){let i=e[r],a=t[r];JSON.stringify(i)!==JSON.stringify(a)&&(n[r]={oldValue:i,newValue:a})}for(let r of Object.keys(e))r in t||(n[r]={oldValue:e[r],newValue:void 0});return n}function hi(e){let t=[],n={};for(let r of e){let e={...n};n=pi(n,r);let i={...n};t.push({event:r,stateBefore:e,stateAfter:i,diff:mi(e,i)})}return t}var gi=hi(ui);function _i(e){let t=e.trim().toLowerCase();return t.length<2?!1:t.includes(`demo`)||`order-demo-001`.includes(t)}function vi(e){return _i(e)?[si]:[]}function yi(e){let t=Math.min(Math.max(e,1),500);return[...ui].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,t)}function bi(e,t,n){if(e!==`order-demo-001`)return{events:[],totalEvents:0};let r=ui.length,i=Math.max(0,n),a=Math.min(Math.max(t,1),1e3);return i>=r?{events:[],totalEvents:r}:{events:ui.slice(i,i+a),totalEvents:r}}function xi(e){return e===`order-demo-001`?gi:[]}function Si(e){let t=Math.min(Math.max(e,1),500);return fi.slice(0,t)}function Ci(){return[...ui].sort((e,t)=>t.globalPosition-e.globalPosition).slice(0,40)}function wi(){return{status:`UP`,version:`demo`,demo:!0}}function Ti(e=1,t=24){let n=Math.max(e,1)*60*60*1e3,r=[...ui].sort((e,t)=>Date.parse(e.timestamp)-Date.parse(t.timestamp)),i=new Map,a=new Map,o=new Map;for(let e of r){let t=new Date(Math.floor(Date.parse(e.timestamp)/n)*n).toISOString();i.set(t,(i.get(t)??0)+1),a.set(e.eventType,(a.get(e.eventType)??0)+1),o.set(e.aggregateType,(o.get(e.aggregateType)??0)+1)}return{totalEvents:ui.length,distinctAggregates:1,eventTypes:[...a.entries()].map(([e,t])=>({type:e,count:t})),aggregateTypes:[...o.entries()].map(([e,t])=>({type:e,count:t})),throughput:[...i.entries()].slice(-Math.max(t,1)).map(([e,t])=>({bucket:e,count:t})),available:!0,message:null}}function R(){return!1}var z=L.create({baseURL:`/api`,withCredentials:!0}),Ei=null;z.interceptors.request.use(e=>{let t=(e.method??`get`).toUpperCase();return Ei&&![`GET`,`HEAD`,`OPTIONS`].includes(t)&&(e.headers=e.headers??{},e.headers[`X-CSRF-Token`]=Ei),e});function Di(e){return new Promise(t=>{setTimeout(t,e)})}function Oi(e,t){return t?`${e}${e.includes(`?`)?`&`:`?`}source=${encodeURIComponent(t)}`:e}function ki(e,t){let n=`${e}:${t}`,r=new TextEncoder().encode(n),i=``;return r.forEach(e=>{i+=String.fromCharCode(e)}),btoa(i)}var Ai=async(e,t=20,n)=>{let r=Oi(`/aggregates/search?q=${encodeURIComponent(e)}&limit=${t}`,n);if(R()){await Di(40);let n=vi(e);try{let e=await z.get(r);return[...new Set([...n,...e.data])].slice(0,t)}catch{return n}}return z.get(r).then(e=>e.data)},ji=async(e,t=500,n=0,r,i=`full`)=>{if(R()&&e===`order-demo-001`)return await Di(50),bi(e,t,n);let a=Oi(`/aggregates/${e}/timeline?limit=${t}&offset=${n}&fields=${i}`,r);return z.get(a).then(e=>e.data)},Mi=async(e,t)=>R()&&e===`order-demo-001`?(await Di(50),xi(e)):z.get(Oi(`/aggregates/${e}/transitions`,t)).then(e=>e.data),B=async(e=100,t)=>R()?(await Di(45),Si(e)):z.get(Oi(`/anomalies/recent?limit=${e}`,t)).then(e=>e.data),V=async(e=50,t)=>R()?(await Di(35),yi(e)):z.get(Oi(`/events/recent?limit=${e}`,t)).then(e=>e.data),Ni=async(e,t=1,n=24)=>{if(R())return await Di(30),Ti(t,n);let r=Oi(`/v1/statistics?bucketHours=${t}&maxBuckets=${n}`,e);return z.get(r).then(e=>e.data)},Pi=async()=>R()?(await Di(20),wi()):z.get(`/health`).then(e=>e.data),Fi=async()=>R()?{authenticated:!0,principal:{userId:`demo-user`,displayName:`Demo User`,authMethod:`demo`,roles:[`demo`]}}:z.get(`/v1/auth/session`).then(e=>e.data),Ii=async(e,t,n)=>z.post(`/v1/auth/login/basic`,{returnHash:n},{headers:{Authorization:`Basic ${ki(e,t)}`}}).then(e=>e.data),Li=async()=>R()?{authenticated:!1}:z.post(`/v1/auth/logout`).then(e=>e.data),Ri=e=>`/api/v1/auth/login/oidc?returnHash=${encodeURIComponent(e)}`,zi=e=>{Ei=e},Bi=async()=>R()?(await Di(20),[{id:`demo-primary`,displayName:`Demo Primary`,status:`ready`,healthMessage:`Frontend demo datasource`,capabilities:[`timeline`,`replay`,`statistics`]}]):z.get(`/v1/datasources`).then(e=>e.data),Vi=async()=>R()?(await Di(20),[{instanceId:`demo-source`,typeId:`demo`,displayName:`Demo Source Plugin`,pluginType:`EVENT_SOURCE`,lifecycle:`ready`,health:{state:`up`,message:`Frontend demo plugin`},lastHealthCheck:new Date().toISOString(),failureReason:null}]):z.get(`/v1/plugins`).then(e=>e.data);function Hi(e,t){let[n,r]=(0,j.useState)(e);return(0,j.useEffect)(()=>{let n=setTimeout(()=>r(e),t);return()=>clearTimeout(n)},[e,t]),n}function Ui({onSelect:e,source:t,selectedValue:n}){let[r,i]=(0,j.useState)(``),[a,o]=(0,j.useState)(!1),s=(0,j.useRef)(null),c=Hi(r,300),{data:l=[]}=N({queryKey:[`search`,c,t??`default`],queryFn:()=>Ai(c,20,t),enabled:c.length>=2,staleTime:5e3});(0,j.useEffect)(()=>{i(n??``)},[n]),(0,j.useEffect)(()=>{let e=e=>{s.current&&!s.current.contains(e.target)&&o(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[]);let u=(0,j.useRef)(null),d=(0,j.useCallback)(()=>{u.current?.focus(),u.current?.select()},[]);(0,j.useEffect)(()=>{let e=document.getElementById(`aggregate-search`);return e?.addEventListener(`focus`,d),()=>e?.removeEventListener(`focus`,d)},[d]);let f=t=>{i(t),o(!1),e(t)};return(0,M.jsxs)(`div`,{className:`search-wrapper`,ref:s,children:[(0,M.jsx)(`span`,{className:`search-icon`,children:`??`}),(0,M.jsx)(`input`,{id:`aggregate-search`,ref:u,type:`text`,className:`search-input`,placeholder:`Search by aggregate ID (e.g. UUID or stream key)`,value:r,onChange:e=>{i(e.target.value),o(!0)},onFocus:()=>r.length>=2&&o(!0),onKeyDown:e=>{e.key===`Enter`&&r.trim()&&f(r.trim()),e.key===`Escape`&&o(!1)},autoComplete:`off`,"aria-autocomplete":`list`}),a&&l.length>0&&(0,M.jsx)(`div`,{className:`search-results`,role:`listbox`,id:`aggregate-search-results`,children:l.map(e=>(0,M.jsxs)(`button`,{type:`button`,className:`search-result-item`,onClick:()=>f(e),role:`option`,children:[(0,M.jsx)(`span`,{className:`search-result-chevron`,"aria-hidden":!0,children:`?`}),(0,M.jsxs)(`span`,{className:`search-result-body`,children:[(0,M.jsx)(`span`,{className:`search-result-label`,children:`ID`}),(0,M.jsx)(`span`,{className:`search-result-colon`,children:`:`}),(0,M.jsx)(`span`,{className:`search-result-value`,children:e})]})]},e))})]})}function Wi(e,t){return N({queryKey:[`timeline`,e,t??`default`,`metadata`],queryFn:()=>ji(e,500,0,t,`metadata`),enabled:!!e})}function Gi(e){if(typeof e==`number`)return Number.isNaN(e)?new Date:e<0xe8d4a51000?new Date(e*1e3):new Date(e);let t=String(e).trim();if(!t)return new Date;if(t.includes(`T`)||/^\d{4}-\d{2}-\d{2}/.test(t)){let e=Date.parse(t);if(!Number.isNaN(e))return new Date(e)}let n=parseFloat(t);return Number.isNaN(n)?new Date:n<0xe8d4a51000?new Date(n*1e3):new Date(n)}function Ki(e){let t=[],n=0;for(;n=4)t.push({kind:`group`,eventType:r,items:e.slice(n,i),startIndex:n});else for(let r=n;re.sequenceNumber===n),o=t===e;r.push({kind:`group`,key:`group-${e}`,eventType:i.eventType,items:i.items,startIndex:i.startIndex,expanded:o,containsSelection:a}),o&&r.push(...i.items.map((t,n)=>({kind:`group-item`,key:`group-item-${t.sequenceNumber}`,event:t,stepNumber:i.startIndex+n+1,parentKey:e})))}return r}function Yi(e,t){return e===t?null:t}var Xi=[1,6,24,168];function Zi(e){let t=e.toLowerCase();return t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`created`:t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`deleted`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`completed`:t.includes(`failed`)||t.includes(`error`)||t.includes(`blocked`)?`failed`:t.includes(`transfer`)?`transfer`:`default`}function Qi(e){switch(e.kind){case`single`:case`group-item`:return e.event.sequenceNumber;case`group`:return e.items[0]?.sequenceNumber??0}}function $i(e,t){return`Event ${e}, sequence ${t.sequenceNumber}, ${t.eventType}`}function ea(e,t){return t==null?!1:e.kind===`single`?e.event.sequenceNumber===t:e.items.some(e=>e.sequenceNumber===t)}function ta(e,t,n,r,i){let a=e.event,o=t===a.sequenceNumber,s=n===a.sequenceNumber,c=e.kind===`group-item`?` timeline-step-compact`:``;return(0,M.jsxs)(`button`,{type:`button`,className:`timeline-step timeline-step-${Zi(a.eventType)} ${o?`active`:``} ${s?`timeline-step-compare`:``}${c}`,onClick:e=>{e.shiftKey&&i?i(Yi(n,a.sequenceNumber)):r(a.sequenceNumber)},"aria-current":o?`step`:void 0,"aria-label":$i(e.stepNumber,a),children:[(0,M.jsxs)(`span`,{className:`timeline-step-badge`,children:[`Event `,e.stepNumber]}),(0,M.jsxs)(`span`,{className:`timeline-step-seq`,children:[`seq #`,a.sequenceNumber]}),(0,M.jsx)(`span`,{className:`timeline-step-type`,children:a.eventType}),(0,M.jsx)(`span`,{className:`timeline-step-seq`,children:Gi(a.timestamp).toLocaleTimeString()})]},e.key)}function na({aggregateId:e,selectedSequence:t,compareSequence:n,onSelectEvent:r,onSelectCompare:i,source:a}){let{data:o,isLoading:s}=Wi(e,a),[c,l]=(0,j.useState)(``),[u,d]=(0,j.useState)(``),[f,p]=(0,j.useState)(`all`),[m,h]=(0,j.useState)(null),g=(0,j.useRef)(null),_=o?.events??[],v=o?.totalEvents??0,y=(0,j.useMemo)(()=>{let e=_;if(c&&(e=e.filter(e=>e.eventType===c)),f!==`all`&&e.length>0){let t=Gi(e[e.length-1].timestamp).getTime()-f*60*60*1e3;e=e.filter(e=>Gi(e.timestamp).getTime()>=t)}return e},[_,c,f]),b=(0,j.useMemo)(()=>Ki(y),[y]),x=(0,j.useMemo)(()=>[...new Set(_.map(e=>e.eventType))].sort(),[_]);(0,j.useEffect)(()=>{if(t!=null){for(let e of b)if(e.kind===`group`&&e.items.some(e=>e.sequenceNumber===t)){h(qi(e.startIndex,e.items.length));return}h(null)}},[b,t]);let S=(0,j.useMemo)(()=>Ji(b,m,t),[m,b,t]),C=(0,j.useMemo)(()=>t==null?-1:S.findIndex(e=>e.kind===`group`?e.items.some(e=>e.sequenceNumber===t):e.event.sequenceNumber===t),[S,t]);if((0,j.useEffect)(()=>{let e=e=>{let t=e.detail;if(S.length&&(t<0||t>0)){let e=C>=0?C:0;r(Qi(S[Math.max(0,Math.min(S.length-1,e+t))]))}};return window.addEventListener(`eventlens:timeline-step`,e),()=>window.removeEventListener(`eventlens:timeline-step`,e)},[r,S,C]),(0,j.useEffect)(()=>{let e=e=>{let n=e.target;if(!(n&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(n.tagName)||!S.length)&&(e.key===`ArrowLeft`||e.key===`ArrowRight`)&&e.shiftKey){e.preventDefault();let n=e.key===`ArrowLeft`?-1:1,i=S.filter(e=>e.kind===`group`);if(!i.length)return;let a=i.findIndex(e=>t!=null&&e.items.some(e=>e.sequenceNumber===t));r(i[a>=0?Math.max(0,Math.min(i.length-1,a+n)):n>0?0:i.length-1].items[0].sequenceNumber)}};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[r,S,t]),(0,j.useEffect)(()=>{if(!g.current||t==null)return;let e=g.current.querySelector(`[aria-current="step"]`);if(!e)return;let n=g.current.getBoundingClientRect(),r=e.getBoundingClientRect(),i=g.current.scrollLeft,a=i+(r.left-n.left),o=a+r.width,s=i,c=i+g.current.clientWidth;ac-24&&g.current.scrollTo({left:o-g.current.clientWidth+24,behavior:`smooth`})},[m,t]),s)return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,M.jsx)(`div`,{className:`skeleton`,style:{height:160}})]});if(!_.length)return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Timeline`}),(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`},children:`No events found.`})]});let w=y[0]?.sequenceNumber??0,ee=y[y.length-1]?.sequenceNumber??0;return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsxs)(`div`,{className:`timeline-header-row`,children:[(0,M.jsxs)(`div`,{className:`card-title`,style:{marginBottom:0},children:[`Event Sequence`,(0,M.jsxs)(`span`,{className:`timeline-count-pill`,children:[y.length,` / `,v,` events`]})]}),(0,M.jsx)(`div`,{className:`timeline-jump-group`,children:(0,M.jsx)(`input`,{className:`timeline-jump-input`,type:`number`,placeholder:`Jump to seq`,value:u,onChange:e=>d(e.target.value),onKeyDown:e=>{if(e.key===`Enter`){let e=Number(u);y.some(t=>t.sequenceNumber===e)&&(r(e),d(``))}},"aria-label":`Jump to sequence number`})})]}),(0,M.jsx)(`p`,{className:`timeline-hint`,children:`Scroll the rail horizontally from left to right. Repeated event runs collapse into grouped cards; click a group to open the full strip below.`}),(0,M.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Filter by event type`,children:[(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${c?``:`active`}`,onClick:()=>l(``),children:`All`}),x.map(e=>(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${c===e?`active`:``}`,onClick:()=>l(t=>t===e?``:e),children:e},e))]}),(0,M.jsxs)(`div`,{className:`timeline-filter-chips`,role:`group`,"aria-label":`Zoom range`,children:[(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${f===`all`?`active`:``}`,onClick:()=>p(`all`),children:`All`}),Xi.map(e=>(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${f===e?`active`:``}`,onClick:()=>p(e),children:e>=24?`${e/24}d`:`${e}h`},e))]}),(0,M.jsx)(`div`,{className:`timeline-rail`,children:(0,M.jsx)(`div`,{ref:g,className:`timeline-stepper`,children:(0,M.jsx)(`div`,{className:`timeline-stepper-track`,children:b.map((e,a)=>{let o=ah(e=>e===s?null:s),"aria-expanded":c,children:[(0,M.jsxs)(`span`,{className:`timeline-group-chip-top`,children:[(0,M.jsxs)(`span`,{className:`timeline-group-count`,children:[`x`,e.items.length]}),(0,M.jsx)(`span`,{className:`timeline-group-chevron`,"aria-hidden":!0,children:c?`v`:`>`})]}),(0,M.jsx)(`span`,{className:`timeline-group-type`,children:e.eventType}),(0,M.jsxs)(`span`,{className:`timeline-group-range`,children:[`steps `,e.startIndex+1,`-`,e.startIndex+e.items.length,` seq #`,e.items[0].sequenceNumber,`-#`,e.items[e.items.length-1].sequenceNumber]})]}),o,c&&(0,M.jsxs)(`div`,{className:`timeline-expanded-deck`,children:[(0,M.jsxs)(`div`,{className:`timeline-expanded-head`,children:[(0,M.jsx)(`div`,{className:`timeline-expanded-title`,children:e.eventType}),(0,M.jsxs)(`div`,{className:`timeline-group-range`,children:[`Expanded run of `,e.items.length,` events`]}),(0,M.jsx)(`button`,{type:`button`,className:`timeline-collapse-btn`,onClick:()=>h(null),children:`Collapse`})]}),(0,M.jsx)(`div`,{className:`timeline-stepper-track`,children:e.items.map((a,o)=>{let c={kind:`group-item`,key:`group-item-${a.sequenceNumber}`,event:a,stepNumber:e.startIndex+o+1,parentKey:s};return(0,M.jsxs)(`div`,{className:`timeline-track-item`,children:[ta(c,t,n,r,i),or(Number(e.target.value)),"aria-label":`Scrub event sequence`}),(0,M.jsxs)(`div`,{className:`timeline-info`,children:[(0,M.jsxs)(`span`,{className:`timeline-info-edge`,children:[`First seq #`,w]}),(0,M.jsx)(`span`,{className:`timeline-info-center`,children:t==null?`Select an event`:(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`strong`,{children:[`Selected seq #`,t]}),n!=null&&` compared with #${n}`,(0,M.jsxs)(`span`,{className:`timeline-info-muted`,children:[` across `,b.length,` visible segments`]})]})}),(0,M.jsxs)(`span`,{className:`timeline-info-edge`,children:[`Last seq #`,ee]})]})]})}function ra(e,t,n=!0){return N({queryKey:[`transitions`,e,t??`default`],queryFn:()=>Mi(e,t),enabled:n&&!!e})}function ia(e,t,n=!0){let[r,i]=(0,j.useState)([]),[a,o]=(0,j.useState)(0),[s,c]=(0,j.useState)(!1),l=(0,j.useMemo)(()=>`${Date.now()}-${Math.random()}`,[e,t]);return(0,j.useEffect)(()=>{if(!n){i([]),o(0),c(!1);return}let r=new Worker(new URL(`/assets/json-diff.worker-DTKMBXSt.js`,``+import.meta.url),{type:`module`});c(!0);let a=e=>{e.data.requestId===l&&(i(e.data.patches),o(e.data.durationMs),c(!1))};return r.addEventListener(`message`,a),r.postMessage({left:e,right:t,requestId:l}),()=>{r.removeEventListener(`message`,a),r.terminate()}},[n,e,t,l]),{patches:r,durationMs:a,loading:s}}function aa({diff:e,patches:t,title:n=`Changes`}){let r=t&&t.length>0?t.map(e=>({field:e.path,oldValue:e.oldValue,newValue:e.newValue,kind:e.type})):Object.entries(e??{}).map(([e,t])=>({field:e,oldValue:t.oldValue,newValue:t.newValue,kind:`changed`}));return r.length?(0,M.jsxs)(`div`,{className:`diff-panel`,children:[(0,M.jsx)(`div`,{className:`diff-toolbar`,children:(0,M.jsxs)(`div`,{className:`diff-toolbar-title`,children:[n,(0,M.jsxs)(`span`,{className:`diff-count-badge`,"aria-live":`polite`,children:[r.length,` `,r.length===1?`change`:`changes`]})]})}),(0,M.jsx)(`div`,{className:`diff-body`,children:(0,M.jsx)(`div`,{className:`diff-scroll`,children:(0,M.jsxs)(`div`,{className:`diff-list diff-list-split`,children:[(0,M.jsxs)(`div`,{className:`diff-split-head`,children:[(0,M.jsx)(`span`,{className:`diff-split-label diff-split-old-label`,children:`Before`}),(0,M.jsx)(`span`,{className:`diff-split-label diff-split-new-label`,children:`After`})]}),r.map((e,t)=>(0,M.jsxs)(`div`,{className:`diff-split-row diff-split-row--${e.kind}`,children:[(0,M.jsx)(`span`,{className:`diff-line-no`,"aria-hidden":!0,children:t+1}),(0,M.jsxs)(`div`,{className:`diff-split-cells`,children:[(0,M.jsxs)(`div`,{className:`diff-split-cell diff-split-old`,children:[(0,M.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,M.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.oldValue)})]}),(0,M.jsxs)(`div`,{className:`diff-split-cell diff-split-new`,children:[(0,M.jsx)(`span`,{className:`diff-field`,children:e.field}),(0,M.jsx)(`span`,{className:`diff-cell-value`,children:JSON.stringify(e.newValue)})]})]})]},`${e.field}-${t}`))]})})})]}):null}function oa({open:e,onToggle:t}){return(0,M.jsx)(`button`,{type:`button`,className:`json-tree-toggle`,onClick:e=>{e.stopPropagation(),t()},"aria-expanded":e,"aria-label":e?`Collapse`:`Expand`,children:e?`-`:`+`})}function sa({value:e}){return e===null?(0,M.jsx)(`span`,{className:`json-null`,children:`null`}):typeof e==`boolean`?(0,M.jsx)(`span`,{className:`json-boolean`,children:String(e)}):typeof e==`number`?(0,M.jsx)(`span`,{className:`json-number`,children:e}):(0,M.jsx)(`span`,{className:`json-string`,children:JSON.stringify(e)})}function ca({value:e,changedKeys:t}){return(0,M.jsx)(`div`,{className:`json-tree json-tree-root`,children:(0,M.jsx)(la,{value:e,depth:0,changedKeys:t,keyPath:``})})}function la({value:e,depth:t,propertyKey:n,changedKeys:r,keyPath:i=``}){let a=r&&n!==void 0&&r.has(n),o=r&&i&&[...r].some(e=>e.startsWith(i+`.`)),[s,c]=(0,j.useState)(r?t<3||!!a||!!o:t<3),l={paddingLeft:Math.min(t,12)*14},u=a?{background:`rgba(255, 170, 0, 0.12)`,borderRadius:3}:{};if(e===null||typeof e==`boolean`||typeof e==`number`||typeof e==`string`)return(0,M.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(sa,{value:e})]});let d=e=>i?`${i}.${e}`:e;if(Array.isArray(e))return e.length===0?(0,M.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`[]`})]}):(0,M.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,M.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,M.jsx)(oa,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`[`}),!s&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,e.length,` items `]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`]`})]})]}),s&&(0,M.jsxs)(M.Fragment,{children:[e.map((e,n)=>(0,M.jsx)(la,{value:e,depth:t+1,changedKeys:r,keyPath:d(String(n))},n)),(0,M.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,M.jsx)(`span`,{className:`json-punct`,children:`]`})})]})]});if(typeof e==`object`){let i=Object.entries(e);return i.length===0?(0,M.jsxs)(`div`,{className:`json-tree-line`,style:l,children:[n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`{}`})]}):(0,M.jsxs)(`div`,{className:`json-tree-branch`,children:[(0,M.jsxs)(`div`,{className:`json-tree-line${a?` json-tree-changed`:``}`,style:{...l,...u},children:[(0,M.jsx)(oa,{open:s,onToggle:()=>c(e=>!e)}),n!==void 0&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-key`,children:[`"`,n,`"`]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`: `})]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`{`}),!s&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`span`,{className:`json-ellipsis`,children:[` `,i.length,` keys `]}),(0,M.jsx)(`span`,{className:`json-punct`,children:`}`})]})]}),s&&(0,M.jsxs)(M.Fragment,{children:[i.map(([e,n])=>(0,M.jsx)(la,{value:n,depth:t+1,propertyKey:e,changedKeys:r,keyPath:d(e)},e)),(0,M.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,M.jsx)(`span`,{className:`json-punct`,children:`}`})})]})]})}return(0,M.jsx)(`div`,{className:`json-tree-line`,style:l,children:(0,M.jsx)(`span`,{className:`json-unknown`,children:String(e)})})}var ua=[{id:`changes`,label:`Changes`},{id:`before-after`,label:`Before / After`},{id:`raw`,label:`Raw JSON`}];function da({aggregateId:e,sequence:t,compareSequence:n,activeTab:r,onTabChange:i,active:a=!1,onActivate:o,source:s}){let{data:c=[],isLoading:l}=ra(e,s),[u,d]=(0,j.useState)(`changes`),f=r??u,p=e=>{d(e),i?.(e)},m=c.find(e=>e.event.sequenceNumber===t)??null,h=n==null?null:c.find(e=>e.event.sequenceNumber===n)??null,g=m!=null&&h!=null&&m.event.sequenceNumber!==h.event.sequenceNumber,_=g?h.stateAfter:m?.stateBefore,v=m?.stateAfter,{patches:y,loading:b,durationMs:x}=ia(_,v,g&&!!_&&!!v),S=(0,j.useMemo)(()=>g?new Set(y.map(e=>e.path.replace(/^\$\./,``).split(`.`)[0])):new Set(Object.keys(m?.diff??{})),[g,y,m?.diff]);return l?(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`State`}),(0,M.jsx)(`div`,{className:`skeleton`,style:{height:160}})]}):m?(0,M.jsxs)(`section`,{className:`card`,role:`region`,"aria-label":`State viewer`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,M.jsxs)(`div`,{className:`card-title`,children:[`State at Event #`,m.event.sequenceNumber,(0,M.jsx)(`span`,{className:`diff-count-badge`,children:m.event.eventType}),g&&(0,M.jsxs)(`span`,{className:`diff-count-badge`,children:[`Compared with #`,h?.event.sequenceNumber]}),g&&!b&&(0,M.jsxs)(`span`,{className:`diff-count-badge`,children:[`Worker `,x.toFixed(1),`ms`]})]}),(0,M.jsx)(`div`,{className:`state-tabs`,role:`tablist`,children:ua.map(e=>(0,M.jsx)(`button`,{type:`button`,role:`tab`,"aria-selected":f===e.id,className:`state-tab ${f===e.id?`active`:``}`,onClick:()=>p(e.id),children:e.label},e.id))}),(0,M.jsxs)(`div`,{className:`state-tab-content`,role:`tabpanel`,children:[f===`changes`&&(0,M.jsx)(`div`,{children:g?b?(0,M.jsx)(`div`,{className:`skeleton`,style:{height:120}}):(0,M.jsx)(aa,{patches:y,title:`Structural diff`}):m.diff&&Object.keys(m.diff).length>0?(0,M.jsx)(aa,{diff:m.diff}):(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`,marginTop:12,fontSize:13},children:`No field changes at this event.`})}),f===`before-after`&&(0,M.jsxs)(`div`,{className:`state-grid`,style:{marginTop:12},children:[(0,M.jsxs)(`div`,{className:`state-panel state-panel-before`,children:[(0,M.jsx)(`h4`,{children:g?`Event #${h?.event.sequenceNumber}`:`Before`}),(0,M.jsx)(ca,{value:_??{},changedKeys:S})]}),(0,M.jsxs)(`div`,{className:`state-panel state-panel-after`,children:[(0,M.jsx)(`h4`,{children:g?`Event #${m.event.sequenceNumber}`:`After`}),(0,M.jsx)(ca,{value:v??{},changedKeys:S})]})]}),f===`raw`&&(0,M.jsx)(`div`,{style:{marginTop:12},children:(0,M.jsx)(`div`,{className:`json-block`,style:{maxHeight:340},children:JSON.stringify(g?{leftEvent:h?.event,rightEvent:m.event,leftState:_,rightState:v}:m.event,null,2)})})]})]}):null}var fa=1e3,pa=3e4;function ma(e,t,n){let r=n?.enabled??!0,[i,a]=(0,j.useState)(()=>r?`connecting`:`connected`),o=(0,j.useRef)(null),s=(0,j.useRef)(t),c=(0,j.useRef)(fa);return s.current=t,(0,j.useEffect)(()=>{if(!r)return a(`connected`),()=>{};let t=!1,n,i=()=>{if(t)return;let r=new WebSocket(`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.host}${e}`);o.current=r,r.onopen=()=>{c.current=fa,a(`connected`)},r.onclose=()=>{if(a(`disconnected`),!t){let e=c.current;n=setTimeout(()=>{c.current=Math.min(e*2,pa),i()},e)}},r.onerror=()=>a(`disconnected`),r.onmessage=e=>{try{let t=JSON.parse(e.data);s.current(t)}catch{}}};return i(),()=>{t=!0,clearTimeout(n),o.current?.close()}},[e,r]),i}var ha=(0,j.createContext)(void 0);function ga({children:e}){let[t,n]=(0,j.useState)([]),r=(0,j.useCallback)(e=>{let t=Date.now();n(n=>[...n,{id:t,message:e}]),setTimeout(()=>{n(e=>e.filter(e=>e.id!==t))},4e3)},[]);return(0,M.jsxs)(ha.Provider,{value:{notify:r},children:[e,(0,M.jsx)(`div`,{className:`toast-container`,children:t.map(e=>(0,M.jsx)(`div`,{className:`toast`,children:e.message},e.id))})]})}function _a(){let e=(0,j.useContext)(ha);if(!e)throw Error(`useToast must be used within ToastProvider`);return e}function va(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`type-deleted`:t.includes(`withdrawn`)||t.includes(`debit`)?`type-withdrawn`:t.includes(`deposited`)||t.includes(`credit`)?`type-deposited`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`type-created`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)||t.includes(`assigned`)?`type-completed`:t.includes(`failed`)||t.includes(`error`)?`type-failed`:t.includes(`transfer`)?`type-transfer`:`type-default`}function ya(e){return va(e)}var ba=100;function xa(e){let t=e.toLowerCase();return t.includes(`deleted`)||t.includes(`closed`)||t.includes(`cancelled`)||t.includes(`rejected`)?`✖`:t.includes(`withdrawn`)||t.includes(`debit`)?`↩`:t.includes(`deposited`)||t.includes(`credit`)?`↪`:t.includes(`created`)||t.includes(`opened`)||t.includes(`placed`)||t.includes(`submitted`)?`✦`:t.includes(`completed`)||t.includes(`resolved`)||t.includes(`accepted`)||t.includes(`approved`)?`✔`:t.includes(`failed`)||t.includes(`error`)?`⚠`:t.includes(`transfer`)?`⇄`:`◆`}function Sa({source:e,onSelectAggregate:t}){return(0,M.jsx)(Ta,{source:e,onSelectAggregate:t})}function Ca(e){return`type`in e&&e.type===`NO_LIVE_STREAM`}function wa(e){return e?`/ws/live?source=${encodeURIComponent(e)}`:`/ws/live`}function Ta({source:e,onSelectAggregate:t}){let n=R(),[r,i]=(0,j.useState)(()=>n?Ci():[]),[a,o]=(0,j.useState)(!1),[s,c]=(0,j.useState)(null),l=(0,j.useRef)(null),u=(0,j.useRef)(a);u.current=a;let{notify:d}=_a();(0,j.useEffect)(()=>{c(null),i(n?Ci():[])},[e,n]);let f=ma(wa(e),e=>{if(Ca(e)){c(e.source),i([]);return}c(null),!u.current&&i(t=>[...t.slice(-(ba-1)),e])},{enabled:!n}),p=(0,j.useRef)(d);p.current=d;let m=(0,j.useRef)(0);return(0,j.useEffect)(()=>{n||(f===`disconnected`?(m.current++,m.current<=1&&p.current(`Live stream disconnected. Retrying…`)):f===`connected`&&(m.current=0))},[f,n]),(0,j.useEffect)(()=>{!a&&l.current&&(l.current.scrollTop=l.current.scrollHeight)},[r,a]),(0,j.useEffect)(()=>{let e=()=>o(e=>!e);return window.addEventListener(`eventlens:togglestream`,e),()=>window.removeEventListener(`eventlens:togglestream`,e)},[]),(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsxs)(`div`,{className:`live-header`,children:[(0,M.jsx)(`div`,{className:`card-title`,style:{marginBottom:0},children:`📡 Live Event Stream`}),(0,M.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,M.jsxs)(`div`,{className:`live-indicator`,children:[(0,M.jsx)(`span`,{className:`dot ${f===`connected`?`dot-green`:f===`connecting`?`dot-yellow`:`dot-red`}`}),(0,M.jsx)(`span`,{style:{color:f===`connected`?`var(--neon-green)`:`var(--text-muted)`,fontSize:11},children:f===`connected`?a?`Paused`:`Live`:f})]}),(0,M.jsx)(`button`,{className:`pause-btn`,onClick:()=>o(!a),children:a?`▶ Resume`:`⏸ Pause`})]})]}),(0,M.jsxs)(`div`,{className:`event-stream`,ref:l,children:[s&&(0,M.jsxs)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:[`Live stream not available for this source`,e?` (${e})`:s?` (${s})`:``,`.`]}),r.length===0&&(0,M.jsx)(`div`,{style:{color:`var(--text-muted)`,padding:`20px 0`,fontSize:12,fontFamily:`var(--font-mono)`},children:s?null:n?`Demo stream (static sample events)`:`Waiting for events…`}),r.map(e=>(0,M.jsxs)(`div`,{className:`event-row ${ya(e.eventType)}`,children:[(0,M.jsx)(`span`,{className:`event-icon`,children:xa(e.eventType)}),(0,M.jsx)(`span`,{className:`event-time`,children:Gi(e.timestamp).toLocaleTimeString()}),(0,M.jsx)(`span`,{className:`event-type ${va(e.eventType)}`,children:e.eventType}),t?(0,M.jsx)(`button`,{type:`button`,className:`event-agg event-agg-button`,onClick:()=>t(e.aggregateId),children:e.aggregateId}):(0,M.jsx)(`span`,{className:`event-agg`,children:e.aggregateId})]},e.eventId))]})]})}function Ea(e){switch(e){case`CRITICAL`:return`sev-critical`;case`HIGH`:return`sev-high`;case`MEDIUM`:return`sev-medium`;case`LOW`:return`sev-low`;default:return`sev-low`}}function Da(e){switch(e){case`CRITICAL`:return`Critical`;case`HIGH`:return`High`;case`MEDIUM`:return`Warning`;case`LOW`:return`Info`;default:return e}}function Oa(){return(0,M.jsxs)(`svg`,{viewBox:`0 0 64 64`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,M.jsx)(`defs`,{children:(0,M.jsxs)(`linearGradient`,{id:`shield-grad`,x1:`16`,y1:`8`,x2:`48`,y2:`56`,children:[(0,M.jsx)(`stop`,{offset:`0%`,stopColor:`#00ff88`,stopOpacity:`0.9`}),(0,M.jsx)(`stop`,{offset:`100%`,stopColor:`#00cc66`,stopOpacity:`0.6`})]})}),(0,M.jsx)(`path`,{d:`M32 4 L52 14 L52 32 C52 46 32 58 32 58 C32 58 12 46 12 32 L12 14 Z`,stroke:`url(#shield-grad)`,strokeWidth:`2`,fill:`rgba(0, 255, 136, 0.06)`}),(0,M.jsx)(`path`,{d:`M32 10 L48 18 L48 32 C48 43 32 53 32 53 C32 53 16 43 16 32 L16 18 Z`,stroke:`rgba(0, 255, 136, 0.2)`,strokeWidth:`1`,fill:`none`}),(0,M.jsx)(`polyline`,{points:`22,32 29,40 42,24`,stroke:`#00ff88`,strokeWidth:`3`,strokeLinecap:`round`,strokeLinejoin:`round`,fill:`none`})]})}function ka({color:e}){return(0,M.jsx)(`div`,{className:`gauge-wave`,children:Array.from({length:12},(e,t)=>t).map(t=>(0,M.jsx)(`div`,{className:`gauge-wave-bar ${e}`,style:{animationDelay:`${t*.12}s`}},t))})}function Aa({source:e,onSelectAggregate:t}){let{data:n,isLoading:r}=N({queryKey:[`anomalies`,e??`default`],queryFn:()=>B(100,e),refetchInterval:3e4}),i=n&&n.length>0;return(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsxs)(`div`,{className:`card-title anomaly-card-title-row`,children:[(0,M.jsx)(`span`,{className:`anomaly-title-text`,children:`⚠️ Anomaly Detection`}),!r&&i&&(0,M.jsx)(`span`,{className:`anomaly-header-count`,"aria-label":`${n.length} anomalies`,children:n.length})]}),r&&(0,M.jsx)(`div`,{className:`skeleton`,style:{height:120}}),!r&&!i&&(0,M.jsxs)(`div`,{className:`anomaly-panel-inner`,children:[(0,M.jsxs)(`div`,{className:`anomaly-shield`,children:[(0,M.jsx)(`div`,{className:`shield-icon`,children:(0,M.jsx)(Oa,{})}),(0,M.jsx)(`div`,{className:`shield-text`,children:`No anomalies detected`})]}),(0,M.jsxs)(`div`,{className:`gauge-row`,children:[(0,M.jsxs)(`div`,{className:`gauge-card optimal`,children:[(0,M.jsx)(`div`,{className:`gauge-label`,children:`Data Integrity`}),(0,M.jsx)(`div`,{className:`gauge-value optimal`,children:`OPTIMAL`}),(0,M.jsx)(ka,{color:`green`})]}),(0,M.jsxs)(`div`,{className:`gauge-card baseline`,children:[(0,M.jsx)(`div`,{className:`gauge-label`,children:`Pattern Scan`}),(0,M.jsx)(`div`,{className:`gauge-value baseline`,children:`BASELINE`}),(0,M.jsx)(ka,{color:`cyan`})]}),(0,M.jsxs)(`div`,{className:`gauge-card zero`,children:[(0,M.jsx)(`div`,{className:`gauge-label`,children:`Threat Level`}),(0,M.jsx)(`div`,{className:`gauge-value zero`,children:`ZERO`}),(0,M.jsx)(ka,{color:`green`})]})]})]}),!r&&i&&(0,M.jsx)(`div`,{className:`anomaly-scroll-region`,children:(0,M.jsx)(`div`,{className:`anomaly-list-inner`,children:n.map((e,n)=>(0,M.jsxs)(`details`,{className:`anomaly-card ${e.severity}`,children:[(0,M.jsxs)(`summary`,{className:`anomaly-card-summary`,children:[(0,M.jsx)(`span`,{className:`anomaly-severity-badge ${Ea(e.severity)}`,children:Da(e.severity)}),(0,M.jsx)(`span`,{className:`anomaly-card-title`,children:e.description}),(0,M.jsx)(`span`,{className:`anomaly-card-chevron`,"aria-hidden":!0,children:`▼`})]}),(0,M.jsxs)(`div`,{className:`anomaly-card-body`,children:[(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Aggregate`}),t?(0,M.jsx)(`button`,{type:`button`,className:`anomaly-aggregate-link`,onClick:()=>t(e.aggregateId),children:(0,M.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})}):(0,M.jsx)(`code`,{className:`anomaly-meta-value`,children:e.aggregateId})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Sequence`}),(0,M.jsxs)(`span`,{className:`anomaly-meta-value`,children:[`#`,e.atSequence]})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Event type`}),(0,M.jsx)(`span`,{className:`anomaly-meta-value`,children:e.triggeringEventType})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`When`}),(0,M.jsx)(`span`,{className:`anomaly-meta-value`,children:Gi(e.timestamp).toLocaleString()})]}),(0,M.jsxs)(`p`,{className:`anomaly-card-meta`,children:[(0,M.jsx)(`span`,{className:`anomaly-meta-label`,children:`Code`}),(0,M.jsx)(`code`,{className:`anomaly-meta-value`,children:e.code})]})]})]},`${e.aggregateId}-${e.atSequence}-${n}`))})})]})}var ja=[{keys:`j / k`,desc:`Move selection through the timeline`},{keys:`Shift+Click`,desc:`Pick a compare event for diff mode`},{keys:`1 - 3`,desc:`Switch state tabs`},{keys:`Ctrl/Cmd+K`,desc:`Open command palette`},{keys:`/`,desc:`Focus aggregate search`},{keys:`Space`,desc:`Pause or resume live stream`},{keys:`?`,desc:`Toggle shortcut help`}];function Ma(){let[e,t]=(0,j.useState)(!1);return(0,j.useEffect)(()=>{let e=e=>{e.target.tagName!==`INPUT`&&e.key===`?`&&(e.preventDefault(),t(e=>!e))};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]),(0,M.jsx)(`div`,{className:`keyboard-hints ${e?`keyboard-hints--expanded`:``}`,"aria-label":`Keyboard shortcuts`,children:e?(0,M.jsxs)(`div`,{className:`keyboard-hints-grid`,children:[ja.map(e=>(0,M.jsxs)(`div`,{className:`keyboard-hint-row`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key`,children:e.keys}),(0,M.jsx)(`span`,{className:`keyboard-hint-desc`,children:e.desc})]},e.keys)),(0,M.jsx)(`button`,{type:`button`,className:`keyboard-hints-close`,onClick:()=>t(!1),"aria-label":`Close shortcuts`,children:`Close`})]}):(0,M.jsxs)(`div`,{className:`keyboard-hints-bar`,children:[(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`j/k`}),` Navigate`]}),(0,M.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Shift+Click`}),` Compare`]}),(0,M.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`Ctrl/Cmd+K`}),` Palette`]}),(0,M.jsx)(`span`,{className:`keyboard-hints-sep`,children:`·`}),(0,M.jsxs)(`span`,{className:`keyboard-hints-item`,children:[(0,M.jsx)(`kbd`,{className:`keyboard-key-mini`,children:`?`}),` All shortcuts`]})]})})}var Na=[2,3,5,10];function Pa({transitions:e,selectedSequence:t,compareSequence:n,onSelectSequence:r,onClearCompare:i,active:a=!1,onActivate:o}){let[s,c]=(0,j.useState)(null),[l,u]=(0,j.useState)(3),[d,f]=(0,j.useState)(!1);if((0,j.useEffect)(()=>{n==null&&s!=null&&(s.type===`autoplay`&&f(!0),r(s.sequence),c(null))},[n,r,s]),!e.length)return null;let p=e.find(e=>e.event.sequenceNumber===t)??e[0],m=e.findIndex(e=>e.event.sequenceNumber===p.event.sequenceNumber),h=n!=null&&n!==p.event.sequenceNumber,g=m>=e.length-1,_=m<=0;(0,j.useEffect)(()=>{h&&d&&f(!1)},[h,d]),(0,j.useEffect)(()=>{if(!d||g){g&&d&&f(!1);return}let t=window.setInterval(()=>{let t=e[Math.min(m+1,e.length-1)];if(!t||t.event.sequenceNumber===p.event.sequenceNumber){f(!1);return}r(t.event.sequenceNumber)},l*1e3);return()=>window.clearInterval(t)},[g,l,p.event.sequenceNumber,m,d,r,e]);let v=e=>{if(e!==p.event.sequenceNumber){if(h&&i){c({type:`select`,sequence:e});return}f(!1),r(e)}},y=()=>{if(d){f(!1);return}if(g)return;let t=e[Math.min(m+1,e.length-1)].event.sequenceNumber;if(h&&i){c({type:`autoplay`,sequence:t});return}f(!0)};return(0,M.jsxs)(`section`,{className:`card replay-debugger`,tabIndex:0,role:`region`,"aria-label":`Replay debugger`,"aria-current":a?`page`:void 0,onFocus:o,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Replay Debugger`}),(0,M.jsxs)(`div`,{className:`replay-toolbar`,children:[(0,M.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.max(m-1,0)].event.sequenceNumber),title:`Step backward`,"aria-label":`Replay previous event`,disabled:_,children:`Previous`}),(0,M.jsxs)(`div`,{className:`replay-controls-center`,children:[(0,M.jsxs)(`div`,{className:`replay-position`,children:[`Event `,m+1,` of `,e.length]}),(0,M.jsxs)(`div`,{className:`replay-auto-controls`,children:[(0,M.jsx)(`button`,{type:`button`,className:`replay-auto-toggle ${d?`active`:``}`,onClick:y,disabled:g,"aria-pressed":d,children:d?`Pause Auto Replay`:`Start Auto Replay`}),(0,M.jsxs)(`label`,{className:`replay-interval-picker`,children:[(0,M.jsx)(`span`,{children:`Speed`}),(0,M.jsx)(`select`,{value:l,onChange:e=>u(Number(e.target.value)),disabled:d,"aria-label":`Auto replay interval`,children:Na.map(e=>(0,M.jsxs)(`option`,{value:e,children:[e,`s / event`]},e))})]})]})]}),(0,M.jsx)(`button`,{type:`button`,onClick:()=>v(e[Math.min(m+1,e.length-1)].event.sequenceNumber),title:`Step forward`,"aria-label":`Replay next event`,disabled:g,children:`Next`})]}),h&&(0,M.jsxs)(`div`,{className:`replay-compare-warning`,role:`alert`,"aria-live":`polite`,children:[(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`strong`,{children:`Compare mode is active.`}),` Replay stepping or auto replay will replace the comparison and continue from the selected event.`]}),s==null?(0,M.jsx)(`div`,{className:`replay-warning-hint`,children:`Use Previous, Next, the slider, or auto replay. We’ll ask before clearing compare mode.`}):(0,M.jsxs)(`div`,{className:`replay-compare-actions`,children:[(0,M.jsx)(`button`,{type:`button`,className:`replay-warning-btn replay-warning-btn--primary`,onClick:()=>i?.(),children:s.type===`autoplay`?`Start replay and replace compare`:`Continue and replace compare`}),(0,M.jsx)(`button`,{type:`button`,className:`replay-warning-btn`,onClick:()=>c(null),children:`Keep compare`})]})]}),(0,M.jsx)(`input`,{type:`range`,min:0,max:Math.max(e.length-1,0),value:m,onChange:t=>v(e[Number(t.target.value)].event.sequenceNumber),"aria-label":`Replay position`}),(0,M.jsxs)(`div`,{className:`state-panel replay-state`,children:[(0,M.jsx)(`h4`,{children:`Current State`}),(0,M.jsx)(ca,{value:p.stateAfter,changedKeys:new Set(Object.keys(p.diff))})]})]})}function Fa(e,t=1,n=24){return N({queryKey:[`statistics`,e??`default`,t,n],queryFn:()=>Ni(e,t,n),staleTime:3e4})}function Ia({source:e,onBack:t}){let[n,r]=(0,j.useState)(24),{data:i,isLoading:a}=Fa(e,1,n);return(0,M.jsxs)(`section`,{className:`card statistics-panel`,role:`region`,"aria-label":`Statistics panel`,"aria-busy":a,children:[(0,M.jsxs)(`div`,{className:`statistics-panel-header`,children:[(0,M.jsxs)(`div`,{className:`card-title`,children:[`Statistics `,e?`- ${e}`:``]}),(0,M.jsx)(`button`,{type:`button`,className:`statistics-back-btn`,onClick:t,children:`Back to workspace`})]}),(0,M.jsx)(`div`,{className:`statistics-toolbar`,children:[6,24,72].map(e=>(0,M.jsx)(`button`,{type:`button`,className:`filter-chip ${n===e?`active`:``}`,onClick:()=>r(e),children:e===72?`3d`:`${e}h`},e))}),a&&(0,M.jsx)(`div`,{className:`skeleton`,style:{height:140}}),!a&&i&&!i.available&&(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`},children:i.message??`Statistics not available.`}),!a&&i?.available&&(0,M.jsxs)(M.Fragment,{children:[(0,M.jsxs)(`div`,{className:`stats-kpis`,children:[(0,M.jsxs)(`div`,{className:`stat-card`,children:[(0,M.jsx)(`strong`,{children:i.totalEvents}),(0,M.jsx)(`span`,{children:`Total events`})]}),(0,M.jsxs)(`div`,{className:`stat-card`,children:[(0,M.jsx)(`strong`,{children:i.distinctAggregates}),(0,M.jsx)(`span`,{children:`Aggregates`})]}),(0,M.jsxs)(`div`,{className:`stat-card`,children:[(0,M.jsx)(`strong`,{children:i.eventTypes.length}),(0,M.jsx)(`span`,{children:`Event types`})]})]}),(0,M.jsx)(`div`,{className:`stats-chart`,children:i.throughput.map(e=>(0,M.jsxs)(`div`,{className:`stats-bar-row`,children:[(0,M.jsx)(`span`,{children:e.bucket.slice(11,16)}),(0,M.jsx)(`div`,{className:`stats-bar-track`,children:(0,M.jsx)(`div`,{className:`stats-bar-fill`,style:{width:`${Math.max(8,e.count/Math.max(...i.throughput.map(e=>e.count),1)*100)}%`}})}),(0,M.jsx)(`strong`,{children:e.count})]},e.bucket))}),(0,M.jsxs)(`div`,{className:`stats-distribution`,children:[(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`h4`,{children:`Event Types`}),i.eventTypes.map(e=>(0,M.jsxs)(`div`,{className:`stats-list-row`,children:[(0,M.jsx)(`span`,{children:e.type}),(0,M.jsx)(`strong`,{children:e.count})]},e.type))]}),(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`h4`,{children:`Aggregate Types`}),i.aggregateTypes.map(e=>(0,M.jsxs)(`div`,{className:`stats-list-row`,children:[(0,M.jsx)(`span`,{children:e.type}),(0,M.jsx)(`strong`,{children:e.count})]},e.type))]})]})]})]})}function La({open:e,selectedSource:t,onClose:n,onSelectAggregate:r,onOpenHome:i,onOpenStats:a}){let[o,s]=(0,j.useState)(``),[c,l]=(0,j.useState)(0),[u,d]=(0,j.useState)(null),{data:f=[]}=N({queryKey:[`palette-search`,o,t??`default`],queryFn:()=>Ai(o,8,t),enabled:e&&o.trim().length>=2,staleTime:5e3});(0,j.useEffect)(()=>{e?(l(0),d(document.activeElement instanceof HTMLElement?document.activeElement:null)):(s(``),u?.focus())},[e,u]);let p=(0,j.useMemo)(()=>{let e=[{id:`home`,label:`Go to main page`,action:i},{id:`stats`,label:`Go to statistics panel`,action:a}],t=f.map(e=>({id:`agg-${e}`,label:`Open aggregate ${e}`,action:()=>r(e)}));return o.trim()&&f.length===0&&e.unshift({id:`direct`,label:`Open aggregate ${o.trim()}`,action:()=>r(o.trim())}),[...t,...e]},[f,i,a,r,o]);return(0,j.useEffect)(()=>{if(!e)return;let t=e=>{e.key===`ArrowDown`?(e.preventDefault(),l(e=>Math.min(e+1,Math.max(p.length-1,0)))):e.key===`ArrowUp`?(e.preventDefault(),l(e=>Math.max(e-1,0))):e.key===`Enter`&&(e.preventDefault(),p[c]?.action(),n())};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[p,n,e,c]),e?(0,M.jsx)(`div`,{className:`command-palette-backdrop`,onClick:n,children:(0,M.jsxs)(`div`,{className:`command-palette`,role:`dialog`,"aria-modal":`true`,"aria-label":`Command palette`,"aria-describedby":`command-palette-help`,onClick:e=>e.stopPropagation(),children:[(0,M.jsx)(`p`,{id:`command-palette-help`,className:`sr-only`,children:`Search aggregates, return to the main page, or open statistics. Use arrow keys to move and Enter to confirm.`}),(0,M.jsx)(`input`,{autoFocus:!0,className:`command-palette-input`,placeholder:`Search aggregates or jump to a panel`,value:o,onChange:e=>s(e.target.value),"aria-label":`Command palette search`}),(0,M.jsxs)(`ul`,{className:`command-palette-list`,role:`listbox`,"aria-label":`Command results`,children:[p.map((e,t)=>(0,M.jsx)(`li`,{className:`command-palette-item ${t===c?`active`:``}`,role:`option`,"aria-selected":t===c,onMouseEnter:()=>l(t),onClick:()=>{e.action(),n()},children:e.label},e.id)),p.length===0&&(0,M.jsx)(`li`,{className:`command-palette-item muted`,children:`Type at least two characters to search.`})]})]})}):null}function Ra({paletteOpen:e,onOpenPalette:t,onClosePalette:n}){return(0,j.useEffect)(()=>{let r=r=>{let i=r.target,a=i&&[`INPUT`,`TEXTAREA`,`SELECT`].includes(i.tagName);if((r.metaKey||r.ctrlKey)&&r.key.toLowerCase()===`k`){r.preventDefault(),t();return}if(r.key===`/`&&!a){r.preventDefault(),document.getElementById(`aggregate-search`)?.focus();return}if(r.key===`Escape`&&e){r.preventDefault(),n();return}!a&&(r.key===`j`||r.key===`k`)&&window.dispatchEvent(new CustomEvent(`eventlens:timeline-step`,{detail:r.key===`j`?1:-1}))};return window.addEventListener(`keydown`,r),()=>window.removeEventListener(`keydown`,r)},[e,n,t]),null}function za(e){let t=e.toLowerCase();return t===`ready`||t===`up`}function Ba(e){return e.toLowerCase()===`ready`}function Va(){let e=[6,12,8,16,10,14,7,11,15,9];return(0,M.jsx)(`div`,{className:`mini-wave`,"aria-hidden":!0,children:e.map((e,t)=>(0,M.jsx)(`div`,{className:`mini-wave-bar`,style:{height:e,animationDelay:`${t*.1}s`}},t))})}function Ha({isUp:e,selectedSource:t,fallbackCount:n}){let[r,i]=(0,j.useState)(0),[a,o]=(0,j.useState)(n),s=(0,j.useRef)(null);(0,j.useEffect)(()=>{let e=Date.now();return s.current=setInterval(()=>{i(Math.floor((Date.now()-e)/1e3))},1e3),()=>{s.current&&clearInterval(s.current)}},[]),(0,j.useEffect)(()=>{o(n)},[n]),(0,j.useEffect)(()=>{let e=!1,r=()=>{V(500,t||null).then(t=>{e||o(t.length)}).catch(()=>{!e&&n!=null&&o(n)})};r();let i=setInterval(r,15e3);return()=>{e=!0,clearInterval(i)}},[n,t]);let c=e=>{let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60;return t>0?`${t}h ${n}m`:n>0?`${n}m ${r}s`:`${r}s`};return(0,M.jsxs)(`div`,{className:`conn-stats`,"aria-label":`Connection metrics`,children:[(0,M.jsx)(Va,{}),(0,M.jsxs)(`div`,{className:`conn-stat`,children:[(0,M.jsx)(`span`,{className:`conn-stat-label`,children:`API`}),(0,M.jsx)(`span`,{className:`conn-stat-value ${e?`green`:`amber`}`,children:e?`Healthy`:`Down`})]}),(0,M.jsxs)(`div`,{className:`conn-stat conn-stat--metric`,children:[(0,M.jsx)(`span`,{className:`conn-stat-label`,children:`Events`}),(0,M.jsx)(`span`,{className:`conn-stat-value`,children:a??`...`})]}),(0,M.jsxs)(`div`,{className:`conn-stat`,children:[(0,M.jsx)(`span`,{className:`conn-stat-label`,children:`Uptime`}),(0,M.jsx)(`span`,{className:`conn-stat-value green conn-stat-value--uptime`,children:c(r)})]})]})}function Ua(){let e=Qe(),[t,n]=(0,j.useState)(`state`),[r,i]=(0,j.useState)(null),[a,o]=(0,j.useState)(null),[s,c]=(0,j.useState)(null),[l,u]=(0,j.useState)(`changes`),[d,f]=(0,j.useState)(``),[p,m]=(0,j.useState)(window.location.hash||``),[h,g]=(0,j.useState)(!1),[_,v]=(0,j.useState)(!1),[y,b]=(0,j.useState)(``),[x,S]=(0,j.useState)(``),[C,w]=(0,j.useState)(null),[ee,te]=(0,j.useState)(!1),[ne,T]=(0,j.useState)(!1),[re,ie]=(0,j.useState)(null);(0,j.useEffect)(()=>{let e=()=>m(window.location.hash||``);return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,j.useEffect)(()=>{p===`#/plugins`&&(window.location.hash=`#/timeline`)},[p]),(0,j.useEffect)(()=>{let e=e=>{let t=e.detail;t&&u(t)};return window.addEventListener(`eventlens:switchtab`,e),()=>window.removeEventListener(`eventlens:switchtab`,e)},[]),(0,j.useEffect)(()=>{let e=new URLSearchParams(window.location.search),t=e.get(`authError`),r=e.get(`aggregateId`),a=e.get(`seq`),s=e.get(`compare`),l=e.get(`tab`),d=e.get(`panel`),p=e.get(`source`);if(t){ie(t.replace(/_/g,` `)),e.delete(`authError`);let n=e.toString(),r=`${window.location.pathname}${n?`?${n}`:``}${window.location.hash||`#/timeline`}`;window.history.replaceState(null,``,r)}r&&i(r),a&&o(Number(a)),s&&c(Number(s)),l&&[`changes`,`before-after`,`raw`].includes(l)&&u(l),(d===`replay`||d===`state`)&&n(d),p&&f(p)},[]),(0,j.useEffect)(()=>{let e=new URLSearchParams(window.location.search);r?e.set(`aggregateId`,r):e.delete(`aggregateId`),a==null?e.delete(`seq`):e.set(`seq`,String(a)),s==null?e.delete(`compare`):e.set(`compare`,String(s)),e.set(`tab`,l),e.set(`panel`,t),d?e.set(`source`,d):e.delete(`source`);let n=e.toString(),i=`${window.location.pathname}${n?`?${n}`:``}${window.location.hash}`;window.history.replaceState(null,``,i)},[t,l,s,r,a,d]);let E=N({queryKey:[`auth-session`],queryFn:Fi,retry:!1,refetchOnWindowFocus:!1}),ae=R()||E.data?.authenticated===!0,oe=R()||E.isSuccess||E.isError;(0,j.useEffect)(()=>{if(R()){zi(null);return}zi(E.data?.authenticated?E.data.csrfToken??null:null)},[E.data]);let{data:se}=N({queryKey:[`health`],queryFn:Pi,refetchInterval:3e4,enabled:ae}),{data:ce=[]}=N({queryKey:[`datasources`],queryFn:Bi,staleTime:1e4,enabled:ae}),{data:D=[]}=N({queryKey:[`plugins`],queryFn:Vi,staleTime:1e4,enabled:ae}),{data:O=[]}=ra(r??``,d||null,ae),{data:le}=N({queryKey:[`timeline-summary`,r,d||`default`],queryFn:()=>ji(r,500,0,d||null,`metadata`),enabled:ae&&!!r,staleTime:3e4}),ue=se?.status===`UP`,de=p===`#/stats`,fe=ce.filter(e=>za(e.status)).length,k=D.filter(e=>za(e.lifecycle)).length,A=ce.length-fe+(D.length-k),pe=e=>{i(e),o(null),c(null),n(`state`),window.location.hash=`#/timeline`},me=()=>{window.location.hash=`#/timeline`},he=async t=>{t.preventDefault(),w(null),ie(null),te(!0);try{let t=await Ii(y,x,p||`#/timeline`);S(``),e.setQueryData([`auth-session`],t),await e.invalidateQueries(),window.location.hash=t.returnHash||p||`#/timeline`}catch{w(`Login failed. Check your credentials and try again.`)}finally{te(!1)}},ge=async()=>{T(!0),w(null),ie(null);try{await Li(),e.setQueryData([`auth-session`],{authenticated:!1}),await e.invalidateQueries(),S(``),window.location.hash=`#/timeline`}finally{T(!1)}};if(!oe)return(0,M.jsx)(`div`,{className:`app auth-shell`,children:(0,M.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Session status`,children:(0,M.jsxs)(`section`,{className:`auth-card`,children:[(0,M.jsx)(`div`,{className:`auth-eyebrow`,children:`Security Session`}),(0,M.jsx)(`h1`,{className:`auth-title`,children:`Checking active session`}),(0,M.jsx)(`p`,{className:`auth-copy`,children:`We are verifying whether this browser already has an active EventLens session.`})]})})});if(!ae){let e=E.data?.provider??`basic`,t=E.data?.basicLoginEnabled??e!==`oidc`;return(0,M.jsx)(`div`,{className:`app auth-shell`,children:(0,M.jsx)(`main`,{className:`auth-screen`,role:`main`,"aria-label":`Login`,children:(0,M.jsxs)(`section`,{className:`auth-card`,children:[(0,M.jsx)(`div`,{className:`auth-eyebrow`,children:`EventLens Security`}),(0,M.jsx)(`h1`,{className:`auth-title`,children:E.isError?`Server unavailable`:`Sign in to continue`}),(0,M.jsx)(`p`,{className:`auth-copy`,children:E.isError?`We could not reach the EventLens API to check session state. Start the server or restore connectivity, then refresh this page.`:e===`oidc`?`This deployment expects OpenID Connect sign-in and then creates a server-side browser session for EventLens.`:`This v5 transition uses a server-side browser session. After login, we return you to the current workspace route.`}),re&&(0,M.jsxs)(`div`,{className:`auth-error`,role:`alert`,children:[`OIDC sign-in failed: `,re]}),!E.isError&&e===`oidc`&&(0,M.jsx)(`button`,{className:`auth-submit auth-submit--secondary`,type:`button`,onClick:()=>window.location.assign(Ri(p||`#/timeline`)),children:`Sign in with OpenID Connect`}),!E.isError&&t&&(0,M.jsxs)(`form`,{className:`auth-form`,onSubmit:he,children:[(0,M.jsxs)(`label`,{className:`auth-field`,children:[(0,M.jsx)(`span`,{className:`auth-label`,children:`Username`}),(0,M.jsx)(`input`,{className:`auth-input`,type:`text`,autoComplete:`username`,value:y,onChange:e=>b(e.target.value),disabled:ee,required:!0})]}),(0,M.jsxs)(`label`,{className:`auth-field`,children:[(0,M.jsx)(`span`,{className:`auth-label`,children:`Password`}),(0,M.jsx)(`input`,{className:`auth-input`,type:`password`,autoComplete:`current-password`,value:x,onChange:e=>S(e.target.value),disabled:ee,required:!0})]}),(0,M.jsx)(`div`,{className:`auth-meta`,children:(0,M.jsxs)(`span`,{className:`auth-route`,children:[`Return route: `,p||`#/timeline`]})}),C&&(0,M.jsx)(`div`,{className:`auth-error`,role:`alert`,children:C}),(0,M.jsx)(`button`,{className:`auth-submit`,type:`submit`,disabled:ee,children:ee?`Signing in...`:`Sign in`})]}),E.isError&&(0,M.jsx)(`div`,{className:`auth-error`,role:`alert`,children:`Session check failed. Refresh after the API is available again.`})]})})})}return(0,M.jsxs)(`div`,{className:`app`,children:[(0,M.jsx)(Ra,{paletteOpen:_,onOpenPalette:()=>v(!0),onClosePalette:()=>v(!1)}),(0,M.jsx)(La,{open:_,selectedSource:d||null,onClose:()=>v(!1),onSelectAggregate:pe,onOpenHome:me,onOpenStats:()=>{window.location.hash=`#/stats`}}),(0,M.jsxs)(`header`,{className:`app-header`,children:[(0,M.jsx)(`div`,{className:`brand`,children:(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`div`,{className:`brand-name`,children:`EventLens`}),(0,M.jsx)(`div`,{className:`brand-sub`,children:`Event Store Visual Debugger`})]})}),(0,M.jsxs)(`div`,{className:`header-center`,children:[R()&&(0,M.jsx)(`div`,{className:`header-demo-pill`,role:`status`,children:`Demo mode`}),(0,M.jsx)(`div`,{className:`header-title`,children:`EventLens`})]}),(0,M.jsxs)(`div`,{className:`header-actions`,children:[E.data?.principal&&(0,M.jsxs)(`div`,{className:`header-user`,"aria-label":`Authenticated user`,children:[(0,M.jsxs)(`div`,{className:`header-user-meta`,children:[(0,M.jsx)(`span`,{className:`header-user-label`,children:`Signed in`}),(0,M.jsx)(`strong`,{className:`header-user-name`,children:E.data.principal.displayName})]}),(0,M.jsx)(`button`,{className:`header-user-action`,type:`button`,onClick:ge,disabled:ne,children:ne?`Signing out...`:`Sign out`})]}),(0,M.jsx)(Ha,{isUp:ue,selectedSource:d,fallbackCount:le?.totalEvents??null}),(0,M.jsxs)(`div`,{className:`header-status`,children:[(0,M.jsx)(`span`,{className:`dot ${ue?`dot-green`:`dot-red`}`}),(0,M.jsx)(`span`,{className:`status-text ${ue?``:`offline`}`,children:ue?`Connected`:se?.status??`Connecting`})]})]})]}),(0,M.jsxs)(`aside`,{className:`workspace-dock${h?` workspace-dock--open`:``}`,"aria-label":`Workspace`,children:[(0,M.jsxs)(`div`,{className:`workspace-dock-panel`,id:`workspace-dock-panel`,hidden:!h,children:[(0,M.jsx)(`div`,{className:`workspace-dock-title`,children:`Workspace`}),(0,M.jsxs)(`label`,{className:`workspace-datasource`,children:[(0,M.jsx)(`span`,{className:`workspace-datasource-label`,children:`Datasource`}),(0,M.jsxs)(`select`,{id:`workspace-datasource-select`,className:`workspace-datasource-select`,value:d,onChange:e=>{f(e.target.value),o(null),c(null)},children:[(0,M.jsx)(`option`,{value:``,children:`Auto (primary datasource)`}),ce.map(e=>(0,M.jsxs)(`option`,{value:e.id,disabled:!Ba(e.status),children:[e.id,` [`,e.status,`]`]},e.id))]})]}),(0,M.jsxs)(`div`,{className:`workspace-sidebar-kpis`,children:[(0,M.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,M.jsx)(`span`,{children:`Datasources Healthy`}),(0,M.jsxs)(`strong`,{children:[fe,`/`,ce.length||0]})]}),(0,M.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,M.jsx)(`span`,{children:`Plugins Healthy`}),(0,M.jsxs)(`strong`,{children:[k,`/`,D.length||0]})]}),(0,M.jsxs)(`div`,{className:`workspace-kpi-row`,children:[(0,M.jsx)(`span`,{children:`Issues`}),(0,M.jsx)(`strong`,{children:A})]})]})]}),(0,M.jsx)(`button`,{type:`button`,className:`workspace-dock-handle`,onClick:()=>g(e=>!e),"aria-expanded":h,"aria-controls":`workspace-dock-panel`,"aria-label":h?`Collapse workspace panel`:`Expand workspace panel`,children:(0,M.jsx)(`span`,{className:`workspace-dock-chevron`,"aria-hidden":!0,children:h?`>`:`<`})})]}),(0,M.jsx)(`main`,{className:`app-main`,role:`main`,"aria-label":`EventLens workspace`,children:(0,M.jsxs)(`div`,{className:`workspace-content`,children:[!de&&(0,M.jsxs)(`div`,{className:`card search-panel card--dropdown-host`,children:[(0,M.jsx)(`label`,{className:`control-field-label`,htmlFor:`aggregate-search`,children:`Search Aggregates`}),(0,M.jsx)(Ui,{onSelect:pe,source:d||null,selectedValue:r}),r&&(0,M.jsxs)(`div`,{className:`selection-summary`,children:[`Viewing: `,(0,M.jsx)(`span`,{style:{color:`var(--neon-cyan)`},children:r}),d?(0,M.jsxs)(`span`,{children:[` on `,d]}):(0,M.jsx)(`span`,{children:` on primary datasource`}),s!=null&&(0,M.jsxs)(`span`,{children:[` comparing with seq #`,s]}),(0,M.jsxs)(`span`,{className:`sr-only`,"aria-live":`polite`,children:[`Current panel `,t]}),(0,M.jsx)(`button`,{className:`selection-clear-btn`,onClick:()=>{i(null),o(null),c(null)},children:`× clear`})]})]}),de?(0,M.jsx)(Ia,{source:d||null,onBack:me}):(0,M.jsxs)(M.Fragment,{children:[r&&(0,M.jsx)(na,{aggregateId:r,selectedSequence:a,compareSequence:s,onSelectEvent:e=>{o(e),n(`state`)},onSelectCompare:c,source:d||null}),r&&a!==null&&(0,M.jsx)(da,{aggregateId:r,sequence:a,compareSequence:s,activeTab:l,onTabChange:e=>{u(e),n(`state`)},active:t===`state`,onActivate:()=>n(`state`),source:d||null}),r&&O.length>0&&(0,M.jsx)(Pa,{transitions:O,selectedSequence:a,compareSequence:s,onSelectSequence:e=>{o(e),n(`replay`)},onClearCompare:()=>c(null),active:t===`replay`,onActivate:()=>n(`replay`)}),(0,M.jsxs)(`div`,{className:`bottom-grid`,children:[(0,M.jsx)(Sa,{source:d||null,onSelectAggregate:pe}),(0,M.jsx)(Aa,{source:d||null,onSelectAggregate:pe})]}),r&&le&&(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Selection Summary`}),(0,M.jsxs)(`p`,{style:{color:`var(--text-secondary)`},children:[le.totalEvents,` events available for this aggregate. Use Shift+Click in the timeline to compare two points in time, or open the command palette with Ctrl/Cmd+K.`]})]})]})]})}),(0,M.jsx)(Ma,{})]})}var Wa=class extends j.Component{state={hasError:!1};static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e,t){console.error(`UI error boundary caught:`,e,t)}render(){return this.state.hasError?(0,M.jsx)(`div`,{className:`app`,children:(0,M.jsx)(`main`,{className:`app-main`,children:(0,M.jsxs)(`div`,{className:`card`,children:[(0,M.jsx)(`div`,{className:`card-title`,children:`Something went wrong`}),(0,M.jsx)(`p`,{style:{color:`var(--text-muted)`,fontSize:13},children:`The UI hit an unexpected error. Try refreshing the page. If the problem persists, check the browser console and server logs.`})]})})}):this.props.children}},Ga=new Je({defaultOptions:{queries:{staleTime:3e4,retry:2}}});oi.createRoot(document.getElementById(`root`)).render((0,M.jsx)(j.StrictMode,{children:(0,M.jsx)($e,{client:Ga,children:(0,M.jsx)(ga,{children:(0,M.jsx)(Wa,{children:(0,M.jsx)(Ua,{})})})})})); \ No newline at end of file diff --git a/eventlens-api/src/main/resources/web/index.html b/eventlens-api/src/main/resources/web/index.html index a017a52..10c96c9 100644 --- a/eventlens-api/src/main/resources/web/index.html +++ b/eventlens-api/src/main/resources/web/index.html @@ -9,8 +9,8 @@ - - + +
diff --git a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java index 91f9ea2..4eb6804 100644 --- a/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java +++ b/eventlens-api/src/test/java/io/eventlens/api/AuthenticationIntegrationTest.java @@ -10,6 +10,7 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import io.eventlens.core.EventLensConfig; +import io.eventlens.core.ConfigLoader; import io.eventlens.core.aggregator.ReducerRegistry; import io.eventlens.core.engine.AnomalyDetector; import io.eventlens.core.engine.BisectEngine; @@ -77,7 +78,7 @@ void protectedApiRejectsMissingBasicCredentials() throws Exception { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertThat(response.statusCode()).isEqualTo(401); - assertThat(response.headers().firstValue("WWW-Authenticate")).hasValue("Basic realm=\"EventLens\""); + assertThat(response.headers().firstValue("WWW-Authenticate")).isEmpty(); } @Test @@ -115,6 +116,100 @@ void authDisabledStillAllowsExistingReadFlow() throws Exception { assertThat(response.body()).contains("ORD-1001"); } + @Test + void firstRunSetupBlocksApisThenPersistsBasicBootstrap() throws Exception { + int port = freePort(); + Path configPath = tempDir.resolve("first-run-eventlens.yaml"); + server = startServer(port, false, null, cfg -> { }, configPath, false); + + var client = HttpClient.newHttpClient(); + + var statusRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/setup/status".formatted(port))) + .GET() + .build(); + HttpResponse statusResponse = client.send(statusRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(statusResponse.statusCode()).isEqualTo(200); + assertThat(statusResponse.body()).contains("\"setupRequired\":true"); + + var blockedRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/aggregates/search?q=ord".formatted(port))) + .GET() + .build(); + HttpResponse blockedResponse = client.send(blockedRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(blockedResponse.statusCode()).isEqualTo(503); + assertThat(blockedResponse.body()).contains("setup_required"); + + var applyRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/setup/apply".formatted(port))) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(""" + {"mode":"basic","username":"setup-admin","password":"correct horse battery"} + """)) + .build(); + HttpResponse applyResponse = client.send(applyRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(applyResponse.statusCode()).isEqualTo(200); + assertThat(applyResponse.body()).contains("\"restartRequired\":true"); + assertThat(configPath).exists(); + + server.stop(); + server = null; + + EventLensConfig loaded = ConfigLoader.load(configPath.toString()); + loaded.getServer().setPort(port); + + var replayEngine = new ReplayEngine(reader(), new ReducerRegistry()); + var bisectEngine = new BisectEngine(replayEngine, reader()); + var anomalyDetector = new AnomalyDetector(reader(), replayEngine, loaded.getAnomaly()); + var exportEngine = new ExportEngine(reader(), replayEngine); + var diffEngine = new DiffEngine(replayEngine); + var metadataDatabase = MetadataDatabase.open(loaded.getSecurity().getMetadata()); + + server = new EventLensServer( + loaded, + reader(), + replayEngine, + new ReducerRegistry(), + new PluginManager(30), + "default", + bisectEngine, + anomalyDetector, + exportEngine, + diffEngine, + Map.of(), + metadataDatabase, + configPath.toString(), + true); + server.start(); + + var loginRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/auth/login/basic".formatted(port))) + .header("Authorization", basicAuth("setup-admin", "correct horse battery")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("{\"returnHash\":\"#/timeline\"}")) + .build(); + HttpResponse loginResponse = client.send(loginRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(loginResponse.statusCode()).isEqualTo(200); + } + + @Test + void explicitFalseSetupMarkerForcesWizardEvenWhenConfigFileExists() throws Exception { + int port = freePort(); + Path configPath = tempDir.resolve("force-setup.yaml"); + server = startServer(port, false, null, cfg -> cfg.getSecurity().getSetup().setCompleted(false), configPath, true); + + var client = HttpClient.newHttpClient(); + var statusRequest = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:%d/api/v1/setup/status".formatted(port))) + .GET() + .build(); + + HttpResponse response = client.send(statusRequest, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(response.body()).contains("\"setupRequired\":true"); + } + @Test void basicLoginCanCreateSessionCookieForSubsequentRequests() throws Exception { int port = freePort(); @@ -675,6 +770,16 @@ private EventLensServer startServer(int port, boolean authEnabled, String oidcIs } private EventLensServer startServer(int port, boolean authEnabled, String oidcIssuer, Consumer configCustomizer) { + return startServer(port, authEnabled, oidcIssuer, configCustomizer, tempDir.resolve("eventlens-" + port + ".yaml"), true); + } + + private EventLensServer startServer( + int port, + boolean authEnabled, + String oidcIssuer, + Consumer configCustomizer, + Path configPath, + boolean configPresentAtStartup) { EventStoreReader reader = new EventStoreReader() { @Override public List getEvents(String aggregateId) { @@ -778,11 +883,79 @@ public List searchAggregates(String query, int limit) { exportEngine, diffEngine, java.util.Map.of(), - metadataDatabase); + metadataDatabase, + configPath.toString(), + configPresentAtStartup); eventLensServer.start(); return eventLensServer; } + private EventStoreReader reader() { + return new EventStoreReader() { + @Override + public List getEvents(String aggregateId) { + if (!"ORD-1001".equals(aggregateId)) { + return List.of(); + } + return List.of( + new StoredEvent( + "evt-1", "ORD-1001", "Order", 1, "ORDER_CREATED", + "{\"email\":\"alice@example.com\",\"status\":\"created\"}", + "{\"source\":\"default\"}", + Instant.parse("2026-01-01T00:00:00Z"), + 1L + ), + new StoredEvent( + "evt-2", "ORD-1001", "Order", 2, "ORDER_CONFIRMED", + "{\"status\":\"confirmed\"}", + "{\"source\":\"default\"}", + Instant.parse("2026-01-01T00:01:00Z"), + 2L + ) + ); + } + + @Override + public List getEventsUpTo(String aggregateId, long maxSequence) { + return getEvents(aggregateId).stream() + .filter(event -> event.sequenceNumber() <= maxSequence) + .toList(); + } + + @Override + public List findAggregateIds(String aggregateType, int limit, int offset) { + return List.of("ORD-1001"); + } + + @Override + public List getRecentEvents(int limit) { + return List.of(new StoredEvent( + "evt-1", "ORD-1001", "Order", 1, "ORDER_CREATED", + "{}", "{}", Instant.now(), 1L)); + } + + @Override + public List getEventsAfter(long globalPosition, int limit) { + return List.of(); + } + + @Override + public long countEvents(String aggregateId) { + return getEvents(aggregateId).size(); + } + + @Override + public List getAggregateTypes() { + return List.of("Order"); + } + + @Override + public List searchAggregates(String query, int limit) { + return List.of("ORD-1001"); + } + }; + } + private static String basicAuth(String username, String password) { String raw = username + ":" + password; return "Basic " + Base64.getEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8)); diff --git a/eventlens-app/build.gradle.kts b/eventlens-app/build.gradle.kts index 1b71271..57280a5 100644 --- a/eventlens-app/build.gradle.kts +++ b/eventlens-app/build.gradle.kts @@ -24,6 +24,8 @@ tasks.shadowJar { archiveClassifier.set("") archiveVersion.set("") mergeServiceFiles() + // Ensure all JDBC driver entries survive the service-file merge (SQLite was dropped) + append("META-INF/services/java.sql.Driver") manifest { attributes( diff --git a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java index 10dafcb..05cd1ca 100644 --- a/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java +++ b/eventlens-cli/src/main/java/io/eventlens/cli/ServeCommand.java @@ -65,7 +65,9 @@ public class ServeCommand implements Runnable { @Override public void run() { - EventLensConfig config = configPath != null ? ConfigLoader.load(configPath) : ConfigLoader.load(); + var loadedConfig = configPath != null ? ConfigLoader.loadResolved(configPath) : ConfigLoader.loadResolved(); + EventLensConfig config = loadedConfig.config(); + String effectiveConfigPath = loadedConfig.sourcePath() != null ? loadedConfig.sourcePath() : ConfigLoader.defaultConfigPath(); if (port != null) config.getServer().setPort(port); if (dbUrl != null) config.getDatasource().setUrl(dbUrl); @@ -115,7 +117,9 @@ public void run() { exportEngine, diffEngine, datasourceStreamBindings(config), - metadataDatabase); + metadataDatabase, + effectiveConfigPath, + loadedConfig.fromFile()); Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { diff --git a/eventlens-core/src/main/java/io/eventlens/core/ConfigLoader.java b/eventlens-core/src/main/java/io/eventlens/core/ConfigLoader.java index d132b20..4bd82aa 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/ConfigLoader.java +++ b/eventlens-core/src/main/java/io/eventlens/core/ConfigLoader.java @@ -13,6 +13,8 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; /** * Discovers and loads the EventLens configuration file from well-known locations. @@ -33,23 +35,31 @@ public class ConfigLoader { .findAndRegisterModules(); public static EventLensConfig load() { + return loadResolved().config(); + } + + public static EventLensConfig load(String path) { + return loadResolved(path).config(); + } + + public static LoadedConfig loadResolved() { for (String path : CONFIG_PATHS) { File file = new File(path); if (file.exists()) { try { EventLensConfig config = readAndInterpolate(file); log.info("Loaded config from: {}", file.getAbsolutePath()); - return config; + return new LoadedConfig(config, file.getAbsolutePath(), true); } catch (IOException e) { throw new RuntimeException("Failed to parse config: " + path, e); } } } log.warn("No config file found - using defaults. Searched: {}", String.join(", ", CONFIG_PATHS)); - return new EventLensConfig(); + return new LoadedConfig(new EventLensConfig(), null, false); } - public static EventLensConfig load(String path) { + public static LoadedConfig loadResolved(String path) { File file = new File(path); if (!file.exists()) { throw new RuntimeException("Config file not found: " + path); @@ -57,12 +67,34 @@ public static EventLensConfig load(String path) { try { EventLensConfig config = readAndInterpolate(file); log.info("Loaded config from: {}", file.getAbsolutePath()); - return config; + return new LoadedConfig(config, file.getAbsolutePath(), true); } catch (IOException e) { throw new RuntimeException("Failed to parse config: " + path, e); } } + public static String defaultConfigPath() { + return new File(CONFIG_PATHS[0]).getAbsolutePath(); + } + + public static void save(String path, EventLensConfig config) { + try { + Path target = Path.of(path).toAbsolutePath(); + Path parent = target.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + ObjectNode root = YAML_MAPPER.valueToTree(config); + root.remove("datasource"); + root.remove("kafka"); + root.set("datasources", YAML_MAPPER.valueToTree(config.getDatasourcesOrLegacy())); + root.set("streams", YAML_MAPPER.valueToTree(config.getStreamsOrLegacy())); + YAML_MAPPER.writeValue(target.toFile(), root); + } catch (IOException e) { + throw new RuntimeException("Failed to write config: " + path, e); + } + } + private static EventLensConfig readAndInterpolate(File file) throws IOException { JsonNode root = YAML_MAPPER.readTree(file); if (root == null) { @@ -98,4 +130,7 @@ private static void interpolateNode(JsonNode node) { } } } + + public record LoadedConfig(EventLensConfig config, String sourcePath, boolean fromFile) { + } } diff --git a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java index 8f67770..48e6f88 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java +++ b/eventlens-core/src/main/java/io/eventlens/core/EventLensConfig.java @@ -129,12 +129,15 @@ public static class SecurityConfig { public static class SecurityFeaturesConfig { private boolean productionMode = false; + private SetupConfig setup = new SetupConfig(); private MetadataConfig metadata = new MetadataConfig(); private AuthProviderConfig auth = new AuthProviderConfig(); private AuthorizationConfig authorization = new AuthorizationConfig(); public boolean isProductionMode() { return productionMode; } public void setProductionMode(boolean productionMode) { this.productionMode = productionMode; } + public SetupConfig getSetup() { return setup; } + public void setSetup(SetupConfig setup) { this.setup = setup; } public MetadataConfig getMetadata() { return metadata; } public void setMetadata(MetadataConfig metadata) { this.metadata = metadata; } public AuthProviderConfig getAuth() { return auth; } @@ -143,6 +146,14 @@ public static class SecurityFeaturesConfig { public void setAuthorization(AuthorizationConfig authorization) { this.authorization = authorization; } } + public static class SetupConfig { + private Boolean completed; + + public Boolean getCompleted() { return completed; } + public boolean isCompleted() { return Boolean.TRUE.equals(completed); } + public void setCompleted(Boolean completed) { this.completed = completed; } + } + public static class AuthProviderConfig { private String provider = "disabled"; private SessionConfig session = new SessionConfig(); diff --git a/eventlens-core/src/main/java/io/eventlens/core/JsonUtil.java b/eventlens-core/src/main/java/io/eventlens/core/JsonUtil.java index cb78ce3..a33c5c1 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/JsonUtil.java +++ b/eventlens-core/src/main/java/io/eventlens/core/JsonUtil.java @@ -13,7 +13,8 @@ public final class JsonUtil { private static final ObjectMapper MAPPER = new ObjectMapper() - .findAndRegisterModules(); + .findAndRegisterModules() + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); private JsonUtil() { } diff --git a/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java index 1f9b6f9..9fd2a00 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java +++ b/eventlens-core/src/main/java/io/eventlens/core/metadata/MetadataDatabase.java @@ -47,6 +47,16 @@ public static MetadataDatabase open(EventLensConfig.MetadataConfig config) { ensureSqliteParentDirectory(config.getJdbcUrl()); + // Explicitly load the SQLite driver — in fat JARs the + // META-INF/services/java.sql.Driver merge may lose entries. + try { + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException e) { + throw new ConfigurationException( + "SQLite JDBC driver not found on classpath. " + + "Add org.xerial:sqlite-jdbc to your dependencies.", e); + } + HikariConfig hikari = new HikariConfig(); hikari.setJdbcUrl(config.getJdbcUrl()); hikari.setPoolName("eventlens-metadata"); diff --git a/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java b/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java index 5cde951..239a158 100644 --- a/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java +++ b/eventlens-core/src/main/java/io/eventlens/core/security/ApiKeyService.java @@ -46,8 +46,7 @@ public IssuedApiKey issue(String principalUserId, List roleScopes, Strin now, expiresAt, null, - null - ); + null); repository.insert(record); return new IssuedApiKey( apiKeyId, @@ -57,8 +56,7 @@ public IssuedApiKey issue(String principalUserId, List roleScopes, Strin principalUserId, record.scopes(), now, - expiresAt - ); + expiresAt); } public Optional authenticate(String rawApiKey) { @@ -99,8 +97,7 @@ public Optional authenticate(String rawApiKey) { record.createdAt(), record.expiresAt(), record.revokedAt(), - lastUsedAt - )); + lastUsedAt)); } public List list() { diff --git a/eventlens-core/src/main/java/io/eventlens/core/setup/InstanceSetupService.java b/eventlens-core/src/main/java/io/eventlens/core/setup/InstanceSetupService.java new file mode 100644 index 0000000..435ea97 --- /dev/null +++ b/eventlens-core/src/main/java/io/eventlens/core/setup/InstanceSetupService.java @@ -0,0 +1,140 @@ +package io.eventlens.core.setup; + +import io.eventlens.core.ConfigLoader; +import io.eventlens.core.ConfigValidator; +import io.eventlens.core.EventLensConfig; + +import java.util.Locale; + +/** + * Tracks first-run setup state and persists a minimal security bootstrap choice. + */ +public final class InstanceSetupService { + + private final EventLensConfig config; + private final String configPath; + private final boolean configuredAtStartup; + private volatile boolean restartRequired = false; + + public InstanceSetupService(EventLensConfig config, String configPath, boolean configuredAtStartup) { + this.config = config; + this.configPath = configPath == null || configPath.isBlank() + ? ConfigLoader.defaultConfigPath() + : configPath; + this.configuredAtStartup = configuredAtStartup; + } + + public SetupStatus status() { + boolean completed = isCompleted(); + return new SetupStatus(!completed && !restartRequired, restartRequired, configPath); + } + + public synchronized SetupApplyResult apply(SetupRequest request) { + if (isCompleted() || restartRequired) { + throw new IllegalStateException("Setup has already been completed for this instance"); + } + + String mode = normalizeMode(request.mode()); + switch (mode) { + case "basic" -> applyBasic(request); + case "oidc" -> applyOidc(request); + case "disabled" -> applyDisabled(); + default -> throw new IllegalArgumentException("Unsupported setup mode: " + request.mode()); + } + + config.getSecurity().getSetup().setCompleted(true); + ConfigValidator.validateOrThrow(config); + ConfigLoader.save(configPath, config); + restartRequired = true; + return new SetupApplyResult(true, true, mode, configPath); + } + + private boolean isCompleted() { + if (restartRequired) { + return true; + } + Boolean marker = config.getSecurity().getSetup().getCompleted(); + if (Boolean.FALSE.equals(marker)) { + return false; + } + if (Boolean.TRUE.equals(marker)) { + return true; + } + return configuredAtStartup; + } + + private void applyBasic(SetupRequest request) { + requireText(request.username(), "username"); + requireText(request.password(), "password"); + + config.getServer().getAuth().setEnabled(true); + config.getServer().getAuth().setUsername(request.username().trim()); + config.getServer().getAuth().setPassword(request.password()); + + config.getSecurity().setProductionMode(false); + config.getSecurity().getMetadata().setEnabled(true); + config.getSecurity().getAuth().setProvider("basic"); + applyLocalSessionDefaults(); + config.getSecurity().getAuth().getApiKeys().setEnabled(false); + config.getSecurity().getAuthorization().setEnabled(false); + config.getAudit().setEnabled(true); + } + + private void applyOidc(SetupRequest request) { + requireText(request.issuer(), "issuer"); + requireText(request.clientId(), "clientId"); + requireText(request.clientSecret(), "clientSecret"); + + config.getServer().getAuth().setEnabled(false); + config.getSecurity().setProductionMode(false); + config.getSecurity().getMetadata().setEnabled(true); + config.getSecurity().getAuth().setProvider("oidc"); + applyLocalSessionDefaults(); + config.getSecurity().getAuth().getApiKeys().setEnabled(false); + config.getSecurity().getAuthorization().setEnabled(false); + config.getSecurity().getAuth().getOidc().setIssuer(request.issuer().trim()); + config.getSecurity().getAuth().getOidc().setClientId(request.clientId().trim()); + config.getSecurity().getAuth().getOidc().setClientSecret(request.clientSecret()); + config.getAudit().setEnabled(true); + } + + private void applyDisabled() { + config.getServer().getAuth().setEnabled(false); + config.getSecurity().setProductionMode(false); + config.getSecurity().getMetadata().setEnabled(false); + config.getSecurity().getAuth().setProvider("disabled"); + config.getSecurity().getAuth().getApiKeys().setEnabled(false); + config.getSecurity().getAuthorization().setEnabled(false); + } + + private void applyLocalSessionDefaults() { + config.getSecurity().getAuth().getSession().setCookieName("eventlens_session"); + config.getSecurity().getAuth().getSession().setSecureCookie(false); + config.getSecurity().getAuth().getSession().setSameSite("Lax"); + } + + private static String normalizeMode(String value) { + return value == null ? "" : value.trim().toLowerCase(Locale.ROOT); + } + + private static void requireText(String value, String field) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("Missing required field: " + field); + } + } + + public record SetupStatus(boolean setupRequired, boolean restartRequired, String configPath) { + } + + public record SetupApplyResult(boolean saved, boolean restartRequired, String mode, String configPath) { + } + + public record SetupRequest( + String mode, + String username, + String password, + String issuer, + String clientId, + String clientSecret) { + } +} diff --git a/eventlens-core/src/main/resources/eventlens-example.yaml b/eventlens-core/src/main/resources/eventlens-example.yaml index b472fec..7784277 100644 --- a/eventlens-core/src/main/resources/eventlens-example.yaml +++ b/eventlens-core/src/main/resources/eventlens-example.yaml @@ -89,6 +89,8 @@ audit: security: production-mode: false + setup: + completed: true # set false to force the setup wizard on next boot; leave unset/true for normal manual config metadata: enabled: false jdbc-url: jdbc:sqlite:./data/eventlens-metadata.db @@ -130,6 +132,10 @@ data-protection: regex: "\\d{3}-\\d{2}-\\d{4}" mask: "***-**-****" +# Docker note: +# When metadata storage is enabled in a container, mount /app/data to a persistent volume +# so metadata-backed sessions, audit records, and API keys survive restarts. + export: directory: ./exports max-concurrent: 2 diff --git a/eventlens-ui/src/App.tsx b/eventlens-ui/src/App.tsx index 2fc85ca..c083a68 100644 --- a/eventlens-ui/src/App.tsx +++ b/eventlens-ui/src/App.tsx @@ -11,6 +11,7 @@ import StatisticsPanel from './components/StatisticsPanel'; import CommandPalette from './components/CommandPalette'; import KeyboardManager from './components/KeyboardManager'; import AdminConsole from './components/AdminConsole'; +import SetupWizard from './components/SetupWizard'; import { buildOidcLoginUrl, getAuthSession, @@ -18,10 +19,12 @@ import { getHealth, getPlugins, getRecentEvents, + getSetupStatus, getTimeline, loginWithBasicSession, logoutSession, setCsrfToken, + type SetupApplyResponse, } from './api/client'; import { isDemoMode } from './demo/demoMode'; import { useReplay } from './hooks/useReplay'; @@ -147,6 +150,7 @@ export default function App() { const [isSubmittingLogin, setIsSubmittingLogin] = useState(false); const [isSubmittingLogout, setIsSubmittingLogout] = useState(false); const [oidcError, setOidcError] = useState(null); + const [setupApplied, setSetupApplied] = useState(null); useEffect(() => { const syncHash = () => setCurrentHash(window.location.hash || ''); @@ -206,14 +210,26 @@ export default function App() { window.history.replaceState(null, '', newUrl); }, [activePanel, activeTab, compareSequence, selectedAggregate, selectedSequence, selectedSource]); + const setupQuery = useQuery({ + queryKey: ['setup-status'], + queryFn: getSetupStatus, + retry: false, + refetchOnWindowFocus: false, + }); const authQuery = useQuery({ queryKey: ['auth-session'], queryFn: getAuthSession, retry: false, refetchOnWindowFocus: false, + enabled: isDemoMode() || (setupQuery.isSuccess && !setupQuery.data?.setupRequired && !setupQuery.data?.restartRequired), }); const isAuthenticated = isDemoMode() || authQuery.data?.authenticated === true; - const authReady = isDemoMode() || authQuery.isSuccess || authQuery.isError; + const setupReady = isDemoMode() || setupQuery.isSuccess || setupQuery.isError; + const authReady = isDemoMode() + || authQuery.isSuccess + || authQuery.isError + || !!setupQuery.data?.setupRequired + || !!setupQuery.data?.restartRequired; useEffect(() => { if (isDemoMode()) { @@ -306,6 +322,52 @@ export default function App() { } }; + if (!setupReady) { + return ( +
+
+
+
EventLens Setup
+

Checking instance state

+

+ We are checking whether this EventLens instance still needs first-run security setup. +

+
+
+
+ ); + } + + if (setupQuery.data?.setupRequired && !setupApplied) { + return ; + } + + if (setupApplied || setupQuery.data?.restartRequired) { + const setupResult = setupApplied ?? { + saved: true, + restartRequired: true, + mode: 'saved', + configPath: setupQuery.data?.configPath ?? 'eventlens.yaml', + }; + return ( +
+
+
+
EventLens Setup
+

Restart required

+

+ We saved the new security mode to {setupResult.configPath}. + Restart EventLens once so the new auth pipeline comes up cleanly. +

+
+ Saved mode: {setupResult.mode}. After restart, reload this page and continue with the normal sign-in flow. +
+
+
+
+ ); + } + if (!authReady) { return (
diff --git a/eventlens-ui/src/api/client.ts b/eventlens-ui/src/api/client.ts index 6c7612b..82a3a81 100644 --- a/eventlens-ui/src/api/client.ts +++ b/eventlens-ui/src/api/client.ts @@ -57,6 +57,28 @@ export interface AuthSessionResponse { csrfToken?: string; } +export interface SetupStatusResponse { + setupRequired: boolean; + restartRequired: boolean; + configPath: string; +} + +export interface SetupApplyRequest { + mode: 'basic' | 'oidc' | 'disabled'; + username?: string; + password?: string; + issuer?: string; + clientId?: string; + clientSecret?: string; +} + +export interface SetupApplyResponse { + saved: boolean; + restartRequired: boolean; + mode: string; + configPath: string; +} + export interface AuditEntry { auditId: number; action: string; @@ -259,6 +281,21 @@ export const getAuthSession = async () => { return api.get('/v1/auth/session').then(r => r.data); }; +export const getSetupStatus = async () => { + if (isDemoMode()) { + return { + setupRequired: false, + restartRequired: false, + configPath: 'eventlens.yaml', + } satisfies SetupStatusResponse; + } + return api.get('/v1/setup/status').then(r => r.data); +}; + +export const applySetup = async (payload: SetupApplyRequest) => { + return api.post('/v1/setup/apply', payload).then(r => r.data); +}; + export const getAuditEntries = async (limit = 25) => { return api.get(`/v1/audit?limit=${limit}`).then(r => r.data); }; diff --git a/eventlens-ui/src/components/AdminConsole.tsx b/eventlens-ui/src/components/AdminConsole.tsx index b93abd9..a6dd500 100644 --- a/eventlens-ui/src/components/AdminConsole.tsx +++ b/eventlens-ui/src/components/AdminConsole.tsx @@ -79,7 +79,7 @@ export default function AdminConsole() { principalUserId: principalUserId.trim(), roles: roles.split(',').map(role => role.trim()).filter(Boolean), description: description.trim() || undefined, - expiresAt: expiresAt.trim() || undefined, + expiresAt: expiresAt.trim() ? new Date(expiresAt).toISOString() : undefined, }); }; @@ -171,8 +171,14 @@ export default function AdminConsole() { setDescription(event.target.value)} /> {createError && } + + +
+ +

{modeCopy}

+ +
+ {mode === 'basic' && ( + <> + + + + )} + + {mode === 'oidc' && ( + <> + + + + + )} + + {mode === 'disabled' && ( +
+ EventLens will save a local no-auth configuration. Use this only for a private developer workstation. +
+ )} + + {error &&
{error}
} + +
+ + + + ); +} diff --git a/eventlens-ui/src/index.css b/eventlens-ui/src/index.css index 22b697b..de72485 100644 --- a/eventlens-ui/src/index.css +++ b/eventlens-ui/src/index.css @@ -210,6 +210,75 @@ body::after { box-shadow: none; } +.setup-card { + width: min(760px, 100%); +} + +.setup-path { + color: var(--neon-cyan); + font-family: var(--font-mono); +} + +.setup-mode-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + margin-bottom: 16px; +} + +.setup-mode-card { + display: grid; + gap: 8px; + text-align: left; + padding: 14px; + border-radius: 14px; + border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(10, 14, 26, 0.9); + color: var(--text-primary); + cursor: pointer; + transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition); +} + +.setup-mode-card strong { + font-family: var(--font-display); + font-size: 12px; + letter-spacing: 1px; + text-transform: uppercase; +} + +.setup-mode-card span { + color: var(--text-secondary); + font-size: 13px; + line-height: 1.5; +} + +.setup-mode-card:hover, +.setup-mode-card--active { + border-color: rgba(0, 240, 255, 0.28); + box-shadow: 0 0 24px rgba(0, 240, 255, 0.08); + transform: translateY(-1px); +} + +.setup-mode-copy { + color: var(--text-secondary); + margin-bottom: 18px; +} + +.setup-warning { + padding: 12px 14px; + border-radius: 12px; + border: 1px solid rgba(255, 170, 0, 0.24); + background: rgba(255, 170, 0, 0.08); + color: #ffd27a; + font-size: 13px; +} + +@media (max-width: 760px) { + .setup-mode-grid { + grid-template-columns: 1fr; + } +} + .app-header { --header-pad-x: 24px; --header-pad-right: 48px; /* room for workspace chevron; mirrors visual weight of left brand */ diff --git a/eventlens.yaml.example b/eventlens.yaml.example index 9728143..4fa231f 100644 --- a/eventlens.yaml.example +++ b/eventlens.yaml.example @@ -53,6 +53,8 @@ audit: security: production-mode: false # set true for shared/prod deployments; blocks unsafe startup combinations + setup: + completed: true # set false to force the setup wizard on next boot; omit or leave true for normal manual config metadata: enabled: false jdbc-url: jdbc:sqlite:./data/eventlens-metadata.db @@ -69,6 +71,10 @@ security: header-name: X-API-Key key-prefix: el +# Compose note: +# If metadata.enabled is true inside Docker, mount /app/data to a persistent volume +# so sessions, audit records, and API keys survive container restarts. + # ── 1.9 PII Masking ─────────────────────────────────────────────────────────── # Scans event payloads in API responses for common PII patterns and replaces # matching text with the configured mask string. diff --git a/scripts/reset-setup.py b/scripts/reset-setup.py new file mode 100644 index 0000000..d23628a --- /dev/null +++ b/scripts/reset-setup.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import re +import sys +from pathlib import Path + + +def replace_or_insert_setup_flag(content: str) -> str: + if re.search(r"(?m)^\s*setup:\s*$", content): + updated, count = re.subn( + r"(?ms)^(\s*setup:\s*\n)(\s*completed:\s*(?:true|false|null).*\n?)?", + r"\1 completed: false\n", + content, + count=1, + ) + if count > 0: + return updated + if re.search(r"(?m)^\s*security:\s*$", content): + return re.sub(r"(?m)^(\s*security:\s*)$", r"\1\n setup:\n completed: false", content, count=1) + return content + + +def replace_provider(content: str) -> str: + if re.search(r'(?m)^\s*provider:\s*"?[^"\n]+"?\s*$', content): + return re.sub(r'(?m)^(\s*provider:\s*).+$', r'\1disabled', content, count=1) + if re.search(r"(?m)^\s*auth:\s*$", content): + return re.sub(r"(?m)^(\s*auth:\s*)$", r"\1\n provider: disabled", content, count=1) + return content + + +def disable_legacy_basic_auth(content: str) -> str: + return re.sub( + r"(?ms)(^\s*auth:\s*\n\s*enabled:\s*)true(\s*$)", + r"\1false\2", + content, + count=1, + ) + + +def main() -> int: + target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("eventlens.yaml") + if not target.exists(): + print(f"Error: could not find '{target}'.") + return 1 + + content = target.read_text(encoding="utf-8") + updated = replace_or_insert_setup_flag(content) + updated = replace_provider(updated) + updated = disable_legacy_basic_auth(updated) + target.write_text(updated, encoding="utf-8") + print(f"Reset setup markers in '{target}'. Restart EventLens to see the setup wizard again.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())