Dev Console
@modularityjs/http-dev-console serves a development console at /dev answering the question every debugging session starts with: what actually booted?
Built-in panels:
- Modules (load order) — every module in the topological order the loader derived.
- Pools — each pool with its contributed entry keys.
- Preferences — the winning module per contract token (last-wins already applied).
- HTTP routes — method/path/controller/handler from decorator metadata (the same source
http:route:listreads).
The data comes from AppIntrospection — a frozen snapshot the framework binds into the container on every boot — so the console shows the truth of this process, not a guess from source code.
Wiring
modules: [
HttpModule.forRoot({ port: 3000 }),
HttpFastifyModule,
HttpDevConsoleModule,
],Production-safe by default: the page returns 404 unless NODE_ENV !== 'production'. Override deliberately with HttpDevConsoleModule.forRoot({ enabled: true }) when dogfooding a prod-like environment.
Contributing a panel
The console is itself modular — any package or app module can add a section via DevConsolePanelsPool:
import { Inject, Injectable } from '@modularityjs/di';
import {
DevConsolePanel,
DevConsolePanelsPool,
escapeHtml,
renderTable,
} from '@modularityjs/http-dev-console';
@Injectable()
export class QueueDepthsPanel extends DevConsolePanel {
readonly id = 'queue-depths';
readonly title = 'Queue depths';
constructor(@Inject(QueueService) private readonly queue: QueueService) {
super();
}
async render(): Promise<string> {
const depths = await this.queue.depths();
return renderTable(
['Queue', 'Pending'],
depths.map((d) => [escapeHtml(d.name), String(d.pending)]),
);
}
}pools: [
{ pool: DevConsolePanelsPool, key: 'queue-depths', useClass: QueueDepthsPanel },
],render() returns an HTML fragment (escape dynamic text with the exported escapeHtml; renderTable is provided for the common shape). A panel that throws renders its error message in place — one broken panel never takes the console down.