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

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 +60 -2
  2. package/fesm2022/juice-js-observability-alert.page-BHKAUvR-.mjs +404 -0
  3. package/fesm2022/juice-js-observability-alert.page-BHKAUvR-.mjs.map +1 -0
  4. package/fesm2022/juice-js-observability-alerts.page-C2tXzLQl.mjs +208 -0
  5. package/fesm2022/juice-js-observability-alerts.page-C2tXzLQl.mjs.map +1 -0
  6. package/fesm2022/juice-js-observability-logs.page--j6W1Ej-.mjs +413 -0
  7. package/fesm2022/juice-js-observability-logs.page--j6W1Ej-.mjs.map +1 -0
  8. package/fesm2022/juice-js-observability-metrics.page-DR6qZtfl.mjs +267 -0
  9. package/fesm2022/juice-js-observability-metrics.page-DR6qZtfl.mjs.map +1 -0
  10. package/fesm2022/juice-js-observability-overview.page-DU0GxSXQ.mjs +243 -0
  11. package/fesm2022/juice-js-observability-overview.page-DU0GxSXQ.mjs.map +1 -0
  12. package/fesm2022/juice-js-observability-resource.page-D6qpvo_f.mjs +274 -0
  13. package/fesm2022/juice-js-observability-resource.page-D6qpvo_f.mjs.map +1 -0
  14. package/fesm2022/juice-js-observability-resources.page-CR7-DFsh.mjs +209 -0
  15. package/fesm2022/juice-js-observability-resources.page-CR7-DFsh.mjs.map +1 -0
  16. package/fesm2022/juice-js-observability-rules.page-fq-wO5I_.mjs +175 -0
  17. package/fesm2022/juice-js-observability-rules.page-fq-wO5I_.mjs.map +1 -0
  18. package/fesm2022/juice-js-observability-service.page-Qc6FYChB.mjs +726 -0
  19. package/fesm2022/juice-js-observability-service.page-Qc6FYChB.mjs.map +1 -0
  20. package/fesm2022/juice-js-observability-services.page-CX6S7sUu.mjs +251 -0
  21. package/fesm2022/juice-js-observability-services.page-CX6S7sUu.mjs.map +1 -0
  22. package/fesm2022/juice-js-observability-telemetry-D9EYxkdr.mjs +1783 -0
  23. package/fesm2022/juice-js-observability-telemetry-D9EYxkdr.mjs.map +1 -0
  24. package/fesm2022/juice-js-observability-topology.page-CLz7J5pq.mjs +483 -0
  25. package/fesm2022/juice-js-observability-topology.page-CLz7J5pq.mjs.map +1 -0
  26. package/fesm2022/juice-js-observability-trace.page-DccvH1SV.mjs +518 -0
  27. package/fesm2022/juice-js-observability-trace.page-DccvH1SV.mjs.map +1 -0
  28. package/fesm2022/juice-js-observability-traces.page-BZxnac4Y.mjs +520 -0
  29. package/fesm2022/juice-js-observability-traces.page-BZxnac4Y.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 +216 -0
  34. package/types/juice-js-observability.d.ts +387 -0
