@singhak/nodeui-core 0.1.0 → 0.3.0
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 +111 -82
- package/dist/index.cjs +598 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +168 -10
- package/dist/index.d.ts +168 -10
- package/dist/index.mjs +580 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +63 -63
- package/static/assets/index-CJCpPtrr.js +11 -0
- package/static/assets/index-ChbLMdDF.css +1 -0
- package/static/index.html +2 -2
- package/static/assets/index-DbLC8b3x.css +0 -1
- package/static/assets/index-DowJtJYZ.js +0 -10
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
1
2
|
import { Readable } from 'node:stream';
|
|
2
|
-
import { ServerResponse, IncomingMessage } from 'node:http';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Shared typed contract for the NodeUI developer console.
|
|
@@ -8,7 +8,12 @@ import { ServerResponse, IncomingMessage } from 'node:http';
|
|
|
8
8
|
* contract. Every provider returns a {@link ProviderResult}; every REST
|
|
9
9
|
* endpoint returns an {@link ApiEnvelope} which is the same shape.
|
|
10
10
|
*/
|
|
11
|
-
type
|
|
11
|
+
type BuiltInPanelId = 'health' | 'memory' | 'cpu' | 'event-loop' | 'heap-snapshot' | 'startup' | 'requests' | 'env' | 'routes' | 'logs' | 'metrics' | 'outgoing';
|
|
12
|
+
/**
|
|
13
|
+
* Panel identifier. Built-in panels are listed in {@link BuiltInPanelId};
|
|
14
|
+
* plugins may register any other string id (lowercase letters, digits, `-`).
|
|
15
|
+
*/
|
|
16
|
+
type PanelId = BuiltInPanelId | (string & {});
|
|
12
17
|
/** Resolved, validated console configuration. */
|
|
13
18
|
interface NodeUIConfig {
|
|
14
19
|
/** URL path prefix where the console and its API are served. Default `/nodeui`. */
|
|
@@ -35,6 +40,18 @@ interface NodeUIConfig {
|
|
|
35
40
|
confirmTtlMs: number;
|
|
36
41
|
/** Directory where heap snapshot files are written. */
|
|
37
42
|
heapSnapshotDir: string;
|
|
43
|
+
/** Extra `Host` header hostnames accepted besides loopback names. */
|
|
44
|
+
allowedHosts: string[];
|
|
45
|
+
/** Extra `Origin` values accepted for cross-origin calls. */
|
|
46
|
+
allowedOrigins: string[];
|
|
47
|
+
/** Extra remote IPs / IPv4 CIDRs accepted (e.g. Docker bridge `172.17.0.0/16`). */
|
|
48
|
+
allowedRemoteAddresses: string[];
|
|
49
|
+
/** Accept requests carrying `X-Forwarded-*` headers. */
|
|
50
|
+
trustProxy: boolean;
|
|
51
|
+
/** Whether a shared access token is required. */
|
|
52
|
+
authRequired: boolean;
|
|
53
|
+
/** Maximum concurrent live (SSE) streams. */
|
|
54
|
+
maxSseClients: number;
|
|
38
55
|
}
|
|
39
56
|
/** Context handed to every provider call. */
|
|
40
57
|
interface ProviderContext {
|
|
@@ -72,6 +89,8 @@ type ApiEnvelope<T> = ProviderResult<T>;
|
|
|
72
89
|
*/
|
|
73
90
|
interface NodeUIProvider<T = unknown> {
|
|
74
91
|
readonly id: PanelId;
|
|
92
|
+
/** Display label for plugin panels; defaults to the id. */
|
|
93
|
+
readonly title?: string;
|
|
75
94
|
start?(ctx: ProviderContext): void;
|
|
76
95
|
stop?(ctx: ProviderContext): void;
|
|
77
96
|
get(ctx: ProviderContext): ProviderResult<T> | Promise<ProviderResult<T>>;
|
|
@@ -99,6 +118,12 @@ interface CpuData {
|
|
|
99
118
|
totalPercent: number;
|
|
100
119
|
sampleAtMs: number;
|
|
101
120
|
}
|
|
121
|
+
interface HealthCheckResult {
|
|
122
|
+
name: string;
|
|
123
|
+
status: 'up' | 'down';
|
|
124
|
+
durationMs: number;
|
|
125
|
+
error?: string;
|
|
126
|
+
}
|
|
102
127
|
interface HealthData {
|
|
103
128
|
status: 'ok' | 'degraded' | 'critical' | 'unknown';
|
|
104
129
|
statusReason: string;
|
|
@@ -108,6 +133,8 @@ interface HealthData {
|
|
|
108
133
|
platform: string;
|
|
109
134
|
eventLoopLagMs: number | null;
|
|
110
135
|
memoryUsedPercent: number | null;
|
|
136
|
+
/** Results of user-registered dependency checks; empty when none. */
|
|
137
|
+
checks: HealthCheckResult[];
|
|
111
138
|
}
|
|
112
139
|
interface StartupMark {
|
|
113
140
|
name: string;
|
|
@@ -165,6 +192,21 @@ interface MetricsBucket {
|
|
|
165
192
|
interface MetricsData {
|
|
166
193
|
buckets: MetricsBucket[];
|
|
167
194
|
}
|
|
195
|
+
interface OutgoingRequestEntry {
|
|
196
|
+
id: number;
|
|
197
|
+
method: string;
|
|
198
|
+
/** Target as `host/path` (query string removed). */
|
|
199
|
+
url: string;
|
|
200
|
+
status: number | null;
|
|
201
|
+
durationMs: number;
|
|
202
|
+
timestampMs: number;
|
|
203
|
+
error?: string;
|
|
204
|
+
}
|
|
205
|
+
interface OutgoingData {
|
|
206
|
+
total: number;
|
|
207
|
+
failed: number;
|
|
208
|
+
entries: OutgoingRequestEntry[];
|
|
209
|
+
}
|
|
168
210
|
interface HeapSnapshotData {
|
|
169
211
|
fileName: string;
|
|
170
212
|
filePath: string;
|
|
@@ -194,6 +236,12 @@ interface ConfigData {
|
|
|
194
236
|
enabled: boolean;
|
|
195
237
|
pattern: string;
|
|
196
238
|
};
|
|
239
|
+
authRequired: boolean;
|
|
240
|
+
/** Plugin panel ids and titles, in registration order. */
|
|
241
|
+
plugins: Array<{
|
|
242
|
+
id: PanelId;
|
|
243
|
+
title: string;
|
|
244
|
+
}>;
|
|
197
245
|
}
|
|
198
246
|
|
|
199
247
|
/** Replacement value used when masking secret values. */
|
|
@@ -222,6 +270,12 @@ declare class RingBuffer<T> {
|
|
|
222
270
|
|
|
223
271
|
/** Keys whose values are redacted in any panel output. */
|
|
224
272
|
declare const SECRET_KEY_PATTERN: RegExp;
|
|
273
|
+
/**
|
|
274
|
+
* Redacts secrets embedded in free text: credentials in URLs
|
|
275
|
+
* (`postgres://user:pass@host`), bearer/basic tokens, JWTs and
|
|
276
|
+
* `password=...`-style pairs. Used for log lines and any string value.
|
|
277
|
+
*/
|
|
278
|
+
declare function maskSecretText(text: string): string;
|
|
225
279
|
interface ActivationDecision {
|
|
226
280
|
active: boolean;
|
|
227
281
|
reason: string;
|
|
@@ -234,12 +288,53 @@ interface ActivationDecision {
|
|
|
234
288
|
declare function resolveActivation(env: Record<string, string | undefined>): ActivationDecision;
|
|
235
289
|
/** True when the socket address is a loopback address. */
|
|
236
290
|
declare function isLoopbackAddress(address: string | undefined): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* True when `address` matches one of `allowed`, each an exact IP or an IPv4
|
|
293
|
+
* CIDR (e.g. `172.17.0.0/16`, the default Docker bridge network).
|
|
294
|
+
*/
|
|
295
|
+
declare function matchesAddress(address: string | undefined, allowed: readonly string[]): boolean;
|
|
296
|
+
/** Extracts the lower-cased hostname from a `Host` header value (no port). */
|
|
297
|
+
declare function hostnameFromHostHeader(value: string | undefined): string | null;
|
|
298
|
+
/** True for the hostnames a browser uses to reach a loopback server. */
|
|
299
|
+
declare function isLoopbackHostname(hostname: string | null): boolean;
|
|
237
300
|
/**
|
|
238
301
|
* Deep-clone a value, replacing any value under a key matching the secret
|
|
239
|
-
* pattern with `[REDACTED]
|
|
302
|
+
* pattern with `[REDACTED]` and scrubbing secrets embedded in string values
|
|
303
|
+
* (URL credentials, bearer tokens, `password=...`). The input is not mutated.
|
|
240
304
|
*/
|
|
241
305
|
declare function maskSecrets<T>(value: T, pattern?: RegExp): T;
|
|
242
306
|
|
|
307
|
+
declare const AUTH_COOKIE = "nodeui_token";
|
|
308
|
+
interface GuardOptions {
|
|
309
|
+
/** Configured console host; a loopback value enables the strict checks. */
|
|
310
|
+
host: string;
|
|
311
|
+
/** Extra `Host` header hostnames accepted (e.g. `myapp.local`). */
|
|
312
|
+
allowedHosts: readonly string[];
|
|
313
|
+
/** Extra `Origin` values accepted for cross-origin calls. */
|
|
314
|
+
allowedOrigins: readonly string[];
|
|
315
|
+
/** Extra remote IPs / IPv4 CIDRs accepted (e.g. `172.17.0.0/16` for Docker). */
|
|
316
|
+
allowedRemoteAddresses: readonly string[];
|
|
317
|
+
/** Accept requests carrying `X-Forwarded-*` headers. Default false. */
|
|
318
|
+
trustProxy: boolean;
|
|
319
|
+
/** When set, every request must present this token. */
|
|
320
|
+
authToken?: string;
|
|
321
|
+
}
|
|
322
|
+
type GuardResult = {
|
|
323
|
+
ok: true;
|
|
324
|
+
setToken?: string;
|
|
325
|
+
} | {
|
|
326
|
+
ok: false;
|
|
327
|
+
status: number;
|
|
328
|
+
code: string;
|
|
329
|
+
message: string;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* Builds the request guard for the console. It combines: loopback/remote
|
|
333
|
+
* address policy, `Host` validation (DNS-rebinding defence), `Origin`
|
|
334
|
+
* validation (cross-site request defence) and an optional shared token.
|
|
335
|
+
*/
|
|
336
|
+
declare function createGuard(options: GuardOptions): (req: IncomingMessage) => GuardResult;
|
|
337
|
+
|
|
243
338
|
/**
|
|
244
339
|
* Single-use, expiring confirmation nonces. Mutating actions (such as heap
|
|
245
340
|
* snapshots) require a nonce issued here, presented via the
|
|
@@ -247,8 +342,9 @@ declare function maskSecrets<T>(value: T, pattern?: RegExp): T;
|
|
|
247
342
|
*/
|
|
248
343
|
declare class ConfirmationStore {
|
|
249
344
|
private readonly ttlMs;
|
|
345
|
+
private readonly maxOutstanding;
|
|
250
346
|
private nonces;
|
|
251
|
-
constructor(ttlMs: number);
|
|
347
|
+
constructor(ttlMs: number, maxOutstanding?: number);
|
|
252
348
|
issue(): ConfirmIssued;
|
|
253
349
|
/** Consumes `nonce` and returns true only if it was valid and unexpired. */
|
|
254
350
|
consume(nonce: string): boolean;
|
|
@@ -381,12 +477,45 @@ declare class EventLoopLagProvider implements NodeUIProvider<EventLoopSample> {
|
|
|
381
477
|
};
|
|
382
478
|
}
|
|
383
479
|
|
|
384
|
-
/**
|
|
480
|
+
/**
|
|
481
|
+
* A dependency check (database ping, cache ping, ...). Resolve for healthy,
|
|
482
|
+
* reject/throw (or resolve `false`) for unhealthy.
|
|
483
|
+
*/
|
|
484
|
+
type HealthCheck = () => unknown | Promise<unknown>;
|
|
485
|
+
/** Derives process + event-loop health, plus optional dependency checks. */
|
|
385
486
|
declare class HealthProvider implements NodeUIProvider<HealthData> {
|
|
487
|
+
private readonly checks;
|
|
386
488
|
readonly id: "health";
|
|
387
|
-
|
|
489
|
+
private cache;
|
|
490
|
+
constructor(checks?: Record<string, HealthCheck>);
|
|
491
|
+
private runChecks;
|
|
492
|
+
get(ctx: ProviderContext): Promise<{
|
|
388
493
|
ok: true;
|
|
389
494
|
data: HealthData;
|
|
495
|
+
}>;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
type Recorder = (entry: Omit<OutgoingRequestEntry, 'id'>) => void;
|
|
499
|
+
/**
|
|
500
|
+
* Refcounted instrumentation of outgoing HTTP calls made via `http`,
|
|
501
|
+
* `https` (axios, got, node-fetch, ...) and global `fetch` (undici). The
|
|
502
|
+
* first consumer installs the hooks, the last release restores everything.
|
|
503
|
+
*/
|
|
504
|
+
declare function interceptOutgoing(record: Recorder): () => void;
|
|
505
|
+
/** Recent outgoing HTTP calls, recorded lazily while the panel is active. */
|
|
506
|
+
declare class OutgoingProvider implements NodeUIProvider<OutgoingData> {
|
|
507
|
+
readonly id: "outgoing";
|
|
508
|
+
private buffer;
|
|
509
|
+
private nextId;
|
|
510
|
+
private failed;
|
|
511
|
+
private release;
|
|
512
|
+
constructor(size: number);
|
|
513
|
+
start(): void;
|
|
514
|
+
stop(): void;
|
|
515
|
+
record(entry: Omit<OutgoingRequestEntry, 'id'>): void;
|
|
516
|
+
get(): {
|
|
517
|
+
ok: true;
|
|
518
|
+
data: OutgoingData;
|
|
390
519
|
};
|
|
391
520
|
}
|
|
392
521
|
|
|
@@ -521,7 +650,7 @@ interface SseStream {
|
|
|
521
650
|
* Minimal Server-Sent Events writer over a raw HTTP response. Events are
|
|
522
651
|
* emitted as `data: <json>\n\n`; heartbeats as `:ping\n\n`.
|
|
523
652
|
*/
|
|
524
|
-
declare function startSse(res: ServerResponse): SseStream;
|
|
653
|
+
declare function startSse(res: ServerResponse, extraHeaders?: Record<string, string>): SseStream;
|
|
525
654
|
|
|
526
655
|
interface NodeUIOptions {
|
|
527
656
|
/** URL path prefix. Default `/nodeui` (or `NODEUI_PATH`). */
|
|
@@ -548,8 +677,32 @@ interface NodeUIOptions {
|
|
|
548
677
|
inactivityTimeoutMs?: number;
|
|
549
678
|
/** TTL for mutation confirmation nonces. Default 60000. */
|
|
550
679
|
confirmTtlMs?: number;
|
|
551
|
-
/** Directory for heap snapshot files. Defaults to the OS temp dir. */
|
|
680
|
+
/** Directory for heap snapshot files. Defaults to a private dir under the OS temp dir. */
|
|
552
681
|
heapSnapshotDir?: string;
|
|
682
|
+
/** Extra `Host` hostnames accepted besides loopback names (or `NODEUI_ALLOWED_HOSTS`, comma-separated). */
|
|
683
|
+
allowedHosts?: string[];
|
|
684
|
+
/** Extra `Origin` values accepted for cross-origin calls (or `NODEUI_ALLOWED_ORIGINS`). */
|
|
685
|
+
allowedOrigins?: string[];
|
|
686
|
+
/**
|
|
687
|
+
* Extra remote IPs / IPv4 CIDRs accepted (or `NODEUI_ALLOWED_REMOTE`). Use
|
|
688
|
+
* `['172.16.0.0/12']` to reach the console from the host when the app runs in Docker.
|
|
689
|
+
*/
|
|
690
|
+
allowedRemoteAddresses?: string[];
|
|
691
|
+
/** Accept requests carrying `X-Forwarded-*` headers (or `NODEUI_TRUST_PROXY=true`). Default false. */
|
|
692
|
+
trustProxy?: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Shared access token (or `NODEUI_TOKEN`). When set, open `/nodeui/?token=<token>` once;
|
|
695
|
+
* the browser then keeps an HttpOnly cookie. API clients may send `Authorization: Bearer`.
|
|
696
|
+
*/
|
|
697
|
+
authToken?: string;
|
|
698
|
+
/** Maximum concurrent live (SSE) streams. Default 10. */
|
|
699
|
+
maxSseClients?: number;
|
|
700
|
+
/** Custom panels; each provider needs a unique lowercase id, e.g. `queues`. */
|
|
701
|
+
plugins?: NodeUIProvider[];
|
|
702
|
+
/** Named dependency checks (database, cache, ...) surfaced in the health panel. */
|
|
703
|
+
healthChecks?: Record<string, HealthCheck>;
|
|
704
|
+
/** Max recorded outgoing HTTP calls. Default 200. */
|
|
705
|
+
outgoingLogSize?: number;
|
|
553
706
|
}
|
|
554
707
|
interface NodeUIServer {
|
|
555
708
|
readonly config: NodeUIConfig;
|
|
@@ -559,6 +712,11 @@ interface NodeUIServer {
|
|
|
559
712
|
middleware(): NodeUIMiddleware;
|
|
560
713
|
/** Direct request handling (no `next`); used for tests and adapters. */
|
|
561
714
|
handle(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
715
|
+
/**
|
|
716
|
+
* Supplies the route list directly (frameworks other than Express, e.g.
|
|
717
|
+
* Fastify's `onRoute`). Takes precedence over Express router introspection.
|
|
718
|
+
*/
|
|
719
|
+
setRoutes(source: RouteEntry[] | (() => RouteEntry[])): void;
|
|
562
720
|
/** Records a bootstrap timing mark, e.g. `mark("listening")`. */
|
|
563
721
|
mark(name: string): void;
|
|
564
722
|
/** Whether a background-sampling provider is currently running. */
|
|
@@ -576,7 +734,7 @@ type NodeUIMiddleware = (req: IncomingMessage, res: ServerResponse, next: () =>
|
|
|
576
734
|
* Serializes an API envelope to JSON, applying secret masking so values
|
|
577
735
|
* under keys like `TOKEN`/`KEY`/`SECRET`/`PASSWORD` never reach the UI.
|
|
578
736
|
*/
|
|
579
|
-
declare function serializeEnvelope<T>(envelope: ApiEnvelope<T
|
|
737
|
+
declare function serializeEnvelope<T>(envelope: ApiEnvelope<T>, mask?: boolean): string;
|
|
580
738
|
declare function createNodeUI(options?: NodeUIOptions): NodeUIServer;
|
|
581
739
|
|
|
582
|
-
export { type ActivationDecision, type ApiEnvelope, type ConfigData, type ConfirmIssued, ConfirmationStore, type CpuClock, type CpuData, CpuProvider, type CpuUsageSnapshot, DEFAULT_LOG_SIZE, type EnvData, type EnvEntry, EnvProvider, type EventLoopClock, EventLoopLagProvider, type EventLoopSample, type HealthData, HealthProvider, type HeapSnapshotData, type HeapSnapshotPanelData, HeapSnapshotProvider, type LogEntry, type LogLevel, type LogsData, LogsProvider, type MemoryData, MemoryProvider, type MetricsBucket, type MetricsData, MetricsProvider, type NodeUIConfig, type NodeUIMiddleware, type NodeUIOptions, type NodeUIProvider, type NodeUIServer, type PanelId, type ProviderContext, type ProviderError, ProviderRegistry, type ProviderResult, type RequestEntry, type RequestsData, RequestsProvider, RingBuffer, type RouteEntry, type RoutesData, RoutesProvider, SECRET_KEY_PATTERN, SECRET_MASKED, Sampler, type SamplerOptions, type SseStream, type StartupData, type StartupMark, StartupTracker, type StaticAsset, createNodeUI, extractRoutes, interceptConsole, isLoopbackAddress, maskSecrets, resolveActivation, resolveStaticAsset, serializeEnvelope, startSse };
|
|
740
|
+
export { AUTH_COOKIE, type ActivationDecision, type ApiEnvelope, type BuiltInPanelId, type ConfigData, type ConfirmIssued, ConfirmationStore, type CpuClock, type CpuData, CpuProvider, type CpuUsageSnapshot, DEFAULT_LOG_SIZE, type EnvData, type EnvEntry, EnvProvider, type EventLoopClock, EventLoopLagProvider, type EventLoopSample, type GuardOptions, type GuardResult, type HealthCheck, type HealthCheckResult, type HealthData, HealthProvider, type HeapSnapshotData, type HeapSnapshotPanelData, HeapSnapshotProvider, type LogEntry, type LogLevel, type LogsData, LogsProvider, type MemoryData, MemoryProvider, type MetricsBucket, type MetricsData, MetricsProvider, type NodeUIConfig, type NodeUIMiddleware, type NodeUIOptions, type NodeUIProvider, type NodeUIServer, type OutgoingData, OutgoingProvider, type OutgoingRequestEntry, type PanelId, type ProviderContext, type ProviderError, ProviderRegistry, type ProviderResult, type RequestEntry, type RequestsData, RequestsProvider, RingBuffer, type RouteEntry, type RoutesData, RoutesProvider, SECRET_KEY_PATTERN, SECRET_MASKED, Sampler, type SamplerOptions, type SseStream, type StartupData, type StartupMark, StartupTracker, type StaticAsset, createGuard, createNodeUI, extractRoutes, hostnameFromHostHeader, interceptConsole, interceptOutgoing, isLoopbackAddress, isLoopbackHostname, maskSecretText, maskSecrets, matchesAddress, resolveActivation, resolveStaticAsset, serializeEnvelope, startSse };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
1
2
|
import { Readable } from 'node:stream';
|
|
2
|
-
import { ServerResponse, IncomingMessage } from 'node:http';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Shared typed contract for the NodeUI developer console.
|
|
@@ -8,7 +8,12 @@ import { ServerResponse, IncomingMessage } from 'node:http';
|
|
|
8
8
|
* contract. Every provider returns a {@link ProviderResult}; every REST
|
|
9
9
|
* endpoint returns an {@link ApiEnvelope} which is the same shape.
|
|
10
10
|
*/
|
|
11
|
-
type
|
|
11
|
+
type BuiltInPanelId = 'health' | 'memory' | 'cpu' | 'event-loop' | 'heap-snapshot' | 'startup' | 'requests' | 'env' | 'routes' | 'logs' | 'metrics' | 'outgoing';
|
|
12
|
+
/**
|
|
13
|
+
* Panel identifier. Built-in panels are listed in {@link BuiltInPanelId};
|
|
14
|
+
* plugins may register any other string id (lowercase letters, digits, `-`).
|
|
15
|
+
*/
|
|
16
|
+
type PanelId = BuiltInPanelId | (string & {});
|
|
12
17
|
/** Resolved, validated console configuration. */
|
|
13
18
|
interface NodeUIConfig {
|
|
14
19
|
/** URL path prefix where the console and its API are served. Default `/nodeui`. */
|
|
@@ -35,6 +40,18 @@ interface NodeUIConfig {
|
|
|
35
40
|
confirmTtlMs: number;
|
|
36
41
|
/** Directory where heap snapshot files are written. */
|
|
37
42
|
heapSnapshotDir: string;
|
|
43
|
+
/** Extra `Host` header hostnames accepted besides loopback names. */
|
|
44
|
+
allowedHosts: string[];
|
|
45
|
+
/** Extra `Origin` values accepted for cross-origin calls. */
|
|
46
|
+
allowedOrigins: string[];
|
|
47
|
+
/** Extra remote IPs / IPv4 CIDRs accepted (e.g. Docker bridge `172.17.0.0/16`). */
|
|
48
|
+
allowedRemoteAddresses: string[];
|
|
49
|
+
/** Accept requests carrying `X-Forwarded-*` headers. */
|
|
50
|
+
trustProxy: boolean;
|
|
51
|
+
/** Whether a shared access token is required. */
|
|
52
|
+
authRequired: boolean;
|
|
53
|
+
/** Maximum concurrent live (SSE) streams. */
|
|
54
|
+
maxSseClients: number;
|
|
38
55
|
}
|
|
39
56
|
/** Context handed to every provider call. */
|
|
40
57
|
interface ProviderContext {
|
|
@@ -72,6 +89,8 @@ type ApiEnvelope<T> = ProviderResult<T>;
|
|
|
72
89
|
*/
|
|
73
90
|
interface NodeUIProvider<T = unknown> {
|
|
74
91
|
readonly id: PanelId;
|
|
92
|
+
/** Display label for plugin panels; defaults to the id. */
|
|
93
|
+
readonly title?: string;
|
|
75
94
|
start?(ctx: ProviderContext): void;
|
|
76
95
|
stop?(ctx: ProviderContext): void;
|
|
77
96
|
get(ctx: ProviderContext): ProviderResult<T> | Promise<ProviderResult<T>>;
|
|
@@ -99,6 +118,12 @@ interface CpuData {
|
|
|
99
118
|
totalPercent: number;
|
|
100
119
|
sampleAtMs: number;
|
|
101
120
|
}
|
|
121
|
+
interface HealthCheckResult {
|
|
122
|
+
name: string;
|
|
123
|
+
status: 'up' | 'down';
|
|
124
|
+
durationMs: number;
|
|
125
|
+
error?: string;
|
|
126
|
+
}
|
|
102
127
|
interface HealthData {
|
|
103
128
|
status: 'ok' | 'degraded' | 'critical' | 'unknown';
|
|
104
129
|
statusReason: string;
|
|
@@ -108,6 +133,8 @@ interface HealthData {
|
|
|
108
133
|
platform: string;
|
|
109
134
|
eventLoopLagMs: number | null;
|
|
110
135
|
memoryUsedPercent: number | null;
|
|
136
|
+
/** Results of user-registered dependency checks; empty when none. */
|
|
137
|
+
checks: HealthCheckResult[];
|
|
111
138
|
}
|
|
112
139
|
interface StartupMark {
|
|
113
140
|
name: string;
|
|
@@ -165,6 +192,21 @@ interface MetricsBucket {
|
|
|
165
192
|
interface MetricsData {
|
|
166
193
|
buckets: MetricsBucket[];
|
|
167
194
|
}
|
|
195
|
+
interface OutgoingRequestEntry {
|
|
196
|
+
id: number;
|
|
197
|
+
method: string;
|
|
198
|
+
/** Target as `host/path` (query string removed). */
|
|
199
|
+
url: string;
|
|
200
|
+
status: number | null;
|
|
201
|
+
durationMs: number;
|
|
202
|
+
timestampMs: number;
|
|
203
|
+
error?: string;
|
|
204
|
+
}
|
|
205
|
+
interface OutgoingData {
|
|
206
|
+
total: number;
|
|
207
|
+
failed: number;
|
|
208
|
+
entries: OutgoingRequestEntry[];
|
|
209
|
+
}
|
|
168
210
|
interface HeapSnapshotData {
|
|
169
211
|
fileName: string;
|
|
170
212
|
filePath: string;
|
|
@@ -194,6 +236,12 @@ interface ConfigData {
|
|
|
194
236
|
enabled: boolean;
|
|
195
237
|
pattern: string;
|
|
196
238
|
};
|
|
239
|
+
authRequired: boolean;
|
|
240
|
+
/** Plugin panel ids and titles, in registration order. */
|
|
241
|
+
plugins: Array<{
|
|
242
|
+
id: PanelId;
|
|
243
|
+
title: string;
|
|
244
|
+
}>;
|
|
197
245
|
}
|
|
198
246
|
|
|
199
247
|
/** Replacement value used when masking secret values. */
|
|
@@ -222,6 +270,12 @@ declare class RingBuffer<T> {
|
|
|
222
270
|
|
|
223
271
|
/** Keys whose values are redacted in any panel output. */
|
|
224
272
|
declare const SECRET_KEY_PATTERN: RegExp;
|
|
273
|
+
/**
|
|
274
|
+
* Redacts secrets embedded in free text: credentials in URLs
|
|
275
|
+
* (`postgres://user:pass@host`), bearer/basic tokens, JWTs and
|
|
276
|
+
* `password=...`-style pairs. Used for log lines and any string value.
|
|
277
|
+
*/
|
|
278
|
+
declare function maskSecretText(text: string): string;
|
|
225
279
|
interface ActivationDecision {
|
|
226
280
|
active: boolean;
|
|
227
281
|
reason: string;
|
|
@@ -234,12 +288,53 @@ interface ActivationDecision {
|
|
|
234
288
|
declare function resolveActivation(env: Record<string, string | undefined>): ActivationDecision;
|
|
235
289
|
/** True when the socket address is a loopback address. */
|
|
236
290
|
declare function isLoopbackAddress(address: string | undefined): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* True when `address` matches one of `allowed`, each an exact IP or an IPv4
|
|
293
|
+
* CIDR (e.g. `172.17.0.0/16`, the default Docker bridge network).
|
|
294
|
+
*/
|
|
295
|
+
declare function matchesAddress(address: string | undefined, allowed: readonly string[]): boolean;
|
|
296
|
+
/** Extracts the lower-cased hostname from a `Host` header value (no port). */
|
|
297
|
+
declare function hostnameFromHostHeader(value: string | undefined): string | null;
|
|
298
|
+
/** True for the hostnames a browser uses to reach a loopback server. */
|
|
299
|
+
declare function isLoopbackHostname(hostname: string | null): boolean;
|
|
237
300
|
/**
|
|
238
301
|
* Deep-clone a value, replacing any value under a key matching the secret
|
|
239
|
-
* pattern with `[REDACTED]
|
|
302
|
+
* pattern with `[REDACTED]` and scrubbing secrets embedded in string values
|
|
303
|
+
* (URL credentials, bearer tokens, `password=...`). The input is not mutated.
|
|
240
304
|
*/
|
|
241
305
|
declare function maskSecrets<T>(value: T, pattern?: RegExp): T;
|
|
242
306
|
|
|
307
|
+
declare const AUTH_COOKIE = "nodeui_token";
|
|
308
|
+
interface GuardOptions {
|
|
309
|
+
/** Configured console host; a loopback value enables the strict checks. */
|
|
310
|
+
host: string;
|
|
311
|
+
/** Extra `Host` header hostnames accepted (e.g. `myapp.local`). */
|
|
312
|
+
allowedHosts: readonly string[];
|
|
313
|
+
/** Extra `Origin` values accepted for cross-origin calls. */
|
|
314
|
+
allowedOrigins: readonly string[];
|
|
315
|
+
/** Extra remote IPs / IPv4 CIDRs accepted (e.g. `172.17.0.0/16` for Docker). */
|
|
316
|
+
allowedRemoteAddresses: readonly string[];
|
|
317
|
+
/** Accept requests carrying `X-Forwarded-*` headers. Default false. */
|
|
318
|
+
trustProxy: boolean;
|
|
319
|
+
/** When set, every request must present this token. */
|
|
320
|
+
authToken?: string;
|
|
321
|
+
}
|
|
322
|
+
type GuardResult = {
|
|
323
|
+
ok: true;
|
|
324
|
+
setToken?: string;
|
|
325
|
+
} | {
|
|
326
|
+
ok: false;
|
|
327
|
+
status: number;
|
|
328
|
+
code: string;
|
|
329
|
+
message: string;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* Builds the request guard for the console. It combines: loopback/remote
|
|
333
|
+
* address policy, `Host` validation (DNS-rebinding defence), `Origin`
|
|
334
|
+
* validation (cross-site request defence) and an optional shared token.
|
|
335
|
+
*/
|
|
336
|
+
declare function createGuard(options: GuardOptions): (req: IncomingMessage) => GuardResult;
|
|
337
|
+
|
|
243
338
|
/**
|
|
244
339
|
* Single-use, expiring confirmation nonces. Mutating actions (such as heap
|
|
245
340
|
* snapshots) require a nonce issued here, presented via the
|
|
@@ -247,8 +342,9 @@ declare function maskSecrets<T>(value: T, pattern?: RegExp): T;
|
|
|
247
342
|
*/
|
|
248
343
|
declare class ConfirmationStore {
|
|
249
344
|
private readonly ttlMs;
|
|
345
|
+
private readonly maxOutstanding;
|
|
250
346
|
private nonces;
|
|
251
|
-
constructor(ttlMs: number);
|
|
347
|
+
constructor(ttlMs: number, maxOutstanding?: number);
|
|
252
348
|
issue(): ConfirmIssued;
|
|
253
349
|
/** Consumes `nonce` and returns true only if it was valid and unexpired. */
|
|
254
350
|
consume(nonce: string): boolean;
|
|
@@ -381,12 +477,45 @@ declare class EventLoopLagProvider implements NodeUIProvider<EventLoopSample> {
|
|
|
381
477
|
};
|
|
382
478
|
}
|
|
383
479
|
|
|
384
|
-
/**
|
|
480
|
+
/**
|
|
481
|
+
* A dependency check (database ping, cache ping, ...). Resolve for healthy,
|
|
482
|
+
* reject/throw (or resolve `false`) for unhealthy.
|
|
483
|
+
*/
|
|
484
|
+
type HealthCheck = () => unknown | Promise<unknown>;
|
|
485
|
+
/** Derives process + event-loop health, plus optional dependency checks. */
|
|
385
486
|
declare class HealthProvider implements NodeUIProvider<HealthData> {
|
|
487
|
+
private readonly checks;
|
|
386
488
|
readonly id: "health";
|
|
387
|
-
|
|
489
|
+
private cache;
|
|
490
|
+
constructor(checks?: Record<string, HealthCheck>);
|
|
491
|
+
private runChecks;
|
|
492
|
+
get(ctx: ProviderContext): Promise<{
|
|
388
493
|
ok: true;
|
|
389
494
|
data: HealthData;
|
|
495
|
+
}>;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
type Recorder = (entry: Omit<OutgoingRequestEntry, 'id'>) => void;
|
|
499
|
+
/**
|
|
500
|
+
* Refcounted instrumentation of outgoing HTTP calls made via `http`,
|
|
501
|
+
* `https` (axios, got, node-fetch, ...) and global `fetch` (undici). The
|
|
502
|
+
* first consumer installs the hooks, the last release restores everything.
|
|
503
|
+
*/
|
|
504
|
+
declare function interceptOutgoing(record: Recorder): () => void;
|
|
505
|
+
/** Recent outgoing HTTP calls, recorded lazily while the panel is active. */
|
|
506
|
+
declare class OutgoingProvider implements NodeUIProvider<OutgoingData> {
|
|
507
|
+
readonly id: "outgoing";
|
|
508
|
+
private buffer;
|
|
509
|
+
private nextId;
|
|
510
|
+
private failed;
|
|
511
|
+
private release;
|
|
512
|
+
constructor(size: number);
|
|
513
|
+
start(): void;
|
|
514
|
+
stop(): void;
|
|
515
|
+
record(entry: Omit<OutgoingRequestEntry, 'id'>): void;
|
|
516
|
+
get(): {
|
|
517
|
+
ok: true;
|
|
518
|
+
data: OutgoingData;
|
|
390
519
|
};
|
|
391
520
|
}
|
|
392
521
|
|
|
@@ -521,7 +650,7 @@ interface SseStream {
|
|
|
521
650
|
* Minimal Server-Sent Events writer over a raw HTTP response. Events are
|
|
522
651
|
* emitted as `data: <json>\n\n`; heartbeats as `:ping\n\n`.
|
|
523
652
|
*/
|
|
524
|
-
declare function startSse(res: ServerResponse): SseStream;
|
|
653
|
+
declare function startSse(res: ServerResponse, extraHeaders?: Record<string, string>): SseStream;
|
|
525
654
|
|
|
526
655
|
interface NodeUIOptions {
|
|
527
656
|
/** URL path prefix. Default `/nodeui` (or `NODEUI_PATH`). */
|
|
@@ -548,8 +677,32 @@ interface NodeUIOptions {
|
|
|
548
677
|
inactivityTimeoutMs?: number;
|
|
549
678
|
/** TTL for mutation confirmation nonces. Default 60000. */
|
|
550
679
|
confirmTtlMs?: number;
|
|
551
|
-
/** Directory for heap snapshot files. Defaults to the OS temp dir. */
|
|
680
|
+
/** Directory for heap snapshot files. Defaults to a private dir under the OS temp dir. */
|
|
552
681
|
heapSnapshotDir?: string;
|
|
682
|
+
/** Extra `Host` hostnames accepted besides loopback names (or `NODEUI_ALLOWED_HOSTS`, comma-separated). */
|
|
683
|
+
allowedHosts?: string[];
|
|
684
|
+
/** Extra `Origin` values accepted for cross-origin calls (or `NODEUI_ALLOWED_ORIGINS`). */
|
|
685
|
+
allowedOrigins?: string[];
|
|
686
|
+
/**
|
|
687
|
+
* Extra remote IPs / IPv4 CIDRs accepted (or `NODEUI_ALLOWED_REMOTE`). Use
|
|
688
|
+
* `['172.16.0.0/12']` to reach the console from the host when the app runs in Docker.
|
|
689
|
+
*/
|
|
690
|
+
allowedRemoteAddresses?: string[];
|
|
691
|
+
/** Accept requests carrying `X-Forwarded-*` headers (or `NODEUI_TRUST_PROXY=true`). Default false. */
|
|
692
|
+
trustProxy?: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Shared access token (or `NODEUI_TOKEN`). When set, open `/nodeui/?token=<token>` once;
|
|
695
|
+
* the browser then keeps an HttpOnly cookie. API clients may send `Authorization: Bearer`.
|
|
696
|
+
*/
|
|
697
|
+
authToken?: string;
|
|
698
|
+
/** Maximum concurrent live (SSE) streams. Default 10. */
|
|
699
|
+
maxSseClients?: number;
|
|
700
|
+
/** Custom panels; each provider needs a unique lowercase id, e.g. `queues`. */
|
|
701
|
+
plugins?: NodeUIProvider[];
|
|
702
|
+
/** Named dependency checks (database, cache, ...) surfaced in the health panel. */
|
|
703
|
+
healthChecks?: Record<string, HealthCheck>;
|
|
704
|
+
/** Max recorded outgoing HTTP calls. Default 200. */
|
|
705
|
+
outgoingLogSize?: number;
|
|
553
706
|
}
|
|
554
707
|
interface NodeUIServer {
|
|
555
708
|
readonly config: NodeUIConfig;
|
|
@@ -559,6 +712,11 @@ interface NodeUIServer {
|
|
|
559
712
|
middleware(): NodeUIMiddleware;
|
|
560
713
|
/** Direct request handling (no `next`); used for tests and adapters. */
|
|
561
714
|
handle(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
715
|
+
/**
|
|
716
|
+
* Supplies the route list directly (frameworks other than Express, e.g.
|
|
717
|
+
* Fastify's `onRoute`). Takes precedence over Express router introspection.
|
|
718
|
+
*/
|
|
719
|
+
setRoutes(source: RouteEntry[] | (() => RouteEntry[])): void;
|
|
562
720
|
/** Records a bootstrap timing mark, e.g. `mark("listening")`. */
|
|
563
721
|
mark(name: string): void;
|
|
564
722
|
/** Whether a background-sampling provider is currently running. */
|
|
@@ -576,7 +734,7 @@ type NodeUIMiddleware = (req: IncomingMessage, res: ServerResponse, next: () =>
|
|
|
576
734
|
* Serializes an API envelope to JSON, applying secret masking so values
|
|
577
735
|
* under keys like `TOKEN`/`KEY`/`SECRET`/`PASSWORD` never reach the UI.
|
|
578
736
|
*/
|
|
579
|
-
declare function serializeEnvelope<T>(envelope: ApiEnvelope<T
|
|
737
|
+
declare function serializeEnvelope<T>(envelope: ApiEnvelope<T>, mask?: boolean): string;
|
|
580
738
|
declare function createNodeUI(options?: NodeUIOptions): NodeUIServer;
|
|
581
739
|
|
|
582
|
-
export { type ActivationDecision, type ApiEnvelope, type ConfigData, type ConfirmIssued, ConfirmationStore, type CpuClock, type CpuData, CpuProvider, type CpuUsageSnapshot, DEFAULT_LOG_SIZE, type EnvData, type EnvEntry, EnvProvider, type EventLoopClock, EventLoopLagProvider, type EventLoopSample, type HealthData, HealthProvider, type HeapSnapshotData, type HeapSnapshotPanelData, HeapSnapshotProvider, type LogEntry, type LogLevel, type LogsData, LogsProvider, type MemoryData, MemoryProvider, type MetricsBucket, type MetricsData, MetricsProvider, type NodeUIConfig, type NodeUIMiddleware, type NodeUIOptions, type NodeUIProvider, type NodeUIServer, type PanelId, type ProviderContext, type ProviderError, ProviderRegistry, type ProviderResult, type RequestEntry, type RequestsData, RequestsProvider, RingBuffer, type RouteEntry, type RoutesData, RoutesProvider, SECRET_KEY_PATTERN, SECRET_MASKED, Sampler, type SamplerOptions, type SseStream, type StartupData, type StartupMark, StartupTracker, type StaticAsset, createNodeUI, extractRoutes, interceptConsole, isLoopbackAddress, maskSecrets, resolveActivation, resolveStaticAsset, serializeEnvelope, startSse };
|
|
740
|
+
export { AUTH_COOKIE, type ActivationDecision, type ApiEnvelope, type BuiltInPanelId, type ConfigData, type ConfirmIssued, ConfirmationStore, type CpuClock, type CpuData, CpuProvider, type CpuUsageSnapshot, DEFAULT_LOG_SIZE, type EnvData, type EnvEntry, EnvProvider, type EventLoopClock, EventLoopLagProvider, type EventLoopSample, type GuardOptions, type GuardResult, type HealthCheck, type HealthCheckResult, type HealthData, HealthProvider, type HeapSnapshotData, type HeapSnapshotPanelData, HeapSnapshotProvider, type LogEntry, type LogLevel, type LogsData, LogsProvider, type MemoryData, MemoryProvider, type MetricsBucket, type MetricsData, MetricsProvider, type NodeUIConfig, type NodeUIMiddleware, type NodeUIOptions, type NodeUIProvider, type NodeUIServer, type OutgoingData, OutgoingProvider, type OutgoingRequestEntry, type PanelId, type ProviderContext, type ProviderError, ProviderRegistry, type ProviderResult, type RequestEntry, type RequestsData, RequestsProvider, RingBuffer, type RouteEntry, type RoutesData, RoutesProvider, SECRET_KEY_PATTERN, SECRET_MASKED, Sampler, type SamplerOptions, type SseStream, type StartupData, type StartupMark, StartupTracker, type StaticAsset, createGuard, createNodeUI, extractRoutes, hostnameFromHostHeader, interceptConsole, interceptOutgoing, isLoopbackAddress, isLoopbackHostname, maskSecretText, maskSecrets, matchesAddress, resolveActivation, resolveStaticAsset, serializeEnvelope, startSse };
|