Ming Wang commited on
Commit
bf28b11
·
1 Parent(s): 1f7eb48

Phase 3: fake backend and API integration

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules/
backend/db.json ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "kpis": [
3
+ {
4
+ "id": "network-health",
5
+ "title": "Network Health",
6
+ "value": "98.2",
7
+ "unit": "%",
8
+ "trend": "up",
9
+ "trendLabel": "+1.5% from last hour",
10
+ "icon": "hub"
11
+ },
12
+ {
13
+ "id": "critical-incidents",
14
+ "title": "Critical Incidents",
15
+ "value": "3",
16
+ "trend": "down",
17
+ "trendLabel": "-2 from yesterday",
18
+ "icon": "error_outline"
19
+ },
20
+ {
21
+ "id": "threat-severity",
22
+ "title": "Threat Severity",
23
+ "value": "Low",
24
+ "trend": "stable",
25
+ "trendLabel": "No change",
26
+ "icon": "shield"
27
+ },
28
+ {
29
+ "id": "site-availability",
30
+ "title": "Site Availability",
31
+ "value": "99.97",
32
+ "unit": "%",
33
+ "trend": "up",
34
+ "trendLabel": "+0.03% this week",
35
+ "icon": "cloud_done"
36
+ }
37
+ ],
38
+ "traffic": [
39
+ { "time": "00:00", "inbound": 120, "outbound": 95, "latency": 12 },
40
+ { "time": "01:00", "inbound": 85, "outbound": 70, "latency": 10 },
41
+ { "time": "02:00", "inbound": 60, "outbound": 52, "latency": 8 },
42
+ { "time": "03:00", "inbound": 45, "outbound": 38, "latency": 7 },
43
+ { "time": "04:00", "inbound": 40, "outbound": 35, "latency": 6 },
44
+ { "time": "05:00", "inbound": 55, "outbound": 42, "latency": 8 },
45
+ { "time": "06:00", "inbound": 90, "outbound": 78, "latency": 11 },
46
+ { "time": "07:00", "inbound": 145, "outbound": 120, "latency": 15 },
47
+ { "time": "08:00", "inbound": 210, "outbound": 180, "latency": 22 },
48
+ { "time": "09:00", "inbound": 280, "outbound": 245, "latency": 28 },
49
+ { "time": "10:00", "inbound": 310, "outbound": 270, "latency": 32 },
50
+ { "time": "11:00", "inbound": 295, "outbound": 260, "latency": 30 },
51
+ { "time": "12:00", "inbound": 270, "outbound": 235, "latency": 27 },
52
+ { "time": "13:00", "inbound": 285, "outbound": 250, "latency": 29 },
53
+ { "time": "14:00", "inbound": 320, "outbound": 280, "latency": 35 },
54
+ { "time": "15:00", "inbound": 300, "outbound": 265, "latency": 31 },
55
+ { "time": "16:00", "inbound": 275, "outbound": 240, "latency": 26 },
56
+ { "time": "17:00", "inbound": 250, "outbound": 215, "latency": 24 },
57
+ { "time": "18:00", "inbound": 220, "outbound": 190, "latency": 20 },
58
+ { "time": "19:00", "inbound": 185, "outbound": 160, "latency": 18 },
59
+ { "time": "20:00", "inbound": 160, "outbound": 138, "latency": 16 },
60
+ { "time": "21:00", "inbound": 140, "outbound": 118, "latency": 14 },
61
+ { "time": "22:00", "inbound": 125, "outbound": 105, "latency": 13 },
62
+ { "time": "23:00", "inbound": 110, "outbound": 90, "latency": 11 }
63
+ ],
64
+ "incidents": [
65
+ { "region": "US-East", "critical": 5, "warning": 12, "info": 8 },
66
+ { "region": "US-West", "critical": 2, "warning": 8, "info": 15 },
67
+ { "region": "EU-West", "critical": 7, "warning": 10, "info": 6 },
68
+ { "region": "AP-South", "critical": 3, "warning": 14, "info": 11 },
69
+ { "region": "AP-East", "critical": 1, "warning": 6, "info": 9 }
70
+ ],
71
+ "alerts": [
72
+ {
73
+ "id": 1,
74
+ "timestamp": "2026-03-24T10:32:00Z",
75
+ "severity": "critical",
76
+ "message": "Node B-12 unresponsive for 5 minutes",
77
+ "source": "Network Monitor"
78
+ },
79
+ {
80
+ "id": 2,
81
+ "timestamp": "2026-03-24T10:28:00Z",
82
+ "severity": "warning",
83
+ "message": "CPU usage above 85% on Cluster A",
84
+ "source": "Resource Monitor"
85
+ },
86
+ {
87
+ "id": 3,
88
+ "timestamp": "2026-03-24T10:15:00Z",
89
+ "severity": "info",
90
+ "message": "AI detected anomalous traffic pattern in Zone 3",
91
+ "source": "AI Insights"
92
+ },
93
+ {
94
+ "id": 4,
95
+ "timestamp": "2026-03-24T09:58:00Z",
96
+ "severity": "warning",
97
+ "message": "SSL certificate expires in 7 days for api.example.com",
98
+ "source": "Cert Monitor"
99
+ },
100
+ {
101
+ "id": 5,
102
+ "timestamp": "2026-03-24T09:45:00Z",
103
+ "severity": "info",
104
+ "message": "Auto-scaling triggered: +2 instances in Region US-East",
105
+ "source": "AI Insights"
106
+ }
107
+ ],
108
+ "cpuLoad": []
109
+ }
frontend/angular.json CHANGED
@@ -81,6 +81,9 @@
81
  },
