@juice-js/observability 0.0.0-stage → 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +31 -2
  2. package/fesm2022/juice-js-observability-alert.page-D2OL0zTT.mjs +405 -0
  3. package/fesm2022/juice-js-observability-alert.page-D2OL0zTT.mjs.map +1 -0
  4. package/fesm2022/juice-js-observability-alerts.page-BLJvD_op.mjs +208 -0
  5. package/fesm2022/juice-js-observability-alerts.page-BLJvD_op.mjs.map +1 -0
  6. package/fesm2022/juice-js-observability-logs.page-BWFLleyX.mjs +414 -0
  7. package/fesm2022/juice-js-observability-logs.page-BWFLleyX.mjs.map +1 -0
  8. package/fesm2022/juice-js-observability-metrics.page-D1caPbQq.mjs +268 -0
  9. package/fesm2022/juice-js-observability-metrics.page-D1caPbQq.mjs.map +1 -0
  10. package/fesm2022/juice-js-observability-overview.page-UBwHjlyq.mjs +213 -0
  11. package/fesm2022/juice-js-observability-overview.page-UBwHjlyq.mjs.map +1 -0
  12. package/fesm2022/juice-js-observability-resource.page-BZwLK_vU.mjs +275 -0
  13. package/fesm2022/juice-js-observability-resource.page-BZwLK_vU.mjs.map +1 -0
  14. package/fesm2022/juice-js-observability-resources.page-C0xWOb5I.mjs +210 -0
  15. package/fesm2022/juice-js-observability-resources.page-C0xWOb5I.mjs.map +1 -0
  16. package/fesm2022/juice-js-observability-rules.page-Db_U5rRu.mjs +176 -0
  17. package/fesm2022/juice-js-observability-rules.page-Db_U5rRu.mjs.map +1 -0
  18. package/fesm2022/juice-js-observability-service.page-B-SWJ9I5.mjs +727 -0
  19. package/fesm2022/juice-js-observability-service.page-B-SWJ9I5.mjs.map +1 -0
  20. package/fesm2022/juice-js-observability-services.page-BmWgFnC8.mjs +252 -0
  21. package/fesm2022/juice-js-observability-services.page-BmWgFnC8.mjs.map +1 -0
  22. package/fesm2022/juice-js-observability-telemetry-VA4_3nzr.mjs +1748 -0
  23. package/fesm2022/juice-js-observability-telemetry-VA4_3nzr.mjs.map +1 -0
  24. package/fesm2022/juice-js-observability-topology.page-DEUNp10U.mjs +458 -0
  25. package/fesm2022/juice-js-observability-topology.page-DEUNp10U.mjs.map +1 -0
  26. package/fesm2022/juice-js-observability-trace.page-gBhMG2eQ.mjs +530 -0
  27. package/fesm2022/juice-js-observability-trace.page-gBhMG2eQ.mjs.map +1 -0
  28. package/fesm2022/juice-js-observability-traces.page-BCQg4V4o.mjs +521 -0
  29. package/fesm2022/juice-js-observability-traces.page-BCQg4V4o.mjs.map +1 -0
  30. package/fesm2022/juice-js-observability.mjs +396 -0
  31. package/fesm2022/juice-js-observability.mjs.map +1 -0
  32. package/package.json +48 -4
  33. package/styles/_observability.scss +133 -0
  34. package/types/juice-js-observability.d.ts +387 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"juice-js-observability-telemetry-VA4_3nzr.mjs","names":["i1","i2","i3"],"sources":["../../../../projects/juice-js/observability/src/lib/shared/format.ts","../../../../projects/juice-js/observability/src/lib/shared/ui.ts","../../../../projects/juice-js/observability/src/lib/shared/paging.ts","../../../../projects/juice-js/observability/src/lib/shared/telemetry.ts"],"sourcesContent":["import { Pipe, PipeTransform } from '@angular/core';\n\n/** Milliseconds since the epoch of an ISO 8601 time, keeping the sub-millisecond digits that Date.parse drops. */\nexport function preciseTime(iso: string): number {\n const fraction = /\\.\\d{3}(\\d+)/.exec(iso)?.[1];\n return Date.parse(iso) + (fraction ? Number(`0.${fraction}`) : 0);\n}\n\n/** The 32-hex trace id of a W3C traceparent (\"00-<trace id>-<span id>-01\"); any other text unchanged but trimmed and lower-cased. */\nexport function plainTraceId(text: string): string {\n const lower = text.trim().toLowerCase();\n return /^[0-9a-f]{2}-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}$/.exec(lower)?.[1] ?? lower;\n}\n\n/** 0.8 ms, 120 ms, 1.42 s, 2 m 05 s. */\nexport function formatDuration(ms: number | null | undefined): string {\n if (ms === null || ms === undefined || Number.isNaN(ms)) return '—';\n if (ms < 1) return `${ms.toFixed(2)} ms`;\n if (ms < 1000) return `${Math.round(ms * 10) / 10} ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(2)} s`;\n const s = Math.round(ms / 1000);\n return `${Math.floor(s / 60)} m ${String(s % 60).padStart(2, '0')} s`;\n}\n\nexport function formatPercent(rate: number | null | undefined): string {\n if (rate === null || rate === undefined) return '—';\n const p = rate * 100;\n return `${p === 0 ? '0' : p < 0.1 ? '<0.1' : p.toFixed(p < 10 ? 1 : 0)} %`;\n}\n\nexport function formatAgo(iso: string | null | undefined): string {\n if (!iso) return '—';\n const s = Math.round((Date.now() - new Date(iso).getTime()) / 1000);\n if (s < 60) return `${Math.max(s, 0)} s ago`;\n if (s < 3600) return `${Math.round(s / 60)} min ago`;\n if (s < 86_400) return `${Math.round(s / 3600)} h ago`;\n return `${Math.round(s / 86_400)} d ago`;\n}\n\n@Pipe({ name: 'duration' })\nexport class DurationPipe implements PipeTransform {\n transform(ms: number | null | undefined) { return formatDuration(ms); }\n}\n\n@Pipe({ name: 'percent2' })\nexport class PercentPipe implements PipeTransform {\n transform(rate: number | null | undefined) { return formatPercent(rate); }\n}\n\n@Pipe({ name: 'ago' })\nexport class AgoPipe implements PipeTransform {\n transform(iso: string | null | undefined) { return formatAgo(iso); }\n}\n\n/** A reason code such as `dependency_failed:shop/payments` as words. */\n@Pipe({ name: 'reason' })\nexport class ReasonPipe implements PipeTransform {\n transform(code: string) {\n const [what, subject] = code.split(/:(.*)/s);\n const text = what.replace(/_/g, ' ');\n return subject ? `${text}: ${subject}` : text;\n }\n}\n","import { ObservabilityLinks } from '../core/options';\nimport {\n ChangeDetectionStrategy, Component, ElementRef, OnDestroy, afterNextRender, computed, effect, inject, input, signal, viewChild,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatSelectModule } from '@angular/material/select';\nimport { Router } from '@angular/router';\nimport { catchError, map, of } from 'rxjs';\nimport type { ECharts, EChartsOption } from 'echarts';\nimport { ApiService } from '../core/api.service';\nimport { ContextService, RANGES } from '../core/context.service';\nimport { HealthState } from '../core/models';\n\n/** A health state, with \"no data\" for a missing one (Console requirements §10: missing data is not zero). */\n@Component({\n selector: 'jc-health',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<span class=\"jc-badge\" [class]=\"'jc-health-' + (state() ?? 'none').toLowerCase()\"\n [attr.title]=\"stale() ? 'Evaluation has stopped: this state may be out of date' : null\">\n {{ state() ?? 'No data' }}@if (stale()) {<span class=\"jc-stale\"> · stale</span>}</span>`,\n})\nexport class HealthBadge {\n readonly state = input<HealthState | null | undefined>();\n readonly stale = input(false);\n}\n\n/** Observed or inferred dependency evidence (Console requirements §9): inferred is never shown as confirmed. */\n@Component({\n selector: 'jc-confidence',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<span class=\"jc-badge\" [class.jc-observed]=\"observed()\" [class.jc-inferred]=\"!observed()\"\n [attr.title]=\"observed() ? 'Seen in traces: a call from one service to the other' : 'Inferred from span attributes, not a confirmed call'\">\n {{ observed() ? 'observed' : 'inferred' }}</span>`,\n})\nexport class ConfidenceBadge {\n readonly confidence = input.required<string>();\n protected readonly observed = computed(() => this.confidence().toLowerCase() === 'observed');\n}\n\n/** Attributes as a key/value table, sorted, values wrapped. */\n@Component({\n selector: 'jc-kv',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (entries().length === 0) { <p class=\"jc-muted\">{{ empty() }}</p> }\n @else {\n <table class=\"jc-kv\"><tbody>\n @for (e of entries(); track e[0]) { <tr><th>{{ e[0] }}</th><td>{{ e[1] }}</td></tr> }\n </tbody></table>\n }`,\n})\nexport class KeyValueTable {\n readonly values = input<Record<string, string | null | undefined>>({});\n readonly empty = input('None');\n protected readonly entries = computed(() =>\n Object.entries(this.values()).filter((e): e is [string, string] => e[1] !== null && e[1] !== undefined && e[1] !== '')\n .sort((a, b) => a[0].localeCompare(b[0])));\n}\n\n/**\n * The context bar on every page (Console requirements §13, §17): environment, time range, and a global lookup for a\n * trace id, a span id or a service name.\n */\n@Component({\n selector: 'jc-context-bar',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatSelectModule, MatIconModule, MatButtonModule],\n template: `\n <div class=\"jc-context\">\n <ng-content />\n <span class=\"jc-spacer\"></span>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Environment</mat-label>\n <mat-select [value]=\"ctx.environment()\" (valueChange)=\"ctx.set({ env: $event || null })\">\n <mat-option value=\"\">All</mat-option>\n @for (e of environments(); track e.id) { <mat-option [value]=\"e.key\">{{ e.key }}</mat-option> }\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Time range</mat-label>\n <mat-select [value]=\"ctx.range()\" (valueChange)=\"ctx.set({ range: $event })\">\n @for (r of ranges; track r) { <mat-option [value]=\"r\">Last {{ r }}</mat-option> }\n @if (ctx.range() === 'custom') { <mat-option value=\"custom\" disabled>Custom window</mat-option> }\n </mat-select>\n </mat-form-field>\n <form class=\"jc-lookup\" (ngSubmit)=\"lookup()\">\n <mat-icon>search</mat-icon>\n <input name=\"q\" [(ngModel)]=\"query\" placeholder=\"Trace ID, span ID or service\" aria-label=\"Go to trace, span or service\">\n </form>\n </div>\n @if (message()) { <p class=\"jc-muted jc-lookup-message\">{{ message() }}</p> }`,\n})\nexport class ContextBar {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n private readonly router = inject(Router);\n private readonly links = inject(ObservabilityLinks);\n protected readonly ranges = Object.keys(RANGES);\n protected readonly environments = toSignal(this.api.environments().pipe(map(p => p.items), catchError(() => of([]))), { initialValue: [] });\n protected query = '';\n protected readonly message = signal('');\n\n lookup() {\n const q = this.query.trim();\n if (!q) return;\n this.message.set('Searching…');\n this.api.search(q).subscribe({\n next: page => {\n const hit = page.items[0];\n if (!hit) { this.message.set(`Nothing found for \"${q}\".`); return; }\n this.message.set(page.items.length > 1 ? `${page.items.length} matches; showing the first.` : '');\n const extra = hit.kind === 'span' ? { span: hit.id } : {};\n void (hit.kind === 'service'\n ? this.router.navigate(this.links.to('services', hit.id), { queryParams: this.ctx.keep() })\n : this.router.navigate(this.links.to('traces', hit.traceId), { queryParams: this.ctx.keep(extra) }));\n },\n error: () => this.message.set('Search failed.'),\n });\n }\n}\n\nexport interface ChartSeries { name: string; points: { at: string; value: number | null }[]; unit?: string; }\n\n/**\n * The API returns no point for a bucket without data; give each missing bucket a null so the chart shows a gap\n * instead of drawing a line across it (no data is not zero).\n */\nexport function withGaps(points: { at: string; value: number | null }[], from: string, to: string, stepSeconds: number) {\n const step = stepSeconds * 1000;\n const byTime = new Map(points.map(p => [new Date(p.at).getTime(), p.value]));\n const out: { at: string; value: number | null }[] = [];\n for (let t = Math.floor(new Date(from).getTime() / step) * step; t < new Date(to).getTime(); t += step) {\n out.push({ at: new Date(t).toISOString(), value: byTime.get(t) ?? null });\n }\n return out;\n}\n\n/**\n * A time series chart (Apache ECharts, loaded on first use). A missing bucket is a gap in the line, never zero.\n */\n@Component({\n selector: 'jc-chart',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<div class=\"jc-chart-title\">{{ title() }}</div>\n @if (empty()) { <div class=\"jc-chart jc-chart-empty\">No data in this window</div> }\n <div #host class=\"jc-chart\" [hidden]=\"empty()\"></div>`,\n})\nexport class Chart implements OnDestroy {\n readonly title = input('');\n readonly series = input<ChartSeries[]>([]);\n readonly format = input<(v: number) => string>(v => String(Math.round(v * 100) / 100));\n private readonly host = viewChild.required<ElementRef<HTMLDivElement>>('host');\n protected readonly empty = computed(() => this.series().every(s => s.points.every(p => p.value === null)));\n private chart?: ECharts;\n private observer?: ResizeObserver;\n\n constructor() {\n afterNextRender(async () => {\n // Only what a line chart needs: the full library is three times the size.\n const [echarts, charts, components, renderers] = await Promise.all([\n import('echarts/core'), import('echarts/charts'), import('echarts/components'), import('echarts/renderers')]);\n echarts.use([charts.LineChart, components.GridComponent, components.TooltipComponent, components.LegendComponent, renderers.SVGRenderer]);\n this.chart = echarts.init(this.host().nativeElement, undefined, { renderer: 'svg' });\n this.observer = new ResizeObserver(() => this.chart?.resize());\n this.observer.observe(this.host().nativeElement);\n this.render();\n });\n effect(() => { this.series(); this.render(); });\n }\n\n private render() {\n if (!this.chart) return;\n const format = this.format();\n const dark = document.body.classList.contains('dark');\n const option: EChartsOption = {\n animation: false,\n grid: { left: 56, right: 16, top: 16, bottom: 28 },\n tooltip: { trigger: 'axis', valueFormatter: v => (typeof v === 'number' ? format(v) : '—') },\n legend: this.series().length > 1 ? { top: 0, textStyle: { color: dark ? '#ddd' : '#333' } } : undefined,\n xAxis: { type: 'time', axisLabel: { color: dark ? '#bbb' : '#555' } },\n yAxis: { type: 'value', axisLabel: { color: dark ? '#bbb' : '#555', formatter: (v: number) => format(v) },\n splitLine: { lineStyle: { color: dark ? '#333' : '#eee' } } },\n series: this.series().map(s => ({\n name: s.name, type: 'line', showSymbol: false, connectNulls: false,\n data: s.points.map(p => [p.at, p.value]),\n })),\n };\n this.chart.setOption(option, true);\n }\n\n ngOnDestroy() {\n this.observer?.disconnect();\n this.chart?.dispose();\n }\n}\n","import { DestroyRef, Signal, computed, inject, linkedSignal, signal } from '@angular/core';\nimport { rxResource, takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { PageEvent } from '@angular/material/paginator';\nimport { Observable, map } from 'rxjs';\nimport { Query } from '../core/api.service';\n\n/** A page from an API list that pages with a cursor: alerts, traces, logs. */\nexport interface CursorPage<T> { items: T[]; nextCursor?: string | null; }\n\n/**\n * A newest-first list read page by page with the API's cursor. The first page loads whenever `params` change; `more()`\n * appends the next page, asked with exactly the query of the first one (its time window included), so the list grows\n * without shifting, skipping or repeating rows. Call it in an injection context, such as a field initializer.\n */\nexport function cursorList<P, T>(params: () => P, query: (params: P) => Query, fetch: (query: Query) => Observable<CursorPage<T>>) {\n const destroyRef = inject(DestroyRef);\n const first = rxResource({\n params,\n stream: ({ params }) => {\n const q = query(params);\n return fetch(q).pipe(map(page => ({ page, query: q })));\n },\n });\n // Pages after the first; reset whenever the first page reloads.\n const rest = linkedSignal({ source: first.value, computation: () => ({ items: [] as T[], cursor: undefined as string | null | undefined }) });\n const loadingMore = signal(false);\n const moreError = signal<unknown>(null);\n const items = computed(() => [...(first.value()?.page.items ?? []), ...rest().items]);\n const cursor = computed(() => rest().cursor !== undefined ? rest().cursor! : first.value()?.page.nextCursor ?? null);\n\n function more() {\n const current = first.value();\n const next = cursor();\n if (!current || !next || loadingMore()) return;\n loadingMore.set(true);\n moreError.set(null);\n fetch({ ...current.query, cursor: next }).pipe(takeUntilDestroyed(destroyRef)).subscribe({\n next: page => rest.update(r => ({ items: [...r.items, ...page.items], cursor: page.nextCursor ?? null })),\n error: e => { moreError.set(e); loadingMore.set(false); },\n complete: () => loadingMore.set(false),\n });\n }\n\n return { first, items, hasMore: computed(() => cursor() !== null), loadingMore, moreError: moreError.asReadonly(), more };\n}\n\n/**\n * Pages a list already in memory for display (the inventory lists, which the Console sorts and filters itself). Goes\n * back to the first page whenever the rows change, for example when a filter narrows them.\n */\nexport function screenPages<T>(rows: Signal<T[]>, sizes = [50, 100, 200]) {\n const size = signal(sizes[0]);\n const index = linkedSignal({ source: rows, computation: () => 0 });\n return {\n sizes,\n size: size.asReadonly(),\n index: index.asReadonly(),\n visible: computed(() => rows().slice(index() * size(), (index() + 1) * size())),\n /** More rows than one page: show the paginator. */\n needed: computed(() => rows().length > sizes[0]),\n change(event: PageEvent) { size.set(event.pageSize); index.set(event.pageIndex); },\n };\n}\n","import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input, linkedSignal } from '@angular/core';\nimport { rxResource } from '@angular/core/rxjs-interop';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { MatSelectModule } from '@angular/material/select';\nimport { RouterLink } from '@angular/router';\nimport { ApiService, Query } from '../core/api.service';\nimport { ContextService, MAX_SEARCH_MS } from '../core/context.service';\nimport { DurationPipe } from './format';\nimport { cursorList } from './paging';\nimport { Chart, withGaps } from './ui';\n\n/** Shows a request's failure as a short line instead of a broken page. */\nexport function problemText(error: unknown): string {\n const e = error as { status?: number; error?: { title?: string; detail?: string } } | undefined;\n if (!e) return '';\n if (e.status === 0) return 'The Juice API cannot be reached.';\n return [e.error?.title, e.error?.detail].filter(Boolean).join(': ') || `Request failed (${e.status}).`;\n}\n\n/**\n * Traces matching a query in the current context, newest first (Console requirements §5): enough per row to pick a\n * useful one, and one click to its waterfall.\n */\n@Component({\n selector: 'jc-trace-table',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, DatePipe, DurationPipe, MatProgressBarModule, MatButtonModule],\n template: `\n @if (traces.first.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (traces.first.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (traces.first.hasValue()) {\n @if (rows().length === 0) { <p class=\"jc-muted\">No traces match in this window.</p> }\n @else {\n <table class=\"jc-table\">\n <thead><tr><th>Start</th><th>Root operation</th><th>Duration</th><th>Spans</th><th>Status</th><th>Instance</th></tr></thead>\n <tbody>\n @for (t of rows(); track t.traceId) {\n <tr>\n <td class=\"jc-nowrap\">{{ t.startedAt | date: 'MMM d, HH:mm:ss.SSS' }}</td>\n <td><a [routerLink]=\"['traces', t.traceId] | jcLink\" [queryParams]=\"ctx.keep()\">{{ t.rootService }} · {{ t.rootOperation }}</a>\n @if (t.services.length > 1) { <div class=\"jc-muted jc-small\">{{ t.services.join(' → ') }}</div> }</td>\n <td class=\"jc-nowrap\"><span class=\"jc-bar\" [style.width.%]=\"100 * t.durationMs / longest()\"></span>{{ t.durationMs | duration }}</td>\n <td>{{ t.spanCount }}</td>\n <td>@if (t.errorCount > 0) { <span class=\"jc-badge jc-health-unhealthy\">{{ t.errorCount }} error{{ t.errorCount > 1 ? 's' : '' }}</span> }\n @else { <span class=\"jc-badge jc-health-healthy\">ok</span> }</td>\n <td class=\"jc-muted jc-small\">{{ t.rootInstance ?? t.rootHost ?? '—' }}</td>\n </tr>\n }\n </tbody>\n </table>\n <div class=\"jc-more\">\n <span class=\"jc-muted\">{{ rows().length }} trace{{ rows().length === 1 ? '' : 's' }}{{ traces.hasMore() ? ', newest first' : '' }}</span>\n @if (traces.hasMore()) {\n <button mat-button (click)=\"traces.more()\" [disabled]=\"traces.loadingMore()\">{{ traces.loadingMore() ? 'Loading…' : 'Older traces' }}</button>\n }\n @if (traces.moreError()) { <span class=\"jc-error\">{{ moreError() }}</span> }\n </div>\n }\n }`,\n})\nexport class TraceTable {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly query = input<Query>({});\n readonly limit = input(50);\n\n /** \"Older traces\" appends the next page of the same search, over the same window. */\n protected readonly traces = cursorList(\n () => ({ query: this.query(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute(), limit: this.limit() }),\n p => ({ environment: p.env, ...this.ctx.window(MAX_SEARCH_MS), limit: p.limit, ...p.query }),\n q => this.api.traces(q));\n protected readonly rows = this.traces.items;\n protected readonly longest = computed(() => Math.max(1, ...this.rows().map(t => t.durationMs)));\n protected readonly error = computed(() => problemText(this.traces.first.error()));\n protected readonly moreError = computed(() => problemText(this.traces.moreError()));\n}\n\n/**\n * Log records for a query in the current context (Console requirements §7): each with its trace and span, when the\n * record carries them.\n */\n@Component({\n selector: 'jc-log-table',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, DatePipe, MatProgressBarModule, MatButtonModule],\n template: `\n @if (logs.first.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (logs.first.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (logs.first.hasValue()) {\n @if (rows().length === 0) { <p class=\"jc-muted\">No log records match in this window.</p> }\n @else {\n <table class=\"jc-table jc-logs\">\n <thead><tr><th>Time</th><th>Severity</th>@if (!compact()) { <th>Service</th> }<th>Message</th>@if (!compact()) { <th>Trace</th> }</tr></thead>\n <tbody>\n @for (l of rows(); track $index) {\n <tr>\n <td class=\"jc-nowrap\">{{ l.timestamp | date: 'MMM d, HH:mm:ss.SSS' }}</td>\n <td><span class=\"jc-badge\" [class]=\"severityClass(l.severityNumber)\">{{ l.severity || l.severityNumber }}</span></td>\n @if (!compact()) { <td class=\"jc-nowrap\">{{ l.service }}@if (l.instance) {<div class=\"jc-muted jc-small\">{{ l.instance }}</div>}</td> }\n <td><pre class=\"jc-body\">{{ l.body }}@if (l.bodyTruncated) {…}</pre></td>\n @if (!compact()) { <td class=\"jc-nowrap\">\n @if (l.traceId) {\n <a [routerLink]=\"['traces', l.traceId] | jcLink\" [queryParams]=\"ctx.keep(l.spanId ? { span: l.spanId } : {})\">{{ l.spanId ? 'span' : 'trace' }} ↗</a>\n } @else { <span class=\"jc-muted\">—</span> }\n </td> }\n </tr>\n }\n </tbody>\n </table>\n <div class=\"jc-more\">\n <span class=\"jc-muted\">{{ rows().length }} record{{ rows().length === 1 ? '' : 's' }}{{ logs.hasMore() ? ', newest first' : '' }}</span>\n @if (logs.hasMore()) {\n <button mat-button (click)=\"logs.more()\" [disabled]=\"logs.loadingMore()\">{{ logs.loadingMore() ? 'Loading…' : 'Older logs' }}</button>\n }\n @if (logs.moreError()) { <span class=\"jc-error\">{{ moreError() }}</span> }\n </div>\n }\n }`,\n})\nexport class LogTable {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly query = input<Query>({});\n /** Inside a span: its service and trace are already on screen, so only time, severity and message. */\n readonly compact = input(false);\n\n /** \"Older logs\" appends the next page of the same search, over the same window. */\n protected readonly logs = cursorList(\n () => ({ query: this.query(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n p => ({ environment: p.env, ...this.ctx.window(MAX_SEARCH_MS), limit: 200, ...p.query }),\n q => this.api.logs(q));\n protected readonly rows = this.logs.items;\n protected readonly error = computed(() => problemText(this.logs.first.error()));\n protected readonly moreError = computed(() => problemText(this.logs.moreError()));\n\n severityClass(n: number) {\n return n >= 17 ? 'jc-health-unhealthy' : n >= 13 ? 'jc-health-degraded' : n >= 9 ? 'jc-health-healthy' : 'jc-health-none';\n }\n}\n\n/**\n * The metrics reported for a scope (service, instance or host) and a chart of the one selected (Console requirements\n * §7): a counter as a rate, a gauge or histogram as an average.\n */\n@Component({\n selector: 'jc-metric-explorer',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatSelectModule, MatProgressBarModule, Chart],\n template: `\n @if (metrics.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (metrics.error()) { <p class=\"jc-error\">{{ metricsError() }}</p> }\n @if (metrics.hasValue()) {\n @if (names().length === 0) { <p class=\"jc-muted\">No metrics reported in this window.</p> }\n @else {\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-wide\">\n <mat-label>Metric</mat-label>\n <mat-select [value]=\"selected()\" (valueChange)=\"selected.set($event)\">\n @for (m of names(); track m.name) { <mat-option [value]=\"m.name\">{{ m.name }} <span class=\"jc-muted\">({{ m.type }}{{ m.unit ? ', ' + m.unit : '' }})</span></mat-option> }\n </mat-select>\n </mat-form-field>\n @if (series.hasValue()) {\n <jc-chart [title]=\"title()\" [series]=\"[{ name: series.value().name, points: points() }]\" />\n }\n }\n }`,\n})\nexport class MetricExplorer {\n private readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly scope = input<Query>({});\n readonly initial = input<string | null>(null);\n\n protected readonly metrics = rxResource({\n params: () => ({ scope: this.scope(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n stream: ({ params }) => this.api.metrics({ environment: params.env, ...this.ctx.window(), ...params.scope }),\n });\n protected readonly names = computed(() => this.metrics.value()?.items ?? []);\n protected readonly selected = linkedSignal(() => {\n const names = this.names();\n return names.find(m => m.name === this.initial())?.name ?? names[0]?.name ?? null;\n });\n protected readonly series = rxResource({\n // Idle until a metric is chosen.\n params: () => this.selected() ? { name: this.selected()!, scope: this.scope(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() } : undefined,\n stream: ({ params }) => {\n this.window = this.ctx.window();\n return this.api.series({ name: params.name, environment: params.env, ...this.window, ...params.scope });\n },\n });\n private window = { from: '', to: '' };\n protected readonly points = computed(() => {\n const s = this.series.value();\n return s ? withGaps(s.points, this.window.from, this.window.to, s.stepSeconds) : [];\n });\n protected readonly title = computed(() => {\n const s = this.series.value();\n if (!s) return '';\n const what = s.aggregation === 'rate' ? 'rate per second' : 'average';\n return `${s.name} — ${what}${s.unit ? ` (${s.unit}${s.aggregation === 'rate' ? '/s' : ''})` : ''}`;\n });\n protected readonly metricsError = computed(() => problemText(this.metrics.error()));\n}\n\n/** Requests, error rate and p95 of a service (or one operation) over the window. */\n@Component({\n selector: 'jc-service-stats',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [Chart, MatProgressBarModule],\n template: `\n @if (stats.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (stats.hasValue()) {\n <div class=\"jc-grid-3\">\n <jc-chart title=\"Requests per second\" [series]=\"[{ name: 'requests/s', points: pick('requestsPerSecond') }]\" />\n <jc-chart title=\"Error rate\" [series]=\"[{ name: 'errors', points: pick('errorRate') }]\" [format]=\"percent\" />\n <jc-chart title=\"p95 latency\" [series]=\"[{ name: 'p95', points: pick('p95Ms') }]\" [format]=\"ms\" />\n </div>\n }`,\n})\nexport class ServiceStatsCharts {\n private readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly query = input.required<Query>();\n private window = { from: '', to: '' };\n protected readonly percent = (v: number) => `${Math.round(v * 1000) / 10} %`;\n protected readonly ms = (v: number) => `${Math.round(v)} ms`;\n\n protected readonly stats = rxResource({\n params: () => ({ query: this.query(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n stream: ({ params }) => {\n this.window = this.ctx.window();\n return this.api.serviceStats({ environment: params.env, ...this.window, ...params.query });\n },\n });\n\n pick(field: 'requestsPerSecond' | 'errorRate' | 'p95Ms') {\n const s = this.stats.value();\n return s ? withGaps(s.points.map(p => ({ at: p.at, value: p[field] })), this.window.from, this.window.to, s.stepSeconds) : [];\n }\n}\n\n"],"mappings":"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"}