@@ -0,0 +1,267 @@
1
+ import { ApiService, ContextService } from "./juice-js-observability.mjs";
2
+ import { l as ContextBar, n as MetricExplorer, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
3
+ import * as i0 from "@angular/core";
4
+ import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core";
5
+ import { map } from "rxjs";
6
+ import { Router } from "@angular/router";
7
+ import * as i1 from "@angular/forms";
8
+ import { FormsModule } from "@angular/forms";
9
+ import { MatFormFieldModule } from "@angular/material/form-field";
10
+ import * as i2 from "@angular/material/select";
11
+ import { MatSelectModule } from "@angular/material/select";
12
+ var MetricsPage = class MetricsPage {
13
+ ctx = inject(ContextService);
14
+ api = inject(ApiService);
15
+ router = inject(Router);
16
+ service = input(...ngDevMode ? [void 0, { debugName: "service" }] : /* istanbul ignore next */ []);
17
+ resource = input(...ngDevMode ? [void 0, { debugName: "resource" }] : /* istanbul ignore next */ []);
18
+ metric = input(...ngDevMode ? [void 0, { debugName: "metric" }] : /* istanbul ignore next */ []);
19
+ services = apiResource({ stream: () => this.api.resources("service").pipe(map((p) => p.items)) });
20
+ hosts = apiResource({ stream: () => this.api.resources("host").pipe(map((p) => p.items)) });
21
+ query = computed(() => ({
22
+ serviceId: this.service(),
23
+ resourceId: this.resource()
24
+ }), ...ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []);
25
+ scope(change) {
26
+ this.router.navigate([], {
27
+ queryParams: change,
28
+ queryParamsHandling: "merge"
29
+ });
30
+ }
31
+ static ɵfac = i0.ɵɵngDeclareFactory({
32
+ minVersion: "12.0.0",
33
+ version: "22.2.4",
34
+ ngImport: i0,
35
+ type: MetricsPage,
36
+ deps: [],
37
+ target: i0.ɵɵFactoryTarget.Component
38
+ });
39
+ static ɵcmp = i0.ɵɵngDeclareComponent({
40
+ minVersion: "17.0.0",
41
+ version: "22.2.4",
42
+ type: MetricsPage,
43
+ isStandalone: true,
44
+ selector: "ng-component",
45
+ inputs: {
46
+ service: {
47
+ classPropertyName: "service",
48
+ publicName: "service",
49
+ isSignal: true,
50
+ isRequired: false,
51
+ transformFunction: null
52
+ },
53
+ resource: {
54
+ classPropertyName: "resource",
55
+ publicName: "resource",
56
+ isSignal: true,
57
+ isRequired: false,
58
+ transformFunction: null
59
+ },
60
+ metric: {
61
+ classPropertyName: "metric",
62
+ publicName: "metric",
63
+ isSignal: true,
64
+ isRequired: false,
65
+ transformFunction: null
66
+ }
67
+ },
68
+ ngImport: i0,
69
+ template: `
70
+ <jc-context-bar><h1>Metrics</h1></jc-context-bar>
71
+ <div class="jc-filter-form">
72
+ <mat-form-field subscriptSizing="dynamic">
73
+ <mat-label>Service</mat-label>
74
+ <mat-select [ngModel]="service() ?? ''" (ngModelChange)="scope({ service: $event || null, resource: null })">
75
+ <mat-option value="">Any</mat-option>
76
+ @for (s of services.value() ?? []; track s.id) { <mat-option [value]="s.id">{{ s.key }}</mat-option> }
77
+ </mat-select>
78
+ </mat-form-field>
79
+ <mat-form-field subscriptSizing="dynamic">
80
+ <mat-label>Host</mat-label>
81
+ <mat-select [ngModel]="resource() ?? ''" (ngModelChange)="scope({ resource: $event || null, service: null })">
82
+ <mat-option value="">Any</mat-option>
83
+ @for (h of hosts.value() ?? []; track h.id) { <mat-option [value]="h.id">{{ h.displayName }}</mat-option> }
84
+ </mat-select>
85
+ </mat-form-field>
86
+ </div>
87
+ <jc-metric-explorer [scope]="query()" [initial]="metric() ?? null" />`,
88
+ isInline: true,
89
+ dependencies: [
90
+ {
91
+ kind: "ngmodule",
92
+ type: FormsModule
93
+ },
94
+ {
95
+ kind: "directive",
96
+ type: i1.NgControlStatus,
97
+ selector: "[formControlName],[ngModel],[formControl]"
98
+ },
99
+ {
100
+ kind: "directive",
101
+ type: i1.NgModel,
102
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
103
+ inputs: [
104
+ "name",
105
+ "disabled",
106
+ "ngModel",
107
+ "ngModelOptions"
108
+ ],
109
+ outputs: ["ngModelChange"],
110
+ exportAs: ["ngModel"]
111
+ },
112
+ {
113
+ kind: "ngmodule",
114
+ type: MatFormFieldModule
115
+ },
116
+ {
117
+ kind: "component",
118
+ type: i2.MatFormField,
119
+ selector: "mat-form-field",
120
+ inputs: [
121
+ "hideRequiredMarker",
122
+ "color",
123
+ "floatLabel",
124
+ "appearance",
125
+ "subscriptSizing",
126
+ "hintLabel"
127
+ ],
128
+ exportAs: ["matFormField"]
129
+ },
130
+ {
131
+ kind: "directive",
132
+ type: i2.MatLabel,
133
+ selector: "mat-label"
134
+ },
135
+ {
136
+ kind: "ngmodule",
137
+ type: MatSelectModule
138
+ },
139
+ {
140
+ kind: "component",
141
+ type: i2.MatSelect,
142
+ selector: "mat-select",
143
+ inputs: [
144
+ "aria-describedby",
145
+ "panelClass",
146
+ "disabled",
147
+ "disableRipple",
148
+ "tabIndex",
149
+ "hideSingleSelectionIndicator",
150
+ "placeholder",
151
+ "required",
152
+ "multiple",
153
+ "disableOptionCentering",
154
+ "compareWith",
155
+ "value",
156
+ "aria-label",
157
+ "aria-labelledby",
158
+ "errorStateMatcher",
159
+ "typeaheadDebounceInterval",
160
+ "sortComparator",
161
+ "id",
162
+ "panelWidth",
163
+ "canSelectNullableOptions"
164
+ ],
165
+ outputs: [
166
+ "openedChange",
167
+ "opened",
168
+ "closed",
169
+ "selectionChange",
170
+ "valueChange"
171
+ ],
172
+ exportAs: ["matSelect"]
173
+ },
174
+ {
175
+ kind: "component",
176
+ type: i2.MatOption,
177
+ selector: "mat-option",
178
+ inputs: [
179
+ "value",
180
+ "id",
181
+ "disabled"
182
+ ],
183
+ outputs: ["onSelectionChange"],
184
+ exportAs: ["matOption"]
185
+ },
186
+ {
187
+ kind: "component",
188
+ type: ContextBar,
189
+ selector: "jc-context-bar"
190
+ },
191
+ {
192
+ kind: "component",
193
+ type: MetricExplorer,
194
+ selector: "jc-metric-explorer",
195
+ inputs: ["scope", "initial"]
196
+ }
197
+ ],
198
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
199
+ });
200
+ };
201
+ i0.ɵɵngDeclareClassMetadata({
202
+ minVersion: "12.0.0",
203
+ version: "22.2.4",
204
+ ngImport: i0,
205
+ type: MetricsPage,
206
+ decorators: [{
207
+ type: Component,
208
+ args: [{
209
+ changeDetection: ChangeDetectionStrategy.OnPush,
210
+ imports: [
211
+ FormsModule,
212
+ MatFormFieldModule,
213
+ MatSelectModule,
214
+ ContextBar,
215
+ MetricExplorer
216
+ ],
217
+ template: `
218
+ <jc-context-bar><h1>Metrics</h1></jc-context-bar>
219
+ <div class="jc-filter-form">
220
+ <mat-form-field subscriptSizing="dynamic">
221
+ <mat-label>Service</mat-label>
222
+ <mat-select [ngModel]="service() ?? ''" (ngModelChange)="scope({ service: $event || null, resource: null })">
223
+ <mat-option value="">Any</mat-option>
224
+ @for (s of services.value() ?? []; track s.id) { <mat-option [value]="s.id">{{ s.key }}</mat-option> }
225
+ </mat-select>
226
+ </mat-form-field>
227
+ <mat-form-field subscriptSizing="dynamic">
228
+ <mat-label>Host</mat-label>
229
+ <mat-select [ngModel]="resource() ?? ''" (ngModelChange)="scope({ resource: $event || null, service: null })">
230
+ <mat-option value="">Any</mat-option>
231
+ @for (h of hosts.value() ?? []; track h.id) { <mat-option [value]="h.id">{{ h.displayName }}</mat-option> }
232
+ </mat-select>
233
+ </mat-form-field>
234
+ </div>
235
+ <jc-metric-explorer [scope]="query()" [initial]="metric() ?? null" />`
236
+ }]
237
+ }],
238
+ propDecorators: {
239
+ service: [{
240
+ type: i0.Input,
241
+ args: [{
242
+ isSignal: true,
243
+ alias: "service",
244
+ required: false
245
+ }]
246
+ }],
247
+ resource: [{
248
+ type: i0.Input,
249
+ args: [{
250
+ isSignal: true,
251
+ alias: "resource",
252
+ required: false
253
+ }]
254
+ }],
255
+ metric: [{
256
+ type: i0.Input,
257
+ args: [{
258
+ isSignal: true,
259
+ alias: "metric",
260
+ required: false
261
+ }]
262
+ }]
263
+ }
264
+ });
265
+ export { MetricsPage };
266
+
267
+ //# sourceMappingURL=juice-js-observability-metrics.page-DR6qZtfl.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"juice-js-observability-metrics.page-DR6qZtfl.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/metrics.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { FormsModule } from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatSelectModule } from '@angular/material/select';\nimport { Router } from '@angular/router';\nimport { map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { ContextBar } from '../shared/ui';\nimport { MetricExplorer } from '../shared/telemetry';\n\n/** Metrics of a service or a host (Console requirements §7): pick a scope, then a metric. */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatFormFieldModule, MatSelectModule, ContextBar, MetricExplorer],\n template: `\n <jc-context-bar><h1>Metrics</h1></jc-context-bar>\n <div class=\"jc-filter-form\">\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-label>Service</mat-label>\n <mat-select [ngModel]=\"service() ?? ''\" (ngModelChange)=\"scope({ service: $event || null, resource: null })\">\n <mat-option value=\"\">Any</mat-option>\n @for (s of services.value() ?? []; track s.id) { <mat-option [value]=\"s.id\">{{ s.key }}</mat-option> }\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-label>Host</mat-label>\n <mat-select [ngModel]=\"resource() ?? ''\" (ngModelChange)=\"scope({ resource: $event || null, service: null })\">\n <mat-option value=\"\">Any</mat-option>\n @for (h of hosts.value() ?? []; track h.id) { <mat-option [value]=\"h.id\">{{ h.displayName }}</mat-option> }\n </mat-select>\n </mat-form-field>\n </div>\n <jc-metric-explorer [scope]=\"query()\" [initial]=\"metric() ?? null\" />`,\n})\nexport class MetricsPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n private readonly router = inject(Router);\n readonly service = input<string>();\n readonly resource = input<string>();\n readonly metric = input<string>();\n\n protected readonly services = apiResource({ stream: () => this.api.resources('service').pipe(map(p => p.items)) });\n protected readonly hosts = apiResource({ stream: () => this.api.resources('host').pipe(map(p => p.items)) });\n protected readonly query = computed(() => ({ serviceId: this.service(), resourceId: this.resource() }));\n\n scope(change: Record<string, string | null>) {\n void this.router.navigate([], { queryParams: change, queryParamsHandling: 'merge' });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
2
+ import { _ as ReasonPipe, a as problemText, l as ContextBar, m as AgoPipe, u as HealthBadge, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
3
+ import * as i0 from "@angular/core";
4
+ import { ChangeDetectionStrategy, Component, computed, inject } from "@angular/core";
5
+ import { map } from "rxjs";
6
+ import { RouterLink } from "@angular/router";
7
+ import * as i1 from "@angular/material/progress-bar";
8
+ import { MatProgressBarModule } from "@angular/material/progress-bar";
9
+ const ORDER = [
10
+ "Critical",
11
+ "Unhealthy",
12
+ "Degraded",
13
+ "Offline",
14
+ "Unknown",
15
+ "Healthy"
16
+ ];
17
+ const SHOWN = 10;
18
+ var OverviewPage = class OverviewPage {
19
+ ctx = inject(ContextService);
20
+ api = inject(ApiService);
21
+ order = ORDER;
22
+ shown = SHOWN;
23
+ reasonPipe = new ReasonPipe();
24
+ reason = (r) => this.reasonPipe.transform(r);
25
+ health = apiResource({
26
+ params: () => this.ctx.environment(),
27
+ stream: ({ params }) => this.api.health({
28
+ subjectType: "service",
29
+ environment: params
30
+ }).pipe(map((p) => p.items))
31
+ });
32
+ alerts = apiResource({ stream: () => this.api.allAlerts({ state: "firing" }).pipe(map((p) => p.items)) });
33
+ error = computed(() => problemText(this.health.error()), ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
34
+ firing = computed(() => [...this.alerts.value() ?? []].sort((a, b) => Number(b.severity === "Critical") - Number(a.severity === "Critical") || (b.firedAt ?? "").localeCompare(a.firedAt ?? "")), ...ngDevMode ? [{ debugName: "firing" }] : /* istanbul ignore next */ []);
35
+ firingShown = computed(() => this.firing().slice(0, SHOWN), ...ngDevMode ? [{ debugName: "firingShown" }] : /* istanbul ignore next */ []);
36
+ criticalCount = computed(() => this.firing().filter((a) => a.severity === "Critical").length, ...ngDevMode ? [{ debugName: "criticalCount" }] : /* istanbul ignore next */ []);
37
+ counts = computed(() => {
38
+ const counts = {};
39
+ for (const h of this.health.value() ?? []) counts[h.state] = (counts[h.state] ?? 0) + 1;
40
+ return counts;
41
+ }, ...ngDevMode ? [{ debugName: "counts" }] : /* istanbul ignore next */ []);
42
+ attention = computed(() => (this.health.value() ?? []).filter((h) => h.state !== "Healthy").sort((a, b) => ORDER.indexOf(a.state) - ORDER.indexOf(b.state)), ...ngDevMode ? [{ debugName: "attention" }] : /* istanbul ignore next */ []);
43
+ static ɵfac = i0.ɵɵngDeclareFactory({
44
+ minVersion: "12.0.0",
45
+ version: "22.2.4",
46
+ ngImport: i0,
47
+ type: OverviewPage,
48
+ deps: [],
49
+ target: i0.ɵɵFactoryTarget.Component
50
+ });
51
+ static ɵcmp = i0.ɵɵngDeclareComponent({
52
+ minVersion: "17.0.0",
53
+ version: "22.2.4",
54
+ type: OverviewPage,
55
+ isStandalone: true,
56
+ selector: "ng-component",
57
+ ngImport: i0,
58
+ template: `
59
+ <jc-context-bar><h1>Overview</h1></jc-context-bar>
60
+ @if (health.isLoading() || alerts.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
61
+ @if (health.error()) { <p class="jc-error">{{ error() }}</p> }
62
+
63
+ <div class="jc-tiles">
64
+ @for (s of order; track s) {
65
+ <a class="jc-card jc-tile" [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">
66
+ <jc-health [state]="s" /><strong>{{ counts()[s] ?? 0 }}</strong><span class="jc-muted">services</span>
67
+ </a>
68
+ }
69
+ </div>
70
+
71
+ <section class="jc-card">
72
+ <div class="jc-row">
73
+ <h2>Firing alerts @if (firing().length) { <span class="jc-muted">({{ firing().length }}@if (criticalCount()) { · {{ criticalCount() }} critical})</span> }</h2>
74
+ @if (firing().length) { <a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">All firing alerts</a> }
75
+ </div>
76
+ @for (a of firingShown(); track a.id) {
77
+ <div class="jc-list-row">
78
+ <span class="jc-badge" [class.jc-health-unhealthy]="a.severity === 'Critical'" [class.jc-health-degraded]="a.severity !== 'Critical'">{{ a.severity }}</span>
79
+ <a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.rule }}</a>
80
+ <span>{{ a.subjectKey }}</span>
81
+ <span class="jc-muted">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>
82
+ </div>
83
+ } @empty { <p class="jc-muted">Nothing firing.</p> }
84
+ @if (firing().length > shown) {
85
+ <p class="jc-more"><span class="jc-muted">Critical first, then the newest; {{ firing().length - shown }} more.</span>
86
+ <a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">See all {{ firing().length }}</a></p>
87
+ }
88
+ </section>
89
+
90
+ <section class="jc-card">
91
+ <h2>Services needing attention @if (attention().length) { <span class="jc-muted">({{ attention().length }})</span> }</h2>
92
+ @for (h of attention().slice(0, shown); track h.subjectId + h.environment) {
93
+ <div class="jc-list-row">
94
+ <jc-health [state]="h.state" [stale]="h.stale" />
95
+ <a [routerLink]="['services', h.subjectId] | jcLink" [queryParams]="ctx.keep({ env: h.environment })">{{ h.subjectName }}</a>
96
+ <span class="jc-muted">{{ h.environment }}</span>
97
+ <span>{{ h.reasons.slice(0, 3).map(reason).join(' · ') }}</span>
98
+ <span class="jc-muted">since {{ h.since | ago }}</span>
99
+ </div>
100
+ } @empty { <p class="jc-muted">Every evaluated service is healthy.</p> }
101
+ @if (attention().length > shown) {
102
+ <p class="jc-more"><span class="jc-muted">The worst first; {{ attention().length - shown }} more. The counts above open each state's list:</span>
103
+ @for (s of order; track s) { @if (s !== 'Healthy' && counts()[s]) {
104
+ <a [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">{{ counts()[s] }} {{ s.toLowerCase() }}</a>
105
+ } }</p>
106
+ }
107
+ </section>`,
108
+ isInline: true,
109
+ dependencies: [
110
+ {
111
+ kind: "directive",
112
+ type: RouterLink,
113
+ selector: "[routerLink]",
114
+ inputs: [
115
+ "target",
116
+ "queryParams",
117
+ "fragment",
118
+ "queryParamsHandling",
119
+ "state",
120
+ "info",
121
+ "relativeTo",
122
+ "preserveFragment",
123
+ "skipLocationChange",
124
+ "replaceUrl",
125
+ "browserUrl",
126
+ "routerLink"
127
+ ]
128
+ },
129
+ {
130
+ kind: "ngmodule",
131
+ type: MatProgressBarModule
132
+ },
133
+ {
134
+ kind: "component",
135
+ type: i1.MatProgressBar,
136
+ selector: "mat-progress-bar",
137
+ inputs: [
138
+ "color",
139
+ "value",
140
+ "bufferValue",
141
+ "mode"
142
+ ],
143
+ outputs: ["animationEnd"],
144
+ exportAs: ["matProgressBar"]
145
+ },
146
+ {
147
+ kind: "component",
148
+ type: ContextBar,
149
+ selector: "jc-context-bar"
150
+ },
151
+ {
152
+ kind: "component",
153
+ type: HealthBadge,
154
+ selector: "jc-health",
155
+ inputs: ["state", "stale"]
156
+ },
157
+ {
158
+ kind: "pipe",
159
+ type: LinkPipe,
160
+ name: "jcLink"
161
+ },
162
+ {
163
+ kind: "pipe",
164
+ type: AgoPipe,
165
+ name: "ago"
166
+ }
167
+ ],
168
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
169
+ });
170
+ };
171
+ i0.ɵɵngDeclareClassMetadata({
172
+ minVersion: "12.0.0",
173
+ version: "22.2.4",
174
+ ngImport: i0,
175
+ type: OverviewPage,
176
+ decorators: [{
177
+ type: Component,
178
+ args: [{
179
+ changeDetection: ChangeDetectionStrategy.OnPush,
180
+ imports: [
181
+ LinkPipe,
182
+ RouterLink,
183
+ MatProgressBarModule,
184
+ ContextBar,
185
+ HealthBadge,
186
+ AgoPipe
187
+ ],
188
+ template: `
189
+ <jc-context-bar><h1>Overview</h1></jc-context-bar>
190
+ @if (health.isLoading() || alerts.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
191
+ @if (health.error()) { <p class="jc-error">{{ error() }}</p> }
192
+
193
+ <div class="jc-tiles">
194
+ @for (s of order; track s) {
195
+ <a class="jc-card jc-tile" [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">
196
+ <jc-health [state]="s" /><strong>{{ counts()[s] ?? 0 }}</strong><span class="jc-muted">services</span>
197
+ </a>
198
+ }
199
+ </div>
200
+
201
+ <section class="jc-card">
202
+ <div class="jc-row">
203
+ <h2>Firing alerts @if (firing().length) { <span class="jc-muted">({{ firing().length }}@if (criticalCount()) { · {{ criticalCount() }} critical})</span> }</h2>
204
+ @if (firing().length) { <a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">All firing alerts</a> }
205
+ </div>
206
+ @for (a of firingShown(); track a.id) {
207
+ <div class="jc-list-row">
208
+ <span class="jc-badge" [class.jc-health-unhealthy]="a.severity === 'Critical'" [class.jc-health-degraded]="a.severity !== 'Critical'">{{ a.severity }}</span>
209
+ <a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.rule }}</a>
210
+ <span>{{ a.subjectKey }}</span>
211
+ <span class="jc-muted">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>
212
+ </div>
213
+ } @empty { <p class="jc-muted">Nothing firing.</p> }
214
+ @if (firing().length > shown) {
215
+ <p class="jc-more"><span class="jc-muted">Critical first, then the newest; {{ firing().length - shown }} more.</span>
216
+ <a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">See all {{ firing().length }}</a></p>
217
+ }
218
+ </section>
219
+
220
+ <section class="jc-card">
221
+ <h2>Services needing attention @if (attention().length) { <span class="jc-muted">({{ attention().length }})</span> }</h2>
222
+ @for (h of attention().slice(0, shown); track h.subjectId + h.environment) {
223
+ <div class="jc-list-row">
224
+ <jc-health [state]="h.state" [stale]="h.stale" />
225
+ <a [routerLink]="['services', h.subjectId] | jcLink" [queryParams]="ctx.keep({ env: h.environment })">{{ h.subjectName }}</a>
226
+ <span class="jc-muted">{{ h.environment }}</span>
227
+ <span>{{ h.reasons.slice(0, 3).map(reason).join(' · ') }}</span>
228
+ <span class="jc-muted">since {{ h.since | ago }}</span>
229
+ </div>
230
+ } @empty { <p class="jc-muted">Every evaluated service is healthy.</p> }
231
+ @if (attention().length > shown) {
232
+ <p class="jc-more"><span class="jc-muted">The worst first; {{ attention().length - shown }} more. The counts above open each state's list:</span>
233
+ @for (s of order; track s) { @if (s !== 'Healthy' && counts()[s]) {
234
+ <a [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">{{ counts()[s] }} {{ s.toLowerCase() }}</a>
235
+ } }</p>
236
+ }
237
+ </section>`
238
+ }]
239
+ }]
240
+ });
241
+ export { OverviewPage };
242
+
243
+ //# sourceMappingURL=juice-js-observability-overview.page-DU0GxSXQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"juice-js-observability-overview.page-DU0GxSXQ.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/overview.page.ts"],"sourcesContent":["import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { RouterLink } from '@angular/router';\nimport { map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { HealthState } from '../core/models';\nimport { AgoPipe, ReasonPipe } from '../shared/format';\nimport { ContextBar, HealthBadge } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\n\nconst ORDER: HealthState[] = ['Critical', 'Unhealthy', 'Degraded', 'Offline', 'Unknown', 'Healthy'];\n\n/** How many rows each list shows: the overview orients, the alerts and services pages list everything. */\nconst SHOWN = 10;\n\n/**\n * Where an investigation starts (Console requirements §4, §21 step 1): what is firing and which services are not\n * healthy, each one click from its service page. Orientation, not a dashboard.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatProgressBarModule, ContextBar, HealthBadge, AgoPipe],\n template: `\n <jc-context-bar><h1>Overview</h1></jc-context-bar>\n @if (health.isLoading() || alerts.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (health.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n\n <div class=\"jc-tiles\">\n @for (s of order; track s) {\n <a class=\"jc-card jc-tile\" [routerLink]=\"['services'] | jcLink\" [queryParams]=\"ctx.keep({ state: s })\">\n <jc-health [state]=\"s\" /><strong>{{ counts()[s] ?? 0 }}</strong><span class=\"jc-muted\">services</span>\n </a>\n }\n </div>\n\n <section class=\"jc-card\">\n <div class=\"jc-row\">\n <h2>Firing alerts @if (firing().length) { <span class=\"jc-muted\">({{ firing().length }}@if (criticalCount()) { · {{ criticalCount() }} critical})</span> }</h2>\n @if (firing().length) { <a [routerLink]=\"['alerts'] | jcLink\" [queryParams]=\"ctx.keep({ state: 'firing' })\">All firing alerts</a> }\n </div>\n @for (a of firingShown(); track a.id) {\n <div class=\"jc-list-row\">\n <span class=\"jc-badge\" [class.jc-health-unhealthy]=\"a.severity === 'Critical'\" [class.jc-health-degraded]=\"a.severity !== 'Critical'\">{{ a.severity }}</span>\n <a [routerLink]=\"['alerts', a.id] | jcLink\" [queryParams]=\"ctx.keep()\">{{ a.rule }}</a>\n <span>{{ a.subjectKey }}</span>\n <span class=\"jc-muted\">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>\n </div>\n } @empty { <p class=\"jc-muted\">Nothing firing.</p> }\n @if (firing().length > shown) {\n <p class=\"jc-more\"><span class=\"jc-muted\">Critical first, then the newest; {{ firing().length - shown }} more.</span>\n <a [routerLink]=\"['alerts'] | jcLink\" [queryParams]=\"ctx.keep({ state: 'firing' })\">See all {{ firing().length }}</a></p>\n }\n </section>\n\n <section class=\"jc-card\">\n <h2>Services needing attention @if (attention().length) { <span class=\"jc-muted\">({{ attention().length }})</span> }</h2>\n @for (h of attention().slice(0, shown); track h.subjectId + h.environment) {\n <div class=\"jc-list-row\">\n <jc-health [state]=\"h.state\" [stale]=\"h.stale\" />\n <a [routerLink]=\"['services', h.subjectId] | jcLink\" [queryParams]=\"ctx.keep({ env: h.environment })\">{{ h.subjectName }}</a>\n <span class=\"jc-muted\">{{ h.environment }}</span>\n <span>{{ h.reasons.slice(0, 3).map(reason).join(' · ') }}</span>\n <span class=\"jc-muted\">since {{ h.since | ago }}</span>\n </div>\n } @empty { <p class=\"jc-muted\">Every evaluated service is healthy.</p> }\n @if (attention().length > shown) {\n <p class=\"jc-more\"><span class=\"jc-muted\">The worst first; {{ attention().length - shown }} more. The counts above open each state's list:</span>\n @for (s of order; track s) { @if (s !== 'Healthy' && counts()[s]) {\n <a [routerLink]=\"['services'] | jcLink\" [queryParams]=\"ctx.keep({ state: s })\">{{ counts()[s] }} {{ s.toLowerCase() }}</a>\n } }</p>\n }\n </section>`,\n})\nexport class OverviewPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n protected readonly order = ORDER;\n protected readonly shown = SHOWN;\n private readonly reasonPipe = new ReasonPipe();\n protected readonly reason = (r: string) => this.reasonPipe.transform(r);\n\n protected readonly health = apiResource({\n params: () => this.ctx.environment(),\n stream: ({ params }) => this.api.health({ subjectType: 'service', environment: params }).pipe(map(p => p.items)),\n });\n protected readonly alerts = apiResource({ stream: () => this.api.allAlerts({ state: 'firing' }).pipe(map(p => p.items)) });\n protected readonly error = computed(() => problemText(this.health.error()));\n /** Critical first, then the newest. */\n protected readonly firing = computed(() => [...(this.alerts.value() ?? [])].sort((a, b) =>\n Number(b.severity === 'Critical') - Number(a.severity === 'Critical')\n || (b.firedAt ?? '').localeCompare(a.firedAt ?? '')));\n protected readonly firingShown = computed(() => this.firing().slice(0, SHOWN));\n protected readonly criticalCount = computed(() => this.firing().filter(a => a.severity === 'Critical').length);\n protected readonly counts = computed(() => {\n const counts: Partial<Record<HealthState, number>> = {};\n for (const h of this.health.value() ?? []) counts[h.state] = (counts[h.state] ?? 0) + 1;\n return counts;\n });\n protected readonly attention = computed(() => (this.health.value() ?? [])\n .filter(h => h.state !== 'Healthy')\n .sort((a, b) => ORDER.indexOf(a.state) - ORDER.indexOf(b.state)));\n}\n"],"mappings":"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"}