@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,268 @@
1
+ import { ApiService, ContextService } from "./juice-js-observability.mjs";
2
+ import { l as ContextBar, n as MetricExplorer } from "./juice-js-observability-telemetry-VA4_3nzr.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 { rxResource } from "@angular/core/rxjs-interop";
7
+ import { Router } from "@angular/router";
8
+ import * as i1 from "@angular/forms";
9
+ import { FormsModule } from "@angular/forms";
10
+ import { MatFormFieldModule } from "@angular/material/form-field";
11
+ import * as i2 from "@angular/material/select";
12
+ import { MatSelectModule } from "@angular/material/select";
13
+ var MetricsPage = class MetricsPage {
14
+ ctx = inject(ContextService);
15
+ api = inject(ApiService);
16
+ router = inject(Router);
17
+ service = input(...ngDevMode ? [void 0, { debugName: "service" }] : /* istanbul ignore next */ []);
18
+ resource = input(...ngDevMode ? [void 0, { debugName: "resource" }] : /* istanbul ignore next */ []);
19
+ metric = input(...ngDevMode ? [void 0, { debugName: "metric" }] : /* istanbul ignore next */ []);
20
+ services = rxResource({ stream: () => this.api.resources("service").pipe(map((p) => p.items)) });
21
+ hosts = rxResource({ stream: () => this.api.resources("host").pipe(map((p) => p.items)) });
22
+ query = computed(() => ({
23
+ serviceId: this.service(),
24
+ resourceId: this.resource()
25
+ }), ...ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []);
26
+ scope(change) {
27
+ this.router.navigate([], {
28
+ queryParams: change,
29
+ queryParamsHandling: "merge"
30
+ });
31
+ }
32
+ static ɵfac = i0.ɵɵngDeclareFactory({
33
+ minVersion: "12.0.0",
34
+ version: "22.2.4",
35
+ ngImport: i0,
36
+ type: MetricsPage,
37
+ deps: [],
38
+ target: i0.ɵɵFactoryTarget.Component
39
+ });
40
+ static ɵcmp = i0.ɵɵngDeclareComponent({
41
+ minVersion: "17.0.0",
42
+ version: "22.2.4",
43
+ type: MetricsPage,
44
+ isStandalone: true,
45
+ selector: "ng-component",
46
+ inputs: {
47
+ service: {
48
+ classPropertyName: "service",
49
+ publicName: "service",
50
+ isSignal: true,
51
+ isRequired: false,
52
+ transformFunction: null
53
+ },
54
+ resource: {
55
+ classPropertyName: "resource",
56
+ publicName: "resource",
57
+ isSignal: true,
58
+ isRequired: false,
59
+ transformFunction: null
60
+ },
61
+ metric: {
62
+ classPropertyName: "metric",
63
+ publicName: "metric",
64
+ isSignal: true,
65
+ isRequired: false,
66
+ transformFunction: null
67
+ }
68
+ },
69
+ ngImport: i0,
70
+ template: `
71
+ <jc-context-bar><h1>Metrics</h1></jc-context-bar>
72
+ <div class="jc-filter-form">
73
+ <mat-form-field subscriptSizing="dynamic">
74
+ <mat-label>Service</mat-label>
75
+ <mat-select [ngModel]="service() ?? ''" (ngModelChange)="scope({ service: $event || null, resource: null })">
76
+ <mat-option value="">Any</mat-option>
77
+ @for (s of services.value() ?? []; track s.id) { <mat-option [value]="s.id">{{ s.key }}</mat-option> }
78
+ </mat-select>
79
+ </mat-form-field>
80
+ <mat-form-field subscriptSizing="dynamic">
81
+ <mat-label>Host</mat-label>
82
+ <mat-select [ngModel]="resource() ?? ''" (ngModelChange)="scope({ resource: $event || null, service: null })">
83
+ <mat-option value="">Any</mat-option>
84
+ @for (h of hosts.value() ?? []; track h.id) { <mat-option [value]="h.id">{{ h.displayName }}</mat-option> }
85
+ </mat-select>
86
+ </mat-form-field>
87
+ </div>
88
+ <jc-metric-explorer [scope]="query()" [initial]="metric() ?? null" />`,
89
+ isInline: true,
90
+ dependencies: [
91
+ {
92
+ kind: "ngmodule",
93
+ type: FormsModule
94
+ },
95
+ {
96
+ kind: "directive",
97
+ type: i1.NgControlStatus,
98
+ selector: "[formControlName],[ngModel],[formControl]"
99
+ },
100
+ {
101
+ kind: "directive",
102
+ type: i1.NgModel,
103
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
104
+ inputs: [
105
+ "name",
106
+ "disabled",
107
+ "ngModel",
108
+ "ngModelOptions"
109
+ ],
110
+ outputs: ["ngModelChange"],
111
+ exportAs: ["ngModel"]
112
+ },
113
+ {
114
+ kind: "ngmodule",
115
+ type: MatFormFieldModule
116
+ },
117
+ {
118
+ kind: "component",
119
+ type: i2.MatFormField,
120
+ selector: "mat-form-field",
121
+ inputs: [
122
+ "hideRequiredMarker",
123
+ "color",
124
+ "floatLabel",
125
+ "appearance",
126
+ "subscriptSizing",
127
+ "hintLabel"
128
+ ],
129
+ exportAs: ["matFormField"]
130
+ },
131
+ {
132
+ kind: "directive",
133
+ type: i2.MatLabel,
134
+ selector: "mat-label"
135
+ },
136
+ {
137
+ kind: "ngmodule",
138
+ type: MatSelectModule
139
+ },
140
+ {
141
+ kind: "component",
142
+ type: i2.MatSelect,
143
+ selector: "mat-select",
144
+ inputs: [
145
+ "aria-describedby",
146
+ "panelClass",
147
+ "disabled",
148
+ "disableRipple",
149
+ "tabIndex",
150
+ "hideSingleSelectionIndicator",
151
+ "placeholder",
152
+ "required",
153
+ "multiple",
154
+ "disableOptionCentering",
155
+ "compareWith",
156
+ "value",
157
+ "aria-label",
158
+ "aria-labelledby",
159
+ "errorStateMatcher",
160
+ "typeaheadDebounceInterval",
161
+ "sortComparator",
162
+ "id",
163
+ "panelWidth",
164
+ "canSelectNullableOptions"
165
+ ],
166
+ outputs: [
167
+ "openedChange",
168
+ "opened",
169
+ "closed",
170
+ "selectionChange",
171
+ "valueChange"
172
+ ],
173
+ exportAs: ["matSelect"]
174
+ },
175
+ {
176
+ kind: "component",
177
+ type: i2.MatOption,
178
+ selector: "mat-option",
179
+ inputs: [
180
+ "value",
181
+ "id",
182
+ "disabled"
183
+ ],
184
+ outputs: ["onSelectionChange"],
185
+ exportAs: ["matOption"]
186
+ },
187
+ {
188
+ kind: "component",
189
+ type: ContextBar,
190
+ selector: "jc-context-bar"
191
+ },
192
+ {
193
+ kind: "component",
194
+ type: MetricExplorer,
195
+ selector: "jc-metric-explorer",
196
+ inputs: ["scope", "initial"]
197
+ }
198
+ ],
199
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
200
+ });
201
+ };
202
+ i0.ɵɵngDeclareClassMetadata({
203
+ minVersion: "12.0.0",
204
+ version: "22.2.4",
205
+ ngImport: i0,
206
+ type: MetricsPage,
207
+ decorators: [{
208
+ type: Component,
209
+ args: [{
210
+ changeDetection: ChangeDetectionStrategy.OnPush,
211
+ imports: [
212
+ FormsModule,
213
+ MatFormFieldModule,
214
+ MatSelectModule,
215
+ ContextBar,
216
+ MetricExplorer
217
+ ],
218
+ template: `
219
+ <jc-context-bar><h1>Metrics</h1></jc-context-bar>
220
+ <div class="jc-filter-form">
221
+ <mat-form-field subscriptSizing="dynamic">
222
+ <mat-label>Service</mat-label>
223
+ <mat-select [ngModel]="service() ?? ''" (ngModelChange)="scope({ service: $event || null, resource: null })">
224
+ <mat-option value="">Any</mat-option>
225
+ @for (s of services.value() ?? []; track s.id) { <mat-option [value]="s.id">{{ s.key }}</mat-option> }
226
+ </mat-select>
227
+ </mat-form-field>
228
+ <mat-form-field subscriptSizing="dynamic">
229
+ <mat-label>Host</mat-label>
230
+ <mat-select [ngModel]="resource() ?? ''" (ngModelChange)="scope({ resource: $event || null, service: null })">
231
+ <mat-option value="">Any</mat-option>
232
+ @for (h of hosts.value() ?? []; track h.id) { <mat-option [value]="h.id">{{ h.displayName }}</mat-option> }
233
+ </mat-select>
234
+ </mat-form-field>
235
+ </div>
236
+ <jc-metric-explorer [scope]="query()" [initial]="metric() ?? null" />`
237
+ }]
238
+ }],
239
+ propDecorators: {
240
+ service: [{
241
+ type: i0.Input,
242
+ args: [{
243
+ isSignal: true,
244
+ alias: "service",
245
+ required: false
246
+ }]
247
+ }],
248
+ resource: [{
249
+ type: i0.Input,
250
+ args: [{
251
+ isSignal: true,
252
+ alias: "resource",
253
+ required: false
254
+ }]
255
+ }],
256
+ metric: [{
257
+ type: i0.Input,
258
+ args: [{
259
+ isSignal: true,
260
+ alias: "metric",
261
+ required: false
262
+ }]
263
+ }]
264
+ }
265
+ });
266
+ export { MetricsPage };
267
+
268
+ //# sourceMappingURL=juice-js-observability-metrics.page-D1caPbQq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"juice-js-observability-metrics.page-D1caPbQq.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/metrics.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { rxResource } from '@angular/core/rxjs-interop';\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 = rxResource({ stream: () => this.api.resources('service').pipe(map(p => p.items)) });\n protected readonly hosts = rxResource({ 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,213 @@
1
+ import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
2
+ import { a as problemText, f as AgoPipe, h as ReasonPipe, l as ContextBar, u as HealthBadge } from "./juice-js-observability-telemetry-VA4_3nzr.mjs";
3
+ import * as i0 from "@angular/core";
4
+ import { ChangeDetectionStrategy, Component, computed, inject } from "@angular/core";
5
+ import { map } from "rxjs";
6
+ import { rxResource } from "@angular/core/rxjs-interop";
7
+ import { RouterLink } from "@angular/router";
8
+ import * as i1 from "@angular/material/progress-bar";
9
+ import { MatProgressBarModule } from "@angular/material/progress-bar";
10
+ const ORDER = [
11
+ "Critical",
12
+ "Unhealthy",
13
+ "Degraded",
14
+ "Offline",
15
+ "Unknown",
16
+ "Healthy"
17
+ ];
18
+ var OverviewPage = class OverviewPage {
19
+ ctx = inject(ContextService);
20
+ api = inject(ApiService);
21
+ order = ORDER;
22
+ reasonPipe = new ReasonPipe();
23
+ reason = (r) => this.reasonPipe.transform(r);
24
+ health = rxResource({
25
+ params: () => this.ctx.environment(),
26
+ stream: ({ params }) => this.api.health({
27
+ subjectType: "service",
28
+ environment: params
29
+ }).pipe(map((p) => p.items))
30
+ });
31
+ alerts = rxResource({ stream: () => this.api.allAlerts({ state: "firing" }).pipe(map((p) => p.items)) });
32
+ error = computed(() => problemText(this.health.error()), ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
33
+ counts = computed(() => {
34
+ const counts = {};
35
+ for (const h of this.health.value() ?? []) counts[h.state] = (counts[h.state] ?? 0) + 1;
36
+ return counts;
37
+ }, ...ngDevMode ? [{ debugName: "counts" }] : /* istanbul ignore next */ []);
38
+ 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 */ []);
39
+ static ɵfac = i0.ɵɵngDeclareFactory({
40
+ minVersion: "12.0.0",
41
+ version: "22.2.4",
42
+ ngImport: i0,
43
+ type: OverviewPage,
44
+ deps: [],
45
+ target: i0.ɵɵFactoryTarget.Component
46
+ });
47
+ static ɵcmp = i0.ɵɵngDeclareComponent({
48
+ minVersion: "17.0.0",
49
+ version: "22.2.4",
50
+ type: OverviewPage,
51
+ isStandalone: true,
52
+ selector: "ng-component",
53
+ ngImport: i0,
54
+ template: `
55
+ <jc-context-bar><h1>Overview</h1></jc-context-bar>
56
+ @if (health.isLoading() || alerts.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
57
+ @if (health.error()) { <p class="jc-error">{{ error() }}</p> }
58
+
59
+ <div class="jc-tiles">
60
+ @for (s of order; track s) {
61
+ <a class="jc-card jc-tile" [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">
62
+ <jc-health [state]="s" /><strong>{{ counts()[s] ?? 0 }}</strong><span class="jc-muted">services</span>
63
+ </a>
64
+ }
65
+ </div>
66
+
67
+ <section class="jc-card">
68
+ <h2>Firing alerts</h2>
69
+ @for (a of alerts.value() ?? []; track a.id) {
70
+ <div class="jc-list-row">
71
+ <span class="jc-badge" [class.jc-health-unhealthy]="a.severity === 'Critical'" [class.jc-health-degraded]="a.severity !== 'Critical'">{{ a.severity }}</span>
72
+ <a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.rule }}</a>
73
+ <span>{{ a.subjectKey }}</span>
74
+ <span class="jc-muted">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>
75
+ </div>
76
+ } @empty { <p class="jc-muted">Nothing firing.</p> }
77
+ </section>
78
+
79
+ <section class="jc-card">
80
+ <h2>Services needing attention</h2>
81
+ @for (h of attention(); track h.subjectId + h.environment) {
82
+ <div class="jc-list-row">
83
+ <jc-health [state]="h.state" [stale]="h.stale" />
84
+ <a [routerLink]="['services', h.subjectId] | jcLink" [queryParams]="ctx.keep({ env: h.environment })">{{ h.subjectName }}</a>
85
+ <span class="jc-muted">{{ h.environment }}</span>
86
+ <span>{{ h.reasons.slice(0, 3).map(reason).join(' · ') }}</span>
87
+ <span class="jc-muted">since {{ h.since | ago }}</span>
88
+ </div>
89
+ } @empty { <p class="jc-muted">Every evaluated service is healthy.</p> }
90
+ </section>`,
91
+ isInline: true,
92
+ dependencies: [
93
+ {
94
+ kind: "directive",
95
+ type: RouterLink,
96
+ selector: "[routerLink]",
97
+ inputs: [
98
+ "target",
99
+ "queryParams",
100
+ "fragment",
101
+ "queryParamsHandling",
102
+ "state",
103
+ "info",
104
+ "relativeTo",
105
+ "preserveFragment",
106
+ "skipLocationChange",
107
+ "replaceUrl",
108
+ "browserUrl",
109
+ "routerLink"
110
+ ]
111
+ },
112
+ {
113
+ kind: "ngmodule",
114
+ type: MatProgressBarModule
115
+ },
116
+ {
117
+ kind: "component",
118
+ type: i1.MatProgressBar,
119
+ selector: "mat-progress-bar",
120
+ inputs: [
121
+ "color",
122
+ "value",
123
+ "bufferValue",
124
+ "mode"
125
+ ],
126
+ outputs: ["animationEnd"],
127
+ exportAs: ["matProgressBar"]
128
+ },
129
+ {
130
+ kind: "component",
131
+ type: ContextBar,
132
+ selector: "jc-context-bar"
133
+ },
134
+ {
135
+ kind: "component",
136
+ type: HealthBadge,
137
+ selector: "jc-health",
138
+ inputs: ["state", "stale"]
139
+ },
140
+ {
141
+ kind: "pipe",
142
+ type: LinkPipe,
143
+ name: "jcLink"
144
+ },
145
+ {
146
+ kind: "pipe",
147
+ type: AgoPipe,
148
+ name: "ago"
149
+ }
150
+ ],
151
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
152
+ });
153
+ };
154
+ i0.ɵɵngDeclareClassMetadata({
155
+ minVersion: "12.0.0",
156
+ version: "22.2.4",
157
+ ngImport: i0,
158
+ type: OverviewPage,
159
+ decorators: [{
160
+ type: Component,
161
+ args: [{
162
+ changeDetection: ChangeDetectionStrategy.OnPush,
163
+ imports: [
164
+ LinkPipe,
165
+ RouterLink,
166
+ MatProgressBarModule,
167
+ ContextBar,
168
+ HealthBadge,
169
+ AgoPipe
170
+ ],
171
+ template: `
172
+ <jc-context-bar><h1>Overview</h1></jc-context-bar>
173
+ @if (health.isLoading() || alerts.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
174
+ @if (health.error()) { <p class="jc-error">{{ error() }}</p> }
175
+
176
+ <div class="jc-tiles">
177
+ @for (s of order; track s) {
178
+ <a class="jc-card jc-tile" [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">
179
+ <jc-health [state]="s" /><strong>{{ counts()[s] ?? 0 }}</strong><span class="jc-muted">services</span>
180
+ </a>
181
+ }
182
+ </div>
183
+
184
+ <section class="jc-card">
185
+ <h2>Firing alerts</h2>
186
+ @for (a of alerts.value() ?? []; track a.id) {
187
+ <div class="jc-list-row">
188
+ <span class="jc-badge" [class.jc-health-unhealthy]="a.severity === 'Critical'" [class.jc-health-degraded]="a.severity !== 'Critical'">{{ a.severity }}</span>
189
+ <a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.rule }}</a>
190
+ <span>{{ a.subjectKey }}</span>
191
+ <span class="jc-muted">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>
192
+ </div>
193
+ } @empty { <p class="jc-muted">Nothing firing.</p> }
194
+ </section>
195
+
196
+ <section class="jc-card">
197
+ <h2>Services needing attention</h2>
198
+ @for (h of attention(); track h.subjectId + h.environment) {
199
+ <div class="jc-list-row">
200
+ <jc-health [state]="h.state" [stale]="h.stale" />
201
+ <a [routerLink]="['services', h.subjectId] | jcLink" [queryParams]="ctx.keep({ env: h.environment })">{{ h.subjectName }}</a>
202
+ <span class="jc-muted">{{ h.environment }}</span>
203
+ <span>{{ h.reasons.slice(0, 3).map(reason).join(' · ') }}</span>
204
+ <span class="jc-muted">since {{ h.since | ago }}</span>
205
+ </div>
206
+ } @empty { <p class="jc-muted">Every evaluated service is healthy.</p> }
207
+ </section>`
208
+ }]
209
+ }]
210
+ });
211
+ export { OverviewPage };
212
+
213
+ //# sourceMappingURL=juice-js-observability-overview.page-UBwHjlyq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"juice-js-observability-overview.page-UBwHjlyq.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 { rxResource } from '@angular/core/rxjs-interop';\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/**\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 <h2>Firing alerts</h2>\n @for (a of alerts.value() ?? []; 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 </section>\n\n <section class=\"jc-card\">\n <h2>Services needing attention</h2>\n @for (h of attention(); 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 </section>`,\n})\nexport class OverviewPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n protected readonly order = ORDER;\n private readonly reasonPipe = new ReasonPipe();\n protected readonly reason = (r: string) => this.reasonPipe.transform(r);\n\n protected readonly health = rxResource({\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 = rxResource({ stream: () => this.api.allAlerts({ state: 'firing' }).pipe(map(p => p.items)) });\n protected readonly error = computed(() => problemText(this.health.error()));\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":";;;;;;;;;AAaA,MAAM,QAAuB;CAAC;CAAY;CAAa;CAAY;CAAW;CAAW;AAAS;AA+ClG,IAAa,eAAb,MAAa,aAAY;CACJ,MAAM,OAAO,cAAc;CAC7B,MAAM,OAAO,UAAU;CACrB,QAAQ;CACV,aAAa,IAAI,WAAU;CACzB,UAAU,MAAc,KAAK,WAAW,UAAU,CAAC;CAEnD,SAAS,WAAW;EACrC,cAAc,KAAK,IAAI,YAAW;EAClC,SAAS,EAAE,aAAa,KAAK,IAAI,OAAO;GAAE,aAAa;GAAW,aAAa;EAAM,CAAE,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC;CAChH,CAAA;CACkB,SAAS,WAAW,EAAE,cAAc,KAAK,IAAI,UAAU,EAAE,OAAO,SAAQ,CAAE,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC,EAAC,CAAE;CACrG,QAAQ,eAAe,YAAY,KAAK,OAAO,MAAK,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACxD,SAAS,eAAc;EACxC,MAAM,SAA+C,CAAA;EACrD,KAAK,MAAM,KAAK,KAAK,OAAO,MAAK,KAAM,CAAA,GAAI,OAAO,EAAE,UAAU,OAAO,EAAE,UAAU,KAAK;EACtF,OAAO;CACT,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CACiB,YAAY,gBAAgB,KAAK,OAAO,MAAK,KAAM,CAAA,EAAA,CACnE,QAAO,MAAK,EAAE,UAAU,SAAS,CAAC,CAClC,MAAM,GAAG,MAAM,MAAM,QAAQ,EAAE,KAAK,IAAI,MAAM,QAAQ,EAAE,KAAK,CAAC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CApBxD,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAY,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAZ,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAY,cAAA;EAAA,UAAA;EAAA,UAAA;EAAA,UAtCb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoCG,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MArCO;IAAU,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAoB;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,cAAA;IAAA,UAAA,CAAA,gBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAU,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAW,UAAA;IAAA,QAAA,CAAA,SAAA,OAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAnE;IAAQ,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAA6D;IAAO,MAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAuC3E,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAY,YAAA,CAAA;EAzCxB,MAAA;EAAU,MAAA,CAAA;GACT,iBAAiB,wBAAwB;GACzC,SAAS;IAAC;IAAU;IAAY;IAAsB;IAAY;IAAa;GAAO;GACtF,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCX,CAAA"}