82
  "serve": {
83
  "builder": "@angular/build:dev-server",
 
 
 
84
  "configurations": {
85
  "production": {
86
  "buildTarget": "frontend:build:production"
 
81
  },
82
  "serve": {
83
  "builder": "@angular/build:dev-server",
84
+ "options": {
85
+ "proxyConfig": "proxy.conf.json"
86
+ },
87
  "configurations": {
88
  "production": {
89
  "buildTarget": "frontend:build:production"
frontend/proxy.conf.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "/api": {
3
+ "target": "http://localhost:3000",
4
+ "secure": false,
5
+ "pathRewrite": { "^/api": "" }
6
+ }
7
+ }
frontend/src/app/app.config.ts CHANGED
@@ -1,8 +1,9 @@
1
  import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
 
2
 
3
  export const appConfig: ApplicationConfig = {
4
  providers: [
5
  provideBrowserGlobalErrorListeners(),
6
-
7
  ]
8
  };
 
1
  import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
2
+ import { provideHttpClient } from '@angular/common/http';
3
 
4
  export const appConfig: ApplicationConfig = {
5
  providers: [
6
  provideBrowserGlobalErrorListeners(),
7
+ provideHttpClient(),
8
  ]
9
  };
frontend/src/app/core/models/api.models.ts ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface KpiDto {
2
+ id: string;
3
+ title: string;
4
+ value: string;
5
+ unit?: string;
6
+ trend: 'up' | 'down' | 'stable';
7
+ trendLabel: string;
8
+ icon: string;
9
+ }
10
+
11
+ export interface TrafficDto {
12
+ time: string;
13
+ inbound: number;
14
+ outbound: number;
15
+ latency: number;
16
+ }
17
+
18
+ export interface IncidentDto {
19
+ region: string;
20
+ critical: number;
21
+ warning: number;
22
+ info: number;
23
+ }
24
+
25
+ export interface AlertDto {
26
+ id: number;
27
+ timestamp: string;
28
+ severity: 'critical' | 'warning' | 'info';
29
+ message: string;
30
+ source: string;
31
+ }
frontend/src/app/core/services/dashboard-api.service.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable, inject } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+ import { Observable } from 'rxjs';
4
+ import { KpiDto, TrafficDto, IncidentDto, AlertDto } from '../models/api.models';
5
+ import { environment } from '../../../environments/environment';
6
+
7
+ @Injectable({ providedIn: 'root' })
8
+ export class DashboardApi {
9
+ private readonly http = inject(HttpClient);
10
+ private readonly base = environment.apiBaseUrl;
11
+
12
+ getKpis(): Observable<KpiDto[]> {
13
+ return this.http.get<KpiDto[]>(`${this.base}/kpis`);
14
+ }
15
+
16
+ getTraffic(): Observable<TrafficDto[]> {
17
+ return this.http.get<TrafficDto[]>(`${this.base}/traffic`);
18
+ }
19
+
20
+ getIncidents(): Observable<IncidentDto[]> {
21
+ return this.http.get<IncidentDto[]>(`${this.base}/incidents`);
22
+ }
23
+
24
+ getAlerts(): Observable<AlertDto[]> {
25
+ return this.http.get<AlertDto[]>(`${this.base}/alerts`);
26
+ }
27
+ }
frontend/src/app/dashboard/alert-list/alert-list.ts CHANGED
@@ -2,7 +2,7 @@ import { Component, input } from '@angular/core';
2
  import { DatePipe } from '@angular/common';
3
  import { MatCard, MatCardContent } from '@angular/material/card';
4
  import { MatIcon } from '@angular/material/icon';
5
- import { AlertData } from '../../core/mocks/alerts.mock';
6
 
7
  @Component({
8
  selector: 'app-alert-list',
@@ -11,9 +11,9 @@ import { AlertData } from '../../core/mocks/alerts.mock';
11
  styleUrl: './alert-list.scss',
12
  })
13
  export class AlertList {
14
- alerts = input.required<AlertData[]>();
15
 
16
- protected severityIcon(severity: AlertData['severity']): string {
17
  const icons: Record<string, string> = {
18
  critical: 'error',
19
  warning: 'warning',
 
2
  import { DatePipe } from '@angular/common';
3
  import { MatCard, MatCardContent } from '@angular/material/card';
4
  import { MatIcon } from '@angular/material/icon';
5
+ import { AlertDto } from '../../core/models/api.models';
6
 
7
  @Component({
8
  selector: 'app-alert-list',
 
11
  styleUrl: './alert-list.scss',
12
  })
13
  export class AlertList {
14
+ alerts = input.required<AlertDto[]>();
15
 
16
+ protected severityIcon(severity: AlertDto['severity']): string {
17
  const icons: Record<string, string> = {
18
  critical: 'error',
19
  warning: 'warning',
frontend/src/app/dashboard/chart-config.factory.ts CHANGED
@@ -1,5 +1,5 @@
1
  import type { EChartsOption } from 'echarts';
2
- import { TrafficDataPoint, IncidentRegionData } from '../core/mocks/charts.mock';
3
 
4
  /**
5
  * Chart color palette aligned with Material 3 violet theme.
@@ -26,7 +26,7 @@ function baseGrid(): EChartsOption['grid'] {
26
  return { left: 48, right: 24, top: 40, bottom: 32, containLabel: true };
27
  }
28
 
29
- export function buildTrafficLatencyOption(data: TrafficDataPoint[]): EChartsOption {
30
  const times = data.map((d) => d.time);
31
 
32
  return {
@@ -91,7 +91,7 @@ export function buildTrafficLatencyOption(data: TrafficDataPoint[]): EChartsOpti
91
  };
92
  }
93
 
94
- export function buildIncidentRegionOption(data: IncidentRegionData[]): EChartsOption {
95
  const regions = data.map((d) => d.region);
96
 
97
  return {
 
1
  import type { EChartsOption } from 'echarts';
2
+ import { TrafficDto, IncidentDto } from '../core/models/api.models';
3
 
4
  /**
5
  * Chart color palette aligned with Material 3 violet theme.
 
26
  return { left: 48, right: 24, top: 40, bottom: 32, containLabel: true };
27
  }
28
 
29
+ export function buildTrafficLatencyOption(data: TrafficDto[]): EChartsOption {
30
  const times = data.map((d) => d.time);
31
 
32
  return {
 
91
  };
92
  }
93
 
94
+ export function buildIncidentRegionOption(data: IncidentDto[]): EChartsOption {
95
  const regions = data.map((d) => d.region);
96
 
97
  return {
frontend/src/app/dashboard/dashboard.html CHANGED
@@ -4,25 +4,35 @@
4
  <p class="dashboard-subtitle">Real-time infrastructure monitoring dashboard</p>
5
  </header>
6
 
7
- <div class="kpi-grid">
8
- @for (kpi of kpis; track kpi.title) {
9
- <app-kpi-card
10
- [title]="kpi.title"
11
- [value]="kpi.value"
12
- [unit]="kpi.unit ?? ''"
13
- [trend]="kpi.trend"
14
- [trendLabel]="kpi.trendLabel"
15
- [icon]="kpi.icon"
16
- />
17
- }
18
- </div>
 
 
 
 
 
 
 
 
 
19
 
20
- <div class="chart-grid">
21
- <app-chart-widget title="24h Traffic / Latency Trend" [options]="trafficChartOptions" />
22
- <app-chart-widget title="Incident Severity by Region" [options]="incidentChartOptions" />
23
- </div>
24
 
25
- <app-alert-list [alerts]="alerts" />
 
26
 
27
  <div id="ai-dropzone" class="ai-dropzone">
28
  <span class="dropzone-label">AI Widget Drop Zone</span>
 
4
  <p class="dashboard-subtitle">Real-time infrastructure monitoring dashboard</p>
5
  </header>
6
 
7
+ @if (loading()) {
8
+ <div class="state-container">
9
+ <mat-spinner diameter="48" />
10
+ </div>
11
+ } @else if (error()) {
12
+ <div class="state-container state-error">
13
+ <span class="error-message">{{ error() }}</span>
14
+ </div>
15
+ } @else {
16
+ <div class="kpi-grid">
17
+ @for (kpi of kpis(); track kpi.title) {
18
+ <app-kpi-card
19
+ [title]="kpi.title"
20
+ [value]="kpi.value"
21
+ [unit]="kpi.unit ?? ''"
22
+ [trend]="kpi.trend"
23
+ [trendLabel]="kpi.trendLabel"
24
+ [icon]="kpi.icon"
25
+ />
26
+ }
27
+ </div>
28
 
29
+ <div class="chart-grid">
30
+ <app-chart-widget title="24h Traffic / Latency Trend" [options]="trafficChartOptions()" />
31
+ <app-chart-widget title="Incident Severity by Region" [options]="incidentChartOptions()" />
32
+ </div>
33
 
34
+ <app-alert-list [alerts]="alerts()" />
35
+ }
36
 
37
  <div id="ai-dropzone" class="ai-dropzone">
38
  <span class="dropzone-label">AI Widget Drop Zone</span>
frontend/src/app/dashboard/dashboard.scss CHANGED
@@ -53,6 +53,21 @@
53
  color: var(--mat-sys-outline);
54
  }
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  @media (max-width: 1024px) {
57
  .kpi-grid {
58
  grid-template-columns: repeat(2, 1fr);
 
53
  color: var(--mat-sys-outline);
54
  }
55
 
56
+ .state-container {
57
+ display: flex;
58
+ justify-content: center;
59
+ align-items: center;
60
+ min-height: 200px;
61
+ }
62
+
63
+ .state-error {
64
+ color: var(--pf-severity-critical);
65
+ }
66
+
67
+ .error-message {
68
+ font: var(--mat-sys-body-large);
69
+ }
70
+
71
  @media (max-width: 1024px) {
72
  .kpi-grid {
73
  grid-template-columns: repeat(2, 1fr);
frontend/src/app/dashboard/dashboard.ts CHANGED
@@ -1,21 +1,48 @@
1
- import { Component } from '@angular/core';
 
2
  import { KpiCard } from './kpi-card/kpi-card';
3
  import { ChartWidget } from './chart-widget/chart-widget';
4
  import { AlertList } from './alert-list/alert-list';
5
- import { KPI_MOCKS } from '../core/mocks/kpi.mock';
6
- import { ALERT_MOCKS } from '../core/mocks/alerts.mock';
7
- import { TRAFFIC_MOCK, INCIDENT_REGION_MOCK } from '../core/mocks/charts.mock';
8
  import { buildTrafficLatencyOption, buildIncidentRegionOption } from './chart-config.factory';
 
 
9
 
10
  @Component({
11
  selector: 'app-dashboard',
12
- imports: [KpiCard, ChartWidget, AlertList],
13
  templateUrl: './dashboard.html',
14
  styleUrl: './dashboard.scss',
15
  })
16
- export class Dashboard {
17
- protected readonly kpis = KPI_MOCKS;
18
- protected readonly alerts = ALERT_MOCKS;
19
- protected readonly trafficChartOptions = buildTrafficLatencyOption(TRAFFIC_MOCK);
20
- protected readonly incidentChartOptions = buildIncidentRegionOption(INCIDENT_REGION_MOCK);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
 
1
+ import { Component, inject, OnInit, signal } from '@angular/core';
2
+ import { forkJoin } from 'rxjs';
3
  import { KpiCard } from './kpi-card/kpi-card';
4
  import { ChartWidget } from './chart-widget/chart-widget';
5
  import { AlertList } from './alert-list/alert-list';
6
+ import { DashboardApi } from '../core/services/dashboard-api.service';
7
+ import { KpiDto, AlertDto, TrafficDto, IncidentDto } from '../core/models/api.models';
 
8
  import { buildTrafficLatencyOption, buildIncidentRegionOption } from './chart-config.factory';
9
+ import type { EChartsOption } from 'echarts';
10
+ import { MatProgressSpinner } from '@angular/material/progress-spinner';
11
 
12
  @Component({
13
  selector: 'app-dashboard',
14
+ imports: [KpiCard, ChartWidget, AlertList, MatProgressSpinner],
15
  templateUrl: './dashboard.html',
16
  styleUrl: './dashboard.scss',
17
  })
18
+ export class Dashboard implements OnInit {
19
+ private readonly api = inject(DashboardApi);
20
+
21
+ protected readonly loading = signal(true);
22
+ protected readonly error = signal<string | null>(null);
23
+ protected readonly kpis = signal<KpiDto[]>([]);
24
+ protected readonly alerts = signal<AlertDto[]>([]);
25
+ protected readonly trafficChartOptions = signal<EChartsOption>({});
26
+ protected readonly incidentChartOptions = signal<EChartsOption>({});
27
+
28
+ ngOnInit(): void {
29
+ forkJoin({
30
+ kpis: this.api.getKpis(),
31
+ traffic: this.api.getTraffic(),
32
+ incidents: this.api.getIncidents(),
33
+ alerts: this.api.getAlerts(),
34
+ }).subscribe({
35
+ next: (data) => {
36
+ this.kpis.set(data.kpis);
37
+ this.alerts.set(data.alerts);
38
+ this.trafficChartOptions.set(buildTrafficLatencyOption(data.traffic));
39
+ this.incidentChartOptions.set(buildIncidentRegionOption(data.incidents));
40
+ this.loading.set(false);
41
+ },
42
+ error: (err) => {
43
+ this.error.set(err?.message ?? 'Failed to load dashboard data');
44
+ this.loading.set(false);
45
+ },
46
+ });
47
+ }
48
  }
frontend/src/environments/environment.prod.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ export const environment = {
2
+ production: true,
3
+ apiBaseUrl: '/api',
4
+ };
frontend/src/environments/environment.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ export const environment = {
2
+ production: false,
3
+ apiBaseUrl: '/api',
4
+ };
package-lock.json ADDED
@@ -0,0 +1,893 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "pulsefabric-monitor",
3
+ "version": "0.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "pulsefabric-monitor",
9
+ "version": "0.0.0",
10
+ "devDependencies": {
11
+ "concurrently": "^9.2.1",
12
+ "json-server": "^1.0.0-beta.15"
13
+ }
14
+ },
15
+ "node_modules/@polka/url": {
16
+ "version": "1.0.0-next.29",
17
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
18
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
19
+ "dev": true,
20
+ "license": "MIT"
21
+ },
22
+ "node_modules/@tinyhttp/accepts": {
23
+ "version": "2.3.0",
24
+ "resolved": "https://registry.npmjs.org/@tinyhttp/accepts/-/accepts-2.3.0.tgz",
25
+ "integrity": "sha512-hdKkMGAUqnagpWO1R8rVBYqbu4sWQ2Fo682gkJmO0nl54DPvnzxx81b2WZtV3VwB7EdLfUoasj2BAkyTcyZ5aw==",
26
+ "dev": true,
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "mime": "4.1.0"
30
+ },
31
+ "engines": {
32
+ "node": ">=14.13.1"
33
+ },
34
+ "funding": {
35
+ "type": "individual",
36
+ "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
37
+ }
38
+ },
39
+ "node_modules/@tinyhttp/app": {
40
+ "version": "3.0.6",
41
+ "resolved": "https://registry.npmjs.org/@tinyhttp/app/-/app-3.0.6.tgz",
42
+ "integrity": "sha512-jJYl9mc7nXY4gCyvot3tplCG6hVj+zm7sOZrT91et2Rg8M0oxBCVKVeX1tPhvVfOtYHQsz7mRlC1yk6yNLk79Q==",
43
+ "dev": true,
44
+ "license": "MIT",
45
+ "dependencies": {
46
+ "@tinyhttp/accepts": "^2.3.0",
47
+ "@tinyhttp/cookie": "2.1.1",
48
+ "@tinyhttp/proxy-addr": "3.0.1",
49
+ "@tinyhttp/req": "2.2.8",
50
+ "@tinyhttp/res": "2.2.10",
51
+ "@tinyhttp/router": "2.2.5",
52
+ "regexparam": "^2.0.2"
53
+ },
54
+ "engines": {
55
+ "node": ">=16.10.0"
56
+ },
57
+ "funding": {
58
+ "type": "individual",
59
+ "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
60
+ }
61
+ },
62
+ "node_modules/@tinyhttp/content-disposition": {
63
+ "version": "2.2.4",
64
+ "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.4.tgz",
65
+ "integrity": "sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==",
66
+ "dev": true,
67
+ "license": "MIT",
68
+ "engines": {
69
+ "node": ">=12.17.0"
70
+ },
71
+ "funding": {
72
+ "type": "individual",
73
+ "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
74
+ }
75
+ },
76
+ "node_modules/@tinyhttp/content-type": {
77
+ "version": "0.1.4",
78
+ "resolved": "https://registry.npmjs.org/@tinyhttp/content-type/-/content-type-0.1.4.tgz",
79
+ "integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==",
80
+ "dev": true,
81
+ "license": "MIT",
82
+ "engines": {
83
+ "node": ">=12.4"
84
+ }
85
+ },
86
+ "node_modules/@tinyhttp/cookie": {
87
+ "version": "2.1.1",
88
+ "resolved": "https://registry.npmjs.org/@tinyhttp/cookie/-/cookie-2.1.1.tgz",
89
+ "integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==",
90
+ "dev": true,
91
+ "license": "MIT",
92
+ "engines": {
93
+ "node": ">=12.20.0"
94
+ },
95
+ "funding": {
96
+ "type": "individual",
97
+ "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
98
+ }
99
+ },
100
+ "node_modules/@tinyhttp/cookie-signature": {
101
+ "version": "2.1.1",
102
+ "resolved": "https://registry.npmjs.org/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz",
103
+ "integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==",
104
+ "dev": true,
105
+ "license": "MIT",
106
+ "engines": {
107
+ "node": ">=12.20.0"
108
+ }
109
+ },
110
+ "node_modules/@tinyhttp/cors": {
111
+ "version": "2.0.1",
112
+ "resolved": "https://registry.npmjs.org/@tinyhttp/cors/-/cors-2.0.1.tgz",
113
+ "integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==",
114
+ "dev": true,
115
+ "license": "MIT",
116
+ "dependencies": {
117
+ "@tinyhttp/vary": "^0.1.3"
118
+ },
119
+ "engines": {
120
+ "node": ">=12.20 || 14.x || >=16"
121
+ }
122
+ },
123
+ "node_modules/@tinyhttp/encode-url": {
124
+ "version": "2.1.1",
125
+ "resolved": "https://registry.npmjs.org/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz",
126
+ "integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==",
127
+ "dev": true,
128
+ "license": "MIT",
129
+ "engines": {
130
+ "node": ">=12.20.0"
131
+ }
132
+ },
133
+ "node_modules/@tinyhttp/etag": {
134
+ "version": "2.1.2",
135
+ "resolved": "https://registry.npmjs.org/@tinyhttp/etag/-/etag-2.1.2.tgz",
136
+ "integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==",
137
+ "dev": true,
138
+ "license": "MIT",
139
+ "engines": {
140
+ "node": ">=12.20.0"
141
+ }
142
+ },
143
+ "node_modules/@tinyhttp/forwarded": {
144
+ "version": "2.1.2",
145
+ "resolved": "https://registry.npmjs.org/@tinyhttp/forwarded/-/forwarded-2.1.2.tgz",
146
+ "integrity": "sha512-9H/eulJ68ElY/+zYpTpNhZ7vxGV+cnwaR6+oQSm7bVgZMyuQfgROW/qvZuhmgDTIxnGMXst+Ba4ij6w6Krcs3w==",
147
+ "dev": true,
148
+ "license": "MIT",
149
+ "engines": {
150
+ "node": ">=12.20.0"
151
+ }
152
+ },
153
+ "node_modules/@tinyhttp/logger": {
154
+ "version": "2.1.0",
155
+ "resolved": "https://registry.npmjs.org/@tinyhttp/logger/-/logger-2.1.0.tgz",
156
+ "integrity": "sha512-Ma1fJ9CwUbn9r61/4HW6+nflsVoslpOnCrfQ6UeZq7GGIgwLzofms3HoSVG7M+AyRMJpxlfcDdbH5oFVroDMKA==",
157
+ "dev": true,
158
+ "license": "MIT",
159
+ "dependencies": {
160
+ "colorette": "^2.0.20",
161
+ "dayjs": "^1.11.13",
162
+ "http-status-emojis": "^2.2.0"
163
+ },
164
+ "engines": {
165
+ "node": ">=14.18 || >=16.20"
166
+ }
167
+ },
168
+ "node_modules/@tinyhttp/proxy-addr": {
169
+ "version": "3.0.1",
170
+ "resolved": "https://registry.npmjs.org/@tinyhttp/proxy-addr/-/proxy-addr-3.0.1.tgz",
171
+ "integrity": "sha512-vP0JVsy9ZMIldsaP/QHdMF+sb3B6wn7e2QXRdqpX/Cqz1ie35Am29DK88DeVmiwdTQle3FtYaVNtU3RgTGYZ+w==",
172
+ "dev": true,
173
+ "license": "MIT",
174
+ "dependencies": {
175
+ "@tinyhttp/forwarded": "2.1.2"
176
+ },
177
+ "engines": {
178
+ "node": ">=16.10.0"
179
+ }
180
+ },
181
+ "node_modules/@tinyhttp/req": {
182
+ "version": "2.2.8",
183
+ "resolved": "https://registry.npmjs.org/@tinyhttp/req/-/req-2.2.8.tgz",
184
+ "integrity": "sha512-HCsceFNgMpssUsnRao16iJyyfdWRwKlhL7OMTPUEjZsZGREnBzpjlrPHA31G5xNNzR7XOWVXDXGyrGgSpcwGSA==",
185
+ "dev": true,
186
+ "license": "MIT",
187
+ "dependencies": {
188
+ "@tinyhttp/accepts": "2.3.0",
189
+ "@tinyhttp/type-is": "2.2.5",
190
+ "@tinyhttp/url": "2.1.1",
191
+ "header-range-parser": "^1.1.3"
192
+ },
193
+ "engines": {
194
+ "node": ">=14.13.1"
195
+ }
196
+ },
197
+ "node_modules/@tinyhttp/res": {
198
+ "version": "2.2.10",
199
+ "resolved": "https://registry.npmjs.org/@tinyhttp/res/-/res-2.2.10.tgz",
200
+ "integrity": "sha512-ou+7T+x14xMrGwW0Rv+FOu4zYfXkTRf25yKMMOIuFyPyYOg27pbxtdiDVYJU1D5s6DFFdmenu0iXOSd1KABfYA==",
201
+ "dev": true,
202
+ "license": "MIT",
203
+ "dependencies": {
204
+ "@tinyhttp/content-disposition": "2.2.4",
205
+ "@tinyhttp/cookie": "2.1.1",
206
+ "@tinyhttp/cookie-signature": "2.1.1",
207
+ "@tinyhttp/encode-url": "2.1.1",
208
+ "@tinyhttp/req": "2.2.8",
209
+ "@tinyhttp/send": "2.2.4",
210
+ "@tinyhttp/vary": "^0.1.3",
211
+ "mime": "4.1.0"
212
+ },
213
+ "engines": {
214
+ "node": ">=14.13.1"
215
+ }
216
+ },
217
+ "node_modules/@tinyhttp/router": {
218
+ "version": "2.2.5",
219
+ "resolved": "https://registry.npmjs.org/@tinyhttp/router/-/router-2.2.5.tgz",
220
+ "integrity": "sha512-HI9Mpo9+IVpCzx/36okjJvtvifBSh3Ufhl9n1vylAbNLEykceJiMBkr06+W0qqRlo8TiZeUtg2XinEJu+GFcRA==",
221
+ "dev": true,
222
+ "license": "MIT",
223
+ "dependencies": {
224
+ "regexparam": "^2.0.2"
225
+ },
226
+ "engines": {
227
+ "node": ">=14.0.0"
228
+ }
229
+ },
230
+ "node_modules/@tinyhttp/send": {
231
+ "version": "2.2.4",
232
+ "resolved": "https://registry.npmjs.org/@tinyhttp/send/-/send-2.2.4.tgz",
233
+ "integrity": "sha512-DgEY185oQUd2LTKHM+he/m3sjYWB7yL0WLNrasoSEHaCfzstm0+viltqqOvEV7UtkSB3oRRo+OWiEtYKmd6h5w==",
234
+ "dev": true,
235
+ "license": "MIT",
236
+ "dependencies": {
237
+ "@tinyhttp/content-type": "^0.1.4",
238
+ "@tinyhttp/etag": "2.1.2",
239
+ "mime": "4.1.0"
240
+ },
241
+ "engines": {
242
+ "node": ">=14.13.1"
243
+ }
244
+ },
245
+ "node_modules/@tinyhttp/type-is": {
246
+ "version": "2.2.5",
247
+ "resolved": "https://registry.npmjs.org/@tinyhttp/type-is/-/type-is-2.2.5.tgz",
248
+ "integrity": "sha512-BCPEB+NV8v/9lzEE9GbfRPAKVsyayp84m6SSWn70j8yFkPBXeuVeq004pwVrjW1CRdmAZz9ZSH147pqqzAdr5g==",
249
+ "dev": true,
250
+ "license": "MIT",
251
+ "dependencies": {
252
+ "@tinyhttp/content-type": "^0.1.4",
253
+ "mime": "4.1.0"
254
+ },
255
+ "engines": {
256
+ "node": ">=14.13.1"
257
+ }
258
+ },
259
+ "node_modules/@tinyhttp/url": {
260
+ "version": "2.1.1",
261
+ "resolved": "https://registry.npmjs.org/@tinyhttp/url/-/url-2.1.1.tgz",
262
+ "integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==",
263
+ "dev": true,
264
+ "license": "MIT",
265
+ "engines": {
266
+ "node": ">=12.20.0"
267
+ }
268
+ },
269
+ "node_modules/@tinyhttp/vary": {
270
+ "version": "0.1.3",
271
+ "resolved": "https://registry.npmjs.org/@tinyhttp/vary/-/vary-0.1.3.tgz",
272
+ "integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==",
273
+ "dev": true,
274
+ "license": "MIT",
275
+ "engines": {
276
+ "node": ">=12.20"
277
+ }
278
+ },
279
+ "node_modules/ansi-regex": {
280
+ "version": "5.0.1",
281
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
282
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
283
+ "dev": true,
284
+ "license": "MIT",
285
+ "engines": {
286
+ "node": ">=8"
287
+ }
288
+ },
289
+ "node_modules/ansi-styles": {
290
+ "version": "4.3.0",
291
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
292
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
293
+ "dev": true,
294
+ "license": "MIT",
295
+ "dependencies": {
296
+ "color-convert": "^2.0.1"
297
+ },
298
+ "engines": {
299
+ "node": ">=8"
300
+ },
301
+ "funding": {
302
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
303
+ }
304
+ },
305
+ "node_modules/chalk": {
306
+ "version": "4.1.2",
307
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
308
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
309
+ "dev": true,
310
+ "license": "MIT",
311
+ "dependencies": {
312
+ "ansi-styles": "^4.1.0",
313
+ "supports-color": "^7.1.0"
314
+ },
315
+ "engines": {
316
+ "node": ">=10"
317
+ },
318
+ "funding": {
319
+ "url": "https://github.com/chalk/chalk?sponsor=1"
320
+ }
321
+ },
322
+ "node_modules/chalk/node_modules/supports-color": {
323
+ "version": "7.2.0",
324
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
325
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
326
+ "dev": true,
327
+ "license": "MIT",
328
+ "dependencies": {
329
+ "has-flag": "^4.0.0"
330
+ },
331
+ "engines": {
332
+ "node": ">=8"
333
+ }
334
+ },
335
+ "node_modules/chokidar": {
336
+ "version": "5.0.0",
337
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
338
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
339
+ "dev": true,
340
+ "license": "MIT",
341
+ "dependencies": {
342
+ "readdirp": "^5.0.0"
343
+ },
344
+ "engines": {
345
+ "node": ">= 20.19.0"
346
+ },
347
+ "funding": {
348
+ "url": "https://paulmillr.com/funding/"
349
+ }
350
+ },
351
+ "node_modules/cliui": {
352
+ "version": "8.0.1",
353
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
354
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
355
+ "dev": true,
356
+ "license": "ISC",
357
+ "dependencies": {
358
+ "string-width": "^4.2.0",
359
+ "strip-ansi": "^6.0.1",
360
+ "wrap-ansi": "^7.0.0"
361
+ },
362
+ "engines": {
363
+ "node": ">=12"
364
+ }
365
+ },
366
+ "node_modules/color-convert": {
367
+ "version": "2.0.1",
368
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
369
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
370
+ "dev": true,
371
+ "license": "MIT",
372
+ "dependencies": {
373
+ "color-name": "~1.1.4"
374
+ },
375
+ "engines": {
376
+ "node": ">=7.0.0"
377
+ }
378
+ },
379
+ "node_modules/color-name": {
380
+ "version": "1.1.4",
381
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
382
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
383
+ "dev": true,
384
+ "license": "MIT"
385
+ },
386
+ "node_modules/colorette": {
387
+ "version": "2.0.20",
388
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
389
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
390
+ "dev": true,
391
+ "license": "MIT"
392
+ },
393
+ "node_modules/concurrently": {
394
+ "version": "9.2.1",
395
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
396
+ "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
397
+ "dev": true,
398
+ "license": "MIT",
399
+ "dependencies": {
400
+ "chalk": "4.1.2",
401
+ "rxjs": "7.8.2",
402
+ "shell-quote": "1.8.3",
403
+ "supports-color": "8.1.1",
404
+ "tree-kill": "1.2.2",
405
+ "yargs": "17.7.2"
406
+ },
407
+ "bin": {
408
+ "conc": "dist/bin/concurrently.js",
409
+ "concurrently": "dist/bin/concurrently.js"
410
+ },
411
+ "engines": {
412
+ "node": ">=18"
413
+ },
414
+ "funding": {
415
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
416
+ }
417
+ },
418
+ "node_modules/dayjs": {
419
+ "version": "1.11.20",
420
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
421
+ "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
422
+ "dev": true,
423
+ "license": "MIT"
424
+ },
425
+ "node_modules/dot-prop": {
426
+ "version": "10.1.0",
427
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz",
428
+ "integrity": "sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==",
429
+ "dev": true,
430
+ "license": "MIT",
431
+ "dependencies": {
432
+ "type-fest": "^5.0.0"
433
+ },
434
+ "engines": {
435
+ "node": ">=20"
436
+ },
437
+ "funding": {
438
+ "url": "https://github.com/sponsors/sindresorhus"
439
+ }
440
+ },
441
+ "node_modules/emoji-regex": {
442
+ "version": "8.0.0",
443
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
444
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
445
+ "dev": true,
446
+ "license": "MIT"
447
+ },
448
+ "node_modules/escalade": {
449
+ "version": "3.2.0",
450
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
451
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
452
+ "dev": true,
453
+ "license": "MIT",
454
+ "engines": {
455
+ "node": ">=6"
456
+ }
457
+ },
458
+ "node_modules/eta": {
459
+ "version": "4.5.1",
460
+ "resolved": "https://registry.npmjs.org/eta/-/eta-4.5.1.tgz",
461
+ "integrity": "sha512-EaNCGm+8XEIU7YNcc+THptWAO5NfKBHHARxt+wxZljj9bTr/+arRoOm9/MpGt4n6xn9fLnPFRSoLD0WFYGFUxQ==",
462
+ "dev": true,
463
+ "license": "MIT",
464
+ "engines": {
465
+ "node": ">=20"
466
+ },
467
+ "funding": {
468
+ "url": "https://github.com/bgub/eta?sponsor=1"
469
+ }
470
+ },
471
+ "node_modules/get-caller-file": {
472
+ "version": "2.0.5",
473
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
474
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
475
+ "dev": true,
476
+ "license": "ISC",
477
+ "engines": {
478
+ "node": "6.* || 8.* || >= 10.*"
479
+ }
480
+ },
481
+ "node_modules/has-flag": {
482
+ "version": "4.0.0",
483
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
484
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
485
+ "dev": true,
486
+ "license": "MIT",
487
+ "engines": {
488
+ "node": ">=8"
489
+ }
490
+ },
491
+ "node_modules/header-range-parser": {
492
+ "version": "1.1.5",
493
+ "resolved": "https://registry.npmjs.org/header-range-parser/-/header-range-parser-1.1.5.tgz",
494
+ "integrity": "sha512-n5JOx67HBL0MGqtu6NFoEYWb+xDYAOgBI5dBkyMDff1xHbhGnjCMglj1aiMNPHps6HwXO+2i5jbPU/zJSk7etQ==",
495
+ "dev": true,
496
+ "license": "MIT",
497
+ "engines": {
498
+ "node": ">=12.22.0"
499
+ }
500
+ },
501
+ "node_modules/http-status-emojis": {
502
+ "version": "2.2.0",
503
+ "resolved": "https://registry.npmjs.org/http-status-emojis/-/http-status-emojis-2.2.0.tgz",
504
+ "integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==",
505
+ "dev": true,
506
+ "license": "MIT"
507
+ },
508
+ "node_modules/inflection": {
509
+ "version": "3.0.2",
510
+ "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz",
511
+ "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==",
512
+ "dev": true,
513
+ "license": "MIT",
514
+ "engines": {
515
+ "node": ">=18.0.0"
516
+ }
517
+ },
518
+ "node_modules/is-fullwidth-code-point": {
519
+ "version": "3.0.0",
520
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
521
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
522
+ "dev": true,
523
+ "license": "MIT",
524
+ "engines": {
525
+ "node": ">=8"
526
+ }
527
+ },
528
+ "node_modules/json-server": {
529
+ "version": "1.0.0-beta.15",
530
+ "resolved": "https://registry.npmjs.org/json-server/-/json-server-1.0.0-beta.15.tgz",
531
+ "integrity": "sha512-I5UB/OWHLGoQW9IVld2yzZFYYiGYNBn2OYRlGbkfj5xDidT26yKYkV7Sr093zJtPh9zbofaLgyT89Ov5U0RRBQ==",
532
+ "dev": true,
533
+ "license": "MIT",
534
+ "dependencies": {
535
+ "@tinyhttp/app": "^3.0.1",
536
+ "@tinyhttp/cors": "^2.0.1",
537
+ "@tinyhttp/logger": "^2.1.0",
538
+ "chalk": "^5.6.2",
539
+ "chokidar": "^5.0.0",
540
+ "dot-prop": "^10.1.0",
541
+ "eta": "^4.5.0",
542
+ "inflection": "^3.0.2",
543
+ "json5": "^2.2.3",
544
+ "lowdb": "^7.0.1",
545
+ "milliparsec": "^5.1.0",
546
+ "sirv": "^3.0.2",
547
+ "sort-on": "^7.0.0"
548
+ },
549
+ "bin": {
550
+ "json-server": "lib/bin.js"
551
+ },
552
+ "engines": {
553
+ "node": ">=22.12.0"
554
+ }
555
+ },
556
+ "node_modules/json-server/node_modules/chalk": {
557
+ "version": "5.6.2",
558
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
559
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
560
+ "dev": true,
561
+ "license": "MIT",
562
+ "engines": {
563
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
564
+ },
565
+ "funding": {
566
+ "url": "https://github.com/chalk/chalk?sponsor=1"
567
+ }
568
+ },
569
+ "node_modules/json5": {
570
+ "version": "2.2.3",
571
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
572
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
573
+ "dev": true,
574
+ "license": "MIT",
575
+ "bin": {
576
+ "json5": "lib/cli.js"
577
+ },
578
+ "engines": {
579
+ "node": ">=6"
580
+ }
581
+ },
582
+ "node_modules/lowdb": {
583
+ "version": "7.0.1",
584
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz",
585
+ "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==",
586
+ "dev": true,
587
+ "license": "MIT",
588
+ "dependencies": {
589
+ "steno": "^4.0.2"
590
+ },
591
+ "engines": {
592
+ "node": ">=18"
593
+ },
594
+ "funding": {
595
+ "url": "https://github.com/sponsors/typicode"
596
+ }
597
+ },
598
+ "node_modules/milliparsec": {
599
+ "version": "5.1.1",
600
+ "resolved": "https://registry.npmjs.org/milliparsec/-/milliparsec-5.1.1.tgz",
601
+ "integrity": "sha512-jkEDaSWZp4/Q3vprqdqukBqUEyNNqC1pwTjZ5cp9YkaR1wv5fvTCd8VFsecbw7i8DNBGjzhJ83MDoPZlcTaPQg==",
602
+ "dev": true,
603
+ "license": "MIT",
604
+ "engines": {
605
+ "node": ">=18.13 || >=19.20 || >=20"
606
+ }
607
+ },
608
+ "node_modules/mime": {
609
+ "version": "4.1.0",
610
+ "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
611
+ "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
612
+ "dev": true,
613
+ "funding": [
614
+ "https://github.com/sponsors/broofa"
615
+ ],
616
+ "license": "MIT",
617
+ "bin": {
618
+ "mime": "bin/cli.js"
619
+ },
620
+ "engines": {
621
+ "node": ">=16"
622
+ }
623
+ },
624
+ "node_modules/mrmime": {
625
+ "version": "2.0.1",
626
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
627
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
628
+ "dev": true,
629
+ "license": "MIT",
630
+ "engines": {
631
+ "node": ">=10"
632
+ }
633
+ },
634
+ "node_modules/readdirp": {
635
+ "version": "5.0.0",
636
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
637
+ "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
638
+ "dev": true,
639
+ "license": "MIT",
640
+ "engines": {
641
+ "node": ">= 20.19.0"
642
+ },
643
+ "funding": {
644
+ "type": "individual",
645
+ "url": "https://paulmillr.com/funding/"
646
+ }
647
+ },
648
+ "node_modules/regexparam": {
649
+ "version": "2.0.2",
650
+ "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz",
651
+ "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==",
652
+ "dev": true,
653
+ "license": "MIT",
654
+ "engines": {
655
+ "node": ">=8"
656
+ }
657
+ },
658
+ "node_modules/require-directory": {
659
+ "version": "2.1.1",
660
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
661
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
662
+ "dev": true,
663
+ "license": "MIT",
664
+ "engines": {
665
+ "node": ">=0.10.0"
666
+ }
667
+ },
668
+ "node_modules/rxjs": {
669
+ "version": "7.8.2",
670
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
671
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
672
+ "dev": true,
673
+ "license": "Apache-2.0",
674
+ "dependencies": {
675
+ "tslib": "^2.1.0"
676
+ }
677
+ },
678
+ "node_modules/shell-quote": {
679
+ "version": "1.8.3",
680
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
681
+ "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
682
+ "dev": true,
683
+ "license": "MIT",
684
+ "engines": {
685
+ "node": ">= 0.4"
686
+ },
687
+ "funding": {
688
+ "url": "https://github.com/sponsors/ljharb"
689
+ }
690
+ },
691
+ "node_modules/sirv": {
692
+ "version": "3.0.2",
693
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
694
+ "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
695
+ "dev": true,
696
+ "license": "MIT",
697
+ "dependencies": {
698
+ "@polka/url": "^1.0.0-next.24",
699
+ "mrmime": "^2.0.0",
700
+ "totalist": "^3.0.0"
701
+ },
702
+ "engines": {
703
+ "node": ">=18"
704
+ }
705
+ },
706
+ "node_modules/sort-on": {
707
+ "version": "7.0.0",
708
+ "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-7.0.0.tgz",
709
+ "integrity": "sha512-e+4RRxt7jsWdGPp4H5PKOER/ELYlemNB1plvW686Qi3j4WVaCjCpro2zaTD7Cn0VtBImq/hg3x1JfovMNXXfJQ==",
710
+ "dev": true,
711
+ "license": "MIT",
712
+ "dependencies": {
713
+ "dot-prop": "^10.1.0"
714
+ },
715
+ "engines": {
716
+ "node": ">=20"
717
+ },
718
+ "funding": {
719
+ "url": "https://github.com/sponsors/sindresorhus"
720
+ }
721
+ },
722
+ "node_modules/steno": {
723
+ "version": "4.0.2",
724
+ "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz",
725
+ "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==",
726
+ "dev": true,
727
+ "license": "MIT",
728
+ "engines": {
729
+ "node": ">=18"
730
+ },
731
+ "funding": {
732
+ "url": "https://github.com/sponsors/typicode"
733
+ }
734
+ },
735
+ "node_modules/string-width": {
736
+ "version": "4.2.3",
737
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
738
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
739
+ "dev": true,
740
+ "license": "MIT",
741
+ "dependencies": {
742
+ "emoji-regex": "^8.0.0",
743
+ "is-fullwidth-code-point": "^3.0.0",
744
+ "strip-ansi": "^6.0.1"
745
+ },
746
+ "engines": {
747
+ "node": ">=8"
748
+ }
749
+ },
750
+ "node_modules/strip-ansi": {
751
+ "version": "6.0.1",
752
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
753
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
754
+ "dev": true,
755
+ "license": "MIT",
756
+ "dependencies": {
757
+ "ansi-regex": "^5.0.1"
758
+ },
759
+ "engines": {
760
+ "node": ">=8"
761
+ }
762
+ },
763
+ "node_modules/supports-color": {
764
+ "version": "8.1.1",
765
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
766
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
767
+ "dev": true,
768
+ "license": "MIT",
769
+ "dependencies": {
770
+ "has-flag": "^4.0.0"
771
+ },
772
+ "engines": {
773
+ "node": ">=10"
774
+ },
775
+ "funding": {
776
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
777
+ }
778
+ },
779
+ "node_modules/tagged-tag": {
780
+ "version": "1.0.0",
781
+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
782
+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
783
+ "dev": true,
784
+ "license": "MIT",
785
+ "engines": {
786
+ "node": ">=20"
787
+ },
788
+ "funding": {
789
+ "url": "https://github.com/sponsors/sindresorhus"
790
+ }
791
+ },
792
+ "node_modules/totalist": {
793
+ "version": "3.0.1",
794
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
795
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
796
+ "dev": true,
797
+ "license": "MIT",
798
+ "engines": {
799
+ "node": ">=6"
800
+ }
801
+ },
802
+ "node_modules/tree-kill": {
803
+ "version": "1.2.2",
804
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
805
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
806
+ "dev": true,
807
+ "license": "MIT",
808
+ "bin": {
809
+ "tree-kill": "cli.js"
810
+ }
811
+ },
812
+ "node_modules/tslib": {
813
+ "version": "2.8.1",
814
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
815
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
816
+ "dev": true,
817
+ "license": "0BSD"
818
+ },
819
+ "node_modules/type-fest": {
820
+ "version": "5.5.0",
821
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz",
822
+ "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==",
823
+ "dev": true,
824
+ "license": "(MIT OR CC0-1.0)",
825
+ "dependencies": {
826
+ "tagged-tag": "^1.0.0"
827
+ },
828
+ "engines": {
829
+ "node": ">=20"
830
+ },
831
+ "funding": {
832
+ "url": "https://github.com/sponsors/sindresorhus"
833
+ }
834
+ },
835
+ "node_modules/wrap-ansi": {
836
+ "version": "7.0.0",
837
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
838
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
839
+ "dev": true,
840
+ "license": "MIT",
841
+ "dependencies": {
842
+ "ansi-styles": "^4.0.0",
843
+ "string-width": "^4.1.0",
844
+ "strip-ansi": "^6.0.0"
845
+ },
846
+ "engines": {
847
+ "node": ">=10"
848
+ },
849
+ "funding": {
850
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
851
+ }
852
+ },
853
+ "node_modules/y18n": {
854
+ "version": "5.0.8",
855
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
856
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
857
+ "dev": true,
858
+ "license": "ISC",
859
+ "engines": {
860
+ "node": ">=10"
861
+ }
862
+ },
863
+ "node_modules/yargs": {
864
+ "version": "17.7.2",
865
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
866
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
867
+ "dev": true,
868
+ "license": "MIT",
869
+ "dependencies": {
870
+ "cliui": "^8.0.1",
871
+ "escalade": "^3.1.1",
872
+ "get-caller-file": "^2.0.5",
873
+ "require-directory": "^2.1.1",
874
+ "string-width": "^4.2.3",
875
+ "y18n": "^5.0.5",
876
+ "yargs-parser": "^21.1.1"
877
+ },
878
+ "engines": {
879
+ "node": ">=12"
880
+ }
881
+ },
882
+ "node_modules/yargs-parser": {
883
+ "version": "21.1.1",
884
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
885
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
886
+ "dev": true,
887
+ "license": "ISC",
888
+ "engines": {
889
+ "node": ">=12"
890
+ }
891
+ }
892
+ }
893
+ }
package.json CHANGED
@@ -4,11 +4,14 @@
4
  "private": true,
5
  "description": "PulseFabric Monitor — GitHub Codespaces + Copilot Demo",
6
  "scripts": {
7
- "start": "echo \"TODO: start frontend + backend\"",
8
- "start:frontend": "echo \"TODO: ng serve\"",
9
- "start:api": "echo \"TODO: json-server\"",
10
- "build": "echo \"TODO: ng build\"",
11
- "test": "echo \"TODO: ng test\"",
12
- "lint": "echo \"TODO: ng lint\""
 
 
 
13
  }
14
  }
 
4
  "private": true,
5
  "description": "PulseFabric Monitor — GitHub Codespaces + Copilot Demo",
6
  "scripts": {
7
+ "dev:web": "cd frontend && npx ng serve",
8
+ "build:web": "cd frontend && npx ng build",
9
+ "dev:api": "npx json-server backend/db.json --port 3000",
10
+ "dev": "npx concurrently -n api,web -c blue,green \"npm run dev:api\" \"npm run dev:web\"",
11
+ "validate": "cd frontend && npx ng build && npx ng lint"
12
+ },
13
+ "devDependencies": {
14
+ "concurrently": "^9.2.1",
15
+ "json-server": "^1.0.0-beta.15"
16
  }
17
  }