@@ -0,0 +1,458 @@
1
+ import { ApiService, ContextService, LinkPipe, ObservabilityLinks } from "./juice-js-observability.mjs";
2
+ import { a as problemText, c as ConfidenceBadge, f as AgoPipe, l as ContextBar, m as PercentPipe, p as DurationPipe, u as HealthBadge } from "./juice-js-observability-telemetry-VA4_3nzr.mjs";
3
+ import * as i0 from "@angular/core";
4
+ import { ChangeDetectionStrategy, Component, afterNextRender, computed, effect, inject, input, viewChild } from "@angular/core";
5
+ import { rxResource } from "@angular/core/rxjs-interop";
6
+ import { Router, RouterLink } from "@angular/router";
7
+ import * as i1 from "@angular/material/button";
8
+ import { MatButtonModule } from "@angular/material/button";
9
+ import * as i2 from "@angular/material/progress-bar";
10
+ import { MatProgressBarModule } from "@angular/material/progress-bar";
11
+ import { MatIconModule } from "@angular/material/icon";
12
+ const HEALTH_COLORS = {
13
+ Healthy: "#16a34a",
14
+ Degraded: "#d97706",
15
+ Unhealthy: "#dc2626",
16
+ Critical: "#991b1b",
17
+ Offline: "#6b7280",
18
+ Unknown: "#9ca3af"
19
+ };
20
+ var TopologyPage = class TopologyPage {
21
+ ctx = inject(ContextService);
22
+ api = inject(ApiService);
23
+ router = inject(Router);
24
+ links = inject(ObservabilityLinks);
25
+ edge = input(...ngDevMode ? [void 0, { debugName: "edge" }] : /* istanbul ignore next */ []);
26
+ node = input(...ngDevMode ? [void 0, { debugName: "node" }] : /* istanbul ignore next */ []);
27
+ graph = viewChild.required("graph", ...ngDevMode ? [{ debugName: "graph" }] : /* istanbul ignore next */ []);
28
+ cy;
29
+ topology = rxResource({
30
+ params: () => this.ctx.environment(),
31
+ stream: ({ params }) => this.api.topology(params || null)
32
+ });
33
+ error = computed(() => problemText(this.topology.error()), ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
34
+ nodes = computed(() => new Map((this.topology.value()?.nodes ?? []).map((n) => [n.id, n])), ...ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []);
35
+ selectedEdge = computed(() => this.topology.value()?.edges.find((e) => e.id === this.edge()) ?? null, ...ngDevMode ? [{ debugName: "selectedEdge" }] : /* istanbul ignore next */ []);
36
+ selectedNode = computed(() => (this.node() ? this.nodes().get(this.node()) : null) ?? null, ...ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []);
37
+ evidence = rxResource({
38
+ params: () => this.edge() ? {
39
+ edge: this.edge(),
40
+ env: this.ctx.environment(),
41
+ range: this.ctx.range(),
42
+ abs: this.ctx.absolute()
43
+ } : void 0,
44
+ stream: ({ params }) => this.api.dependency(params.edge, {
45
+ environment: params.env,
46
+ ...this.ctx.window()
47
+ })
48
+ });
49
+ constructor() {
50
+ afterNextRender(async () => {
51
+ const cytoscape = (await import("cytoscape")).default;
52
+ this.cy = cytoscape({
53
+ container: this.graph().nativeElement,
54
+ wheelSensitivity: .3,
55
+ style: [
56
+ {
57
+ selector: "node",
58
+ style: {
59
+ label: "data(label)",
60
+ "font-size": 11,
61
+ color: "#888",
62
+ "text-valign": "bottom",
63
+ "text-margin-y": 4,
64
+ "background-color": "data(color)",
65
+ width: 26,
66
+ height: 26,
67
+ "border-width": 2,
68
+ "border-color": "#fff"
69
+ }
70
+ },
71
+ {
72
+ selector: "node[type = \"database\"]",
73
+ style: { shape: "diamond" }
74
+ },
75
+ {
76
+ selector: "node[type = \"messaging-destination\"]",
77
+ style: { shape: "hexagon" }
78
+ },
79
+ {
80
+ selector: "node[type = \"service\"]",
81
+ style: { shape: "round-rectangle" }
82
+ },
83
+ {
84
+ selector: "edge",
85
+ style: {
86
+ width: 2,
87
+ "curve-style": "bezier",
88
+ "target-arrow-shape": "triangle",
89
+ "line-color": "#94a3b8",
90
+ "target-arrow-color": "#94a3b8",
91
+ label: "data(label)",
92
+ "font-size": 9,
93
+ color: "#999",
94
+ "text-rotation": "autorotate"
95
+ }
96
+ },
97
+ {
98
+ selector: "edge[confidence = \"inferred\"]",
99
+ style: { "line-style": "dashed" }
100
+ },
101
+ {
102
+ selector: ":selected",
103
+ style: {
104
+ "line-color": "#2563eb",
105
+ "target-arrow-color": "#2563eb",
106
+ "border-color": "#2563eb",
107
+ "border-width": 3
108
+ }
109
+ }
110
+ ]
111
+ });
112
+ this.cy.on("tap", "edge", (e) => this.choose({
113
+ edge: e.target.id(),
114
+ node: null
115
+ }));
116
+ this.cy.on("tap", "node", (e) => this.choose({
117
+ node: e.target.id(),
118
+ edge: null
119
+ }));
120
+ this.cy.on("tap", (e) => {
121
+ if (e.target === this.cy) this.choose({
122
+ node: null,
123
+ edge: null
124
+ });
125
+ });
126
+ this.draw(this.topology.value());
127
+ });
128
+ effect(() => this.draw(this.topology.value()));
129
+ effect(() => {
130
+ const id = this.edge() ?? this.node();
131
+ this.cy?.elements().unselect();
132
+ if (id) this.cy?.getElementById(id).select();
133
+ });
134
+ }
135
+ draw(topology) {
136
+ if (!this.cy || !topology) return;
137
+ this.cy.elements().remove();
138
+ this.cy.add([...topology.nodes.map((n) => ({ data: {
139
+ id: n.id,
140
+ label: n.displayName,
141
+ type: n.type,
142
+ color: n.type === "service" ? HEALTH_COLORS[n.health ?? "Unknown"] : "#64748b"
143
+ } })), ...topology.edges.filter((e) => this.nodes().has(e.source) && this.nodes().has(e.target)).map((e) => ({ data: {
144
+ id: e.id,
145
+ source: e.source,
146
+ target: e.target,
147
+ confidence: e.confidence.toLowerCase(),
148
+ label: e.confidence.toLowerCase() === "inferred" ? `${e.relation} (inferred)` : e.relation
149
+ } }))]);
150
+ this.cy.layout({
151
+ name: "breadthfirst",
152
+ directed: true,
153
+ padding: 30,
154
+ spacingFactor: 1.2
155
+ }).run();
156
+ const id = this.edge() ?? this.node();
157
+ if (id) this.cy.getElementById(id).select();
158
+ }
159
+ fit() {
160
+ this.cy?.fit(void 0, 30);
161
+ }
162
+ choose(selection) {
163
+ this.router.navigate([], {
164
+ queryParams: selection,
165
+ queryParamsHandling: "merge",
166
+ replaceUrl: true
167
+ });
168
+ }
169
+ name(id) {
170
+ return this.nodes().get(id)?.displayName ?? id;
171
+ }
172
+ link(id) {
173
+ return this.links.to(this.nodes().get(id)?.type === "service" ? "services" : "resources", id);
174
+ }
175
+ ngOnDestroy() {
176
+ this.cy?.destroy();
177
+ }
178
+ static ɵfac = i0.ɵɵngDeclareFactory({
179
+ minVersion: "12.0.0",
180
+ version: "22.2.4",
181
+ ngImport: i0,
182
+ type: TopologyPage,
183
+ deps: [],
184
+ target: i0.ɵɵFactoryTarget.Component
185
+ });
186
+ static ɵcmp = i0.ɵɵngDeclareComponent({
187
+ minVersion: "17.0.0",
188
+ version: "22.2.4",
189
+ type: TopologyPage,
190
+ isStandalone: true,
191
+ selector: "ng-component",
192
+ inputs: {
193
+ edge: {
194
+ classPropertyName: "edge",
195
+ publicName: "edge",
196
+ isSignal: true,
197
+ isRequired: false,
198
+ transformFunction: null
199
+ },
200
+ node: {
201
+ classPropertyName: "node",
202
+ publicName: "node",
203
+ isSignal: true,
204
+ isRequired: false,
205
+ transformFunction: null
206
+ }
207
+ },
208
+ viewQueries: [{
209
+ propertyName: "graph",
210
+ first: true,
211
+ predicate: ["graph"],
212
+ descendants: true,
213
+ isSignal: true
214
+ }],
215
+ ngImport: i0,
216
+ template: `
217
+ <jc-context-bar><h1>Dependencies</h1></jc-context-bar>
218
+ @if (topology.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
219
+ @if (topology.error()) { <p class="jc-error">{{ error() }}</p> }
220
+ <div class="jc-topology">
221
+ <div class="jc-graph-wrap">
222
+ <div #graph class="jc-graph" aria-label="Dependency graph"></div>
223
+ <div class="jc-graph-legend">
224
+ <span><i class="jc-line"></i> observed</span><span><i class="jc-line jc-dashed"></i> inferred</span>
225
+ <span>■ service ◆ database ⬣ destination</span>
226
+ <button mat-button (click)="fit()">Fit</button>
227
+ </div>
228
+ @if (topology.value()?.truncated) { <p class="jc-muted">Showing the most recent edges only.</p> }
229
+ @if (topology.hasValue() && topology.value()!.nodes.length === 0) { <p class="jc-muted">No dependencies seen{{ ctx.environment() ? ' in ' + ctx.environment() : '' }}.</p> }
230
+ </div>
231
+
232
+ <aside class="jc-card jc-side">
233
+ @if (selectedEdge(); as e) {
234
+ <h2>{{ name(e.source) }} → {{ name(e.target) }}</h2>
235
+ <p>{{ e.relation }} <jc-confidence [confidence]="e.confidence" /></p>
236
+ @if (e.confidence.toLowerCase() !== 'observed') {
237
+ <p class="jc-muted jc-small">Inferred from span attributes (for example db.system.name or messaging.destination.name), not a call
238
+ seen between two instrumented services.</p>
239
+ }
240
+ @if (evidence.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
241
+ @if (evidence.value(); as ev) {
242
+ <dl class="jc-facts">
243
+ <dt>Calls in window</dt><dd>{{ ev.count }}</dd>
244
+ <dt>Error rate</dt><dd [class.jc-bad]="ev.errorRate >= 0.05">{{ ev.count ? (ev.errorRate | percent2) : 'no data' }}</dd>
245
+ <dt>p95</dt><dd>{{ ev.p95Ms | duration }}</dd>
246
+ <dt>Last seen</dt><dd>{{ ev.lastSeen ?? e.lastSeen | ago }}</dd>
247
+ </dl>
248
+ <h3>Representative traces</h3>
249
+ @for (t of ev.errorTraces; track t) { <div><a [routerLink]="['traces', t] | jcLink" [queryParams]="ctx.keep()" class="jc-mono">{{ t.slice(0, 16) }}…</a> <span class="jc-badge jc-health-unhealthy">error</span></div> }
250
+ @for (t of ev.slowestTraces; track t) { <div><a [routerLink]="['traces', t] | jcLink" [queryParams]="ctx.keep()" class="jc-mono">{{ t.slice(0, 16) }}…</a> <span class="jc-muted jc-small">slow</span></div> }
251
+ @if (!ev.errorTraces.length && !ev.slowestTraces.length) { <p class="jc-muted">No calls in this window.</p> }
252
+ <a mat-stroked-button [routerLink]="['traces'] | jcLink" [queryParams]="ctx.keep({ dependency: e.id })">All traces through this edge</a>
253
+ }
254
+ <p><a [routerLink]="link(e.source)" [queryParams]="ctx.keep()">Open {{ name(e.source) }}</a> ·
255
+ <a [routerLink]="link(e.target)" [queryParams]="ctx.keep()">Open {{ name(e.target) }}</a></p>
256
+ } @else if (selectedNode(); as n) {
257
+ <h2>{{ n.displayName }}</h2>
258
+ <p>{{ n.type }} <jc-health [state]="n.health" /></p>
259
+ <a mat-stroked-button [routerLink]="link(n.id)" [queryParams]="ctx.keep()">Open</a>
260
+ } @else {
261
+ <p class="jc-muted">Select a node or an edge. Scroll to zoom, drag to pan.</p>
262
+ }
263
+ </aside>
264
+ </div>`,
265
+ isInline: true,
266
+ dependencies: [
267
+ {
268
+ kind: "directive",
269
+ type: RouterLink,
270
+ selector: "[routerLink]",
271
+ inputs: [
272
+ "target",
273
+ "queryParams",
274
+ "fragment",
275
+ "queryParamsHandling",
276
+ "state",
277
+ "info",
278
+ "relativeTo",
279
+ "preserveFragment",
280
+ "skipLocationChange",
281
+ "replaceUrl",
282
+ "browserUrl",
283
+ "routerLink"
284
+ ]
285
+ },
286
+ {
287
+ kind: "ngmodule",
288
+ type: MatButtonModule
289
+ },
290
+ {
291
+ kind: "component",
292
+ type: i1.MatButton,
293
+ selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ",
294
+ inputs: ["matButton"],
295
+ exportAs: ["matButton", "matAnchor"]
296
+ },
297
+ {
298
+ kind: "ngmodule",
299
+ type: MatIconModule
300
+ },
301
+ {
302
+ kind: "ngmodule",
303
+ type: MatProgressBarModule
304
+ },
305
+ {
306
+ kind: "component",
307
+ type: i2.MatProgressBar,
308
+ selector: "mat-progress-bar",
309
+ inputs: [
310
+ "color",
311
+ "value",
312
+ "bufferValue",
313
+ "mode"
314
+ ],
315
+ outputs: ["animationEnd"],
316
+ exportAs: ["matProgressBar"]
317
+ },
318
+ {
319
+ kind: "component",
320
+ type: ContextBar,
321
+ selector: "jc-context-bar"
322
+ },
323
+ {
324
+ kind: "component",
325
+ type: HealthBadge,
326
+ selector: "jc-health",
327
+ inputs: ["state", "stale"]
328
+ },
329
+ {
330
+ kind: "component",
331
+ type: ConfidenceBadge,
332
+ selector: "jc-confidence",
333
+ inputs: ["confidence"]
334
+ },
335
+ {
336
+ kind: "pipe",
337
+ type: LinkPipe,
338
+ name: "jcLink"
339
+ },
340
+ {
341
+ kind: "pipe",
342
+ type: AgoPipe,
343
+ name: "ago"
344
+ },
345
+ {
346
+ kind: "pipe",
347
+ type: DurationPipe,
348
+ name: "duration"
349
+ },
350
+ {
351
+ kind: "pipe",
352
+ type: PercentPipe,
353
+ name: "percent2"
354
+ }
355
+ ],
356
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
357
+ });
358
+ };
359
+ i0.ɵɵngDeclareClassMetadata({
360
+ minVersion: "12.0.0",
361
+ version: "22.2.4",
362
+ ngImport: i0,
363
+ type: TopologyPage,
364
+ decorators: [{
365
+ type: Component,
366
+ args: [{
367
+ changeDetection: ChangeDetectionStrategy.OnPush,
368
+ imports: [
369
+ LinkPipe,
370
+ RouterLink,
371
+ MatButtonModule,
372
+ MatIconModule,
373
+ MatProgressBarModule,
374
+ ContextBar,
375
+ HealthBadge,
376
+ ConfidenceBadge,
377
+ AgoPipe,
378
+ DurationPipe,
379
+ PercentPipe
380
+ ],
381
+ template: `
382
+ <jc-context-bar><h1>Dependencies</h1></jc-context-bar>
383
+ @if (topology.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
384
+ @if (topology.error()) { <p class="jc-error">{{ error() }}</p> }
385
+ <div class="jc-topology">
386
+ <div class="jc-graph-wrap">
387
+ <div #graph class="jc-graph" aria-label="Dependency graph"></div>
388
+ <div class="jc-graph-legend">
389
+ <span><i class="jc-line"></i> observed</span><span><i class="jc-line jc-dashed"></i> inferred</span>
390
+ <span>■ service ◆ database ⬣ destination</span>
391
+ <button mat-button (click)="fit()">Fit</button>
392
+ </div>
393
+ @if (topology.value()?.truncated) { <p class="jc-muted">Showing the most recent edges only.</p> }
394
+ @if (topology.hasValue() && topology.value()!.nodes.length === 0) { <p class="jc-muted">No dependencies seen{{ ctx.environment() ? ' in ' + ctx.environment() : '' }}.</p> }
395
+ </div>
396
+
397
+ <aside class="jc-card jc-side">
398
+ @if (selectedEdge(); as e) {
399
+ <h2>{{ name(e.source) }} → {{ name(e.target) }}</h2>
400
+ <p>{{ e.relation }} <jc-confidence [confidence]="e.confidence" /></p>
401
+ @if (e.confidence.toLowerCase() !== 'observed') {
402
+ <p class="jc-muted jc-small">Inferred from span attributes (for example db.system.name or messaging.destination.name), not a call
403
+ seen between two instrumented services.</p>
404
+ }
405
+ @if (evidence.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
406
+ @if (evidence.value(); as ev) {
407
+ <dl class="jc-facts">
408
+ <dt>Calls in window</dt><dd>{{ ev.count }}</dd>
409
+ <dt>Error rate</dt><dd [class.jc-bad]="ev.errorRate >= 0.05">{{ ev.count ? (ev.errorRate | percent2) : 'no data' }}</dd>
410
+ <dt>p95</dt><dd>{{ ev.p95Ms | duration }}</dd>
411
+ <dt>Last seen</dt><dd>{{ ev.lastSeen ?? e.lastSeen | ago }}</dd>
412
+ </dl>
413
+ <h3>Representative traces</h3>
414
+ @for (t of ev.errorTraces; track t) { <div><a [routerLink]="['traces', t] | jcLink" [queryParams]="ctx.keep()" class="jc-mono">{{ t.slice(0, 16) }}…</a> <span class="jc-badge jc-health-unhealthy">error</span></div> }
415
+ @for (t of ev.slowestTraces; track t) { <div><a [routerLink]="['traces', t] | jcLink" [queryParams]="ctx.keep()" class="jc-mono">{{ t.slice(0, 16) }}…</a> <span class="jc-muted jc-small">slow</span></div> }
416
+ @if (!ev.errorTraces.length && !ev.slowestTraces.length) { <p class="jc-muted">No calls in this window.</p> }
417
+ <a mat-stroked-button [routerLink]="['traces'] | jcLink" [queryParams]="ctx.keep({ dependency: e.id })">All traces through this edge</a>
418
+ }
419
+ <p><a [routerLink]="link(e.source)" [queryParams]="ctx.keep()">Open {{ name(e.source) }}</a> ·
420
+ <a [routerLink]="link(e.target)" [queryParams]="ctx.keep()">Open {{ name(e.target) }}</a></p>
421
+ } @else if (selectedNode(); as n) {
422
+ <h2>{{ n.displayName }}</h2>
423
+ <p>{{ n.type }} <jc-health [state]="n.health" /></p>
424
+ <a mat-stroked-button [routerLink]="link(n.id)" [queryParams]="ctx.keep()">Open</a>
425
+ } @else {
426
+ <p class="jc-muted">Select a node or an edge. Scroll to zoom, drag to pan.</p>
427
+ }
428
+ </aside>
429
+ </div>`
430
+ }]
431
+ }],
432
+ ctorParameters: () => [],
433
+ propDecorators: {
434
+ edge: [{
435
+ type: i0.Input,
436
+ args: [{
437
+ isSignal: true,
438
+ alias: "edge",
439
+ required: false
440
+ }]
441
+ }],
442
+ node: [{
443
+ type: i0.Input,
444
+ args: [{
445
+ isSignal: true,
446
+ alias: "node",
447
+ required: false
448
+ }]
449
+ }],
450
+ graph: [{
451
+ type: i0.ViewChild,
452
+ args: ["graph", { isSignal: true }]
453
+ }]
454
+ }
455
+ });
456
+ export { TopologyPage };
457
+
458
+ //# sourceMappingURL=juice-js-observability-topology.page-DEUNp10U.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"juice-js-observability-topology.page-DEUNp10U.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/topology.page.ts"],"sourcesContent":["import { LinkPipe, ObservabilityLinks } from '../core/options';\nimport {\n ChangeDetectionStrategy, Component, ElementRef, OnDestroy, afterNextRender, computed, effect, inject, input, viewChild,\n} from '@angular/core';\nimport { rxResource } from '@angular/core/rxjs-interop';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { Router, RouterLink } from '@angular/router';\nimport type { Core } from 'cytoscape';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { Topology } from '../core/models';\nimport { AgoPipe, DurationPipe, PercentPipe } from '../shared/format';\nimport { ConfidenceBadge, ContextBar, HealthBadge } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\n\nconst HEALTH_COLORS: Record<string, string> = {\n Healthy: '#16a34a', Degraded: '#d97706', Unhealthy: '#dc2626', Critical: '#991b1b', Offline: '#6b7280', Unknown: '#9ca3af',\n};\n\n/**\n * Dependency topology (Console requirements §9): services, databases and destinations; zoom, pan and selection.\n * Observed edges are solid, inferred ones dashed and labelled, so an inference never reads as a confirmed call. A\n * selected edge shows its evidence and representative traces; the selection is in the URL.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatButtonModule, MatIconModule, MatProgressBarModule, ContextBar, HealthBadge, ConfidenceBadge,\n AgoPipe, DurationPipe, PercentPipe],\n template: `\n <jc-context-bar><h1>Dependencies</h1></jc-context-bar>\n @if (topology.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (topology.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n <div class=\"jc-topology\">\n <div class=\"jc-graph-wrap\">\n <div #graph class=\"jc-graph\" aria-label=\"Dependency graph\"></div>\n <div class=\"jc-graph-legend\">\n <span><i class=\"jc-line\"></i> observed</span><span><i class=\"jc-line jc-dashed\"></i> inferred</span>\n <span>■ service ◆ database ⬣ destination</span>\n <button mat-button (click)=\"fit()\">Fit</button>\n </div>\n @if (topology.value()?.truncated) { <p class=\"jc-muted\">Showing the most recent edges only.</p> }\n @if (topology.hasValue() && topology.value()!.nodes.length === 0) { <p class=\"jc-muted\">No dependencies seen{{ ctx.environment() ? ' in ' + ctx.environment() : '' }}.</p> }\n </div>\n\n <aside class=\"jc-card jc-side\">\n @if (selectedEdge(); as e) {\n <h2>{{ name(e.source) }} → {{ name(e.target) }}</h2>\n <p>{{ e.relation }} <jc-confidence [confidence]=\"e.confidence\" /></p>\n @if (e.confidence.toLowerCase() !== 'observed') {\n <p class=\"jc-muted jc-small\">Inferred from span attributes (for example db.system.name or messaging.destination.name), not a call\n seen between two instrumented services.</p>\n }\n @if (evidence.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (evidence.value(); as ev) {\n <dl class=\"jc-facts\">\n <dt>Calls in window</dt><dd>{{ ev.count }}</dd>\n <dt>Error rate</dt><dd [class.jc-bad]=\"ev.errorRate >= 0.05\">{{ ev.count ? (ev.errorRate | percent2) : 'no data' }}</dd>\n <dt>p95</dt><dd>{{ ev.p95Ms | duration }}</dd>\n <dt>Last seen</dt><dd>{{ ev.lastSeen ?? e.lastSeen | ago }}</dd>\n </dl>\n <h3>Representative traces</h3>\n @for (t of ev.errorTraces; track t) { <div><a [routerLink]=\"['traces', t] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-mono\">{{ t.slice(0, 16) }}…</a> <span class=\"jc-badge jc-health-unhealthy\">error</span></div> }\n @for (t of ev.slowestTraces; track t) { <div><a [routerLink]=\"['traces', t] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-mono\">{{ t.slice(0, 16) }}…</a> <span class=\"jc-muted jc-small\">slow</span></div> }\n @if (!ev.errorTraces.length && !ev.slowestTraces.length) { <p class=\"jc-muted\">No calls in this window.</p> }\n <a mat-stroked-button [routerLink]=\"['traces'] | jcLink\" [queryParams]=\"ctx.keep({ dependency: e.id })\">All traces through this edge</a>\n }\n <p><a [routerLink]=\"link(e.source)\" [queryParams]=\"ctx.keep()\">Open {{ name(e.source) }}</a> ·\n <a [routerLink]=\"link(e.target)\" [queryParams]=\"ctx.keep()\">Open {{ name(e.target) }}</a></p>\n } @else if (selectedNode(); as n) {\n <h2>{{ n.displayName }}</h2>\n <p>{{ n.type }} <jc-health [state]=\"n.health\" /></p>\n <a mat-stroked-button [routerLink]=\"link(n.id)\" [queryParams]=\"ctx.keep()\">Open</a>\n } @else {\n <p class=\"jc-muted\">Select a node or an edge. Scroll to zoom, drag to pan.</p>\n }\n </aside>\n </div>`,\n})\nexport class TopologyPage implements OnDestroy {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n private readonly router = inject(Router);\n private readonly links = inject(ObservabilityLinks);\n\n readonly edge = input<string>();\n readonly node = input<string>();\n\n private readonly graph = viewChild.required<ElementRef<HTMLDivElement>>('graph');\n private cy?: Core;\n\n protected readonly topology = rxResource({ params: () => this.ctx.environment(), stream: ({ params }) => this.api.topology(params || null) });\n protected readonly error = computed(() => problemText(this.topology.error()));\n private readonly nodes = computed(() => new Map((this.topology.value()?.nodes ?? []).map(n => [n.id, n])));\n protected readonly selectedEdge = computed(() => this.topology.value()?.edges.find(e => e.id === this.edge()) ?? null);\n protected readonly selectedNode = computed(() => (this.node() ? this.nodes().get(this.node()!) : null) ?? null);\n protected readonly evidence = rxResource({\n // No edge selected: undefined params leave the resource idle.\n params: () => this.edge() ? { edge: this.edge()!, env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() } : undefined,\n stream: ({ params }) => this.api.dependency(params.edge, { environment: params.env, ...this.ctx.window() }),\n });\n\n constructor() {\n afterNextRender(async () => {\n const cytoscape = (await import('cytoscape')).default;\n this.cy = cytoscape({\n container: this.graph().nativeElement,\n wheelSensitivity: 0.3,\n style: [\n { selector: 'node', style: {\n label: 'data(label)', 'font-size': 11, color: '#888', 'text-valign': 'bottom', 'text-margin-y': 4,\n 'background-color': 'data(color)', width: 26, height: 26, 'border-width': 2, 'border-color': '#fff' } },\n { selector: 'node[type = \"database\"]', style: { shape: 'diamond' } },\n { selector: 'node[type = \"messaging-destination\"]', style: { shape: 'hexagon' } },\n { selector: 'node[type = \"service\"]', style: { shape: 'round-rectangle' } },\n { selector: 'edge', style: {\n width: 2, 'curve-style': 'bezier', 'target-arrow-shape': 'triangle', 'line-color': '#94a3b8',\n 'target-arrow-color': '#94a3b8', label: 'data(label)', 'font-size': 9, color: '#999', 'text-rotation': 'autorotate' } },\n { selector: 'edge[confidence = \"inferred\"]', style: { 'line-style': 'dashed' } },\n { selector: ':selected', style: { 'line-color': '#2563eb', 'target-arrow-color': '#2563eb', 'border-color': '#2563eb', 'border-width': 3 } },\n ],\n });\n this.cy.on('tap', 'edge', e => this.choose({ edge: e.target.id(), node: null }));\n this.cy.on('tap', 'node', e => this.choose({ node: e.target.id(), edge: null }));\n this.cy.on('tap', e => { if (e.target === this.cy) this.choose({ node: null, edge: null }); });\n this.draw(this.topology.value());\n });\n effect(() => this.draw(this.topology.value()));\n effect(() => {\n const id = this.edge() ?? this.node();\n this.cy?.elements().unselect();\n if (id) this.cy?.getElementById(id).select();\n });\n }\n\n private draw(topology: Topology | undefined) {\n if (!this.cy || !topology) return;\n this.cy.elements().remove();\n this.cy.add([\n ...topology.nodes.map(n => ({ data: {\n id: n.id, label: n.displayName, type: n.type,\n color: n.type === 'service' ? HEALTH_COLORS[n.health ?? 'Unknown'] : '#64748b' } })),\n ...topology.edges.filter(e => this.nodes().has(e.source) && this.nodes().has(e.target)).map(e => ({ data: {\n id: e.id, source: e.source, target: e.target, confidence: e.confidence.toLowerCase(),\n label: e.confidence.toLowerCase() === 'inferred' ? `${e.relation} (inferred)` : e.relation } })),\n ]);\n this.cy.layout({ name: 'breadthfirst', directed: true, padding: 30, spacingFactor: 1.2 }).run();\n const id = this.edge() ?? this.node();\n if (id) this.cy.getElementById(id).select();\n }\n\n fit() { this.cy?.fit(undefined, 30); }\n\n private choose(selection: { edge?: string | null; node?: string | null }) {\n void this.router.navigate([], { queryParams: selection, queryParamsHandling: 'merge', replaceUrl: true });\n }\n\n name(id: string) { return this.nodes().get(id)?.displayName ?? id; }\n\n link(id: string) { return this.links.to(this.nodes().get(id)?.type === 'service' ? 'services' : 'resources', id); }\n\n ngOnDestroy() { this.cy?.destroy(); }\n}\n"],"mappings":"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"}