adonisjs-server-stats 1.6.1 → 1.6.2
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.
- package/dist/src/collectors/app_collector.d.ts.map +1 -1
- package/dist/src/collectors/app_collector.js +2 -1
- package/dist/src/collectors/auto_detect.d.ts.map +1 -1
- package/dist/src/collectors/auto_detect.js +7 -3
- package/dist/src/collectors/db_pool_collector.d.ts.map +1 -1
- package/dist/src/collectors/db_pool_collector.js +2 -1
- package/dist/src/collectors/http_collector.d.ts +4 -3
- package/dist/src/collectors/http_collector.d.ts.map +1 -1
- package/dist/src/collectors/http_collector.js +3 -5
- package/dist/src/collectors/redis_collector.d.ts.map +1 -1
- package/dist/src/collectors/redis_collector.js +2 -1
- package/dist/src/controller/debug_controller.d.ts +5 -0
- package/dist/src/controller/debug_controller.d.ts.map +1 -1
- package/dist/src/controller/debug_controller.js +24 -1
- package/dist/src/dashboard/chart_aggregator.d.ts.map +1 -1
- package/dist/src/dashboard/chart_aggregator.js +3 -2
- package/dist/src/dashboard/dashboard_store.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_store.js +47 -11
- package/dist/src/dashboard/migrator.d.ts +5 -0
- package/dist/src/dashboard/migrator.d.ts.map +1 -1
- package/dist/src/dashboard/migrator.js +44 -9
- package/dist/src/data/data_access.d.ts +4 -2
- package/dist/src/data/data_access.d.ts.map +1 -1
- package/dist/src/data/data_access.js +8 -3
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +7 -6
- package/dist/src/edge/client-vue/dashboard.js +1 -1
- package/dist/src/edge/client-vue/debug-panel-deferred.js +1 -1
- package/dist/src/edge/client-vue/debug-panel.js +1 -1
- package/dist/src/middleware/request_tracking_middleware.d.ts.map +1 -1
- package/dist/src/middleware/request_tracking_middleware.js +7 -0
- package/dist/src/provider/server_stats_provider.d.ts +9 -0
- package/dist/src/provider/server_stats_provider.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.js +196 -59
- package/dist/src/utils/app_import.d.ts +23 -0
- package/dist/src/utils/app_import.d.ts.map +1 -0
- package/dist/src/utils/app_import.js +44 -0
- package/dist/vue/{CacheSection-CkrIB4-j.js → CacheSection-C788Yfai.js} +1 -1
- package/dist/vue/{ConfigSection-gulpOiq1.js → ConfigSection-CRzYxqW2.js} +1 -1
- package/dist/vue/{CustomPaneTab-J57ED_bh.js → CustomPaneTab-BJxT5Dp7.js} +33 -33
- package/dist/vue/{EmailsSection-BlKvQDx8.js → EmailsSection-C8JFMtW7.js} +1 -1
- package/dist/vue/{EventsSection-BdzQvIVJ.js → EventsSection-C4wXUgxG.js} +1 -1
- package/dist/vue/{JobsSection-DOzuMrG3.js → JobsSection-CsKWTjgN.js} +1 -1
- package/dist/vue/{LogsSection-CNN4y92u.js → LogsSection-BFVjSZ24.js} +12 -12
- package/dist/vue/{LogsTab-CJerb22r.js → LogsTab-DpEQ7euu.js} +17 -17
- package/dist/vue/{OverviewSection-SITNR_dA.js → OverviewSection-CbMdAido.js} +1 -1
- package/dist/vue/{QueriesSection-BAebAHkD.js → QueriesSection-BPiv7u3r.js} +1 -1
- package/dist/vue/{RequestsSection-CIR0IX39.js → RequestsSection-LtImH4rD.js} +1 -1
- package/dist/vue/{RoutesSection-j1U2oa0g.js → RoutesSection-CrxOxmzx.js} +1 -1
- package/dist/vue/{TimelineSection-Dw980UPg.js → TimelineSection-DLxMW2J_.js} +1 -1
- package/dist/vue/{index-COgsk_nv.js → index-qCQpBftQ.js} +2 -2
- package/dist/vue/index.js +1 -1
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/app_collector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/app_collector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAKrD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,IAAI,eAAe,CAgE9C"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appImport } from '../utils/app_import.js';
|
|
1
2
|
import { log, dim, bold } from '../utils/logger.js';
|
|
2
3
|
let warnedLucidMissing = false;
|
|
3
4
|
let warnedSessionsTable = false;
|
|
@@ -24,7 +25,7 @@ export function appCollector() {
|
|
|
24
25
|
},
|
|
25
26
|
async collect() {
|
|
26
27
|
try {
|
|
27
|
-
const { default: db } = await
|
|
28
|
+
const { default: db } = await appImport('@adonisjs/lucid/services/db');
|
|
28
29
|
const [sessions, webhooks, emails] = await Promise.all([
|
|
29
30
|
db
|
|
30
31
|
.from('sessions')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto_detect.d.ts","sourceRoot":"","sources":["../../../src/collectors/auto_detect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auto_detect.d.ts","sourceRoot":"","sources":["../../../src/collectors/auto_detect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAqCrD,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAwGtE"}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import { appImport } from '../utils/app_import.js';
|
|
1
2
|
import { log, dim, green, bold } from '../utils/logger.js';
|
|
2
3
|
/**
|
|
3
4
|
* Probe whether a package is importable at runtime.
|
|
4
5
|
*
|
|
5
|
-
* Uses
|
|
6
|
-
*
|
|
6
|
+
* Uses {@link appImport} which resolves from `process.cwd()` first,
|
|
7
|
+
* handling the common case where adonisjs-server-stats is symlinked
|
|
8
|
+
* (e.g. `file:../../adonisjs-server-stats` in package.json). Without
|
|
9
|
+
* this, `import(pkg)` resolves from the symlink *target* directory,
|
|
10
|
+
* which may have devDependency stubs instead of the real packages.
|
|
7
11
|
*/
|
|
8
12
|
async function isInstalled(pkg) {
|
|
9
13
|
try {
|
|
10
|
-
await
|
|
14
|
+
await appImport(pkg);
|
|
11
15
|
return true;
|
|
12
16
|
}
|
|
13
17
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db_pool_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/db_pool_collector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"db_pool_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/db_pool_collector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAUrD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,sBAAsB,GAAG,eAAe,CA8D9E"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appImport } from '../utils/app_import.js';
|
|
1
2
|
import { log, dim, bold } from '../utils/logger.js';
|
|
2
3
|
/**
|
|
3
4
|
* Monitors the Knex connection pool for a Lucid database connection.
|
|
@@ -33,7 +34,7 @@ export function dbPoolCollector(opts) {
|
|
|
33
34
|
},
|
|
34
35
|
async collect() {
|
|
35
36
|
try {
|
|
36
|
-
const { default: db } = await
|
|
37
|
+
const { default: db } = await appImport('@adonisjs/lucid/services/db');
|
|
37
38
|
const connection = db.manager.get(connectionName);
|
|
38
39
|
if (!connection) {
|
|
39
40
|
if (!warnedConnectionNotFound) {
|
|
@@ -27,12 +27,13 @@ export interface HttpCollectorOptions {
|
|
|
27
27
|
* Returns the shared {@link RequestMetrics} instance created by
|
|
28
28
|
* `httpCollector()`.
|
|
29
29
|
*
|
|
30
|
+
* Returns `null` if `httpCollector()` has not been called yet
|
|
31
|
+
* (e.g. during the startup window before collectors initialize).
|
|
32
|
+
*
|
|
30
33
|
* Useful for accessing request metrics outside of the collector
|
|
31
34
|
* (e.g. in custom middleware or controllers).
|
|
32
|
-
*
|
|
33
|
-
* @throws If `httpCollector()` has not been included in the config.
|
|
34
35
|
*/
|
|
35
|
-
export declare function getRequestMetrics(): RequestMetrics;
|
|
36
|
+
export declare function getRequestMetrics(): RequestMetrics | null;
|
|
36
37
|
/**
|
|
37
38
|
* Tracks HTTP request throughput, response times, and error rates.
|
|
38
39
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/http_collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAG7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAID
|
|
1
|
+
{"version":3,"file":"http_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/http_collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAG7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAID;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAEzD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,oBAAoB,GAAG,eAAe,CAmC1E"}
|
|
@@ -5,15 +5,13 @@ let sharedInstance = null;
|
|
|
5
5
|
* Returns the shared {@link RequestMetrics} instance created by
|
|
6
6
|
* `httpCollector()`.
|
|
7
7
|
*
|
|
8
|
+
* Returns `null` if `httpCollector()` has not been called yet
|
|
9
|
+
* (e.g. during the startup window before collectors initialize).
|
|
10
|
+
*
|
|
8
11
|
* Useful for accessing request metrics outside of the collector
|
|
9
12
|
* (e.g. in custom middleware or controllers).
|
|
10
|
-
*
|
|
11
|
-
* @throws If `httpCollector()` has not been included in the config.
|
|
12
13
|
*/
|
|
13
14
|
export function getRequestMetrics() {
|
|
14
|
-
if (!sharedInstance) {
|
|
15
|
-
throw new Error('RequestMetrics not initialized. Ensure httpCollector() is included in your collectors config.');
|
|
16
|
-
}
|
|
17
15
|
return sharedInstance;
|
|
18
16
|
}
|
|
19
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/redis_collector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redis_collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/redis_collector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAMrD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,IAAI,eAAe,CAoGhD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appImport } from '../utils/app_import.js';
|
|
1
2
|
import { log, dim, bold } from '../utils/logger.js';
|
|
2
3
|
let warnedNotInstalled = false;
|
|
3
4
|
let warnedPingFailed = false;
|
|
@@ -34,7 +35,7 @@ export function redisCollector() {
|
|
|
34
35
|
};
|
|
35
36
|
let redis;
|
|
36
37
|
try {
|
|
37
|
-
const mod = await
|
|
38
|
+
const mod = await appImport('@adonisjs/redis/services/main');
|
|
38
39
|
redis = mod.default;
|
|
39
40
|
}
|
|
40
41
|
catch {
|
|
@@ -2,18 +2,23 @@ import type { DashboardStore } from '../dashboard/dashboard_store.js';
|
|
|
2
2
|
import type { DebugStore } from '../debug/debug_store.js';
|
|
3
3
|
import type { StatsEngine } from '../engine/stats_engine.js';
|
|
4
4
|
import type { ResolvedServerStatsConfig } from '../types.js';
|
|
5
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
5
6
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
6
7
|
interface DiagnosticsDeps {
|
|
7
8
|
getEngine?: () => StatsEngine | null;
|
|
8
9
|
getDashboardStore?: () => DashboardStore | null;
|
|
9
10
|
getProviderDiagnostics?: () => Record<string, unknown>;
|
|
11
|
+
getApp?: () => ApplicationService;
|
|
10
12
|
}
|
|
11
13
|
export default class DebugController {
|
|
12
14
|
private store;
|
|
13
15
|
private serverConfig?;
|
|
14
16
|
private diagnosticsDeps;
|
|
17
|
+
private configInspector;
|
|
15
18
|
constructor(store: DebugStore, serverConfig?: ResolvedServerStatsConfig | undefined, diagnosticsDeps?: DiagnosticsDeps);
|
|
16
19
|
config({ response }: HttpContext): Promise<void>;
|
|
20
|
+
/** Lazily create a ConfigInspector from the app reference. */
|
|
21
|
+
private getConfigInspector;
|
|
17
22
|
diagnostics({ response }: HttpContext): Promise<void>;
|
|
18
23
|
}
|
|
19
24
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug_controller.d.ts","sourceRoot":"","sources":["../../../src/controller/debug_controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debug_controller.d.ts","sourceRoot":"","sources":["../../../src/controller/debug_controller.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAA;IACpC,iBAAiB,CAAC,EAAE,MAAM,cAAc,GAAG,IAAI,CAAA;IAC/C,sBAAsB,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtD,MAAM,CAAC,EAAE,MAAM,kBAAkB,CAAA;CAClC;AAED,MAAM,CAAC,OAAO,OAAO,eAAe;IAKhC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY,CAAC;IALvB,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,eAAe,CAA+B;gBAG5C,KAAK,EAAE,UAAU,EACjB,YAAY,CAAC,EAAE,yBAAyB,YAAA,EAChD,eAAe,CAAC,EAAE,eAAe;IAK7B,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IA8DtC,8DAA8D;IAC9D,OAAO,CAAC,kBAAkB;IAQpB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;CA+D5C"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { ConfigInspector } from '../dashboard/integrations/config_inspector.js';
|
|
3
4
|
export default class DebugController {
|
|
4
5
|
store;
|
|
5
6
|
serverConfig;
|
|
6
7
|
diagnosticsDeps;
|
|
8
|
+
configInspector = null;
|
|
7
9
|
constructor(store, serverConfig, diagnosticsDeps) {
|
|
8
10
|
this.store = store;
|
|
9
11
|
this.serverConfig = serverConfig;
|
|
@@ -46,7 +48,28 @@ export default class DebugController {
|
|
|
46
48
|
const transmit = {
|
|
47
49
|
channelName: cfg?.channelName ?? 'admin/server-stats',
|
|
48
50
|
};
|
|
49
|
-
|
|
51
|
+
// App config + env vars (for the Config tab's APP CONFIG / ENV view)
|
|
52
|
+
const inspector = this.getConfigInspector();
|
|
53
|
+
const appConfig = inspector ? inspector.getConfig().config : {};
|
|
54
|
+
const envVars = inspector ? inspector.getEnvVars().env : {};
|
|
55
|
+
return response.json({
|
|
56
|
+
features,
|
|
57
|
+
customPanes,
|
|
58
|
+
endpoints,
|
|
59
|
+
transmit,
|
|
60
|
+
app: appConfig,
|
|
61
|
+
env: envVars,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/** Lazily create a ConfigInspector from the app reference. */
|
|
65
|
+
getConfigInspector() {
|
|
66
|
+
if (this.configInspector)
|
|
67
|
+
return this.configInspector;
|
|
68
|
+
const app = this.diagnosticsDeps.getApp?.();
|
|
69
|
+
if (!app)
|
|
70
|
+
return null;
|
|
71
|
+
this.configInspector = new ConfigInspector(app);
|
|
72
|
+
return this.configInspector;
|
|
50
73
|
}
|
|
51
74
|
async diagnostics({ response }) {
|
|
52
75
|
const engine = this.diagnosticsDeps.getEngine?.();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart_aggregator.d.ts","sourceRoot":"","sources":["../../../src/dashboard/chart_aggregator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,KAAK,CAA8C;gBAE/C,EAAE,EAAE,IAAI;IAIpB,KAAK,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"chart_aggregator.d.ts","sourceRoot":"","sources":["../../../src/dashboard/chart_aggregator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,KAAK,CAA8C;gBAE/C,EAAE,EAAE,IAAI;IAIpB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;YAOE,SAAS;CA6DxB"}
|
|
@@ -18,8 +18,9 @@ export class ChartAggregator {
|
|
|
18
18
|
this.db = db;
|
|
19
19
|
}
|
|
20
20
|
start() {
|
|
21
|
-
//
|
|
22
|
-
|
|
21
|
+
// Defer the first aggregation so the event loop stays responsive
|
|
22
|
+
// during dashboard initialization. Then run every 60s.
|
|
23
|
+
setTimeout(() => this.aggregate().catch(() => { }), 2_000);
|
|
23
24
|
this.timer = setInterval(() => {
|
|
24
25
|
this.aggregate().catch(() => { });
|
|
25
26
|
}, 60_000);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_store.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"dashboard_store.d.ts","sourceRoot":"","sources":["../../../src/dashboard/dashboard_store.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,35 +50,71 @@ export class DashboardStore {
|
|
|
50
50
|
await mkdir(dirname(dbFilePath), { recursive: true });
|
|
51
51
|
// Create a standalone Knex connection to SQLite — bypasses Lucid's
|
|
52
52
|
// connection manager entirely so we never pollute the app's pool.
|
|
53
|
-
|
|
53
|
+
//
|
|
54
|
+
// Must use appImport — bare import('knex') resolves to this package's
|
|
55
|
+
// devDep copy when symlinked (file: dependency), which may have
|
|
56
|
+
// different native bindings or conflict with the app's copies.
|
|
57
|
+
log.info('dashboard: loading knex...');
|
|
58
|
+
const { appImportWithPath } = await import('../utils/app_import.js');
|
|
59
|
+
let knexModule;
|
|
60
|
+
let knexPath;
|
|
61
|
+
try {
|
|
62
|
+
const result = await appImportWithPath('knex');
|
|
63
|
+
knexModule = result.module;
|
|
64
|
+
knexPath = result.resolvedPath;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
throw new Error(`Could not load knex: ${err?.message}. ` +
|
|
68
|
+
'Install it with: npm install knex better-sqlite3');
|
|
69
|
+
}
|
|
70
|
+
let sqlite3Path;
|
|
71
|
+
try {
|
|
72
|
+
const result = await appImportWithPath('better-sqlite3');
|
|
73
|
+
sqlite3Path = result.resolvedPath;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new Error(`Could not load better-sqlite3: ${err?.message}. ` +
|
|
77
|
+
'Install it with: npm install better-sqlite3');
|
|
78
|
+
}
|
|
79
|
+
log.info(`dashboard: knex resolved from ${knexPath}`);
|
|
80
|
+
log.info(`dashboard: better-sqlite3 resolved from ${sqlite3Path}`);
|
|
54
81
|
const knexFactory = knexModule.default ?? knexModule;
|
|
55
|
-
|
|
82
|
+
log.info(`dashboard: opening SQLite database at ${dbFilePath}`);
|
|
83
|
+
const db = knexFactory({
|
|
56
84
|
client: 'better-sqlite3',
|
|
57
85
|
connection: { filename: dbFilePath },
|
|
58
86
|
useNullAsDefault: true,
|
|
59
87
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await
|
|
63
|
-
await
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
88
|
+
this.db = db;
|
|
89
|
+
log.info('dashboard: setting PRAGMA...');
|
|
90
|
+
await db.raw('PRAGMA journal_mode=WAL');
|
|
91
|
+
await db.raw('PRAGMA foreign_keys=ON');
|
|
92
|
+
log.info('dashboard: PRAGMA set');
|
|
93
|
+
log.info('dashboard: running migrations...');
|
|
94
|
+
await autoMigrate(db);
|
|
95
|
+
log.info('dashboard: migrations complete');
|
|
96
|
+
// Defer retention cleanup — not critical during startup.
|
|
97
|
+
// Run first cleanup after 30s, then hourly.
|
|
98
|
+
const runCleanup = async () => {
|
|
67
99
|
try {
|
|
68
100
|
if (this.db) {
|
|
69
101
|
await runRetentionCleanup(this.db, this.config.retentionDays);
|
|
70
102
|
this.lastCleanupAt = Date.now();
|
|
103
|
+
log.info('dashboard: retention cleanup complete');
|
|
71
104
|
}
|
|
72
105
|
}
|
|
73
106
|
catch (err) {
|
|
74
107
|
log.warn('dashboard: retention cleanup failed — ' + err?.message);
|
|
75
108
|
}
|
|
76
|
-
}
|
|
109
|
+
};
|
|
110
|
+
setTimeout(() => runCleanup(), 30_000);
|
|
111
|
+
this.retentionTimer = setInterval(() => runCleanup(), 60 * 60 * 1000);
|
|
77
112
|
// Start chart aggregation (every 60s)
|
|
78
|
-
this.chartAggregator = new ChartAggregator(
|
|
113
|
+
this.chartAggregator = new ChartAggregator(db);
|
|
79
114
|
this.chartAggregator.start();
|
|
80
115
|
// Wire email event listeners
|
|
81
116
|
this.wireEventListeners();
|
|
117
|
+
log.info('dashboard: store initialized');
|
|
82
118
|
}
|
|
83
119
|
/** Shut down timers, event listeners, and database connection. */
|
|
84
120
|
async stop() {
|
|
@@ -5,6 +5,9 @@ import type { Knex } from 'knex';
|
|
|
5
5
|
* Uses raw SQL (not Lucid migrations) so we never pollute the host
|
|
6
6
|
* application's migration history. Each `CREATE TABLE` / `CREATE INDEX`
|
|
7
7
|
* uses `IF NOT EXISTS` so the function is idempotent.
|
|
8
|
+
*
|
|
9
|
+
* Yields to the event loop between each table so the server can
|
|
10
|
+
* continue processing HTTP requests during migration.
|
|
8
11
|
*/
|
|
9
12
|
export declare function autoMigrate(db: Knex): Promise<void>;
|
|
10
13
|
/**
|
|
@@ -13,6 +16,8 @@ export declare function autoMigrate(db: Knex): Promise<void>;
|
|
|
13
16
|
* Foreign-key cascades on `server_stats_requests` handle the child
|
|
14
17
|
* tables (queries, events, traces). Standalone tables (logs, emails,
|
|
15
18
|
* metrics, saved_filters) are pruned individually.
|
|
19
|
+
*
|
|
20
|
+
* Yields between each DELETE so the event loop stays responsive.
|
|
16
21
|
*/
|
|
17
22
|
export declare function runRetentionCleanup(db: Knex, retentionDays: number): Promise<void>;
|
|
18
23
|
//# sourceMappingURL=migrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../../src/dashboard/migrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../../src/dashboard/migrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAahC;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAoJzD;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BxF"}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yield control back to the event loop so Node.js can process pending
|
|
3
|
+
* I/O (incoming HTTP requests, timers, etc.).
|
|
4
|
+
*
|
|
5
|
+
* `better-sqlite3` is fully synchronous — when Knex wraps it, each
|
|
6
|
+
* `await db.raw(...)` resolves via the microtask queue, never actually
|
|
7
|
+
* yielding to the I/O phase. Without explicit yields, 25+ sequential
|
|
8
|
+
* migration statements block the event loop for their entire duration.
|
|
9
|
+
*/
|
|
10
|
+
const yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
|
|
1
11
|
/**
|
|
2
12
|
* Auto-migrate all dashboard SQLite tables.
|
|
3
13
|
*
|
|
4
14
|
* Uses raw SQL (not Lucid migrations) so we never pollute the host
|
|
5
15
|
* application's migration history. Each `CREATE TABLE` / `CREATE INDEX`
|
|
6
16
|
* uses `IF NOT EXISTS` so the function is idempotent.
|
|
17
|
+
*
|
|
18
|
+
* Yields to the event loop between each table so the server can
|
|
19
|
+
* continue processing HTTP requests during migration.
|
|
7
20
|
*/
|
|
8
21
|
export async function autoMigrate(db) {
|
|
9
22
|
// -- server_stats_requests --------------------------------------------------
|
|
@@ -21,6 +34,7 @@ export async function autoMigrate(db) {
|
|
|
21
34
|
`);
|
|
22
35
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_requests_created ON server_stats_requests(created_at)`);
|
|
23
36
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_requests_url ON server_stats_requests(url)`);
|
|
37
|
+
await yieldToEventLoop();
|
|
24
38
|
// -- server_stats_queries ---------------------------------------------------
|
|
25
39
|
await db.raw(`
|
|
26
40
|
CREATE TABLE IF NOT EXISTS server_stats_queries (
|
|
@@ -40,6 +54,7 @@ export async function autoMigrate(db) {
|
|
|
40
54
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_queries_created ON server_stats_queries(created_at)`);
|
|
41
55
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_queries_normalized ON server_stats_queries(sql_normalized)`);
|
|
42
56
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_queries_request ON server_stats_queries(request_id)`);
|
|
57
|
+
await yieldToEventLoop();
|
|
43
58
|
// -- server_stats_events ----------------------------------------------------
|
|
44
59
|
await db.raw(`
|
|
45
60
|
CREATE TABLE IF NOT EXISTS server_stats_events (
|
|
@@ -51,6 +66,7 @@ export async function autoMigrate(db) {
|
|
|
51
66
|
)
|
|
52
67
|
`);
|
|
53
68
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_events_created ON server_stats_events(created_at)`);
|
|
69
|
+
await yieldToEventLoop();
|
|
54
70
|
// -- server_stats_emails ----------------------------------------------------
|
|
55
71
|
await db.raw(`
|
|
56
72
|
CREATE TABLE IF NOT EXISTS server_stats_emails (
|
|
@@ -70,6 +86,7 @@ export async function autoMigrate(db) {
|
|
|
70
86
|
)
|
|
71
87
|
`);
|
|
72
88
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_emails_created ON server_stats_emails(created_at)`);
|
|
89
|
+
await yieldToEventLoop();
|
|
73
90
|
// -- server_stats_logs ------------------------------------------------------
|
|
74
91
|
await db.raw(`
|
|
75
92
|
CREATE TABLE IF NOT EXISTS server_stats_logs (
|
|
@@ -84,6 +101,7 @@ export async function autoMigrate(db) {
|
|
|
84
101
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_logs_created ON server_stats_logs(created_at)`);
|
|
85
102
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_logs_level ON server_stats_logs(level)`);
|
|
86
103
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_logs_request ON server_stats_logs(request_id)`);
|
|
104
|
+
await yieldToEventLoop();
|
|
87
105
|
// -- server_stats_traces ----------------------------------------------------
|
|
88
106
|
await db.raw(`
|
|
89
107
|
CREATE TABLE IF NOT EXISTS server_stats_traces (
|
|
@@ -100,6 +118,7 @@ export async function autoMigrate(db) {
|
|
|
100
118
|
)
|
|
101
119
|
`);
|
|
102
120
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_traces_created ON server_stats_traces(created_at)`);
|
|
121
|
+
await yieldToEventLoop();
|
|
103
122
|
// -- server_stats_metrics ---------------------------------------------------
|
|
104
123
|
await db.raw(`
|
|
105
124
|
CREATE TABLE IF NOT EXISTS server_stats_metrics (
|
|
@@ -115,6 +134,7 @@ export async function autoMigrate(db) {
|
|
|
115
134
|
)
|
|
116
135
|
`);
|
|
117
136
|
await db.raw(`CREATE INDEX IF NOT EXISTS idx_ss_metrics_bucket ON server_stats_metrics(bucket)`);
|
|
137
|
+
await yieldToEventLoop();
|
|
118
138
|
// -- server_stats_saved_filters ---------------------------------------------
|
|
119
139
|
await db.raw(`
|
|
120
140
|
CREATE TABLE IF NOT EXISTS server_stats_saved_filters (
|
|
@@ -132,15 +152,30 @@ export async function autoMigrate(db) {
|
|
|
132
152
|
* Foreign-key cascades on `server_stats_requests` handle the child
|
|
133
153
|
* tables (queries, events, traces). Standalone tables (logs, emails,
|
|
134
154
|
* metrics, saved_filters) are pruned individually.
|
|
155
|
+
*
|
|
156
|
+
* Yields between each DELETE so the event loop stays responsive.
|
|
135
157
|
*/
|
|
136
158
|
export async function runRetentionCleanup(db, retentionDays) {
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
159
|
+
// Use string interpolation instead of parameterized bindings.
|
|
160
|
+
// Knex + better-sqlite3 can hang on parameterized db.raw() calls,
|
|
161
|
+
// while non-parameterized queries (used in migrations) work fine.
|
|
162
|
+
// Safe here — retentionDays is always a controlled integer.
|
|
163
|
+
const days = Math.max(1, Math.floor(retentionDays));
|
|
164
|
+
const cutoff = `datetime('now', '-${days} days')`;
|
|
165
|
+
try {
|
|
166
|
+
// Cascade deletes queries, events, traces via FK ON DELETE CASCADE
|
|
167
|
+
await db.raw(`DELETE FROM server_stats_requests WHERE created_at < ${cutoff}`);
|
|
168
|
+
await yieldToEventLoop();
|
|
169
|
+
// Standalone tables
|
|
170
|
+
await db.raw(`DELETE FROM server_stats_logs WHERE created_at < ${cutoff}`);
|
|
171
|
+
await yieldToEventLoop();
|
|
172
|
+
await db.raw(`DELETE FROM server_stats_emails WHERE created_at < ${cutoff}`);
|
|
173
|
+
await yieldToEventLoop();
|
|
174
|
+
await db.raw(`DELETE FROM server_stats_metrics WHERE created_at < ${cutoff}`);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
// Log but don't throw — retention cleanup failure shouldn't block init
|
|
178
|
+
const { log } = await import('../utils/logger.js');
|
|
179
|
+
log.warn(`dashboard: retention cleanup error — ${err?.message}`);
|
|
180
|
+
}
|
|
146
181
|
}
|
|
@@ -37,11 +37,13 @@ export interface PaginatedResult<T = Record<string, unknown>> {
|
|
|
37
37
|
*/
|
|
38
38
|
export declare class DataAccess {
|
|
39
39
|
private debugStore;
|
|
40
|
-
private
|
|
40
|
+
private getDashboardStore;
|
|
41
41
|
private logPath?;
|
|
42
|
-
constructor(debugStore: DebugStore, dashboardStore: DashboardStore | null, logPath?: string
|
|
42
|
+
constructor(debugStore: DebugStore, dashboardStore: DashboardStore | null | (() => DashboardStore | null), logPath?: string);
|
|
43
43
|
/** Whether SQLite persistence is available and initialised. */
|
|
44
44
|
get hasPersistence(): boolean;
|
|
45
|
+
/** Resolve the dashboard store (may be null if not yet initialized). */
|
|
46
|
+
private get dashboardStore();
|
|
45
47
|
getQueries(opts?: ListOptions): Promise<PaginatedResult<QueryRecord>>;
|
|
46
48
|
getQuerySummary(): {
|
|
47
49
|
total: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data_access.d.ts","sourceRoot":"","sources":["../../../src/data/data_access.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,iCAAiC,CAAA;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAEX,WAAW,EACX,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAM1B,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAEjC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAkED;;;;;;;GAOG;AACH,qBAAa,UAAU;
|
|
1
|
+
{"version":3,"file":"data_access.d.ts","sourceRoot":"","sources":["../../../src/data/data_access.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,iCAAiC,CAAA;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EAEX,WAAW,EACX,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAM1B,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAEjC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAkED;;;;;;;GAOG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,OAAO,CAAC,CAAQ;gBAGtB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,EACrE,OAAO,CAAC,EAAE,MAAM;IAQlB,+DAA+D;IAC/D,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,wEAAwE;IACxE,OAAO,KAAK,cAAc,GAEzB;IAMK,UAAU,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IAsB/E,eAAe,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;IAQrF,SAAS,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IAsB9E;;;;;OAKG;IACG,SAAS,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAwBjE;;;;;OAKG;IACG,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAYrF;;;;;OAKG;IACG,SAAS,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IA4BjE;;;;OAIG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAazF;;;;;OAKG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,WAAW,CAAC;IA8BxD;;;;;;OAMG;IACG,OAAO,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAyB/D;;;;;OAKG;YACW,WAAW;CAoC1B"}
|
|
@@ -56,16 +56,21 @@ function fromDashboardResult(result) {
|
|
|
56
56
|
*/
|
|
57
57
|
export class DataAccess {
|
|
58
58
|
debugStore;
|
|
59
|
-
|
|
59
|
+
getDashboardStore;
|
|
60
60
|
logPath;
|
|
61
61
|
constructor(debugStore, dashboardStore, logPath) {
|
|
62
62
|
this.debugStore = debugStore;
|
|
63
|
-
this.
|
|
63
|
+
this.getDashboardStore =
|
|
64
|
+
typeof dashboardStore === 'function' ? dashboardStore : () => dashboardStore;
|
|
64
65
|
this.logPath = logPath;
|
|
65
66
|
}
|
|
66
67
|
/** Whether SQLite persistence is available and initialised. */
|
|
67
68
|
get hasPersistence() {
|
|
68
|
-
return this.
|
|
69
|
+
return this.getDashboardStore()?.isReady() ?? false;
|
|
70
|
+
}
|
|
71
|
+
/** Resolve the dashboard store (may be null if not yet initialized). */
|
|
72
|
+
get dashboardStore() {
|
|
73
|
+
return this.getDashboardStore();
|
|
69
74
|
}
|
|
70
75
|
// =========================================================================
|
|
71
76
|
// Queries
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAqB,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAqB,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AA4RjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,yBAAyB,CA0CjF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bold, dim
|
|
1
|
+
import { bold, dim } from './utils/logger.js';
|
|
2
2
|
/**
|
|
3
3
|
* Resolve `toolbar`, `dashboard`, and `advanced` aliases into a
|
|
4
4
|
* {@link DevToolbarOptions} object, merging with any existing
|
|
@@ -260,20 +260,21 @@ function logDeprecationWarnings(config) {
|
|
|
260
260
|
const TAG = '\x1b[36m[ \x1b[1m\uD83D\uDD0D server-stats\x1b[0m\x1b[36m ]\x1b[0m';
|
|
261
261
|
const lines = [];
|
|
262
262
|
lines.push('');
|
|
263
|
-
lines.push(`${TAG}
|
|
263
|
+
lines.push(`${TAG} Some config options have been renamed — here's how to update:`);
|
|
264
264
|
lines.push('');
|
|
265
265
|
for (const entry of entries) {
|
|
266
|
-
lines.push(` ${
|
|
266
|
+
lines.push(` ${dim(entry.old)} ${dim('\u2192')} ${bold(entry.new)}`);
|
|
267
|
+
lines.push('');
|
|
267
268
|
for (const line of entry.before) {
|
|
268
269
|
lines.push(` ${dim('before:')} ${dim(line)}`);
|
|
269
270
|
}
|
|
270
271
|
for (const line of entry.after) {
|
|
271
|
-
lines.push(` ${
|
|
272
|
+
lines.push(` ${dim('after:')} ${line}`);
|
|
272
273
|
}
|
|
273
274
|
lines.push('');
|
|
274
275
|
}
|
|
275
|
-
lines.push(` ${dim('
|
|
276
|
-
lines.push(` ${dim('Update
|
|
276
|
+
lines.push(` ${dim('No rush — the old names still work. They will be removed in the next major version.')}`);
|
|
277
|
+
lines.push(` ${dim('Update')} ${bold('config/server_stats.ts')} ${dim('when you get a chance.')}`);
|
|
277
278
|
lines.push('');
|
|
278
279
|
console.log(lines.join('\n'));
|
|
279
280
|
}
|