adonisjs-server-stats 1.10.0 → 1.10.3
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/README.md +23 -14
- package/dist/core/config-utils.d.ts +8 -0
- package/dist/core/constants.d.ts +4 -0
- package/dist/core/dashboard-data-controller.d.ts +16 -0
- package/dist/core/dashboard-data-helpers.d.ts +12 -0
- package/dist/core/debug-data-controller.d.ts +4 -0
- package/dist/core/define-config-helpers.d.ts +25 -0
- package/dist/core/feature-detect-helpers.d.ts +36 -0
- package/dist/core/formatters-helpers.d.ts +23 -0
- package/dist/core/index.js +594 -509
- package/dist/core/log-utils-helpers.d.ts +13 -0
- package/dist/core/metrics.d.ts +3 -28
- package/dist/core/pagination.d.ts +0 -9
- package/dist/core/server-stats-controller.d.ts +6 -0
- package/dist/core/transmit-helpers.d.ts +7 -0
- package/dist/core/types-dashboard.d.ts +178 -0
- package/dist/core/types-diagnostics.d.ts +85 -0
- package/dist/core/types.d.ts +10 -442
- package/dist/react/{CacheSection-UCMptWyn.js → CacheSection-baMZotSn.js} +2 -2
- package/dist/react/CacheTab-2cw_rMzj.js +117 -0
- package/dist/react/{ConfigSection-DfFd-WRq.js → ConfigSection-DGgqjAal.js} +1 -1
- package/dist/react/{ConfigTab-Bdg8YMer.js → ConfigTab-H3OnYqmK.js} +1 -1
- package/dist/react/CustomPaneTab-B6r7ha0u.js +98 -0
- package/dist/react/{EmailsSection-CM7stSyh.js → EmailsSection-C-UZISG-.js} +2 -2
- package/dist/react/EmailsTab-DbK4Eobn.js +139 -0
- package/dist/react/{EventsSection-ByQ-9blq.js → EventsSection-C7RQW_LY.js} +2 -2
- package/dist/react/EventsTab-CfVr7AiM.js +57 -0
- package/dist/react/{FilterBar-DQRXpWrb.js → FilterBar-CQ7bD669.js} +15 -15
- package/dist/react/{JobsSection-DF3qEv9O.js → JobsSection-CQHNK_Ls.js} +2 -2
- package/dist/react/{JobsTab-BbrBWIOb.js → JobsTab-znzf6jzk.js} +54 -42
- package/dist/react/{LogsSection-DcFTZY7b.js → LogsSection-Dmm3rE2B.js} +9 -3
- package/dist/react/LogsTab-D8unMV5P.js +108 -0
- package/dist/react/{OverviewSection-C4T1ur51.js → OverviewSection-ABP9ueBo.js} +1 -1
- package/dist/react/{QueriesSection-PswteoF9.js → QueriesSection-CnmSkznA.js} +2 -2
- package/dist/react/{QueriesTab-osLUWd4L.js → QueriesTab-BQzcxEiW.js} +37 -40
- package/dist/react/{RelatedLogs-DFDOyUMr.js → RelatedLogs-3A8RuGKH.js} +15 -3
- package/dist/react/{RequestsSection-Nag30rEA.js → RequestsSection-kW79_M7k.js} +3 -3
- package/dist/react/{RoutesSection-BUSkM6PY.js → RoutesSection-BRhxrtjZ.js} +2 -2
- package/dist/react/RoutesTab-CpYH5lUw.js +68 -0
- package/dist/react/{TimelineTab-Covg5weo.js → TimelineTab-DjLR35Ce.js} +47 -53
- package/dist/react/index-CsImORX6.js +1121 -0
- package/dist/react/index.js +1 -1
- package/dist/react/react/components/{Dashboard/shared → shared}/FilterBar.d.ts +4 -3
- package/dist/react/react/hooks/useDashboardData.d.ts +4 -8
- package/dist/react/style.css +1 -1
- package/dist/src/collectors/app_collector.d.ts +0 -8
- package/dist/src/collectors/app_collector.js +45 -52
- package/dist/src/collectors/auto_detect.d.ts +0 -23
- package/dist/src/collectors/auto_detect.js +33 -55
- package/dist/src/collectors/db_pool_collector.d.ts +14 -16
- package/dist/src/collectors/db_pool_collector.js +72 -57
- package/dist/src/collectors/log_collector.d.ts +0 -47
- package/dist/src/collectors/log_collector.js +36 -65
- package/dist/src/collectors/queue_collector.d.ts +0 -20
- package/dist/src/collectors/queue_collector.js +60 -76
- package/dist/src/collectors/redis_collector.d.ts +10 -10
- package/dist/src/collectors/redis_collector.js +69 -66
- package/dist/src/config/deprecation_migration.d.ts +7 -0
- package/dist/src/config/deprecation_migration.js +201 -0
- package/dist/src/controller/debug_controller.d.ts +1 -1
- package/dist/src/controller/debug_controller.js +87 -81
- package/dist/src/dashboard/cache_handlers.d.ts +14 -0
- package/dist/src/dashboard/cache_handlers.js +52 -0
- package/dist/src/dashboard/chart_aggregator.d.ts +0 -7
- package/dist/src/dashboard/chart_aggregator.js +68 -50
- package/dist/src/dashboard/coalesce_cache.d.ts +25 -0
- package/dist/src/dashboard/coalesce_cache.js +47 -0
- package/dist/src/dashboard/dashboard_controller.d.ts +11 -37
- package/dist/src/dashboard/dashboard_controller.js +51 -544
- package/dist/src/dashboard/dashboard_page_assets.d.ts +17 -0
- package/dist/src/dashboard/dashboard_page_assets.js +51 -0
- package/dist/src/dashboard/dashboard_store.d.ts +19 -218
- package/dist/src/dashboard/dashboard_store.js +115 -1116
- package/dist/src/dashboard/dashboard_types.d.ts +83 -0
- package/dist/src/dashboard/dashboard_types.js +4 -0
- package/dist/src/dashboard/detail_queries.d.ts +19 -0
- package/dist/src/dashboard/detail_queries.js +98 -0
- package/dist/src/dashboard/email_event_builder.d.ts +8 -0
- package/dist/src/dashboard/email_event_builder.js +65 -0
- package/dist/src/dashboard/explain_query.d.ts +8 -0
- package/dist/src/dashboard/explain_query.js +22 -0
- package/dist/src/dashboard/filter_handlers.d.ts +23 -0
- package/dist/src/dashboard/filter_handlers.js +56 -0
- package/dist/src/dashboard/filtered_queries.d.ts +15 -0
- package/dist/src/dashboard/filtered_queries.js +155 -0
- package/dist/src/dashboard/flush_manager.d.ts +25 -0
- package/dist/src/dashboard/flush_manager.js +107 -0
- package/dist/src/dashboard/format_helpers.d.ts +126 -0
- package/dist/src/dashboard/format_helpers.js +140 -0
- package/dist/src/dashboard/inspector_manager.d.ts +36 -0
- package/dist/src/dashboard/inspector_manager.js +102 -0
- package/dist/src/dashboard/integrations/config_inspector.js +11 -13
- package/dist/src/dashboard/integrations/queue_inspector.d.ts +3 -3
- package/dist/src/dashboard/integrations/queue_inspector.js +13 -10
- package/dist/src/dashboard/jobs_handlers.d.ts +14 -0
- package/dist/src/dashboard/jobs_handlers.js +61 -0
- package/dist/src/dashboard/knex_factory.d.ts +18 -0
- package/dist/src/dashboard/knex_factory.js +91 -0
- package/dist/src/dashboard/migrator.js +30 -159
- package/dist/src/dashboard/migrator_tables.d.ts +19 -0
- package/dist/src/dashboard/migrator_tables.js +153 -0
- package/dist/src/dashboard/overview_queries.d.ts +66 -0
- package/dist/src/dashboard/overview_queries.js +155 -0
- package/dist/src/dashboard/overview_query_runners.d.ts +25 -0
- package/dist/src/dashboard/overview_query_runners.js +84 -0
- package/dist/src/dashboard/overview_store_queries.d.ts +40 -0
- package/dist/src/dashboard/overview_store_queries.js +69 -0
- package/dist/src/dashboard/paginate_helper.d.ts +12 -0
- package/dist/src/dashboard/paginate_helper.js +33 -0
- package/dist/src/dashboard/query_explain_handler.d.ts +10 -0
- package/dist/src/dashboard/query_explain_handler.js +80 -0
- package/dist/src/dashboard/read_queries.d.ts +32 -0
- package/dist/src/dashboard/read_queries.js +107 -0
- package/dist/src/dashboard/saved_filter_queries.d.ts +10 -0
- package/dist/src/dashboard/saved_filter_queries.js +24 -0
- package/dist/src/dashboard/storage_stats.d.ts +41 -0
- package/dist/src/dashboard/storage_stats.js +81 -0
- package/dist/src/dashboard/write_queue.d.ts +106 -0
- package/dist/src/dashboard/write_queue.js +225 -0
- package/dist/src/data/data_access.d.ts +2 -39
- package/dist/src/data/data_access.js +17 -193
- package/dist/src/data/data_access_helpers.d.ts +130 -0
- package/dist/src/data/data_access_helpers.js +212 -0
- package/dist/src/debug/debug_store.js +37 -32
- package/dist/src/debug/email_collector.d.ts +1 -10
- package/dist/src/debug/email_collector.js +78 -81
- package/dist/src/debug/event_collector.d.ts +0 -9
- package/dist/src/debug/event_collector.js +79 -62
- package/dist/src/debug/query_collector.js +23 -19
- package/dist/src/debug/route_inspector.d.ts +1 -5
- package/dist/src/debug/route_inspector.js +50 -51
- package/dist/src/debug/trace_collector.d.ts +9 -1
- package/dist/src/debug/trace_collector.js +21 -15
- package/dist/src/debug/types.d.ts +1 -1
- package/dist/src/define_config.d.ts +0 -65
- package/dist/src/define_config.js +93 -333
- package/dist/src/edge/client/dashboard.js +2 -2
- package/dist/src/edge/client/debug-panel-deferred.js +1 -1
- package/dist/src/edge/client/stats-bar.js +1 -1
- package/dist/src/edge/client-vue/dashboard.js +5 -5
- package/dist/src/edge/client-vue/debug-panel-deferred.js +3 -3
- package/dist/src/edge/client-vue/stats-bar.js +3 -3
- package/dist/src/edge/plugin.d.ts +0 -16
- package/dist/src/edge/plugin.js +57 -64
- package/dist/src/engine/request_metrics.d.ts +1 -0
- package/dist/src/engine/request_metrics.js +32 -42
- package/dist/src/middleware/request_tracking_middleware.d.ts +2 -8
- package/dist/src/middleware/request_tracking_middleware.js +65 -93
- package/dist/src/provider/auth_middleware_detector.d.ts +16 -0
- package/dist/src/provider/auth_middleware_detector.js +97 -0
- package/dist/src/provider/boot_helpers.d.ts +20 -0
- package/dist/src/provider/boot_helpers.js +91 -0
- package/dist/src/provider/boot_initializer.d.ts +28 -0
- package/dist/src/provider/boot_initializer.js +35 -0
- package/dist/src/provider/dashboard_init.d.ts +30 -0
- package/dist/src/provider/dashboard_init.js +138 -0
- package/dist/src/provider/dashboard_setup.d.ts +25 -0
- package/dist/src/provider/dashboard_setup.js +78 -0
- package/dist/src/provider/diagnostics.d.ts +134 -0
- package/dist/src/provider/diagnostics.js +127 -0
- package/dist/src/provider/email_bridge.d.ts +43 -0
- package/dist/src/provider/email_bridge.js +80 -0
- package/dist/src/provider/email_helpers.d.ts +13 -0
- package/dist/src/provider/email_helpers.js +68 -0
- package/dist/src/provider/pino_hook.d.ts +17 -0
- package/dist/src/provider/pino_hook.js +35 -0
- package/dist/src/provider/provider_helpers_extra.d.ts +47 -0
- package/dist/src/provider/provider_helpers_extra.js +177 -0
- package/dist/src/provider/server_stats_provider.d.ts +39 -85
- package/dist/src/provider/server_stats_provider.js +132 -951
- package/dist/src/provider/shutdown_helpers.d.ts +43 -0
- package/dist/src/provider/shutdown_helpers.js +70 -0
- package/dist/src/provider/toolbar_setup.d.ts +57 -0
- package/dist/src/provider/toolbar_setup.js +141 -0
- package/dist/src/routes/dashboard_routes.d.ts +14 -0
- package/dist/src/routes/dashboard_routes.js +197 -0
- package/dist/src/routes/debug_routes.d.ts +14 -0
- package/dist/src/routes/debug_routes.js +101 -0
- package/dist/src/routes/register_routes.d.ts +0 -78
- package/dist/src/routes/register_routes.js +22 -352
- package/dist/src/routes/stats_routes.d.ts +5 -0
- package/dist/src/routes/stats_routes.js +14 -0
- package/dist/src/styles/components.css +96 -0
- package/dist/src/styles/dashboard.css +8 -90
- package/dist/src/styles/debug-panel.css +1 -31
- package/dist/src/types.d.ts +305 -14
- package/dist/vue/{CacheSection-oFAJL3mo.js → CacheSection-ITqvpfH5.js} +1 -1
- package/dist/vue/{ConfigSection-BhfJ4KqL.js → ConfigSection-DTn3GslE.js} +1 -1
- package/dist/vue/{EmailsSection-BcNyhyHs.js → EmailsSection-DtLJ4XoS.js} +1 -1
- package/dist/vue/{EventsSection-r60Q5Lmu.js → EventsSection-BOYYz0Ty.js} +1 -1
- package/dist/vue/{JobsSection-BHL-hkQw.js → JobsSection-BazTxcJL.js} +1 -1
- package/dist/vue/{LogsSection-DRMGzJmg.js → LogsSection-D55PjTKX.js} +9 -3
- package/dist/vue/{LogsTab-Bg3o0Mm6.js → LogsTab-47zEK7jL.js} +4 -1
- package/dist/vue/{OverviewSection-CXh6Ja1B.js → OverviewSection-1uBKo-Tu.js} +1 -1
- package/dist/vue/{QueriesSection-IodIsCJ-.js → QueriesSection-rpoZ4ogd.js} +1 -1
- package/dist/vue/{RequestsSection-BPuMdmMc.js → RequestsSection-x7LvT0MC.js} +1 -1
- package/dist/vue/{RoutesSection-NKo3Rbq3.js → RoutesSection-CCD0zZqQ.js} +1 -1
- package/dist/vue/composables/useDashboardData.d.ts +12 -23
- package/dist/vue/index-C8MxnS7Q.js +1232 -0
- package/dist/vue/index.js +1 -1
- package/dist/vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/react/CacheTab-CA8LB1J5.js +0 -123
- package/dist/react/CustomPaneTab-Bxtv_8Rw.js +0 -104
- package/dist/react/EmailsTab-BDhEiomM.js +0 -153
- package/dist/react/EventsTab-CMfY98Rl.js +0 -63
- package/dist/react/LogsTab-CicucmVk.js +0 -103
- package/dist/react/RoutesTab-DgVzd2PZ.js +0 -74
- package/dist/react/index-Cflz9Ebj.js +0 -1069
- package/dist/vue/index-Dtgysd26.js +0 -1229
package/dist/src/edge/plugin.js
CHANGED
|
@@ -6,75 +6,70 @@ import { log } from '../utils/logger.js';
|
|
|
6
6
|
import { loadTransmitClient } from '../utils/transmit_client.js';
|
|
7
7
|
const DIR = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const read = (rel) => readFileSync(join(DIR, rel), 'utf-8');
|
|
9
|
+
/** Build concatenated CSS from component, utility, and stats-bar stylesheets. */
|
|
10
|
+
function buildCss() {
|
|
11
|
+
const componentsCss = read('../styles/components.css');
|
|
12
|
+
const utilitiesCss = read('../styles/utilities.css');
|
|
13
|
+
return componentsCss + '\n' + utilitiesCss + '\n' + read('../styles/stats-bar.css');
|
|
14
|
+
}
|
|
15
|
+
/** Resolve the client asset directory based on renderer config. */
|
|
16
|
+
function resolveClientDir(config) {
|
|
17
|
+
const renderer = config.devToolbar?.renderer || 'preact';
|
|
18
|
+
return renderer === 'vue' ? 'client-vue' : 'client';
|
|
19
|
+
}
|
|
20
|
+
/** Build the bar configuration object for the stats bar client. */
|
|
21
|
+
function buildBarConfig(config) {
|
|
22
|
+
const endpoint = typeof config.endpoint === 'string' ? config.endpoint : '/admin/api/server-stats';
|
|
23
|
+
const showDebug = !!config.devToolbar?.enabled;
|
|
24
|
+
const result = {
|
|
25
|
+
endpoint,
|
|
26
|
+
pollInterval: config.intervalMs || 3000,
|
|
27
|
+
channelName: config.channelName || 'admin/server-stats',
|
|
28
|
+
showDebug,
|
|
29
|
+
};
|
|
30
|
+
if (showDebug) {
|
|
31
|
+
addDebugBarConfig(config, result);
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/** Add debug-specific fields to bar config. */
|
|
36
|
+
function addDebugBarConfig(config, result) {
|
|
37
|
+
result.debugEndpoint = config.devToolbar?.debugEndpoint || '/admin/api/debug';
|
|
38
|
+
result.dashboardPath = config.devToolbar?.dashboard
|
|
39
|
+
? config.devToolbar.dashboardPath || '/__stats'
|
|
40
|
+
: null;
|
|
41
|
+
}
|
|
42
|
+
/** Build template state for the Edge stats-bar partial. */
|
|
43
|
+
function buildTemplateState(config, clientDir) {
|
|
44
|
+
const showDebug = !!config.devToolbar?.enabled;
|
|
45
|
+
const transmitClient = loadTransmitClient(join(process.cwd(), 'package.json'));
|
|
46
|
+
if (!transmitClient) {
|
|
47
|
+
log.info('@adonisjs/transmit-client not found — will use polling');
|
|
48
|
+
}
|
|
49
|
+
const state = {
|
|
50
|
+
css: buildCss(),
|
|
51
|
+
js: read(clientDir + '/stats-bar.js'),
|
|
52
|
+
barConfig: buildBarConfig(config),
|
|
53
|
+
showDebug,
|
|
54
|
+
transmitClient,
|
|
55
|
+
};
|
|
56
|
+
if (showDebug) {
|
|
57
|
+
state.debugCss = read('../styles/debug-panel.css');
|
|
58
|
+
state.debugDeferredJs = read(clientDir + '/debug-panel-deferred.js');
|
|
59
|
+
}
|
|
60
|
+
return state;
|
|
61
|
+
}
|
|
9
62
|
/**
|
|
10
63
|
* Edge plugin that registers the `@serverStats()` tag.
|
|
11
|
-
*
|
|
12
|
-
* - Mounts `views/` as the `ss` Edge disk for partials
|
|
13
|
-
* - Reads CSS/JS client assets from `client/`
|
|
14
|
-
* - Pre-renders the stats-bar template once at boot (via `Template` directly
|
|
15
|
-
* to avoid the `#executePlugins` recursion from `edge.renderSync`)
|
|
16
|
-
* - Registers `@serverStats()` tag that outputs the pre-rendered HTML
|
|
17
|
-
*
|
|
18
|
-
* Usage in the provider's `boot()` method:
|
|
19
|
-
* ```ts
|
|
20
|
-
* edge.use(edgePluginServerStats(config))
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* Usage in Edge templates:
|
|
24
|
-
* ```edge
|
|
25
|
-
* @serverStats()
|
|
26
|
-
* ```
|
|
27
64
|
*/
|
|
28
65
|
export function edgePluginServerStats(config) {
|
|
29
66
|
return (edge) => {
|
|
30
|
-
// Mount Edge views under the `ss` disk (needed for @include resolution)
|
|
31
67
|
edge.mount('ss', join(DIR, 'views'));
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
const utilitiesCss = read('../styles/utilities.css');
|
|
35
|
-
const css = componentsCss + '\n' + utilitiesCss + '\n' + read('../styles/stats-bar.css');
|
|
36
|
-
const renderer = config.devToolbar?.renderer || 'preact';
|
|
37
|
-
const clientDir = renderer === 'vue' ? 'client-vue' : 'client';
|
|
38
|
-
const js = read(clientDir + '/stats-bar.js');
|
|
39
|
-
const endpoint = typeof config.endpoint === 'string' ? config.endpoint : '/admin/api/server-stats';
|
|
40
|
-
const intervalMs = config.intervalMs || 3000;
|
|
41
|
-
const showDebug = !!config.devToolbar?.enabled;
|
|
42
|
-
const channelName = config.channelName || 'admin/server-stats';
|
|
43
|
-
const barConfig = {
|
|
44
|
-
endpoint,
|
|
45
|
-
pollInterval: intervalMs,
|
|
46
|
-
channelName,
|
|
47
|
-
showDebug,
|
|
48
|
-
...(showDebug && {
|
|
49
|
-
debugEndpoint: config.devToolbar?.debugEndpoint || '/admin/api/debug',
|
|
50
|
-
dashboardPath: config.devToolbar?.dashboard
|
|
51
|
-
? config.devToolbar.dashboardPath || '/__stats'
|
|
52
|
-
: null,
|
|
53
|
-
}),
|
|
54
|
-
};
|
|
55
|
-
// Always try to load the Transmit client — both the stats bar and the
|
|
56
|
-
// debug panel use it for live (SSE) updates.
|
|
57
|
-
const transmitClient = loadTransmitClient(join(process.cwd(), 'package.json'));
|
|
58
|
-
if (!transmitClient) {
|
|
59
|
-
log.info('@adonisjs/transmit-client not found — will use polling');
|
|
60
|
-
}
|
|
61
|
-
const state = {
|
|
62
|
-
css,
|
|
63
|
-
js,
|
|
64
|
-
barConfig,
|
|
65
|
-
showDebug,
|
|
66
|
-
transmitClient,
|
|
67
|
-
};
|
|
68
|
-
if (showDebug) {
|
|
69
|
-
state.debugCss = read('../styles/debug-panel.css');
|
|
70
|
-
state.debugDeferredJs = read(clientDir + '/debug-panel-deferred.js');
|
|
71
|
-
}
|
|
72
|
-
// Pre-render via Template directly — bypasses edge.createRenderer() which
|
|
73
|
-
// would re-run #executePlugins and cause infinite recursion.
|
|
68
|
+
const clientDir = resolveClientDir(config);
|
|
69
|
+
const state = buildTemplateState(config, clientDir);
|
|
74
70
|
const template = new Template(edge.compiler, edge.globals, {}, edge.processor);
|
|
75
71
|
const html = template.render('ss::stats-bar', state);
|
|
76
72
|
const escaped = JSON.stringify(html);
|
|
77
|
-
// Track whether shouldShow is configured (controls render-time guard)
|
|
78
73
|
const hasShouldShow = !!config.shouldShow;
|
|
79
74
|
edge.registerTag({
|
|
80
75
|
tagName: 'serverStats',
|
|
@@ -82,13 +77,11 @@ export function edgePluginServerStats(config) {
|
|
|
82
77
|
seekable: true,
|
|
83
78
|
compile(_parser, buffer, token) {
|
|
84
79
|
if (hasShouldShow) {
|
|
85
|
-
|
|
86
|
-
buffer.writeStatement(`if (typeof state.__ssShowFn === 'function' ? state.__ssShowFn() : false) {`, token.filename, token.loc.start.line);
|
|
80
|
+
buffer.writeStatement("if (typeof state.__ssShowFn === 'function' ? state.__ssShowFn() : false) {", token.filename, token.loc.start.line);
|
|
87
81
|
buffer.outputExpression(escaped, token.filename, token.loc.start.line, false);
|
|
88
|
-
buffer.writeStatement(
|
|
82
|
+
buffer.writeStatement('}', token.filename, -1);
|
|
89
83
|
}
|
|
90
84
|
else {
|
|
91
|
-
// No shouldShow configured — always render
|
|
92
85
|
buffer.outputExpression(escaped, token.filename, token.loc.start.line, false);
|
|
93
86
|
}
|
|
94
87
|
},
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { performance } from 'node:perf_hooks';
|
|
2
|
+
function accumulateRecord(acc, r) {
|
|
3
|
+
acc.validCount++;
|
|
4
|
+
acc.totalDuration += r.durationMs;
|
|
5
|
+
if (r.statusCode >= 500)
|
|
6
|
+
acc.errorCount++;
|
|
7
|
+
}
|
|
2
8
|
export class RequestMetrics {
|
|
3
9
|
records = [];
|
|
4
10
|
writeIndex = 0;
|
|
@@ -34,55 +40,39 @@ export class RequestMetrics {
|
|
|
34
40
|
getMetrics() {
|
|
35
41
|
const now = performance.now();
|
|
36
42
|
const cutoff = now - this.windowMs;
|
|
37
|
-
|
|
38
|
-
let errorCount = 0;
|
|
39
|
-
let validCount = 0;
|
|
40
|
-
// When the buffer is full (ring buffer mode), scan from the oldest
|
|
41
|
-
// entry (writeIndex) forward. Records are in chronological order
|
|
42
|
-
// within each wrap, so once we find one >= cutoff, all subsequent
|
|
43
|
-
// records in that direction are also valid — but since they wrap,
|
|
44
|
-
// we still need to check all slots. The key optimization is that
|
|
45
|
-
// we can skip already-overwritten (stale) slots efficiently.
|
|
43
|
+
const acc = { validCount: 0, totalDuration: 0, errorCount: 0 };
|
|
46
44
|
if (this.count === this.maxRecords) {
|
|
47
|
-
|
|
48
|
-
for (let j = 0; j < this.maxRecords; j++) {
|
|
49
|
-
const idx = (this.writeIndex + j) % this.maxRecords;
|
|
50
|
-
const r = this.records[idx];
|
|
51
|
-
if (r.timestamp >= cutoff) {
|
|
52
|
-
validCount++;
|
|
53
|
-
totalDuration += r.durationMs;
|
|
54
|
-
if (r.statusCode >= 500) {
|
|
55
|
-
errorCount++;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
45
|
+
this.#scanFullBuffer(cutoff, acc);
|
|
59
46
|
}
|
|
60
47
|
else {
|
|
61
|
-
|
|
62
|
-
// Scan backwards from newest to find the cutoff point, then
|
|
63
|
-
// aggregate only the valid tail.
|
|
64
|
-
let startIdx = 0;
|
|
65
|
-
for (let i = this.count - 1; i >= 0; i--) {
|
|
66
|
-
if (this.records[i].timestamp < cutoff) {
|
|
67
|
-
startIdx = i + 1;
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
for (let i = startIdx; i < this.count; i++) {
|
|
72
|
-
const r = this.records[i];
|
|
73
|
-
validCount++;
|
|
74
|
-
totalDuration += r.durationMs;
|
|
75
|
-
if (r.statusCode >= 500) {
|
|
76
|
-
errorCount++;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
48
|
+
this.#scanPartialBuffer(cutoff, acc);
|
|
79
49
|
}
|
|
80
50
|
const windowSeconds = this.windowMs / 1000;
|
|
81
51
|
return {
|
|
82
|
-
requestsPerSecond: validCount / windowSeconds,
|
|
83
|
-
averageResponseTimeMs: validCount > 0 ? totalDuration / validCount : 0,
|
|
84
|
-
errorRate: validCount > 0 ? (errorCount / validCount) * 100 : 0,
|
|
52
|
+
requestsPerSecond: acc.validCount / windowSeconds,
|
|
53
|
+
averageResponseTimeMs: acc.validCount > 0 ? acc.totalDuration / acc.validCount : 0,
|
|
54
|
+
errorRate: acc.validCount > 0 ? (acc.errorCount / acc.validCount) * 100 : 0,
|
|
85
55
|
activeConnections: this.activeConnections,
|
|
86
56
|
};
|
|
87
57
|
}
|
|
58
|
+
#scanFullBuffer(cutoff, acc) {
|
|
59
|
+
for (let j = 0; j < this.maxRecords; j++) {
|
|
60
|
+
const idx = (this.writeIndex + j) % this.maxRecords;
|
|
61
|
+
const r = this.records[idx];
|
|
62
|
+
if (r.timestamp >= cutoff)
|
|
63
|
+
accumulateRecord(acc, r);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
#scanPartialBuffer(cutoff, acc) {
|
|
67
|
+
let startIdx = 0;
|
|
68
|
+
for (let i = this.count - 1; i >= 0; i--) {
|
|
69
|
+
if (this.records[i].timestamp < cutoff) {
|
|
70
|
+
startIdx = i + 1;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (let i = startIdx; i < this.count; i++) {
|
|
75
|
+
accumulateRecord(acc, this.records[i]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
88
78
|
}
|
|
@@ -2,19 +2,13 @@ import type { TraceCollector } from '../debug/trace_collector.js';
|
|
|
2
2
|
import type { TraceRecord } from '../debug/types.js';
|
|
3
3
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
4
4
|
import type { NextFn } from '@adonisjs/core/types/http';
|
|
5
|
-
/**
|
|
6
|
-
* Returns true if the current async context is inside an excluded request
|
|
7
|
-
* (e.g. a debug panel polling request). Used by collectors to skip
|
|
8
|
-
* self-generated data.
|
|
9
|
-
*/
|
|
5
|
+
/** Returns true if the current async context is inside an excluded request. */
|
|
10
6
|
export declare function isExcludedRequest(): boolean;
|
|
11
7
|
export declare function setShouldShow(fn: ((ctx: HttpContext) => boolean) | null): void;
|
|
12
8
|
export declare function setTraceCollector(collector: TraceCollector | null): void;
|
|
13
9
|
export declare function setDashboardPath(path: string | null): void;
|
|
14
10
|
export declare function setExcludedPrefixes(prefixes: string[]): void;
|
|
15
|
-
/**
|
|
16
|
-
* Data passed to the onRequestComplete callback.
|
|
17
|
-
*/
|
|
11
|
+
/** Data passed to the onRequestComplete callback. */
|
|
18
12
|
export interface RequestCompleteData {
|
|
19
13
|
method: string;
|
|
20
14
|
url: string;
|
|
@@ -2,74 +2,100 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
2
2
|
import { performance } from 'node:perf_hooks';
|
|
3
3
|
import { getRequestMetrics } from '../collectors/http_collector.js';
|
|
4
4
|
import { log } from '../utils/logger.js';
|
|
5
|
-
/**
|
|
6
|
-
* AsyncLocalStorage that marks the current request as "excluded" from
|
|
7
|
-
* debug collection. Checked by QueryCollector and EventCollector to
|
|
8
|
-
* skip queries/events triggered by the debug panel's own polling.
|
|
9
|
-
*/
|
|
10
5
|
const excludedRequestAls = new AsyncLocalStorage();
|
|
11
|
-
/**
|
|
12
|
-
* Returns true if the current async context is inside an excluded request
|
|
13
|
-
* (e.g. a debug panel polling request). Used by collectors to skip
|
|
14
|
-
* self-generated data.
|
|
15
|
-
*/
|
|
6
|
+
/** Returns true if the current async context is inside an excluded request. */
|
|
16
7
|
export function isExcludedRequest() {
|
|
17
8
|
return excludedRequestAls.getStore() === true;
|
|
18
9
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Warn-once guard for shouldShow callback failures.
|
|
21
|
-
*/
|
|
22
10
|
let warnedShouldShow = false;
|
|
23
|
-
/**
|
|
24
|
-
* Module-level `shouldShow` callback, set by the provider at boot.
|
|
25
|
-
*/
|
|
26
11
|
let shouldShowFn = null;
|
|
27
12
|
export function setShouldShow(fn) {
|
|
28
13
|
shouldShowFn = fn;
|
|
29
14
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Module-level trace collector, set by the provider when tracing is enabled.
|
|
32
|
-
*/
|
|
33
15
|
let traceCollector = null;
|
|
34
16
|
export function setTraceCollector(collector) {
|
|
35
17
|
traceCollector = collector;
|
|
36
18
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Module-level dashboard path, set by the provider when dashboard is enabled.
|
|
39
|
-
* Requests to this path (and sub-paths) are excluded from metrics and tracing.
|
|
40
|
-
*/
|
|
41
19
|
let dashboardPath = null;
|
|
42
20
|
export function setDashboardPath(path) {
|
|
43
21
|
dashboardPath = path;
|
|
44
22
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Module-level list of URL prefixes to exclude from tracing and onRequestComplete.
|
|
47
|
-
* Used to filter out the debug panel's own polling requests (e.g. /admin/api/debug/,
|
|
48
|
-
* /admin/api/server-stats) so they don't flood the timeline.
|
|
49
|
-
*/
|
|
50
23
|
let excludedPrefixes = [];
|
|
51
24
|
export function setExcludedPrefixes(prefixes) {
|
|
52
25
|
excludedPrefixes = prefixes;
|
|
53
26
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Module-level callback fired after each request completes.
|
|
56
|
-
* Used by the provider to pipe request data to the DashboardStore.
|
|
57
|
-
*/
|
|
58
27
|
let onRequestCompleteFn = null;
|
|
59
28
|
export function setOnRequestComplete(fn) {
|
|
60
29
|
onRequestCompleteFn = fn;
|
|
61
30
|
}
|
|
31
|
+
/** Share a lazy shouldShow evaluator with Edge for @serverStats() tag. */
|
|
32
|
+
function shareShouldShowWithEdge(ctx) {
|
|
33
|
+
const ctxView = ctx.view;
|
|
34
|
+
if (!shouldShowFn || typeof ctxView?.share !== 'function')
|
|
35
|
+
return;
|
|
36
|
+
ctxView.share({
|
|
37
|
+
__ssShowFn: () => {
|
|
38
|
+
try {
|
|
39
|
+
return shouldShowFn(ctx);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (!warnedShouldShow) {
|
|
43
|
+
warnedShouldShow = true;
|
|
44
|
+
log.warn('shouldShow callback threw — stats bar will be hidden: ' + err?.message);
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Record request metrics, finish trace, and fire the completion callback. */
|
|
52
|
+
function recordRequestCompletion(opts) {
|
|
53
|
+
const duration = performance.now() - opts.start;
|
|
54
|
+
opts.metrics.decrementActiveConnections();
|
|
55
|
+
opts.metrics.recordRequest(duration, opts.ctx.response.getStatus());
|
|
56
|
+
if (opts.skipTracing)
|
|
57
|
+
return;
|
|
58
|
+
const reqId = typeof opts.ctx.request.id === 'function' ? String(opts.ctx.request.id()) : undefined;
|
|
59
|
+
const traceRecord = traceCollector?.finishTrace(opts.ctx.request.method(), opts.ctx.request.url(true), opts.ctx.response.getStatus(), reqId);
|
|
60
|
+
onRequestCompleteFn?.({
|
|
61
|
+
method: opts.ctx.request.method(),
|
|
62
|
+
url: opts.ctx.request.url(true),
|
|
63
|
+
statusCode: opts.ctx.response.getStatus(),
|
|
64
|
+
duration,
|
|
65
|
+
trace: traceRecord ?? undefined,
|
|
66
|
+
httpRequestId: reqId,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Build the inner request handler that calls next() and records completion. */
|
|
70
|
+
function buildRequestRunner(opts) {
|
|
71
|
+
return async () => {
|
|
72
|
+
try {
|
|
73
|
+
await opts.next();
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
recordRequestCompletion(opts);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Dispatch the request runner through tracing, ALS, or direct execution. */
|
|
81
|
+
async function dispatchRequest(runRequest, skipTracing) {
|
|
82
|
+
if (traceCollector && !skipTracing) {
|
|
83
|
+
await traceCollector.startTrace(runRequest);
|
|
84
|
+
}
|
|
85
|
+
else if (skipTracing) {
|
|
86
|
+
await excludedRequestAls.run(true, runRequest);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
await runRequest();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
62
92
|
export default class RequestTrackingMiddleware {
|
|
63
93
|
async handle(ctx, next) {
|
|
64
|
-
// Self-exclude: skip metrics and tracing for dashboard routes
|
|
65
94
|
const requestUrl = ctx.request.url(true);
|
|
66
95
|
if (dashboardPath && requestUrl.startsWith(dashboardPath)) {
|
|
67
96
|
await next();
|
|
68
97
|
return;
|
|
69
98
|
}
|
|
70
|
-
// Gracefully handle the startup window before collectors initialize.
|
|
71
|
-
// The provider defers initialization via setImmediate, so early
|
|
72
|
-
// requests may arrive before httpCollector() has been called.
|
|
73
99
|
const metrics = getRequestMetrics();
|
|
74
100
|
if (!metrics) {
|
|
75
101
|
await next();
|
|
@@ -77,63 +103,9 @@ export default class RequestTrackingMiddleware {
|
|
|
77
103
|
}
|
|
78
104
|
const start = performance.now();
|
|
79
105
|
metrics.incrementActiveConnections();
|
|
80
|
-
|
|
81
|
-
// Must be lazy (a function, not a boolean) because this server middleware
|
|
82
|
-
// runs BEFORE router middleware like initialize_auth_middleware and
|
|
83
|
-
// silentAuth — so ctx.auth isn't populated yet. The function is called
|
|
84
|
-
// at Edge render time (inside the controller), when auth is available.
|
|
85
|
-
const ctxView = ctx.view;
|
|
86
|
-
if (shouldShowFn && typeof ctxView?.share === 'function') {
|
|
87
|
-
ctxView.share({
|
|
88
|
-
__ssShowFn: () => {
|
|
89
|
-
try {
|
|
90
|
-
return shouldShowFn(ctx);
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
if (!warnedShouldShow) {
|
|
94
|
-
warnedShouldShow = true;
|
|
95
|
-
log.warn('shouldShow callback threw — stats bar will be hidden: ' + err?.message);
|
|
96
|
-
}
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
// Skip tracing and dashboard persistence for the debug panel's own requests
|
|
103
|
-
// (e.g. /admin/api/debug/*, /admin/api/server-stats) so they don't flood
|
|
104
|
-
// the timeline. HTTP metrics (req/s, avg latency) are still recorded.
|
|
106
|
+
shareShouldShowWithEdge(ctx);
|
|
105
107
|
const skipTracing = excludedPrefixes.some((prefix) => requestUrl.startsWith(prefix));
|
|
106
|
-
const runRequest =
|
|
107
|
-
|
|
108
|
-
await next();
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
const duration = performance.now() - start;
|
|
112
|
-
metrics.decrementActiveConnections();
|
|
113
|
-
metrics.recordRequest(duration, ctx.response.getStatus());
|
|
114
|
-
if (!skipTracing) {
|
|
115
|
-
const reqId = typeof ctx.request.id === 'function' ? String(ctx.request.id()) : undefined;
|
|
116
|
-
const traceRecord = traceCollector?.finishTrace(ctx.request.method(), ctx.request.url(true), ctx.response.getStatus(), reqId);
|
|
117
|
-
onRequestCompleteFn?.({
|
|
118
|
-
method: ctx.request.method(),
|
|
119
|
-
url: ctx.request.url(true),
|
|
120
|
-
statusCode: ctx.response.getStatus(),
|
|
121
|
-
duration,
|
|
122
|
-
trace: traceRecord ?? undefined,
|
|
123
|
-
httpRequestId: reqId,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
if (traceCollector && !skipTracing) {
|
|
129
|
-
await traceCollector.startTrace(runRequest);
|
|
130
|
-
}
|
|
131
|
-
else if (skipTracing) {
|
|
132
|
-
// Run inside ALS so collectors can check isExcludedRequest()
|
|
133
|
-
await excludedRequestAls.run(true, runRequest);
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
await runRequest();
|
|
137
|
-
}
|
|
108
|
+
const runRequest = buildRequestRunner({ ctx, next, start, metrics, skipTracing });
|
|
109
|
+
await dispatchRequest(runRequest, skipTracing);
|
|
138
110
|
}
|
|
139
111
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse source code and detect auth-related middleware
|
|
3
|
+
* in `server.use()` or `router.use()` blocks.
|
|
4
|
+
*
|
|
5
|
+
* This is a pure function that operates on source text.
|
|
6
|
+
*/
|
|
7
|
+
export declare function detectAuthMiddlewareInSource(source: string): string[];
|
|
8
|
+
/**
|
|
9
|
+
* Read `start/kernel.{ts,js}` from the app root and detect global auth
|
|
10
|
+
* middleware. Returns an empty array if the file cannot be read.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectGlobalAuthMiddleware(makePath: (dir: string, file: string) => string): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Build the warning message lines for detected auth middleware.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildAuthMiddlewareWarning(found: string[], dimFn: (s: string) => string, boldFn: (s: string) => string): string[];
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Check if an import path refers to an auth-related middleware
|
|
4
|
+
* (excluding `initialize_auth` which only sets up ctx.auth).
|
|
5
|
+
*/
|
|
6
|
+
function isAuthMiddleware(importPath) {
|
|
7
|
+
if (importPath.includes('initialize_auth'))
|
|
8
|
+
return false;
|
|
9
|
+
return (importPath.includes('auth') ||
|
|
10
|
+
importPath.includes('silent_auth') ||
|
|
11
|
+
importPath.includes('silentAuth'));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Extract import paths from a `server.use([...])` or `router.use([...])`
|
|
15
|
+
* block that match auth-related middleware.
|
|
16
|
+
*/
|
|
17
|
+
function extractAuthImportsFromBlock(block) {
|
|
18
|
+
const importRegex = /import\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
19
|
+
const results = [];
|
|
20
|
+
let importMatch;
|
|
21
|
+
while ((importMatch = importRegex.exec(block)) !== null) {
|
|
22
|
+
const importPath = importMatch[1];
|
|
23
|
+
if (isAuthMiddleware(importPath)) {
|
|
24
|
+
results.push(importPath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse source code and detect auth-related middleware
|
|
31
|
+
* in `server.use()` or `router.use()` blocks.
|
|
32
|
+
*
|
|
33
|
+
* This is a pure function that operates on source text.
|
|
34
|
+
*/
|
|
35
|
+
export function detectAuthMiddlewareInSource(source) {
|
|
36
|
+
if (!source)
|
|
37
|
+
return [];
|
|
38
|
+
const found = [];
|
|
39
|
+
const useBlockRegex = /(?:server|router)\.use\(\s*\[([\s\S]*?)\]\s*\)/g;
|
|
40
|
+
let match;
|
|
41
|
+
while ((match = useBlockRegex.exec(source)) !== null) {
|
|
42
|
+
found.push(...extractAuthImportsFromBlock(match[1]));
|
|
43
|
+
}
|
|
44
|
+
return found;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Read the kernel source from disk, trying `.ts` then `.js` extensions.
|
|
48
|
+
*/
|
|
49
|
+
function readKernelSource(makePath) {
|
|
50
|
+
for (const ext of ['ts', 'js']) {
|
|
51
|
+
try {
|
|
52
|
+
const source = readFileSync(makePath('start', `kernel.${ext}`), 'utf-8');
|
|
53
|
+
if (source)
|
|
54
|
+
return source;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Try next extension
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read `start/kernel.{ts,js}` from the app root and detect global auth
|
|
64
|
+
* middleware. Returns an empty array if the file cannot be read.
|
|
65
|
+
*/
|
|
66
|
+
export function detectGlobalAuthMiddleware(makePath) {
|
|
67
|
+
try {
|
|
68
|
+
return detectAuthMiddlewareInSource(readKernelSource(makePath));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build the warning message lines for detected auth middleware.
|
|
76
|
+
*/
|
|
77
|
+
export function buildAuthMiddlewareWarning(found, dimFn, boldFn) {
|
|
78
|
+
return [
|
|
79
|
+
...found.map((m) => dimFn('→') + ' ' + m),
|
|
80
|
+
'',
|
|
81
|
+
dimFn('these routes get polled every ~3s, so auth middleware will'),
|
|
82
|
+
dimFn('trigger a DB query on each poll. here are two ways to fix it:'),
|
|
83
|
+
'',
|
|
84
|
+
boldFn('option 1:') + ' add a shouldShow callback to your config:',
|
|
85
|
+
'',
|
|
86
|
+
dimFn('// config/server_stats.ts'),
|
|
87
|
+
dimFn("shouldShow: (ctx) => ctx.auth?.user?.role === 'admin'"),
|
|
88
|
+
'',
|
|
89
|
+
boldFn('option 2:') + ' move auth middleware from router.use() to a route group:',
|
|
90
|
+
'',
|
|
91
|
+
dimFn('// start/kernel.ts — remove from router.use()'),
|
|
92
|
+
dimFn("// () => import('#middleware/silent_auth_middleware')"),
|
|
93
|
+
'',
|
|
94
|
+
dimFn('// start/routes.ts — add to your route groups instead'),
|
|
95
|
+
dimFn('router.group(() => { ... }).use(middleware.silentAuth())'),
|
|
96
|
+
];
|
|
97
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ResolvedServerStatsConfig } from '../types.js';
|
|
2
|
+
export declare function deriveEndpointPaths(endpoint: string | false | undefined, devToolbar?: {
|
|
3
|
+
enabled?: boolean;
|
|
4
|
+
debugEndpoint?: string;
|
|
5
|
+
dashboard?: boolean;
|
|
6
|
+
dashboardPath?: string;
|
|
7
|
+
}): {
|
|
8
|
+
statsEndpoint: string | false;
|
|
9
|
+
debugEndpoint: string | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare function computeDashboardPath(devToolbar?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
dashboard?: boolean;
|
|
14
|
+
dashboardPath?: string;
|
|
15
|
+
}, depsAvailable?: boolean): string | undefined;
|
|
16
|
+
export declare function collectRegisteredPaths(statsEndpoint: string | false, debugEndpoint?: string, dashboardPath?: string): string[];
|
|
17
|
+
export declare function checkDashboardDeps(appImport: (name: string) => Promise<unknown>): Promise<string[]>;
|
|
18
|
+
export declare function logMissingDeps(missing: string[]): void;
|
|
19
|
+
export declare function warnAboutAuthMiddleware(config: ResolvedServerStatsConfig, makePath: (dir: string, file: string) => string): void;
|
|
20
|
+
export declare function logDashboardError(category: 'missing-dep' | 'timeout' | 'unknown', err: unknown): void;
|