@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.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/ring-buffer.ts","../src/safety.ts","../src/confirmations.ts","../src/registry.ts","../src/static.ts","../src/providers/sampler.ts","../src/providers/startup-tracker.ts","../src/providers/memory.ts","../src/providers/cpu.ts","../src/providers/event-loop.ts","../src/providers/health.ts","../src/providers/heap-snapshot.ts","../src/providers/requests.ts","../src/providers/metrics.ts","../src/providers/env.ts","../src/providers/routes.ts","../src/providers/logs.ts","../src/sse.ts","../src/server.ts"],"sourcesContent":["/**\n * @nodeui/core — framework-neutral engine and safety gate for the NodeUI\n * developer console. Serves the bundled React console and the observability\n * REST API to loopback-only, dev-activated Node.js applications.\n */\n\nexport * from './types';\nexport * from './constants';\nexport * from './ring-buffer';\nexport * from './safety';\nexport * from './confirmations';\nexport * from './registry';\nexport * from './static';\nexport * from './providers';\nexport { startSse, type SseStream } from './sse';\nexport { createNodeUI, serializeEnvelope } from './server';\nexport type { NodeUIOptions, NodeUIServer, NodeUIMiddleware } from './server';\n","/** Replacement value used when masking secret values. */\nexport const SECRET_MASKED = '[REDACTED]';\n\n/** Default ring buffer capacity for the log viewer. */\nexport const DEFAULT_LOG_SIZE = 500;\n","/**\n * Fixed-size ring buffer. When full, pushing drops the oldest item.\n * Used for the HTTP request log to keep memory bounded.\n */\nexport class RingBuffer<T> {\n private buffer: Array<T | undefined>;\n private head = 0;\n private count = 0;\n readonly capacity: number;\n\n constructor(size: number) {\n if (!Number.isInteger(size) || size <= 0) {\n throw new Error('RingBuffer size must be a positive integer');\n }\n this.capacity = size;\n this.buffer = new Array<T | undefined>(size);\n }\n\n push(item: T): void {\n this.buffer[(this.head + this.count) % this.capacity] = item;\n if (this.count < this.capacity) {\n this.count += 1;\n } else {\n this.head = (this.head + 1) % this.capacity;\n }\n }\n\n get length(): number {\n return this.count;\n }\n\n toArray(): T[] {\n return this.slice(0, this.count);\n }\n\n /**\n * Returns up to `limit` items starting at `offset`, oldest first.\n */\n slice(offset = 0, limit?: number): T[] {\n const capped = limit === undefined ? this.count : Math.max(0, limit);\n const out: T[] = [];\n for (let i = 0; i < this.count && out.length < capped; i += 1) {\n if (i >= offset) out.push(this.buffer[(this.head + i) % this.capacity] as T);\n }\n return out;\n }\n}\n","import { SECRET_MASKED } from './constants';\n\n/** Keys whose values are redacted in any panel output. */\nexport const SECRET_KEY_PATTERN = /token|key|secret|password|credential/i;\n\nexport interface ActivationDecision {\n active: boolean;\n reason: string;\n}\n\n/**\n * Safety-gate activation. The console is active when `NODEUI_ENABLED=true`\n * or when `NODE_ENV` is not `production`. `NODEUI_ENABLED=false` explicitly\n * disables it everywhere (including development). Otherwise it fails closed.\n */\nexport function resolveActivation(env: Record<string, string | undefined>): ActivationDecision {\n if (env.NODEUI_ENABLED === 'true') {\n return { active: true, reason: 'activated by NODEUI_ENABLED=true' };\n }\n if (env.NODEUI_ENABLED === 'false') {\n return { active: false, reason: 'inactive: explicitly disabled by NODEUI_ENABLED=false' };\n }\n if (env.NODE_ENV !== 'production') {\n return {\n active: true,\n reason: `activated by NODE_ENV=\"${env.NODE_ENV ?? 'unset'}\" (non-production)`,\n };\n }\n return {\n active: false,\n reason: \"inactive: NODE_ENV=production and NODEUI_ENABLED is not 'true' (fail-closed)\",\n };\n}\n\n/** True when the socket address is a loopback address. */\nexport function isLoopbackAddress(address: string | undefined): boolean {\n if (!address) return false;\n return (\n address.startsWith('127.') ||\n address === '::1' ||\n address.startsWith('::ffff:127.') ||\n address === 'localhost'\n );\n}\n\n/**\n * Deep-clone a value, replacing any value under a key matching the secret\n * pattern with `[REDACTED]`. The input is not mutated.\n */\nexport function maskSecrets<T>(value: T, pattern: RegExp = SECRET_KEY_PATTERN): T {\n if (value !== null && typeof value === 'object') {\n if (Array.isArray(value)) {\n return value.map((v) => maskSecrets(v, pattern)) as T;\n }\n const record = value as Record<string, unknown>;\n const keys = Object.keys(record);\n if (\n keys.length === 2 &&\n keys.includes('key') &&\n keys.includes('value') &&\n typeof record.key === 'string'\n ) {\n const masked = pattern.test(record.key) ? SECRET_MASKED : record.value;\n return { key: record.key, value: maskSecrets(masked, pattern) } as T;\n }\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value)) {\n if (pattern.test(key)) {\n out[key] = SECRET_MASKED;\n } else {\n out[key] = maskSecrets(v, pattern);\n }\n }\n return out as T;\n }\n return value;\n}\n","import { randomBytes } from 'node:crypto';\nimport type { ConfirmIssued } from './types';\n\n/**\n * Single-use, expiring confirmation nonces. Mutating actions (such as heap\n * snapshots) require a nonce issued here, presented via the\n * `x-nodeui-confirm` header, which is consumed exactly once.\n */\nexport class ConfirmationStore {\n private nonces = new Map<string, number>();\n\n constructor(private readonly ttlMs: number) {}\n\n issue(): ConfirmIssued {\n this.prune();\n const nonce = randomBytes(16).toString('hex');\n const expiresAtMs = Date.now() + this.ttlMs;\n this.nonces.set(nonce, expiresAtMs);\n return { nonce, expiresAtMs, ttlMs: this.ttlMs };\n }\n\n /** Consumes `nonce` and returns true only if it was valid and unexpired. */\n consume(nonce: string): boolean {\n this.prune();\n const expiresAt = this.nonces.get(nonce);\n if (expiresAt === undefined) return false;\n this.nonces.delete(nonce);\n return Date.now() <= expiresAt;\n }\n\n /** Number of currently valid nonces (after pruning). */\n size(): number {\n this.prune();\n return this.nonces.size;\n }\n\n /** Removes expired nonces. */\n prune(): void {\n const now = Date.now();\n for (const [nonce, expiresAt] of this.nonces) {\n if (expiresAt <= now) this.nonces.delete(nonce);\n }\n }\n}\n","import type { NodeUIProvider, PanelId } from './types';\n\n/** Ordered registry of the standard panel providers. */\nexport class ProviderRegistry {\n private providers = new Map<PanelId, NodeUIProvider>();\n\n register(provider: NodeUIProvider): void {\n this.providers.set(provider.id, provider);\n }\n\n get(id: PanelId): NodeUIProvider | undefined {\n return this.providers.get(id);\n }\n\n ids(): PanelId[] {\n return [...this.providers.keys()];\n }\n\n all(): NodeUIProvider[] {\n return [...this.providers.values()];\n }\n}\n","import { createReadStream, existsSync, statSync } from 'node:fs';\nimport type { Readable } from 'node:stream';\nimport { join, resolve, sep } from 'node:path';\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.map': 'application/json; charset=utf-8',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.webmanifest': 'application/manifest+json',\n};\n\nexport interface StaticAsset {\n content: Readable;\n contentType: string;\n length: number;\n}\n\n/**\n * Resolves a URL path to a file inside `rootDir`, serving `index.html` for\n * directories and falling back to `index.html` for unknown paths (SPA).\n * Returns null when the path would escape `rootDir`.\n */\nexport function resolveStaticAsset(rootDir: string, urlPath: string): StaticAsset | null {\n const relative = urlPath === '' ? 'index.html' : urlPath.replace(/^\\/+/, '');\n const candidate = resolve(rootDir, relative);\n const rootWithSep = rootDir.endsWith(sep) ? rootDir : rootDir + sep;\n if (candidate !== rootDir && !candidate.startsWith(rootWithSep)) {\n return null;\n }\n\n let file = candidate;\n if (existsSync(file) && statSync(file).isDirectory()) {\n file = join(file, 'index.html');\n }\n if (!existsSync(file) || !statSync(file).isFile()) {\n file = join(rootDir, 'index.html');\n }\n if (!existsSync(file)) {\n return null;\n }\n\n const ext = file.slice(file.lastIndexOf('.')).toLowerCase();\n const stat = statSync(file);\n return {\n content: createReadStream(file),\n contentType: MIME_TYPES[ext] ?? 'application/octet-stream',\n length: stat.size,\n };\n}\n","export interface SamplerOptions<T> {\n intervalMs: number;\n collect: () => T;\n onSample: (sample: T) => void;\n}\n\n/**\n * Wraps a tracked `setInterval`. Idempotent `start`, guaranteed `stop`,\n * and a `collect` that never throws into the event loop. All timers are\n * cleared on `stop` — no leaks on hot reload.\n */\nexport class Sampler<T> {\n private timer: NodeJS.Timeout | null = null;\n\n constructor(private readonly opts: SamplerOptions<T>) {}\n\n start(): void {\n if (this.timer) return;\n const run = (): void => {\n try {\n this.opts.onSample(this.opts.collect());\n } catch {\n // sampling must never throw into the event loop\n }\n };\n this.timer = setInterval(run, this.opts.intervalMs);\n run();\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n get isRunning(): boolean {\n return this.timer !== null;\n }\n}\n","import type { StartupData, StartupMark } from '../types';\n\n/** Records ordered bootstrap timing marks. */\nexport class StartupTracker {\n private startedAtMs = Date.now();\n private marks: StartupMark[] = [];\n\n mark(name: string): void {\n const atMs = Date.now();\n this.marks.push({ name, atMs, sinceFirstMs: atMs - this.startedAtMs });\n }\n\n getData(): StartupData {\n return { startedAtMs: this.startedAtMs, marks: [...this.marks] };\n }\n\n reset(): void {\n this.startedAtMs = Date.now();\n this.marks = [];\n }\n}\n","import { freemem, totalmem } from 'node:os';\nimport type { MemoryData, NodeUIProvider, ProviderContext } from '../types';\nimport { Sampler } from './sampler';\n\nconst ZEROED: MemoryData = {\n heapUsed: 0,\n heapTotal: 0,\n rss: 0,\n external: 0,\n totalMem: 0,\n freeMem: 0,\n sampleAtMs: 0,\n};\n\n/** Samples `process.memoryUsage()` plus OS totals on the poll interval. */\nexport class MemoryProvider implements NodeUIProvider<MemoryData> {\n readonly id = 'memory' as const;\n\n private sampler: Sampler<MemoryData> | null = null;\n private latest: MemoryData = ZEROED;\n\n private collect(): MemoryData {\n const usage = process.memoryUsage();\n return {\n heapUsed: usage.heapUsed,\n heapTotal: usage.heapTotal,\n rss: usage.rss,\n external: usage.external,\n totalMem: totalmem(),\n freeMem: freemem(),\n sampleAtMs: Date.now(),\n };\n }\n\n start(ctx: ProviderContext): void {\n if (this.sampler) return;\n this.sampler = new Sampler({\n intervalMs: ctx.config.pollIntervalMs,\n collect: () => this.collect(),\n onSample: (sample) => {\n this.latest = sample;\n ctx.store[this.id] = sample;\n },\n });\n this.sampler.start();\n }\n\n stop(): void {\n this.sampler?.stop();\n this.sampler = null;\n }\n\n get(): { ok: true; data: MemoryData } {\n return { ok: true, data: this.latest };\n }\n}\n","import type { CpuData, NodeUIProvider, ProviderContext } from '../types';\nimport { Sampler } from './sampler';\n\nexport interface CpuUsageSnapshot {\n user: number;\n system: number;\n}\n\n/**\n * Injectable clock so CPU delta math is unit-testable. `nowUs` returns the\n * wall clock in microseconds; `cpuUsage` returns cumulative microseconds of\n * user/system CPU time since process start (like `process.cpuUsage()`).\n */\nexport interface CpuClock {\n nowUs: () => bigint;\n cpuUsage: () => CpuUsageSnapshot;\n /** Test hook: advance to the next scripted step. */\n tick?: () => void;\n}\n\nconst defaultClock: CpuClock = {\n nowUs: () => process.hrtime.bigint() / 1000n,\n cpuUsage: () => process.cpuUsage(),\n};\n\nconst ZEROED: CpuData = { userPercent: 0, systemPercent: 0, totalPercent: 0, sampleAtMs: 0 };\n\ninterface CpuPoint {\n cpu: CpuUsageSnapshot;\n atUs: bigint;\n}\n\nfunction clampPercent(v: number): number {\n return Math.min(100, Math.max(0, v));\n}\n\n/** Computes CPU usage percent via deltas of `process.cpuUsage()`. */\nexport class CpuProvider implements NodeUIProvider<CpuData> {\n readonly id = 'cpu' as const;\n\n private sampler: Sampler<CpuData> | null = null;\n private latest: CpuData = ZEROED;\n private last: CpuPoint | null = null;\n\n constructor(private readonly clock: CpuClock = defaultClock) {}\n\n /** Runs one sampling step immediately; called by the sampler on each tick. */\n collect(): CpuData {\n const cpu = this.clock.cpuUsage();\n const atUs = this.clock.nowUs();\n let sample: CpuData = { ...ZEROED, sampleAtMs: Date.now() };\n if (this.last) {\n const userUs = cpu.user - this.last.cpu.user;\n const systemUs = cpu.system - this.last.cpu.system;\n const elapsedUs = Number(atUs - this.last.atUs);\n if (elapsedUs > 0) {\n sample = {\n userPercent: clampPercent((userUs / elapsedUs) * 100),\n systemPercent: clampPercent((systemUs / elapsedUs) * 100),\n totalPercent: clampPercent(((userUs + systemUs) / elapsedUs) * 100),\n sampleAtMs: Date.now(),\n };\n }\n }\n this.last = { cpu, atUs };\n this.latest = sample;\n return sample;\n }\n\n start(ctx: ProviderContext): void {\n if (this.sampler) return;\n this.sampler = new Sampler({\n intervalMs: ctx.config.pollIntervalMs,\n collect: () => this.collect(),\n onSample: (sample) => {\n this.latest = sample;\n ctx.store[this.id] = sample;\n },\n });\n this.sampler.start();\n }\n\n stop(): void {\n this.sampler?.stop();\n this.sampler = null;\n }\n\n get(): { ok: true; data: CpuData } {\n return { ok: true, data: this.latest };\n }\n}\n","import type { EventLoopSample, NodeUIProvider, ProviderContext } from '../types';\nimport { Sampler } from './sampler';\n\nexport interface EventLoopClock {\n /** Current time in nanoseconds. */\n nowNs: () => bigint;\n}\n\nconst defaultClock: EventLoopClock = {\n nowNs: () => process.hrtime.bigint(),\n};\n\nconst MAX_KEPT_SAMPLES = 600;\n\nconst ZEROED: EventLoopSample = {\n currentMs: 0,\n maxMs: 0,\n avgMs: 0,\n count: 0,\n sampleAtMs: 0,\n};\n\n/**\n * Measures event-loop lag from a background timer: each tick reports how far\n * behind the nominal interval the loop actually was. Keeps a rolling window\n * of samples so the panel can show current/max/average lag.\n */\nexport class EventLoopLagProvider implements NodeUIProvider<EventLoopSample> {\n readonly id = 'event-loop' as const;\n\n private sampler: Sampler<EventLoopSample> | null = null;\n private samples: number[] = [];\n private latest: EventLoopSample = ZEROED;\n private lastFireNs: bigint | null = null;\n\n constructor(private readonly clock: EventLoopClock = defaultClock) {}\n\n /** Runs one measurement tick; called by the sampler on each interval. */\n collect(intervalMs: number): EventLoopSample {\n const nowNs = this.clock.nowNs();\n let lagMs = 0;\n if (this.lastFireNs !== null) {\n const elapsedMs = Number(nowNs - this.lastFireNs) / 1e6;\n lagMs = Math.max(0, elapsedMs - intervalMs);\n }\n this.lastFireNs = nowNs;\n\n this.samples.push(lagMs);\n if (this.samples.length > MAX_KEPT_SAMPLES) this.samples.shift();\n\n const count = this.samples.length;\n const total = this.samples.reduce((acc, v) => acc + v, 0);\n const sample: EventLoopSample = {\n currentMs: lagMs,\n maxMs: Math.max(...this.samples),\n avgMs: count > 0 ? total / count : 0,\n count,\n sampleAtMs: Date.now(),\n };\n this.latest = sample;\n return sample;\n }\n\n start(ctx: ProviderContext): void {\n if (this.sampler) return;\n this.lastFireNs = this.clock.nowNs();\n this.sampler = new Sampler({\n intervalMs: ctx.config.pollIntervalMs,\n collect: () => this.collect(ctx.config.pollIntervalMs),\n onSample: (sample) => {\n this.latest = sample;\n ctx.store[this.id] = sample;\n },\n });\n this.sampler.start();\n }\n\n stop(): void {\n this.sampler?.stop();\n this.sampler = null;\n }\n\n get(): { ok: true; data: EventLoopSample } {\n return { ok: true, data: this.latest };\n }\n}\n","import type {\n EventLoopSample,\n HealthData,\n MemoryData,\n NodeUIProvider,\n ProviderContext,\n} from '../types';\n\nconst LAG_OK_MS = 50;\nconst LAG_DEGRADED_MS = 200;\n\ntype StoredMemory = Partial<MemoryData>;\ntype StoredEventLoop = Partial<EventLoopSample>;\n\n/** Derives process + event-loop health from the latest sampled values. */\nexport class HealthProvider implements NodeUIProvider<HealthData> {\n readonly id = 'health' as const;\n\n get(ctx: ProviderContext): { ok: true; data: HealthData } {\n const loop = ctx.store['event-loop'] as StoredEventLoop | undefined;\n const memory = ctx.store.memory as StoredMemory | undefined;\n const lagMs = typeof loop?.currentMs === 'number' ? loop.currentMs : null;\n const totalMem = typeof memory?.totalMem === 'number' ? memory.totalMem : 0;\n const rss = typeof memory?.rss === 'number' ? memory.rss : 0;\n const memoryUsedPercent = totalMem > 0 ? (rss / totalMem) * 100 : null;\n\n let status: HealthData['status'];\n let statusReason: string;\n if (lagMs === null) {\n status = 'unknown';\n statusReason = 'no event-loop samples yet (open the Event-loop panel)';\n } else if (lagMs < LAG_OK_MS) {\n status = 'ok';\n statusReason = `event-loop lag ${lagMs.toFixed(1)}ms below ${LAG_OK_MS}ms`;\n } else if (lagMs < LAG_DEGRADED_MS) {\n status = 'degraded';\n statusReason = `event-loop lag ${lagMs.toFixed(1)}ms between ${LAG_OK_MS}ms and ${LAG_DEGRADED_MS}ms`;\n } else {\n status = 'critical';\n statusReason = `event-loop lag ${lagMs.toFixed(1)}ms above ${LAG_DEGRADED_MS}ms`;\n }\n\n return {\n ok: true,\n data: {\n status,\n statusReason,\n uptimeSeconds: Math.round(process.uptime()),\n pid: process.pid,\n nodeVersion: process.version,\n platform: process.platform,\n eventLoopLagMs: lagMs,\n memoryUsedPercent,\n },\n };\n }\n}\n","import { mkdir, stat } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { writeHeapSnapshot } from 'node:v8';\nimport type {\n HeapSnapshotData,\n HeapSnapshotPanelData,\n NodeUIProvider,\n ProviderContext,\n ProviderResult,\n} from '../types';\n\nfunction defaultSnapshotDir(): string {\n return process.env.NODEUI_HEAP_SNAPSHOT_DIR ?? tmpdir();\n}\n\n/**\n * Heap snapshots via `v8.writeHeapSnapshot`. The panel is read-only; the\n * actual file write happens only through `takeSnapshot`, which the server\n * gates behind a confirmation nonce.\n */\nexport class HeapSnapshotProvider implements NodeUIProvider<HeapSnapshotPanelData> {\n readonly id = 'heap-snapshot' as const;\n\n private lastSnapshot: HeapSnapshotData | null = null;\n\n get(): { ok: true; data: HeapSnapshotPanelData } {\n return { ok: true, data: { supported: true, lastSnapshot: this.lastSnapshot } };\n }\n\n async takeSnapshot(ctx: ProviderContext): Promise<ProviderResult<HeapSnapshotData>> {\n const dir = ctx.config.heapSnapshotDir || defaultSnapshotDir();\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n const fileName = `nodeui-heap-${process.pid}-${stamp}.heapsnapshot`;\n const filePath = join(dir, fileName);\n try {\n await mkdir(dir, { recursive: true });\n writeHeapSnapshot(filePath);\n const fileStat = await stat(filePath);\n const data: HeapSnapshotData = {\n fileName,\n filePath,\n sizeBytes: fileStat.size,\n createdAtMs: Date.now(),\n };\n this.lastSnapshot = data;\n return { ok: true, data };\n } catch (err) {\n return {\n ok: false,\n error: {\n code: 'heap-snapshot-failed',\n message: err instanceof Error ? err.message : 'heap snapshot failed',\n },\n };\n }\n }\n}\n","import type { NodeUIProvider, RequestEntry, RequestsData } from '../types';\nimport { RingBuffer } from '../ring-buffer';\n\nconst RESPONSE_PAGE_SIZE = 100;\n\n/** Fixed-size ring buffer of recent HTTP requests, recorded via middleware hook. */\nexport class RequestsProvider implements NodeUIProvider<RequestsData> {\n readonly id = 'requests' as const;\n\n private buffer: RingBuffer<RequestEntry>;\n private nextId = 1;\n\n constructor(size: number) {\n this.buffer = new RingBuffer<RequestEntry>(size);\n }\n\n record(entry: Omit<RequestEntry, 'id'>): void {\n this.buffer.push({ id: this.nextId, ...entry });\n this.nextId += 1;\n }\n\n get length(): number {\n return this.buffer.length;\n }\n\n get(): { ok: true; data: RequestsData } {\n const entries = this.buffer.slice(Math.max(0, this.buffer.length - RESPONSE_PAGE_SIZE));\n return { ok: true, data: { total: this.buffer.length, entries } };\n }\n}\n","import type { MetricsBucket, MetricsData, NodeUIProvider } from '../types';\n\nconst WINDOW_SECONDS = 60;\n\n/**\n * Records one-second buckets of request counts and error counts, derived from\n * the request recorder in the server middleware. Errors are responses with\n * status >= 500.\n */\nexport class MetricsProvider implements NodeUIProvider<MetricsData> {\n readonly id = 'metrics' as const;\n\n private buckets: MetricsBucket[] = [];\n private readonly nowMs: () => number;\n\n constructor(nowMs: () => number = Date.now) {\n this.nowMs = nowMs;\n }\n\n /** Ticks the current second bucket for one completed request. */\n record(status: number): void {\n const ts = Math.floor(this.nowMs() / 1000) * 1000;\n const last = this.buckets[this.buckets.length - 1];\n if (last && last.ts === ts) {\n last.requests += 1;\n if (status >= 500) last.errors += 1;\n return;\n }\n this.buckets.push({ ts, requests: 1, errors: status >= 500 ? 1 : 0 });\n if (this.buckets.length > WINDOW_SECONDS + 1) {\n this.buckets.splice(0, this.buckets.length - WINDOW_SECONDS);\n }\n }\n\n get(): { ok: true; data: MetricsData } {\n const now = Math.floor(this.nowMs() / 1000) * 1000;\n const start = now - (WINDOW_SECONDS - 1) * 1000;\n const byTs = new Map<number, MetricsBucket>(this.buckets.map((b) => [b.ts, b]));\n const buckets: MetricsBucket[] = [];\n for (let ts = start; ts <= now; ts += 1000) {\n const existing = byTs.get(ts);\n buckets.push(existing ? { ...existing } : { ts, requests: 0, errors: 0 });\n }\n return { ok: true, data: { buckets } };\n }\n}\n","import type { EnvData, EnvEntry, NodeUIProvider, ProviderContext } from '../types';\n\nfunction stringify(value: unknown): string {\n if (value === undefined) return '';\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n\nfunction toEntries(source: Record<string, unknown>): EnvEntry[] {\n return Object.entries(source)\n .map(([key, value]) => ({ key, value: stringify(value) }))\n .sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n}\n\n/**\n * Environment and app-config viewer. The environment comes from the process\n * env snapshot; secret masking is applied later at serialization time.\n */\nexport class EnvProvider implements NodeUIProvider<EnvData> {\n readonly id = 'env' as const;\n\n get(ctx: ProviderContext): { ok: true; data: EnvData } {\n const environment = toEntries(ctx.env as Record<string, unknown>);\n const rawConfig = ctx.store['app-config'];\n let config: EnvEntry[] | null = null;\n if (rawConfig !== undefined) {\n const resolved = typeof rawConfig === 'function' ? rawConfig() : rawConfig;\n if (resolved !== undefined && resolved !== null) {\n config = toEntries(resolved as Record<string, unknown>);\n }\n }\n return { ok: true, data: { environment, config } };\n }\n}\n","import type { NodeUIProvider, ProviderContext, RouteEntry, RoutesData } from '../types';\n\ninterface RouterLayer {\n name?: string;\n path?: string;\n route?: {\n path?: string;\n methods?: Record<string, boolean>;\n stack?: Array<{ handle?: { name?: string } }>;\n };\n handle?: { stack?: RouterLayer[] };\n}\n\nfunction joinPath(prefix: string, sub: string): string {\n if (!sub || sub === '/') return prefix || '/';\n return `${prefix.replace(/\\/+$/, '')}/${sub.replace(/^\\/+/, '')}`;\n}\n\n/**\n * Recursively walks an Express-style router stack, extracting concrete\n * method + path + handler entries. Middleware-only layers are skipped.\n *\n * NOTE: This intentionally relies on Express router internals\n * (`router.stack`, `layer.route`, `layer.handle.stack`), which are stable in\n * Express 4 and 5 but not part of the public API. If a future Express\n * release changes the shape, this is the only place that needs updating —\n * the walk degrades to an empty list rather than throwing.\n */\nexport function extractRoutes(router: unknown): RouteEntry[] {\n const out: RouteEntry[] = [];\n const seen = new Set<string>();\n let stack: unknown;\n try {\n stack = (router as { stack?: unknown } | undefined)?.stack;\n } catch {\n return out;\n }\n if (!Array.isArray(stack)) return out;\n\n const walk = (layers: RouterLayer[], prefix: string): void => {\n for (const layer of layers) {\n if (!layer || typeof layer !== 'object') continue;\n if (layer.route && typeof layer.route.path === 'string') {\n const path = joinPath(prefix, layer.route.path);\n const methods = Object.entries(layer.route.methods ?? {})\n .filter(([, enabled]) => enabled)\n .map(([method]) => method.toUpperCase());\n const handler = layer.route.stack?.[0]?.handle?.name || 'anonymous';\n if (methods.length === 0) continue;\n for (const method of methods) {\n const key = `${method} ${path}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ method, path, handler });\n }\n continue;\n }\n const nested = layer.handle?.stack;\n if (Array.isArray(nested)) {\n walk(nested, joinPath(prefix, layer.path ?? ''));\n }\n }\n };\n\n try {\n walk(stack as RouterLayer[], '');\n } catch {\n return [];\n }\n out.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));\n return out;\n}\n\n/**\n * Lists the HTTP routes of the host application by introspecting the Express\n * router captured from `req.app._router` (or `req.app.router` on Express 5)\n * on the first handled request. Results are cached per router reference so\n * repeated fetches do not re-walk the stack.\n */\nexport class RoutesProvider implements NodeUIProvider<RoutesData> {\n readonly id = 'routes' as const;\n\n private cache = new WeakMap<object, RouteEntry[]>();\n\n get(\n ctx: ProviderContext,\n ): { ok: true; data: RoutesData } | { ok: false; error: { code: string; message: string } } {\n const router = ctx.store['express-router'];\n if (!router) {\n return {\n ok: false,\n error: {\n code: 'router-unavailable',\n message: 'No Express router captured yet. Send at least one request through the app.',\n },\n };\n }\n if (typeof router !== 'object' && typeof router !== 'function') {\n return {\n ok: false,\n error: {\n code: 'router-invalid',\n message: 'Captured router has an unexpected shape; route listing is unavailable.',\n },\n };\n }\n let routes = this.cache.get(router as object);\n if (!routes) {\n routes = extractRoutes(router);\n this.cache.set(router as object, routes);\n }\n return { ok: true, data: { routes } };\n }\n}\n","import { DEFAULT_LOG_SIZE } from '../constants';\nimport { RingBuffer } from '../ring-buffer';\nimport type { LogEntry, LogLevel, LogsData, NodeUIProvider, ProviderContext } from '../types';\n\nconst CONSOLE_METHODS: Array<[keyof Console, LogLevel]> = [\n ['debug', 'debug'],\n ['info', 'info'],\n ['log', 'info'],\n ['warn', 'warn'],\n ['error', 'error'],\n];\n\nfunction formatArg(arg: unknown): string {\n if (typeof arg === 'string') return arg;\n try {\n const json = JSON.stringify(arg);\n return json === undefined ? String(arg) : json;\n } catch {\n return String(arg);\n }\n}\n\nlet refs = 0;\nconst pushes = new Set<(entry: Omit<LogEntry, 'timestamp'>) => void>();\nconst wrappers = new Map<keyof Console, (...args: unknown[]) => void>();\nlet originals: Partial<Record<keyof Console, (...args: unknown[]) => void>> = {};\n\nfunction consoleRef(): Record<string, (...args: unknown[]) => void> {\n return console as unknown as Record<string, (...args: unknown[]) => void>;\n}\n\nfunction installWrappers(): void {\n for (const [method, level] of CONSOLE_METHODS) {\n const original = consoleRef()[method];\n originals[method] = original;\n const wrapper = (...args: unknown[]): void => {\n for (const push of pushes) {\n try {\n push({ level, message: args.map(formatArg).join(' ') });\n } catch {\n // interception must never throw into the caller\n }\n }\n if (original) original(...args);\n };\n wrappers.set(method, wrapper);\n consoleRef()[method] = wrapper;\n }\n}\n\nfunction uninstallWrappers(): void {\n for (const [method] of CONSOLE_METHODS) {\n const current = consoleRef()[method];\n if (current === wrappers.get(method)) {\n const original = originals[method];\n if (original) {\n consoleRef()[method] = original;\n } else {\n delete consoleRef()[method];\n }\n }\n // If another layer patched console after us, the current method is not\n // ours — leave the chain intact so the host app's interceptor survives.\n }\n wrappers.clear();\n originals = {};\n}\n\n/**\n * Wraps `console.debug/info/log/warn/error` to tee entries to every active\n * `push` callback. Refcounted: the first install replaces the methods, the\n * last release restores the originals. Supports concurrent consumers (e.g.\n * multiple `createNodeUI()` instances) — each registers its own push and is\n * removed independently. Never throws into the caller.\n */\nexport function interceptConsole(push: (entry: Omit<LogEntry, 'timestamp'>) => void): () => void {\n refs += 1;\n pushes.add(push);\n if (refs === 1) installWrappers();\n return () => {\n pushes.delete(push);\n refs = Math.max(0, refs - 1);\n if (refs === 0) uninstallWrappers();\n };\n}\n\n/**\n * Log viewer provider. While active it intercepts `console.*` output and\n * also accepts entries pushed via {@link addSource}. Filtering happens\n * server-side via `ctx.query.level` and `ctx.query.query`.\n */\nexport class LogsProvider implements NodeUIProvider<LogsData> {\n readonly id = 'logs' as const;\n\n private buffer: RingBuffer<LogEntry>;\n private release: (() => void) | null = null;\n\n constructor(size: number = DEFAULT_LOG_SIZE) {\n this.buffer = new RingBuffer<LogEntry>(size);\n }\n\n start(): void {\n if (this.release) return;\n this.release = interceptConsole(({ level, message }) => {\n this.buffer.push({ level, message, timestamp: Date.now() });\n });\n }\n\n stop(): void {\n this.release?.();\n this.release = null;\n }\n\n /** Pushes an external entry (logger adapter). */\n addSource(entry: { level: LogLevel; message: string }): void {\n this.buffer.push({ ...entry, timestamp: Date.now() });\n }\n\n get(ctx: ProviderContext): { ok: true; data: LogsData } {\n let entries = this.buffer.toArray();\n const level = ctx.query?.level;\n if (level) entries = entries.filter((e) => e.level === level);\n const query = ctx.query?.query;\n if (query) {\n const needle = query.toLowerCase();\n entries = entries.filter((e) => e.message.toLowerCase().includes(needle));\n }\n return { ok: true, data: { entries } };\n }\n}\n","import type { ServerResponse } from 'node:http';\n\nexport interface SseStream {\n send(payload: unknown): void;\n heartbeat(): void;\n close(): void;\n}\n\n/**\n * Minimal Server-Sent Events writer over a raw HTTP response. Events are\n * emitted as `data: <json>\\n\\n`; heartbeats as `:ping\\n\\n`.\n */\nexport function startSse(res: ServerResponse): SseStream {\n let closed = false;\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n });\n res.flushHeaders?.();\n\n return {\n send(payload: unknown): void {\n if (closed || res.destroyed) return;\n res.write(`data: ${JSON.stringify(payload)}\\n\\n`);\n },\n heartbeat(): void {\n if (closed || res.destroyed) return;\n res.write(':ping\\n\\n');\n },\n close(): void {\n if (closed || res.destroyed) return;\n closed = true;\n res.end();\n },\n };\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { tmpdir } from 'node:os';\nimport { resolve } from 'node:path';\nimport type {\n ApiEnvelope,\n ConfigData,\n ConfirmIssued,\n HeapSnapshotData,\n LogLevel,\n NodeUIConfig,\n NodeUIProvider,\n PanelId,\n ProviderContext,\n ProviderResult,\n StartupData,\n} from './types';\nimport { maskSecrets, resolveActivation, SECRET_KEY_PATTERN } from './safety';\nimport { isLoopbackAddress } from './safety';\nimport { ConfirmationStore } from './confirmations';\nimport { DEFAULT_LOG_SIZE } from './constants';\nimport { startSse } from './sse';\nimport { ProviderRegistry } from './registry';\nimport { resolveStaticAsset } from './static';\nimport { MemoryProvider } from './providers/memory';\nimport { CpuProvider } from './providers/cpu';\nimport { EventLoopLagProvider } from './providers/event-loop';\nimport { HealthProvider } from './providers/health';\nimport { HeapSnapshotProvider } from './providers/heap-snapshot';\nimport { StartupTracker } from './providers/startup-tracker';\nimport { RequestsProvider } from './providers/requests';\nimport { MetricsProvider, LogsProvider, EnvProvider, RoutesProvider } from './providers';\n\nexport interface NodeUIOptions {\n /** URL path prefix. Default `/nodeui` (or `NODEUI_PATH`). */\n path?: string;\n /** Interface the console considers its own. Default `127.0.0.1` (or `NODEUI_HOST`). */\n host?: string;\n /** Informational port of the host application. */\n port?: number;\n /** Ring buffer capacity for the request log. Default 500. */\n requestLogSize?: number;\n /** Ring buffer capacity for the log viewer. Default 500. */\n logSize?: number;\n /** App config surfaced in the env panel; may be an object or a getter. */\n config?: unknown | (() => unknown);\n /** Sampling/polling interval in ms. Default 2000. */\n pollIntervalMs?: number;\n /** Explicit activation override. Defaults to env-based activation. */\n enabled?: boolean;\n /** Environment snapshot. Defaults to `process.env`. */\n env?: Record<string, string | undefined>;\n /** Whether secret masking applies to panel output. Default true. */\n maskSecrets?: boolean;\n /** Idle time after which background samplers stop. Default 60000. */\n inactivityTimeoutMs?: number;\n /** TTL for mutation confirmation nonces. Default 60000. */\n confirmTtlMs?: number;\n /** Directory for heap snapshot files. Defaults to the OS temp dir. */\n heapSnapshotDir?: string;\n}\n\nexport interface NodeUIServer {\n readonly config: NodeUIConfig;\n readonly active: boolean;\n readonly activationReason: string;\n /** Express/Nest compatible middleware that serves the console and API. */\n middleware(): NodeUIMiddleware;\n /** Direct request handling (no `next`); used for tests and adapters. */\n handle(req: IncomingMessage, res: ServerResponse): Promise<void>;\n /** Records a bootstrap timing mark, e.g. `mark(\"listening\")`. */\n mark(name: string): void;\n /** Whether a background-sampling provider is currently running. */\n isProviderActive(id: PanelId): boolean;\n /** Pushes an external log entry into the log viewer (logger adapter). */\n addLogSource(entry: { level: LogLevel; message: string }): void;\n /** Stops all timers and background samplers. */\n shutdown(): void;\n}\n\nexport type NodeUIMiddleware = (\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void,\n) => void;\n\nconst STATIC_ROOT = resolve(__dirname, '..', 'static');\n\nconst SSE_HEARTBEAT_MS = 15_000;\n\n/**\n * Serializes an API envelope to JSON, applying secret masking so values\n * under keys like `TOKEN`/`KEY`/`SECRET`/`PASSWORD` never reach the UI.\n */\nexport function serializeEnvelope<T>(envelope: ApiEnvelope<T>): string {\n return JSON.stringify(maskSecrets(envelope));\n}\n\nfunction pathnameOf(req: IncomingMessage): string {\n const raw = (req as IncomingMessage & { originalUrl?: string }).originalUrl ?? req.url ?? '/';\n try {\n return new URL(raw, 'http://localhost').pathname;\n } catch {\n return raw;\n }\n}\n\nfunction normalizePath(value: string): string {\n const trimmed = value.trim();\n if (!trimmed.startsWith('/')) throw new Error(`NODEUI_PATH must start with \"/\", got \"${value}\"`);\n return trimmed.replace(/\\/+$/, '') || '/';\n}\n\nfunction positiveInt(value: number | undefined, fallback: number, name: string): number {\n const n = value === undefined ? fallback : value;\n if (!Number.isInteger(n) || n <= 0) throw new Error(`${name} must be a positive integer`);\n return n;\n}\n\nexport function createNodeUI(options: NodeUIOptions = {}): NodeUIServer {\n const env = options.env ?? process.env;\n\n const activation =\n options.enabled !== undefined\n ? {\n active: options.enabled,\n reason: options.enabled\n ? 'explicitly enabled via options'\n : 'explicitly disabled via options',\n }\n : resolveActivation(env);\n\n const path = normalizePath(options.path ?? env.NODEUI_PATH ?? '/nodeui');\n const host = options.host ?? env.NODEUI_HOST ?? '127.0.0.1';\n const config: NodeUIConfig = {\n path,\n host,\n port: options.port ?? (env.NODEUI_PORT ? Number(env.NODEUI_PORT) : 0),\n requestLogSize: positiveInt(\n options.requestLogSize,\n env.NODEUI_REQUEST_LOG_SIZE ? Number(env.NODEUI_REQUEST_LOG_SIZE) : 500,\n 'NODEUI_REQUEST_LOG_SIZE',\n ),\n logSize: positiveInt(\n options.logSize,\n env.NODEUI_LOG_SIZE ? Number(env.NODEUI_LOG_SIZE) : DEFAULT_LOG_SIZE,\n 'NODEUI_LOG_SIZE',\n ),\n pollIntervalMs: positiveInt(\n options.pollIntervalMs,\n env.NODEUI_POLL_INTERVAL_MS ? Number(env.NODEUI_POLL_INTERVAL_MS) : 2000,\n 'NODEUI_POLL_INTERVAL_MS',\n ),\n enabled: activation.active,\n activationReason: activation.reason,\n maskSecrets: options.maskSecrets ?? true,\n inactivityTimeoutMs: positiveInt(\n options.inactivityTimeoutMs,\n env.NODEUI_INACTIVITY_TIMEOUT_MS ? Number(env.NODEUI_INACTIVITY_TIMEOUT_MS) : 60_000,\n 'NODEUI_INACTIVITY_TIMEOUT_MS',\n ),\n confirmTtlMs: positiveInt(\n options.confirmTtlMs,\n env.NODEUI_CONFIRM_TTL_MS ? Number(env.NODEUI_CONFIRM_TTL_MS) : 60_000,\n 'NODEUI_CONFIRM_TTL_MS',\n ),\n heapSnapshotDir: options.heapSnapshotDir ?? env.NODEUI_HEAP_SNAPSHOT_DIR ?? tmpdir(),\n };\n\n if (!isLoopbackAddress(config.host)) {\n console.warn(\n `[nodeui] NODEUI_HOST is set to non-loopback \"${config.host}\". ` +\n 'The developer console will be reachable from other hosts; only do this deliberately.',\n );\n }\n\n const ctx: ProviderContext = { config, env, store: {} };\n if (options.config !== undefined) {\n ctx.store['app-config'] = options.config;\n }\n const registry = new ProviderRegistry();\n registry.register(new HealthProvider());\n registry.register(new MemoryProvider());\n registry.register(new CpuProvider());\n registry.register(new EventLoopLagProvider());\n registry.register(new HeapSnapshotProvider());\n\n const startupTracker = new StartupTracker();\n startupTracker.mark('nodeui.init');\n const startupProvider: NodeUIProvider<StartupData> = {\n id: 'startup',\n get: () => ({ ok: true, data: startupTracker.getData() }),\n };\n registry.register(startupProvider);\n\n const requestsProvider = new RequestsProvider(config.requestLogSize);\n registry.register(requestsProvider);\n\n const metricsProvider = new MetricsProvider();\n registry.register(metricsProvider);\n\n const logsProvider = new LogsProvider(config.logSize);\n registry.register(logsProvider);\n\n const envProvider = new EnvProvider();\n registry.register(envProvider);\n const routesProvider = new RoutesProvider();\n registry.register(routesProvider);\n\n const confirmations = new ConfirmationStore(config.confirmTtlMs);\n const activeProviders = new Set<PanelId>();\n let inactivityTimer: NodeJS.Timeout | null = null;\n\n function stopAll(): void {\n for (const id of activeProviders) {\n registry.get(id)?.stop?.(ctx);\n }\n activeProviders.clear();\n if (inactivityTimer) {\n clearTimeout(inactivityTimer);\n inactivityTimer = null;\n }\n }\n\n function resetInactivityTimer(): void {\n if (inactivityTimer) clearTimeout(inactivityTimer);\n inactivityTimer = setTimeout(stopAll, config.inactivityTimeoutMs);\n if (typeof inactivityTimer.unref === 'function') inactivityTimer.unref();\n }\n\n function ensureActive(id: PanelId): void {\n if (!activeProviders.has(id)) {\n registry.get(id)?.start?.(ctx);\n activeProviders.add(id);\n }\n resetInactivityTimer();\n }\n\n function sendJson(res: ServerResponse, status: number, envelope: ApiEnvelope<unknown>): void {\n const body = serializeEnvelope(envelope);\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n 'Cache-Control': 'no-store',\n });\n res.end(body);\n }\n\n function notFound(res: ServerResponse): void {\n sendJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Not found' } });\n }\n\n function isUnderPath(urlPath: string): boolean {\n return urlPath === path || urlPath.startsWith(`${path}/`);\n }\n\n async function servePanel(\n res: ServerResponse,\n id: PanelId,\n query?: Record<string, string>,\n ): Promise<void> {\n ensureActive(id);\n const provider = registry.get(id);\n if (!provider) return notFound(res);\n const requestCtx = query ? { ...ctx, query } : ctx;\n const result = (await provider.get(requestCtx)) as ProviderResult<unknown>;\n sendJson(res, result.ok ? 200 : 500, result);\n }\n\n function configData(): ConfigData {\n return {\n enabled: config.enabled,\n activationReason: config.activationReason,\n path: config.path,\n host: config.host,\n port: config.port,\n requestLogSize: config.requestLogSize,\n logSize: config.logSize,\n pollIntervalMs: config.pollIntervalMs,\n panels: registry.ids(),\n masking: { enabled: config.maskSecrets, pattern: SECRET_KEY_PATTERN.source },\n };\n }\n\n function issueConfirmation(res: ServerResponse): void {\n const issued: ConfirmIssued = confirmations.issue();\n sendJson(res, 200, { ok: true, data: issued });\n }\n\n async function takeHeapSnapshot(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const header = req.headers['x-nodeui-confirm'];\n const nonce = Array.isArray(header) ? header[0] : header;\n if (typeof nonce !== 'string' || !confirmations.consume(nonce)) {\n sendJson(res, 409, {\n ok: false,\n error: {\n code: 'confirmation-required',\n message:\n 'This mutating action needs a fresh confirmation nonce. Issue one via ' +\n 'POST ' +\n `${path}/api/confirmations` +\n ' and repeat the request with an x-nodeui-confirm header.',\n },\n });\n return;\n }\n const heapProvider = registry.get('heap-snapshot');\n if (!heapProvider || !('takeSnapshot' in heapProvider)) return notFound(res);\n const result = (await (heapProvider as HeapSnapshotProvider).takeSnapshot(\n ctx,\n )) as ProviderResult<HeapSnapshotData>;\n sendJson(res, result.ok ? 200 : 500, result);\n }\n\n async function handleLive(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const search = new URL(req.url ?? '/', 'http://localhost').searchParams;\n const requested = (search.get('panels') ?? '').split(',').filter(Boolean);\n const panels = (\n requested.length > 0\n ? requested.filter((id): id is PanelId => registry.get(id as PanelId) !== undefined)\n : registry.ids()\n ).filter((value, index, self) => self.indexOf(value) === index);\n\n if (panels.length === 0) return notFound(res);\n\n const stream = startSse(res);\n const push = async (): Promise<void> => {\n for (const id of panels) {\n ensureActive(id);\n const provider = registry.get(id);\n if (!provider) continue;\n const result = (await provider.get(ctx)) as ProviderResult<unknown>;\n stream.send({ panel: id, envelope: result });\n }\n };\n void push();\n const pushTimer = setInterval(() => void push(), config.pollIntervalMs);\n const heartbeatTimer = setInterval(() => stream.heartbeat(), SSE_HEARTBEAT_MS);\n if (typeof pushTimer.unref === 'function') pushTimer.unref();\n if (typeof heartbeatTimer.unref === 'function') heartbeatTimer.unref();\n\n const cleanup = (): void => {\n clearInterval(pushTimer);\n clearInterval(heartbeatTimer);\n stream.close();\n };\n req.on('close', cleanup);\n res.on('close', cleanup);\n }\n\n async function handleApi(\n req: IncomingMessage,\n res: ServerResponse,\n apiPath: string,\n ): Promise<void> {\n const method = (req.method ?? 'GET').toUpperCase();\n if (method === 'GET' && apiPath === '/config') {\n sendJson(res, 200, { ok: true, data: configData() });\n return;\n }\n if (method === 'GET' && apiPath === '/live') {\n return handleLive(req, res);\n }\n if (method === 'GET') {\n const panel = registry.get(apiPath.slice(1) as PanelId);\n if (panel) {\n const query = Object.fromEntries(new URL(req.url ?? '/', 'http://localhost').searchParams);\n return servePanel(res, panel.id, query);\n }\n }\n if (method === 'POST') {\n if (apiPath === '/confirmations') {\n issueConfirmation(res);\n return;\n }\n if (apiPath === '/heap-snapshot') {\n return takeHeapSnapshot(req, res);\n }\n }\n notFound(res);\n }\n\n function serveStatic(req: IncomingMessage, res: ServerResponse, urlPath: string): void {\n const relative = urlPath.slice(path.length);\n const asset = resolveStaticAsset(STATIC_ROOT, relative);\n if (!asset) {\n sendJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Not found' } });\n return;\n }\n res.writeHead(200, {\n 'Content-Type': asset.contentType,\n 'Content-Length': asset.length,\n 'Cache-Control': 'no-cache',\n });\n asset.content.pipe(res);\n }\n\n function guardLoopback(req: IncomingMessage, res: ServerResponse): boolean {\n if (isLoopbackAddress(config.host)) {\n const remote = req.socket.remoteAddress;\n if (!isLoopbackAddress(remote)) {\n sendJson(res, 403, {\n ok: false,\n error: {\n code: 'forbidden',\n message: 'nodeui is loopback-only; requests from other hosts are rejected',\n },\n });\n return false;\n }\n }\n return true;\n }\n\n async function handleNodeUIPath(\n req: IncomingMessage,\n res: ServerResponse,\n urlPath: string,\n ): Promise<void> {\n if (!guardLoopback(req, res)) return;\n const apiBase = `${path}/api`;\n if (urlPath === apiBase || urlPath.startsWith(`${apiBase}/`)) {\n await handleApi(req, res, urlPath.slice(apiBase.length));\n return;\n }\n serveStatic(req, res, urlPath);\n }\n\n function captureRouter(req: IncomingMessage): void {\n if (ctx.store['express-router'] !== undefined) return;\n const app = (req as IncomingMessage & { app?: { _router?: unknown; router?: unknown } }).app;\n if (!app) return;\n let router: unknown;\n try {\n // Express 4 and 5 both expose the router on `_router` once the app has\n // handled a request. Reading it directly avoids Express 4's deprecated\n // `app.router` getter, which throws on access.\n router = app._router;\n } catch {\n router = undefined;\n }\n if (router === undefined) {\n try {\n router = app.router;\n } catch {\n router = undefined;\n }\n }\n if (router) {\n ctx.store['express-router'] = router;\n }\n }\n\n function recordAppRequest(req: IncomingMessage, res: ServerResponse): void {\n const started = process.hrtime.bigint();\n const timestampMs = Date.now();\n res.on('finish', () => {\n const durationMs = Number(process.hrtime.bigint() - started) / 1e6;\n requestsProvider.record({\n method: req.method ?? '?',\n path: pathnameOf(req),\n status: res.statusCode,\n durationMs,\n timestampMs,\n ip: req.socket.remoteAddress ?? 'unknown',\n });\n metricsProvider.record(res.statusCode);\n });\n }\n\n const server: NodeUIServer = {\n get config() {\n return config;\n },\n get active() {\n return config.enabled;\n },\n get activationReason() {\n return config.activationReason;\n },\n middleware(): NodeUIMiddleware {\n return (req, res, next) => {\n captureRouter(req);\n if (!config.enabled) {\n next();\n return;\n }\n const urlPath = pathnameOf(req);\n if (isUnderPath(urlPath)) {\n void handleNodeUIPath(req, res, urlPath);\n return;\n }\n recordAppRequest(req, res);\n next();\n };\n },\n async handle(req, res): Promise<void> {\n captureRouter(req);\n if (!config.enabled) {\n notFound(res);\n return;\n }\n const urlPath = pathnameOf(req);\n if (isUnderPath(urlPath)) {\n await handleNodeUIPath(req, res, urlPath);\n return;\n }\n notFound(res);\n },\n mark(name: string): void {\n startupTracker.mark(name);\n },\n isProviderActive(id: PanelId): boolean {\n return activeProviders.has(id);\n },\n addLogSource(entry: { level: LogLevel; message: string }): void {\n logsProvider.addSource(entry);\n },\n shutdown(): void {\n stopAll();\n },\n };\n\n return server;\n}\n"],"mappings":"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","names":["stat","ZEROED","defaultClock","ZEROED","import_node_os","import_node_path","import_node_os","import_node_path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/ring-buffer.ts","../src/safety.ts","../src/guard.ts","../src/confirmations.ts","../src/registry.ts","../src/static.ts","../src/providers/sampler.ts","../src/providers/startup-tracker.ts","../src/providers/memory.ts","../src/providers/cpu.ts","../src/providers/event-loop.ts","../src/providers/health.ts","../src/providers/outgoing.ts","../src/providers/heap-snapshot.ts","../src/providers/requests.ts","../src/providers/metrics.ts","../src/providers/env.ts","../src/providers/routes.ts","../src/providers/logs.ts","../src/sse.ts","../src/server.ts"],"sourcesContent":["/**\r\n * @singhak/nodeui-core — framework-neutral engine and safety gate for the NodeUI\r\n * developer console. Serves the bundled React console and the observability\r\n * REST API to loopback-only, dev-activated Node.js applications.\r\n */\r\n\r\nexport * from './types';\r\nexport * from './constants';\r\nexport * from './ring-buffer';\r\nexport * from './safety';\r\nexport { createGuard, AUTH_COOKIE, type GuardOptions, type GuardResult } from './guard';\r\nexport * from './confirmations';\r\nexport * from './registry';\r\nexport * from './static';\r\nexport * from './providers';\r\nexport { startSse, type SseStream } from './sse';\r\nexport { createNodeUI, serializeEnvelope } from './server';\r\nexport type { NodeUIOptions, NodeUIServer, NodeUIMiddleware } from './server';\r\n","/** Replacement value used when masking secret values. */\nexport const SECRET_MASKED = '[REDACTED]';\n\n/** Default ring buffer capacity for the log viewer. */\nexport const DEFAULT_LOG_SIZE = 500;\n","/**\n * Fixed-size ring buffer. When full, pushing drops the oldest item.\n * Used for the HTTP request log to keep memory bounded.\n */\nexport class RingBuffer<T> {\n private buffer: Array<T | undefined>;\n private head = 0;\n private count = 0;\n readonly capacity: number;\n\n constructor(size: number) {\n if (!Number.isInteger(size) || size <= 0) {\n throw new Error('RingBuffer size must be a positive integer');\n }\n this.capacity = size;\n this.buffer = new Array<T | undefined>(size);\n }\n\n push(item: T): void {\n this.buffer[(this.head + this.count) % this.capacity] = item;\n if (this.count < this.capacity) {\n this.count += 1;\n } else {\n this.head = (this.head + 1) % this.capacity;\n }\n }\n\n get length(): number {\n return this.count;\n }\n\n toArray(): T[] {\n return this.slice(0, this.count);\n }\n\n /**\n * Returns up to `limit` items starting at `offset`, oldest first.\n */\n slice(offset = 0, limit?: number): T[] {\n const capped = limit === undefined ? this.count : Math.max(0, limit);\n const out: T[] = [];\n for (let i = 0; i < this.count && out.length < capped; i += 1) {\n if (i >= offset) out.push(this.buffer[(this.head + i) % this.capacity] as T);\n }\n return out;\n }\n}\n","import { SECRET_MASKED } from './constants';\r\n\r\n/** Keys whose values are redacted in any panel output. */\r\nexport const SECRET_KEY_PATTERN =\r\n /token|key|secret|passw(?:or)?d|credential|auth|cookie|dsn|private|signature|session|bearer|connection.?string|database.?url|conn.?str/i;\r\n\r\n/** Secret-looking `name=value` / `\"name\":\"value\"` pairs inside free text. */\r\nconst TEXT_PAIR_PATTERN =\r\n /\\b([\\w.-]*(?:token|secret|passw(?:or)?d|api[_-]?key|credential|authorization|cookie|dsn)[\\w.-]*[\"']?)(\\s*[=:]\\s*)(\"[^\"]*\"|'[^']*'|[^\\s,;&\"'}]+)/gi;\r\nconst URL_CREDENTIALS_PATTERN = /([a-z][a-z0-9+.-]*:\\/\\/[^\\s:/@]+:)[^\\s@/]+(@)/gi;\r\nconst BEARER_PATTERN = /\\b(Bearer|Basic)\\s+[A-Za-z0-9\\-._~+/]+=*/gi;\r\nconst JWT_PATTERN = /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]*/g;\r\n\r\n/**\r\n * Redacts secrets embedded in free text: credentials in URLs\r\n * (`postgres://user:pass@host`), bearer/basic tokens, JWTs and\r\n * `password=...`-style pairs. Used for log lines and any string value.\r\n */\r\nexport function maskSecretText(text: string): string {\r\n if (text.length < 6) return text;\r\n return text\r\n .replace(URL_CREDENTIALS_PATTERN, `$1${SECRET_MASKED}$2`)\r\n .replace(BEARER_PATTERN, `$1 ${SECRET_MASKED}`)\r\n .replace(JWT_PATTERN, SECRET_MASKED)\r\n .replace(TEXT_PAIR_PATTERN, `$1$2${SECRET_MASKED}`);\r\n}\r\n\r\nexport interface ActivationDecision {\r\n active: boolean;\r\n reason: string;\r\n}\r\n\r\n/**\r\n * Safety-gate activation. The console is active when `NODEUI_ENABLED=true`\r\n * or when `NODE_ENV` is not `production`. `NODEUI_ENABLED=false` explicitly\r\n * disables it everywhere (including development). Otherwise it fails closed.\r\n */\r\nexport function resolveActivation(env: Record<string, string | undefined>): ActivationDecision {\r\n if (env.NODEUI_ENABLED === 'true') {\r\n return { active: true, reason: 'activated by NODEUI_ENABLED=true' };\r\n }\r\n if (env.NODEUI_ENABLED === 'false') {\r\n return { active: false, reason: 'inactive: explicitly disabled by NODEUI_ENABLED=false' };\r\n }\r\n if (env.NODE_ENV !== 'production') {\r\n return {\r\n active: true,\r\n reason: `activated by NODE_ENV=\"${env.NODE_ENV ?? 'unset'}\" (non-production)`,\r\n };\r\n }\r\n return {\r\n active: false,\r\n reason: \"inactive: NODE_ENV=production and NODEUI_ENABLED is not 'true' (fail-closed)\",\r\n };\r\n}\r\n\r\n/** True when the socket address is a loopback address. */\r\nexport function isLoopbackAddress(address: string | undefined): boolean {\r\n if (!address) return false;\r\n return (\r\n address.startsWith('127.') ||\r\n address === '::1' ||\r\n address.startsWith('::ffff:127.') ||\r\n address === 'localhost'\r\n );\r\n}\r\n\r\n/** Strips the IPv4-mapped IPv6 prefix so `::ffff:10.0.0.1` compares as `10.0.0.1`. */\r\nfunction normalizeIp(address: string): string {\r\n return address.startsWith('::ffff:') ? address.slice(7) : address;\r\n}\r\n\r\nfunction ipv4ToInt(ip: string): number | null {\r\n const parts = ip.split('.');\r\n if (parts.length !== 4) return null;\r\n let out = 0;\r\n for (const part of parts) {\r\n const n = Number(part);\r\n if (!Number.isInteger(n) || n < 0 || n > 255) return null;\r\n out = out * 256 + n;\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * True when `address` matches one of `allowed`, each an exact IP or an IPv4\r\n * CIDR (e.g. `172.17.0.0/16`, the default Docker bridge network).\r\n */\r\nexport function matchesAddress(address: string | undefined, allowed: readonly string[]): boolean {\r\n if (!address) return false;\r\n const ip = normalizeIp(address);\r\n for (const entry of allowed) {\r\n if (!entry.includes('/')) {\r\n if (normalizeIp(entry) === ip) return true;\r\n continue;\r\n }\r\n const [base, bitsRaw] = entry.split('/');\r\n const bits = Number(bitsRaw);\r\n const a = ipv4ToInt(ip);\r\n const b = ipv4ToInt(base ?? '');\r\n if (a === null || b === null || !Number.isInteger(bits) || bits < 0 || bits > 32) continue;\r\n const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;\r\n if ((a & mask) >>> 0 === (b & mask) >>> 0) return true;\r\n }\r\n return false;\r\n}\r\n\r\n/** Extracts the lower-cased hostname from a `Host` header value (no port). */\r\nexport function hostnameFromHostHeader(value: string | undefined): string | null {\r\n if (!value) return null;\r\n const trimmed = value.trim().toLowerCase();\r\n if (trimmed.startsWith('[')) {\r\n const end = trimmed.indexOf(']');\r\n return end === -1 ? null : trimmed.slice(1, end);\r\n }\r\n return trimmed.split(':')[0] || null;\r\n}\r\n\r\n/** True for the hostnames a browser uses to reach a loopback server. */\r\nexport function isLoopbackHostname(hostname: string | null): boolean {\r\n if (!hostname) return false;\r\n return hostname === 'localhost' || hostname.endsWith('.localhost') || isLoopbackAddress(hostname);\r\n}\r\n\r\n/**\r\n * Deep-clone a value, replacing any value under a key matching the secret\r\n * pattern with `[REDACTED]` and scrubbing secrets embedded in string values\r\n * (URL credentials, bearer tokens, `password=...`). The input is not mutated.\r\n */\r\nexport function maskSecrets<T>(value: T, pattern: RegExp = SECRET_KEY_PATTERN): T {\r\n if (typeof value === 'string') {\r\n return maskSecretText(value) as T;\r\n }\r\n if (value !== null && typeof value === 'object') {\r\n if (Array.isArray(value)) {\r\n return value.map((v) => maskSecrets(v, pattern)) as T;\r\n }\r\n const record = value as Record<string, unknown>;\r\n const keys = Object.keys(record);\r\n if (\r\n keys.length === 2 &&\r\n keys.includes('key') &&\r\n keys.includes('value') &&\r\n typeof record.key === 'string'\r\n ) {\r\n const masked = pattern.test(record.key) ? SECRET_MASKED : record.value;\r\n return { key: record.key, value: maskSecrets(masked, pattern) } as T;\r\n }\r\n const out: Record<string, unknown> = {};\r\n for (const [key, v] of Object.entries(value)) {\r\n if (pattern.test(key)) {\r\n out[key] = SECRET_MASKED;\r\n } else {\r\n out[key] = maskSecrets(v, pattern);\r\n }\r\n }\r\n return out as T;\r\n }\r\n return value;\r\n}\r\n","import { createHash, timingSafeEqual } from 'node:crypto';\r\nimport type { IncomingMessage } from 'node:http';\r\nimport {\r\n hostnameFromHostHeader,\r\n isLoopbackAddress,\r\n isLoopbackHostname,\r\n matchesAddress,\r\n} from './safety';\r\n\r\nexport const AUTH_COOKIE = 'nodeui_token';\r\n\r\nexport interface GuardOptions {\r\n /** Configured console host; a loopback value enables the strict checks. */\r\n host: string;\r\n /** Extra `Host` header hostnames accepted (e.g. `myapp.local`). */\r\n allowedHosts: readonly string[];\r\n /** Extra `Origin` values accepted for cross-origin calls. */\r\n allowedOrigins: readonly string[];\r\n /** Extra remote IPs / IPv4 CIDRs accepted (e.g. `172.17.0.0/16` for Docker). */\r\n allowedRemoteAddresses: readonly string[];\r\n /** Accept requests carrying `X-Forwarded-*` headers. Default false. */\r\n trustProxy: boolean;\r\n /** When set, every request must present this token. */\r\n authToken?: string;\r\n}\r\n\r\nexport type GuardResult =\r\n { ok: true; setToken?: string } | { ok: false; status: number; code: string; message: string };\r\n\r\nconst FORWARD_HEADERS = ['x-forwarded-for', 'x-forwarded-host', 'forwarded', 'x-real-ip'];\r\n\r\nfunction digest(value: string): Buffer {\r\n return createHash('sha256').update(value).digest();\r\n}\r\n\r\nfunction tokenEquals(a: string, b: string): boolean {\r\n return timingSafeEqual(digest(a), digest(b));\r\n}\r\n\r\nfunction readCookie(header: string | undefined, name: string): string | undefined {\r\n if (!header) return undefined;\r\n for (const part of header.split(';')) {\r\n const [k, ...rest] = part.trim().split('=');\r\n if (k === name) {\r\n try {\r\n return decodeURIComponent(rest.join('='));\r\n } catch {\r\n return undefined;\r\n }\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction firstHeader(value: string | string[] | undefined): string | undefined {\r\n return Array.isArray(value) ? value[0] : value;\r\n}\r\n\r\nfunction deny(status: number, code: string, message: string): GuardResult {\r\n return { ok: false, status, code, message };\r\n}\r\n\r\n/**\r\n * Builds the request guard for the console. It combines: loopback/remote\r\n * address policy, `Host` validation (DNS-rebinding defence), `Origin`\r\n * validation (cross-site request defence) and an optional shared token.\r\n */\r\nexport function createGuard(options: GuardOptions): (req: IncomingMessage) => GuardResult {\r\n const strict = isLoopbackAddress(options.host);\r\n const allowedHosts = options.allowedHosts.map((h) => h.toLowerCase());\r\n const allowedOrigins = options.allowedOrigins.map((o) => o.toLowerCase().replace(/\\/+$/, ''));\r\n\r\n return (req) => {\r\n const remote = req.socket.remoteAddress;\r\n const remoteLoopback = isLoopbackAddress(remote);\r\n const remoteAllowed = remoteLoopback || matchesAddress(remote, options.allowedRemoteAddresses);\r\n\r\n if (strict && !remoteAllowed) {\r\n return deny(\r\n 403,\r\n 'forbidden',\r\n 'nodeui is loopback-only; requests from other hosts are rejected',\r\n );\r\n }\r\n if (!strict && options.allowedRemoteAddresses.length > 0 && !remoteAllowed) {\r\n return deny(403, 'forbidden', 'remote address is not in the nodeui allow-list');\r\n }\r\n\r\n if (strict && !options.trustProxy && !matchesAddress(remote, options.allowedRemoteAddresses)) {\r\n if (FORWARD_HEADERS.some((h) => req.headers[h] !== undefined)) {\r\n return deny(\r\n 403,\r\n 'forbidden',\r\n 'proxied requests are rejected (set trustProxy to allow forwarded traffic)',\r\n );\r\n }\r\n }\r\n\r\n const hostHeader = firstHeader(req.headers.host);\r\n const hostname = hostnameFromHostHeader(hostHeader);\r\n if (hostname !== null) {\r\n const hostOk =\r\n isLoopbackHostname(hostname) ||\r\n allowedHosts.includes(hostname) ||\r\n (!strict && allowedHosts.length === 0);\r\n if (!hostOk) {\r\n return deny(\r\n 403,\r\n 'forbidden',\r\n `Host \"${hostname}\" is not allowed (add it to allowedHosts to use nodeui via this name)`,\r\n );\r\n }\r\n }\r\n\r\n const origin = firstHeader(req.headers.origin);\r\n if (origin !== undefined) {\r\n let originOk = false;\r\n const normalized = origin.toLowerCase().replace(/\\/+$/, '');\r\n if (allowedOrigins.includes(normalized)) {\r\n originOk = true;\r\n } else {\r\n try {\r\n originOk = hostHeader !== undefined && new URL(origin).host === hostHeader.toLowerCase();\r\n } catch {\r\n originOk = false;\r\n }\r\n }\r\n if (!originOk) {\r\n return deny(403, 'forbidden', 'cross-origin requests to nodeui are rejected');\r\n }\r\n }\r\n\r\n if (options.authToken) {\r\n const authHeader = firstHeader(req.headers.authorization);\r\n const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;\r\n const queryToken = new URL(req.url ?? '/', 'http://localhost').searchParams.get('token');\r\n const cookieToken = readCookie(firstHeader(req.headers.cookie), AUTH_COOKIE);\r\n const candidates = [\r\n bearer,\r\n firstHeader(req.headers['x-nodeui-token']),\r\n cookieToken,\r\n queryToken ?? undefined,\r\n ];\r\n const matched = candidates.find((c) => c !== undefined && tokenEquals(c, options.authToken!));\r\n if (matched === undefined) {\r\n return deny(401, 'unauthorized', 'a valid nodeui token is required');\r\n }\r\n if (queryToken !== null && matched === queryToken) {\r\n return { ok: true, setToken: queryToken };\r\n }\r\n }\r\n\r\n return { ok: true };\r\n };\r\n}\r\n","import { randomBytes } from 'node:crypto';\r\nimport type { ConfirmIssued } from './types';\r\n\r\n/**\r\n * Single-use, expiring confirmation nonces. Mutating actions (such as heap\r\n * snapshots) require a nonce issued here, presented via the\r\n * `x-nodeui-confirm` header, which is consumed exactly once.\r\n */\r\nexport class ConfirmationStore {\r\n private nonces = new Map<string, number>();\r\n\r\n constructor(\r\n private readonly ttlMs: number,\r\n private readonly maxOutstanding = 100,\r\n ) {}\r\n\r\n issue(): ConfirmIssued {\r\n this.prune();\r\n while (this.nonces.size >= this.maxOutstanding) {\r\n const oldest = this.nonces.keys().next().value;\r\n if (oldest === undefined) break;\r\n this.nonces.delete(oldest);\r\n }\r\n const nonce = randomBytes(16).toString('hex');\r\n const expiresAtMs = Date.now() + this.ttlMs;\r\n this.nonces.set(nonce, expiresAtMs);\r\n return { nonce, expiresAtMs, ttlMs: this.ttlMs };\r\n }\r\n\r\n /** Consumes `nonce` and returns true only if it was valid and unexpired. */\r\n consume(nonce: string): boolean {\r\n this.prune();\r\n const expiresAt = this.nonces.get(nonce);\r\n if (expiresAt === undefined) return false;\r\n this.nonces.delete(nonce);\r\n return Date.now() <= expiresAt;\r\n }\r\n\r\n /** Number of currently valid nonces (after pruning). */\r\n size(): number {\r\n this.prune();\r\n return this.nonces.size;\r\n }\r\n\r\n /** Removes expired nonces. */\r\n prune(): void {\r\n const now = Date.now();\r\n for (const [nonce, expiresAt] of this.nonces) {\r\n if (expiresAt <= now) this.nonces.delete(nonce);\r\n }\r\n }\r\n}\r\n","import type { NodeUIProvider, PanelId } from './types';\n\n/** Ordered registry of the standard panel providers. */\nexport class ProviderRegistry {\n private providers = new Map<PanelId, NodeUIProvider>();\n\n register(provider: NodeUIProvider): void {\n this.providers.set(provider.id, provider);\n }\n\n get(id: PanelId): NodeUIProvider | undefined {\n return this.providers.get(id);\n }\n\n ids(): PanelId[] {\n return [...this.providers.keys()];\n }\n\n all(): NodeUIProvider[] {\n return [...this.providers.values()];\n }\n}\n","import { createReadStream, existsSync, statSync } from 'node:fs';\nimport type { Readable } from 'node:stream';\nimport { join, resolve, sep } from 'node:path';\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.map': 'application/json; charset=utf-8',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.webmanifest': 'application/manifest+json',\n};\n\nexport interface StaticAsset {\n content: Readable;\n contentType: string;\n length: number;\n}\n\n/**\n * Resolves a URL path to a file inside `rootDir`, serving `index.html` for\n * directories and falling back to `index.html` for unknown paths (SPA).\n * Returns null when the path would escape `rootDir`.\n */\nexport function resolveStaticAsset(rootDir: string, urlPath: string): StaticAsset | null {\n const relative = urlPath === '' ? 'index.html' : urlPath.replace(/^\\/+/, '');\n const candidate = resolve(rootDir, relative);\n const rootWithSep = rootDir.endsWith(sep) ? rootDir : rootDir + sep;\n if (candidate !== rootDir && !candidate.startsWith(rootWithSep)) {\n return null;\n }\n\n let file = candidate;\n if (existsSync(file) && statSync(file).isDirectory()) {\n file = join(file, 'index.html');\n }\n if (!existsSync(file) || !statSync(file).isFile()) {\n file = join(rootDir, 'index.html');\n }\n if (!existsSync(file)) {\n return null;\n }\n\n const ext = file.slice(file.lastIndexOf('.')).toLowerCase();\n const stat = statSync(file);\n return {\n content: createReadStream(file),\n contentType: MIME_TYPES[ext] ?? 'application/octet-stream',\n length: stat.size,\n };\n}\n","export interface SamplerOptions<T> {\n intervalMs: number;\n collect: () => T;\n onSample: (sample: T) => void;\n}\n\n/**\n * Wraps a tracked `setInterval`. Idempotent `start`, guaranteed `stop`,\n * and a `collect` that never throws into the event loop. All timers are\n * cleared on `stop` — no leaks on hot reload.\n */\nexport class Sampler<T> {\n private timer: NodeJS.Timeout | null = null;\n\n constructor(private readonly opts: SamplerOptions<T>) {}\n\n start(): void {\n if (this.timer) return;\n const run = (): void => {\n try {\n this.opts.onSample(this.opts.collect());\n } catch {\n // sampling must never throw into the event loop\n }\n };\n this.timer = setInterval(run, this.opts.intervalMs);\n run();\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n get isRunning(): boolean {\n return this.timer !== null;\n }\n}\n","import type { StartupData, StartupMark } from '../types';\n\n/** Records ordered bootstrap timing marks. */\nexport class StartupTracker {\n private startedAtMs = Date.now();\n private marks: StartupMark[] = [];\n\n mark(name: string): void {\n const atMs = Date.now();\n this.marks.push({ name, atMs, sinceFirstMs: atMs - this.startedAtMs });\n }\n\n getData(): StartupData {\n return { startedAtMs: this.startedAtMs, marks: [...this.marks] };\n }\n\n reset(): void {\n this.startedAtMs = Date.now();\n this.marks = [];\n }\n}\n","import { freemem, totalmem } from 'node:os';\nimport type { MemoryData, NodeUIProvider, ProviderContext } from '../types';\nimport { Sampler } from './sampler';\n\nconst ZEROED: MemoryData = {\n heapUsed: 0,\n heapTotal: 0,\n rss: 0,\n external: 0,\n totalMem: 0,\n freeMem: 0,\n sampleAtMs: 0,\n};\n\n/** Samples `process.memoryUsage()` plus OS totals on the poll interval. */\nexport class MemoryProvider implements NodeUIProvider<MemoryData> {\n readonly id = 'memory' as const;\n\n private sampler: Sampler<MemoryData> | null = null;\n private latest: MemoryData = ZEROED;\n\n private collect(): MemoryData {\n const usage = process.memoryUsage();\n return {\n heapUsed: usage.heapUsed,\n heapTotal: usage.heapTotal,\n rss: usage.rss,\n external: usage.external,\n totalMem: totalmem(),\n freeMem: freemem(),\n sampleAtMs: Date.now(),\n };\n }\n\n start(ctx: ProviderContext): void {\n if (this.sampler) return;\n this.sampler = new Sampler({\n intervalMs: ctx.config.pollIntervalMs,\n collect: () => this.collect(),\n onSample: (sample) => {\n this.latest = sample;\n ctx.store[this.id] = sample;\n },\n });\n this.sampler.start();\n }\n\n stop(): void {\n this.sampler?.stop();\n this.sampler = null;\n }\n\n get(): { ok: true; data: MemoryData } {\n return { ok: true, data: this.latest };\n }\n}\n","import type { CpuData, NodeUIProvider, ProviderContext } from '../types';\nimport { Sampler } from './sampler';\n\nexport interface CpuUsageSnapshot {\n user: number;\n system: number;\n}\n\n/**\n * Injectable clock so CPU delta math is unit-testable. `nowUs` returns the\n * wall clock in microseconds; `cpuUsage` returns cumulative microseconds of\n * user/system CPU time since process start (like `process.cpuUsage()`).\n */\nexport interface CpuClock {\n nowUs: () => bigint;\n cpuUsage: () => CpuUsageSnapshot;\n /** Test hook: advance to the next scripted step. */\n tick?: () => void;\n}\n\nconst defaultClock: CpuClock = {\n nowUs: () => process.hrtime.bigint() / 1000n,\n cpuUsage: () => process.cpuUsage(),\n};\n\nconst ZEROED: CpuData = { userPercent: 0, systemPercent: 0, totalPercent: 0, sampleAtMs: 0 };\n\ninterface CpuPoint {\n cpu: CpuUsageSnapshot;\n atUs: bigint;\n}\n\nfunction clampPercent(v: number): number {\n return Math.min(100, Math.max(0, v));\n}\n\n/** Computes CPU usage percent via deltas of `process.cpuUsage()`. */\nexport class CpuProvider implements NodeUIProvider<CpuData> {\n readonly id = 'cpu' as const;\n\n private sampler: Sampler<CpuData> | null = null;\n private latest: CpuData = ZEROED;\n private last: CpuPoint | null = null;\n\n constructor(private readonly clock: CpuClock = defaultClock) {}\n\n /** Runs one sampling step immediately; called by the sampler on each tick. */\n collect(): CpuData {\n const cpu = this.clock.cpuUsage();\n const atUs = this.clock.nowUs();\n let sample: CpuData = { ...ZEROED, sampleAtMs: Date.now() };\n if (this.last) {\n const userUs = cpu.user - this.last.cpu.user;\n const systemUs = cpu.system - this.last.cpu.system;\n const elapsedUs = Number(atUs - this.last.atUs);\n if (elapsedUs > 0) {\n sample = {\n userPercent: clampPercent((userUs / elapsedUs) * 100),\n systemPercent: clampPercent((systemUs / elapsedUs) * 100),\n totalPercent: clampPercent(((userUs + systemUs) / elapsedUs) * 100),\n sampleAtMs: Date.now(),\n };\n }\n }\n this.last = { cpu, atUs };\n this.latest = sample;\n return sample;\n }\n\n start(ctx: ProviderContext): void {\n if (this.sampler) return;\n this.sampler = new Sampler({\n intervalMs: ctx.config.pollIntervalMs,\n collect: () => this.collect(),\n onSample: (sample) => {\n this.latest = sample;\n ctx.store[this.id] = sample;\n },\n });\n this.sampler.start();\n }\n\n stop(): void {\n this.sampler?.stop();\n this.sampler = null;\n }\n\n get(): { ok: true; data: CpuData } {\n return { ok: true, data: this.latest };\n }\n}\n","import type { EventLoopSample, NodeUIProvider, ProviderContext } from '../types';\nimport { Sampler } from './sampler';\n\nexport interface EventLoopClock {\n /** Current time in nanoseconds. */\n nowNs: () => bigint;\n}\n\nconst defaultClock: EventLoopClock = {\n nowNs: () => process.hrtime.bigint(),\n};\n\nconst MAX_KEPT_SAMPLES = 600;\n\nconst ZEROED: EventLoopSample = {\n currentMs: 0,\n maxMs: 0,\n avgMs: 0,\n count: 0,\n sampleAtMs: 0,\n};\n\n/**\n * Measures event-loop lag from a background timer: each tick reports how far\n * behind the nominal interval the loop actually was. Keeps a rolling window\n * of samples so the panel can show current/max/average lag.\n */\nexport class EventLoopLagProvider implements NodeUIProvider<EventLoopSample> {\n readonly id = 'event-loop' as const;\n\n private sampler: Sampler<EventLoopSample> | null = null;\n private samples: number[] = [];\n private latest: EventLoopSample = ZEROED;\n private lastFireNs: bigint | null = null;\n\n constructor(private readonly clock: EventLoopClock = defaultClock) {}\n\n /** Runs one measurement tick; called by the sampler on each interval. */\n collect(intervalMs: number): EventLoopSample {\n const nowNs = this.clock.nowNs();\n let lagMs = 0;\n if (this.lastFireNs !== null) {\n const elapsedMs = Number(nowNs - this.lastFireNs) / 1e6;\n lagMs = Math.max(0, elapsedMs - intervalMs);\n }\n this.lastFireNs = nowNs;\n\n this.samples.push(lagMs);\n if (this.samples.length > MAX_KEPT_SAMPLES) this.samples.shift();\n\n const count = this.samples.length;\n const total = this.samples.reduce((acc, v) => acc + v, 0);\n const sample: EventLoopSample = {\n currentMs: lagMs,\n maxMs: Math.max(...this.samples),\n avgMs: count > 0 ? total / count : 0,\n count,\n sampleAtMs: Date.now(),\n };\n this.latest = sample;\n return sample;\n }\n\n start(ctx: ProviderContext): void {\n if (this.sampler) return;\n this.lastFireNs = this.clock.nowNs();\n this.sampler = new Sampler({\n intervalMs: ctx.config.pollIntervalMs,\n collect: () => this.collect(ctx.config.pollIntervalMs),\n onSample: (sample) => {\n this.latest = sample;\n ctx.store[this.id] = sample;\n },\n });\n this.sampler.start();\n }\n\n stop(): void {\n this.sampler?.stop();\n this.sampler = null;\n }\n\n get(): { ok: true; data: EventLoopSample } {\n return { ok: true, data: this.latest };\n }\n}\n","import type {\r\n EventLoopSample,\r\n HealthCheckResult,\r\n HealthData,\r\n MemoryData,\r\n NodeUIProvider,\r\n ProviderContext,\r\n} from '../types';\r\n\r\nconst LAG_OK_MS = 50;\r\nconst LAG_DEGRADED_MS = 200;\r\nconst CHECK_TIMEOUT_MS = 3000;\r\nconst CHECK_CACHE_MS = 5000;\r\n\r\ntype StoredMemory = Partial<MemoryData>;\r\ntype StoredEventLoop = Partial<EventLoopSample>;\r\n\r\n/**\r\n * A dependency check (database ping, cache ping, ...). Resolve for healthy,\r\n * reject/throw (or resolve `false`) for unhealthy.\r\n */\r\nexport type HealthCheck = () => unknown | Promise<unknown>;\r\n\r\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\r\n return new Promise<T>((resolve, reject) => {\r\n const timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);\r\n promise.then(\r\n (value) => {\r\n clearTimeout(timer);\r\n resolve(value);\r\n },\r\n (err: unknown) => {\r\n clearTimeout(timer);\r\n reject(err instanceof Error ? err : new Error(String(err)));\r\n },\r\n );\r\n });\r\n}\r\n\r\n/** Derives process + event-loop health, plus optional dependency checks. */\r\nexport class HealthProvider implements NodeUIProvider<HealthData> {\r\n readonly id = 'health' as const;\r\n\r\n private cache: { at: number; results: HealthCheckResult[] } | null = null;\r\n\r\n constructor(private readonly checks: Record<string, HealthCheck> = {}) {}\r\n\r\n private async runChecks(): Promise<HealthCheckResult[]> {\r\n const names = Object.keys(this.checks);\r\n if (names.length === 0) return [];\r\n if (this.cache && Date.now() - this.cache.at < CHECK_CACHE_MS) return this.cache.results;\r\n const results = await Promise.all(\r\n names.map(async (name): Promise<HealthCheckResult> => {\r\n const started = Date.now();\r\n try {\r\n const outcome = await withTimeout(\r\n Promise.resolve().then(() => this.checks[name]!()),\r\n CHECK_TIMEOUT_MS,\r\n );\r\n if (outcome === false) throw new Error('check returned false');\r\n return { name, status: 'up', durationMs: Date.now() - started };\r\n } catch (err) {\r\n return {\r\n name,\r\n status: 'down',\r\n durationMs: Date.now() - started,\r\n error: err instanceof Error ? err.message : String(err),\r\n };\r\n }\r\n }),\r\n );\r\n this.cache = { at: Date.now(), results };\r\n return results;\r\n }\r\n\r\n async get(ctx: ProviderContext): Promise<{ ok: true; data: HealthData }> {\r\n const loop = ctx.store['event-loop'] as StoredEventLoop | undefined;\r\n const memory = ctx.store.memory as StoredMemory | undefined;\r\n const lagMs = typeof loop?.currentMs === 'number' ? loop.currentMs : null;\r\n const totalMem = typeof memory?.totalMem === 'number' ? memory.totalMem : 0;\r\n const rss = typeof memory?.rss === 'number' ? memory.rss : 0;\r\n const memoryUsedPercent = totalMem > 0 ? (rss / totalMem) * 100 : null;\r\n const checks = await this.runChecks();\r\n\r\n let status: HealthData['status'];\r\n let statusReason: string;\r\n if (lagMs === null) {\r\n status = 'unknown';\r\n statusReason = 'no event-loop samples yet (open the Event-loop panel)';\r\n } else if (lagMs < LAG_OK_MS) {\r\n status = 'ok';\r\n statusReason = `event-loop lag ${lagMs.toFixed(1)}ms below ${LAG_OK_MS}ms`;\r\n } else if (lagMs < LAG_DEGRADED_MS) {\r\n status = 'degraded';\r\n statusReason = `event-loop lag ${lagMs.toFixed(1)}ms between ${LAG_OK_MS}ms and ${LAG_DEGRADED_MS}ms`;\r\n } else {\r\n status = 'critical';\r\n statusReason = `event-loop lag ${lagMs.toFixed(1)}ms above ${LAG_DEGRADED_MS}ms`;\r\n }\r\n\r\n const down = checks.filter((c) => c.status === 'down');\r\n if (down.length > 0) {\r\n status = 'critical';\r\n statusReason = `dependency down: ${down.map((c) => c.name).join(', ')}`;\r\n }\r\n\r\n return {\r\n ok: true,\r\n data: {\r\n status,\r\n statusReason,\r\n uptimeSeconds: Math.round(process.uptime()),\r\n pid: process.pid,\r\n nodeVersion: process.version,\r\n platform: process.platform,\r\n eventLoopLagMs: lagMs,\r\n memoryUsedPercent,\r\n checks,\r\n },\r\n };\r\n }\r\n}\r\n","import diagnosticsChannel from 'node:diagnostics_channel';\r\nimport http from 'node:http';\r\nimport https from 'node:https';\r\nimport { syncBuiltinESMExports } from 'node:module';\r\nimport { RingBuffer } from '../ring-buffer';\r\nimport type { NodeUIProvider, OutgoingData, OutgoingRequestEntry } from '../types';\r\n\r\nconst RESPONSE_PAGE_SIZE = 100;\r\n\r\ntype Recorder = (entry: Omit<OutgoingRequestEntry, 'id'>) => void;\r\ntype AnyFn = (...args: unknown[]) => unknown;\r\n\r\nconst recorders = new Set<Recorder>();\r\nconst originals = new Map<string, AnyFn>();\r\nlet undiciUnsubscribe: (() => void) | null = null;\r\n\r\nfunction describeTarget(\r\n defaultProtocol: string,\r\n args: unknown[],\r\n): { method: string; url: string } | null {\r\n try {\r\n let method = 'GET';\r\n let host = 'unknown';\r\n let path = '/';\r\n const [first, second] = args;\r\n const opts =\r\n typeof first === 'string' || first instanceof URL\r\n ? (second as Record<string, unknown> | undefined)\r\n : (first as Record<string, unknown> | undefined);\r\n if (typeof first === 'string' || first instanceof URL) {\r\n const parsed = new URL(first.toString());\r\n host = parsed.host;\r\n path = parsed.pathname;\r\n }\r\n if (opts && typeof opts === 'object') {\r\n if (typeof opts.method === 'string') method = opts.method.toUpperCase();\r\n if (typeof first !== 'string' && !(first instanceof URL)) {\r\n const name = (opts.hostname ?? opts.host ?? 'localhost') as string;\r\n host = opts.port ? `${name}:${String(opts.port)}` : name;\r\n path = typeof opts.path === 'string' ? (opts.path.split('?')[0] ?? '/') : '/';\r\n const protocol = typeof opts.protocol === 'string' ? opts.protocol : defaultProtocol;\r\n return { method, url: `${protocol}//${host}${path}` };\r\n }\r\n }\r\n return { method, url: `${defaultProtocol}//${host}${path}` };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction emit(entry: Omit<OutgoingRequestEntry, 'id'>): void {\r\n for (const record of recorders) {\r\n try {\r\n record(entry);\r\n } catch {\r\n // instrumentation must never throw into the host app\r\n }\r\n }\r\n}\r\n\r\nfunction wrapModule(mod: typeof http | typeof https, protocol: string): void {\r\n for (const name of ['request', 'get'] as const) {\r\n const key = `${protocol}${name}`;\r\n const original = mod[name] as unknown as AnyFn;\r\n originals.set(key, original);\r\n const wrapped = function (this: unknown, ...args: unknown[]): unknown {\r\n const req = original.apply(this, args) as http.ClientRequest;\r\n const target = recorders.size > 0 ? describeTarget(protocol, args) : null;\r\n if (target && req && typeof req.once === 'function') {\r\n const started = process.hrtime.bigint();\r\n const timestampMs = Date.now();\r\n let done = false;\r\n const finish = (status: number | null, error?: string): void => {\r\n if (done) return;\r\n done = true;\r\n emit({\r\n ...target,\r\n status,\r\n durationMs: Number(process.hrtime.bigint() - started) / 1e6,\r\n timestampMs,\r\n ...(error ? { error } : {}),\r\n });\r\n };\r\n req.once('response', (res: http.IncomingMessage) => {\r\n const status = res.statusCode ?? null;\r\n res.once('end', () => finish(status));\r\n res.once('close', () => finish(status));\r\n });\r\n req.once('error', (err: Error) => finish(null, err.message));\r\n }\r\n return req;\r\n };\r\n (mod as unknown as Record<string, AnyFn>)[name] = wrapped;\r\n }\r\n // ESM consumers (`import { request } from 'node:http'`) only see the patch after a sync.\r\n syncBuiltinESMExports();\r\n}\r\n\r\nfunction unwrapModule(mod: typeof http | typeof https, protocol: string): void {\r\n for (const name of ['request', 'get'] as const) {\r\n const original = originals.get(`${protocol}${name}`);\r\n if (original) (mod as unknown as Record<string, AnyFn>)[name] = original;\r\n originals.delete(`${protocol}${name}`);\r\n }\r\n syncBuiltinESMExports();\r\n}\r\n\r\ninterface UndiciRequest {\r\n origin?: string | URL;\r\n path?: string;\r\n method?: string;\r\n}\r\n\r\nfunction subscribeUndici(): () => void {\r\n const started = new WeakMap<object, { at: bigint; ts: number }>();\r\n const statuses = new WeakMap<object, number>();\r\n const target = (req: UndiciRequest): { method: string; url: string } => ({\r\n method: (req.method ?? 'GET').toUpperCase(),\r\n url: `${String(req.origin ?? '')}${(req.path ?? '/').split('?')[0]}`,\r\n });\r\n const onCreate = (message: unknown): void => {\r\n const request = (message as { request?: object }).request;\r\n if (request) started.set(request, { at: process.hrtime.bigint(), ts: Date.now() });\r\n };\r\n const onHeaders = (message: unknown): void => {\r\n const { request, response } = message as {\r\n request?: object;\r\n response?: { statusCode?: number };\r\n };\r\n if (request && typeof response?.statusCode === 'number') {\r\n statuses.set(request, response.statusCode);\r\n }\r\n };\r\n const finish = (request: object | undefined, error?: string): void => {\r\n if (!request) return;\r\n const begin = started.get(request);\r\n if (!begin) return;\r\n started.delete(request);\r\n emit({\r\n ...target(request as UndiciRequest),\r\n status: statuses.get(request) ?? null,\r\n durationMs: Number(process.hrtime.bigint() - begin.at) / 1e6,\r\n timestampMs: begin.ts,\r\n ...(error ? { error } : {}),\r\n });\r\n };\r\n const onTrailers = (message: unknown): void => finish((message as { request?: object }).request);\r\n const onError = (message: unknown): void => {\r\n const { request, error } = message as { request?: object; error?: Error };\r\n finish(request, error?.message ?? 'request failed');\r\n };\r\n\r\n diagnosticsChannel.subscribe('undici:request:create', onCreate);\r\n diagnosticsChannel.subscribe('undici:request:headers', onHeaders);\r\n diagnosticsChannel.subscribe('undici:request:trailers', onTrailers);\r\n diagnosticsChannel.subscribe('undici:request:error', onError);\r\n return () => {\r\n diagnosticsChannel.unsubscribe('undici:request:create', onCreate);\r\n diagnosticsChannel.unsubscribe('undici:request:headers', onHeaders);\r\n diagnosticsChannel.unsubscribe('undici:request:trailers', onTrailers);\r\n diagnosticsChannel.unsubscribe('undici:request:error', onError);\r\n };\r\n}\r\n\r\n/**\r\n * Refcounted instrumentation of outgoing HTTP calls made via `http`,\r\n * `https` (axios, got, node-fetch, ...) and global `fetch` (undici). The\r\n * first consumer installs the hooks, the last release restores everything.\r\n */\r\nexport function interceptOutgoing(record: Recorder): () => void {\r\n const first = recorders.size === 0;\r\n recorders.add(record);\r\n if (first) {\r\n wrapModule(http, 'http:');\r\n wrapModule(https, 'https:');\r\n try {\r\n undiciUnsubscribe = subscribeUndici();\r\n } catch {\r\n undiciUnsubscribe = null;\r\n }\r\n }\r\n return () => {\r\n if (!recorders.delete(record) || recorders.size > 0) return;\r\n unwrapModule(http, 'http:');\r\n unwrapModule(https, 'https:');\r\n undiciUnsubscribe?.();\r\n undiciUnsubscribe = null;\r\n };\r\n}\r\n\r\n/** Recent outgoing HTTP calls, recorded lazily while the panel is active. */\r\nexport class OutgoingProvider implements NodeUIProvider<OutgoingData> {\r\n readonly id = 'outgoing' as const;\r\n\r\n private buffer: RingBuffer<OutgoingRequestEntry>;\r\n private nextId = 1;\r\n private failed = 0;\r\n private release: (() => void) | null = null;\r\n\r\n constructor(size: number) {\r\n this.buffer = new RingBuffer<OutgoingRequestEntry>(size);\r\n }\r\n\r\n start(): void {\r\n if (this.release) return;\r\n this.release = interceptOutgoing((entry) => this.record(entry));\r\n }\r\n\r\n stop(): void {\r\n this.release?.();\r\n this.release = null;\r\n }\r\n\r\n record(entry: Omit<OutgoingRequestEntry, 'id'>): void {\r\n this.buffer.push({ id: this.nextId, ...entry });\r\n this.nextId += 1;\r\n if (entry.error || (entry.status !== null && entry.status >= 500)) this.failed += 1;\r\n }\r\n\r\n get(): { ok: true; data: OutgoingData } {\r\n const entries = this.buffer.slice(Math.max(0, this.buffer.length - RESPONSE_PAGE_SIZE));\r\n return { ok: true, data: { total: this.nextId - 1, failed: this.failed, entries } };\r\n }\r\n}\r\n","import { chmod, mkdir, stat } from 'node:fs/promises';\r\nimport { tmpdir } from 'node:os';\r\nimport { join } from 'node:path';\r\nimport { writeHeapSnapshot } from 'node:v8';\r\nimport type {\r\n HeapSnapshotData,\r\n HeapSnapshotPanelData,\r\n NodeUIProvider,\r\n ProviderContext,\r\n ProviderResult,\r\n} from '../types';\r\n\r\nfunction defaultSnapshotDir(): string {\r\n return process.env.NODEUI_HEAP_SNAPSHOT_DIR ?? join(tmpdir(), 'nodeui-heap');\r\n}\r\n\r\n/**\r\n * Heap snapshots via `v8.writeHeapSnapshot`. The panel is read-only; the\r\n * actual file write happens only through `takeSnapshot`, which the server\r\n * gates behind a confirmation nonce.\r\n */\r\nexport class HeapSnapshotProvider implements NodeUIProvider<HeapSnapshotPanelData> {\r\n readonly id = 'heap-snapshot' as const;\r\n\r\n private lastSnapshot: HeapSnapshotData | null = null;\r\n\r\n get(): { ok: true; data: HeapSnapshotPanelData } {\r\n return { ok: true, data: { supported: true, lastSnapshot: this.lastSnapshot } };\r\n }\r\n\r\n async takeSnapshot(ctx: ProviderContext): Promise<ProviderResult<HeapSnapshotData>> {\r\n const dir = ctx.config.heapSnapshotDir || defaultSnapshotDir();\r\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\r\n const fileName = `nodeui-heap-${process.pid}-${stamp}.heapsnapshot`;\r\n const filePath = join(dir, fileName);\r\n try {\r\n // Heap dumps contain every secret held in memory: keep them owner-only.\r\n await mkdir(dir, { recursive: true, mode: 0o700 });\r\n writeHeapSnapshot(filePath);\r\n await chmod(filePath, 0o600).catch(() => undefined);\r\n const fileStat = await stat(filePath);\r\n const data: HeapSnapshotData = {\r\n fileName,\r\n filePath,\r\n sizeBytes: fileStat.size,\r\n createdAtMs: Date.now(),\r\n };\r\n this.lastSnapshot = data;\r\n return { ok: true, data };\r\n } catch (err) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: 'heap-snapshot-failed',\r\n message: err instanceof Error ? err.message : 'heap snapshot failed',\r\n },\r\n };\r\n }\r\n }\r\n}\r\n","import type { NodeUIProvider, RequestEntry, RequestsData } from '../types';\nimport { RingBuffer } from '../ring-buffer';\n\nconst RESPONSE_PAGE_SIZE = 100;\n\n/** Fixed-size ring buffer of recent HTTP requests, recorded via middleware hook. */\nexport class RequestsProvider implements NodeUIProvider<RequestsData> {\n readonly id = 'requests' as const;\n\n private buffer: RingBuffer<RequestEntry>;\n private nextId = 1;\n\n constructor(size: number) {\n this.buffer = new RingBuffer<RequestEntry>(size);\n }\n\n record(entry: Omit<RequestEntry, 'id'>): void {\n this.buffer.push({ id: this.nextId, ...entry });\n this.nextId += 1;\n }\n\n get length(): number {\n return this.buffer.length;\n }\n\n get(): { ok: true; data: RequestsData } {\n const entries = this.buffer.slice(Math.max(0, this.buffer.length - RESPONSE_PAGE_SIZE));\n return { ok: true, data: { total: this.buffer.length, entries } };\n }\n}\n","import type { MetricsBucket, MetricsData, NodeUIProvider } from '../types';\n\nconst WINDOW_SECONDS = 60;\n\n/**\n * Records one-second buckets of request counts and error counts, derived from\n * the request recorder in the server middleware. Errors are responses with\n * status >= 500.\n */\nexport class MetricsProvider implements NodeUIProvider<MetricsData> {\n readonly id = 'metrics' as const;\n\n private buckets: MetricsBucket[] = [];\n private readonly nowMs: () => number;\n\n constructor(nowMs: () => number = Date.now) {\n this.nowMs = nowMs;\n }\n\n /** Ticks the current second bucket for one completed request. */\n record(status: number): void {\n const ts = Math.floor(this.nowMs() / 1000) * 1000;\n const last = this.buckets[this.buckets.length - 1];\n if (last && last.ts === ts) {\n last.requests += 1;\n if (status >= 500) last.errors += 1;\n return;\n }\n this.buckets.push({ ts, requests: 1, errors: status >= 500 ? 1 : 0 });\n if (this.buckets.length > WINDOW_SECONDS + 1) {\n this.buckets.splice(0, this.buckets.length - WINDOW_SECONDS);\n }\n }\n\n get(): { ok: true; data: MetricsData } {\n const now = Math.floor(this.nowMs() / 1000) * 1000;\n const start = now - (WINDOW_SECONDS - 1) * 1000;\n const byTs = new Map<number, MetricsBucket>(this.buckets.map((b) => [b.ts, b]));\n const buckets: MetricsBucket[] = [];\n for (let ts = start; ts <= now; ts += 1000) {\n const existing = byTs.get(ts);\n buckets.push(existing ? { ...existing } : { ts, requests: 0, errors: 0 });\n }\n return { ok: true, data: { buckets } };\n }\n}\n","import type { EnvData, EnvEntry, NodeUIProvider, ProviderContext } from '../types';\n\nfunction stringify(value: unknown): string {\n if (value === undefined) return '';\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n\nfunction toEntries(source: Record<string, unknown>): EnvEntry[] {\n return Object.entries(source)\n .map(([key, value]) => ({ key, value: stringify(value) }))\n .sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n}\n\n/**\n * Environment and app-config viewer. The environment comes from the process\n * env snapshot; secret masking is applied later at serialization time.\n */\nexport class EnvProvider implements NodeUIProvider<EnvData> {\n readonly id = 'env' as const;\n\n get(ctx: ProviderContext): { ok: true; data: EnvData } {\n const environment = toEntries(ctx.env as Record<string, unknown>);\n const rawConfig = ctx.store['app-config'];\n let config: EnvEntry[] | null = null;\n if (rawConfig !== undefined) {\n const resolved = typeof rawConfig === 'function' ? rawConfig() : rawConfig;\n if (resolved !== undefined && resolved !== null) {\n config = toEntries(resolved as Record<string, unknown>);\n }\n }\n return { ok: true, data: { environment, config } };\n }\n}\n","import type { NodeUIProvider, ProviderContext, RouteEntry, RoutesData } from '../types';\r\n\r\ninterface RouterLayer {\r\n name?: string;\r\n path?: string;\r\n route?: {\r\n path?: string;\r\n methods?: Record<string, boolean>;\r\n stack?: Array<{ handle?: { name?: string } }>;\r\n };\r\n handle?: { stack?: RouterLayer[] };\r\n}\r\n\r\nfunction joinPath(prefix: string, sub: string): string {\r\n if (!sub || sub === '/') return prefix || '/';\r\n return `${prefix.replace(/\\/+$/, '')}/${sub.replace(/^\\/+/, '')}`;\r\n}\r\n\r\n/**\r\n * Recursively walks an Express-style router stack, extracting concrete\r\n * method + path + handler entries. Middleware-only layers are skipped.\r\n *\r\n * NOTE: This intentionally relies on Express router internals\r\n * (`router.stack`, `layer.route`, `layer.handle.stack`), which are stable in\r\n * Express 4 and 5 but not part of the public API. If a future Express\r\n * release changes the shape, this is the only place that needs updating —\r\n * the walk degrades to an empty list rather than throwing.\r\n */\r\nexport function extractRoutes(router: unknown): RouteEntry[] {\r\n const out: RouteEntry[] = [];\r\n const seen = new Set<string>();\r\n let stack: unknown;\r\n try {\r\n stack = (router as { stack?: unknown } | undefined)?.stack;\r\n } catch {\r\n return out;\r\n }\r\n if (!Array.isArray(stack)) return out;\r\n\r\n const walk = (layers: RouterLayer[], prefix: string): void => {\r\n for (const layer of layers) {\r\n if (!layer || typeof layer !== 'object') continue;\r\n if (layer.route && typeof layer.route.path === 'string') {\r\n const path = joinPath(prefix, layer.route.path);\r\n const methods = Object.entries(layer.route.methods ?? {})\r\n .filter(([, enabled]) => enabled)\r\n .map(([method]) => method.toUpperCase());\r\n const handler = layer.route.stack?.[0]?.handle?.name || 'anonymous';\r\n if (methods.length === 0) continue;\r\n for (const method of methods) {\r\n const key = `${method} ${path}`;\r\n if (seen.has(key)) continue;\r\n seen.add(key);\r\n out.push({ method, path, handler });\r\n }\r\n continue;\r\n }\r\n const nested = layer.handle?.stack;\r\n if (Array.isArray(nested)) {\r\n walk(nested, joinPath(prefix, layer.path ?? ''));\r\n }\r\n }\r\n };\r\n\r\n try {\r\n walk(stack as RouterLayer[], '');\r\n } catch {\r\n return [];\r\n }\r\n out.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));\r\n return out;\r\n}\r\n\r\n/**\r\n * Lists the HTTP routes of the host application by introspecting the Express\r\n * router captured from `req.app._router` (or `req.app.router` on Express 5)\r\n * on the first handled request. Results are cached per router reference so\r\n * repeated fetches do not re-walk the stack.\r\n */\r\nexport class RoutesProvider implements NodeUIProvider<RoutesData> {\r\n readonly id = 'routes' as const;\r\n\r\n private cache = new WeakMap<object, RouteEntry[]>();\r\n\r\n get(\r\n ctx: ProviderContext,\r\n ): { ok: true; data: RoutesData } | { ok: false; error: { code: string; message: string } } {\r\n const source = ctx.store['routes-source'] as RouteEntry[] | (() => RouteEntry[]) | undefined;\r\n if (source !== undefined) {\r\n const routes = typeof source === 'function' ? source() : source;\r\n return { ok: true, data: { routes } };\r\n }\r\n const router = ctx.store['express-router'];\r\n if (!router) {\r\n return {\r\n ok: false,\r\n error: {\r\n code: 'router-unavailable',\r\n message: 'No Express router captured yet. Send at least one request through the app.',\r\n },\r\n };\r\n }\r\n if (typeof router !== 'object' && typeof router !== 'function') {\r\n return {\r\n ok: false,\r\n error: {\r\n code: 'router-invalid',\r\n message: 'Captured router has an unexpected shape; route listing is unavailable.',\r\n },\r\n };\r\n }\r\n let routes = this.cache.get(router as object);\r\n if (!routes) {\r\n routes = extractRoutes(router);\r\n this.cache.set(router as object, routes);\r\n }\r\n return { ok: true, data: { routes } };\r\n }\r\n}\r\n","import { DEFAULT_LOG_SIZE } from '../constants';\nimport { RingBuffer } from '../ring-buffer';\nimport type { LogEntry, LogLevel, LogsData, NodeUIProvider, ProviderContext } from '../types';\n\nconst CONSOLE_METHODS: Array<[keyof Console, LogLevel]> = [\n ['debug', 'debug'],\n ['info', 'info'],\n ['log', 'info'],\n ['warn', 'warn'],\n ['error', 'error'],\n];\n\nfunction formatArg(arg: unknown): string {\n if (typeof arg === 'string') return arg;\n try {\n const json = JSON.stringify(arg);\n return json === undefined ? String(arg) : json;\n } catch {\n return String(arg);\n }\n}\n\nlet refs = 0;\nconst pushes = new Set<(entry: Omit<LogEntry, 'timestamp'>) => void>();\nconst wrappers = new Map<keyof Console, (...args: unknown[]) => void>();\nlet originals: Partial<Record<keyof Console, (...args: unknown[]) => void>> = {};\n\nfunction consoleRef(): Record<string, (...args: unknown[]) => void> {\n return console as unknown as Record<string, (...args: unknown[]) => void>;\n}\n\nfunction installWrappers(): void {\n for (const [method, level] of CONSOLE_METHODS) {\n const original = consoleRef()[method];\n originals[method] = original;\n const wrapper = (...args: unknown[]): void => {\n for (const push of pushes) {\n try {\n push({ level, message: args.map(formatArg).join(' ') });\n } catch {\n // interception must never throw into the caller\n }\n }\n if (original) original(...args);\n };\n wrappers.set(method, wrapper);\n consoleRef()[method] = wrapper;\n }\n}\n\nfunction uninstallWrappers(): void {\n for (const [method] of CONSOLE_METHODS) {\n const current = consoleRef()[method];\n if (current === wrappers.get(method)) {\n const original = originals[method];\n if (original) {\n consoleRef()[method] = original;\n } else {\n delete consoleRef()[method];\n }\n }\n // If another layer patched console after us, the current method is not\n // ours — leave the chain intact so the host app's interceptor survives.\n }\n wrappers.clear();\n originals = {};\n}\n\n/**\n * Wraps `console.debug/info/log/warn/error` to tee entries to every active\n * `push` callback. Refcounted: the first install replaces the methods, the\n * last release restores the originals. Supports concurrent consumers (e.g.\n * multiple `createNodeUI()` instances) — each registers its own push and is\n * removed independently. Never throws into the caller.\n */\nexport function interceptConsole(push: (entry: Omit<LogEntry, 'timestamp'>) => void): () => void {\n refs += 1;\n pushes.add(push);\n if (refs === 1) installWrappers();\n return () => {\n pushes.delete(push);\n refs = Math.max(0, refs - 1);\n if (refs === 0) uninstallWrappers();\n };\n}\n\n/**\n * Log viewer provider. While active it intercepts `console.*` output and\n * also accepts entries pushed via {@link addSource}. Filtering happens\n * server-side via `ctx.query.level` and `ctx.query.query`.\n */\nexport class LogsProvider implements NodeUIProvider<LogsData> {\n readonly id = 'logs' as const;\n\n private buffer: RingBuffer<LogEntry>;\n private release: (() => void) | null = null;\n\n constructor(size: number = DEFAULT_LOG_SIZE) {\n this.buffer = new RingBuffer<LogEntry>(size);\n }\n\n start(): void {\n if (this.release) return;\n this.release = interceptConsole(({ level, message }) => {\n this.buffer.push({ level, message, timestamp: Date.now() });\n });\n }\n\n stop(): void {\n this.release?.();\n this.release = null;\n }\n\n /** Pushes an external entry (logger adapter). */\n addSource(entry: { level: LogLevel; message: string }): void {\n this.buffer.push({ ...entry, timestamp: Date.now() });\n }\n\n get(ctx: ProviderContext): { ok: true; data: LogsData } {\n let entries = this.buffer.toArray();\n const level = ctx.query?.level;\n if (level) entries = entries.filter((e) => e.level === level);\n const query = ctx.query?.query;\n if (query) {\n const needle = query.toLowerCase();\n entries = entries.filter((e) => e.message.toLowerCase().includes(needle));\n }\n return { ok: true, data: { entries } };\n }\n}\n","import type { ServerResponse } from 'node:http';\r\n\r\nexport interface SseStream {\r\n send(payload: unknown): void;\r\n heartbeat(): void;\r\n close(): void;\r\n}\r\n\r\n/**\r\n * Minimal Server-Sent Events writer over a raw HTTP response. Events are\r\n * emitted as `data: <json>\\n\\n`; heartbeats as `:ping\\n\\n`.\r\n */\r\nexport function startSse(\r\n res: ServerResponse,\r\n extraHeaders: Record<string, string> = {},\r\n): SseStream {\r\n let closed = false;\r\n res.writeHead(200, {\r\n ...extraHeaders,\r\n 'Content-Type': 'text/event-stream',\r\n 'Cache-Control': 'no-cache',\r\n Connection: 'keep-alive',\r\n 'X-Accel-Buffering': 'no',\r\n });\r\n res.flushHeaders?.();\r\n\r\n return {\r\n send(payload: unknown): void {\r\n if (closed || res.destroyed) return;\r\n res.write(`data: ${JSON.stringify(payload)}\\n\\n`);\r\n },\r\n heartbeat(): void {\r\n if (closed || res.destroyed) return;\r\n res.write(':ping\\n\\n');\r\n },\r\n close(): void {\r\n if (closed || res.destroyed) return;\r\n closed = true;\r\n res.end();\r\n },\r\n };\r\n}\r\n","import type { IncomingMessage, ServerResponse } from 'node:http';\r\nimport { tmpdir } from 'node:os';\r\nimport { join, resolve } from 'node:path';\r\nimport type {\r\n ApiEnvelope,\r\n ConfigData,\r\n ConfirmIssued,\r\n HeapSnapshotData,\r\n LogLevel,\r\n NodeUIConfig,\r\n NodeUIProvider,\r\n PanelId,\r\n ProviderContext,\r\n ProviderResult,\r\n RouteEntry,\r\n StartupData,\r\n} from './types';\r\nimport { isLoopbackAddress, maskSecrets, resolveActivation, SECRET_KEY_PATTERN } from './safety';\r\nimport { AUTH_COOKIE, createGuard } from './guard';\r\nimport { ConfirmationStore } from './confirmations';\r\nimport { DEFAULT_LOG_SIZE } from './constants';\r\nimport { startSse } from './sse';\r\nimport { ProviderRegistry } from './registry';\r\nimport { resolveStaticAsset } from './static';\r\nimport { MemoryProvider } from './providers/memory';\r\nimport { CpuProvider } from './providers/cpu';\r\nimport { EventLoopLagProvider } from './providers/event-loop';\r\nimport { HealthProvider } from './providers/health';\r\nimport { HeapSnapshotProvider } from './providers/heap-snapshot';\r\nimport { StartupTracker } from './providers/startup-tracker';\r\nimport { RequestsProvider } from './providers/requests';\r\nimport { OutgoingProvider } from './providers/outgoing';\r\nimport type { HealthCheck } from './providers/health';\r\nimport { MetricsProvider, LogsProvider, EnvProvider, RoutesProvider } from './providers';\r\n\r\nexport interface NodeUIOptions {\r\n /** URL path prefix. Default `/nodeui` (or `NODEUI_PATH`). */\r\n path?: string;\r\n /** Interface the console considers its own. Default `127.0.0.1` (or `NODEUI_HOST`). */\r\n host?: string;\r\n /** Informational port of the host application. */\r\n port?: number;\r\n /** Ring buffer capacity for the request log. Default 500. */\r\n requestLogSize?: number;\r\n /** Ring buffer capacity for the log viewer. Default 500. */\r\n logSize?: number;\r\n /** App config surfaced in the env panel; may be an object or a getter. */\r\n config?: unknown | (() => unknown);\r\n /** Sampling/polling interval in ms. Default 2000. */\r\n pollIntervalMs?: number;\r\n /** Explicit activation override. Defaults to env-based activation. */\r\n enabled?: boolean;\r\n /** Environment snapshot. Defaults to `process.env`. */\r\n env?: Record<string, string | undefined>;\r\n /** Whether secret masking applies to panel output. Default true. */\r\n maskSecrets?: boolean;\r\n /** Idle time after which background samplers stop. Default 60000. */\r\n inactivityTimeoutMs?: number;\r\n /** TTL for mutation confirmation nonces. Default 60000. */\r\n confirmTtlMs?: number;\r\n /** Directory for heap snapshot files. Defaults to a private dir under the OS temp dir. */\r\n heapSnapshotDir?: string;\r\n /** Extra `Host` hostnames accepted besides loopback names (or `NODEUI_ALLOWED_HOSTS`, comma-separated). */\r\n allowedHosts?: string[];\r\n /** Extra `Origin` values accepted for cross-origin calls (or `NODEUI_ALLOWED_ORIGINS`). */\r\n allowedOrigins?: string[];\r\n /**\r\n * Extra remote IPs / IPv4 CIDRs accepted (or `NODEUI_ALLOWED_REMOTE`). Use\r\n * `['172.16.0.0/12']` to reach the console from the host when the app runs in Docker.\r\n */\r\n allowedRemoteAddresses?: string[];\r\n /** Accept requests carrying `X-Forwarded-*` headers (or `NODEUI_TRUST_PROXY=true`). Default false. */\r\n trustProxy?: boolean;\r\n /**\r\n * Shared access token (or `NODEUI_TOKEN`). When set, open `/nodeui/?token=<token>` once;\r\n * the browser then keeps an HttpOnly cookie. API clients may send `Authorization: Bearer`.\r\n */\r\n authToken?: string;\r\n /** Maximum concurrent live (SSE) streams. Default 10. */\r\n maxSseClients?: number;\r\n /** Custom panels; each provider needs a unique lowercase id, e.g. `queues`. */\r\n plugins?: NodeUIProvider[];\r\n /** Named dependency checks (database, cache, ...) surfaced in the health panel. */\r\n healthChecks?: Record<string, HealthCheck>;\r\n /** Max recorded outgoing HTTP calls. Default 200. */\r\n outgoingLogSize?: number;\r\n}\r\n\r\nexport interface NodeUIServer {\r\n readonly config: NodeUIConfig;\r\n readonly active: boolean;\r\n readonly activationReason: string;\r\n /** Express/Nest compatible middleware that serves the console and API. */\r\n middleware(): NodeUIMiddleware;\r\n /** Direct request handling (no `next`); used for tests and adapters. */\r\n handle(req: IncomingMessage, res: ServerResponse): Promise<void>;\r\n /**\r\n * Supplies the route list directly (frameworks other than Express, e.g.\r\n * Fastify's `onRoute`). Takes precedence over Express router introspection.\r\n */\r\n setRoutes(source: RouteEntry[] | (() => RouteEntry[])): void;\r\n /** Records a bootstrap timing mark, e.g. `mark(\"listening\")`. */\r\n mark(name: string): void;\r\n /** Whether a background-sampling provider is currently running. */\r\n isProviderActive(id: PanelId): boolean;\r\n /** Pushes an external log entry into the log viewer (logger adapter). */\r\n addLogSource(entry: { level: LogLevel; message: string }): void;\r\n /** Stops all timers and background samplers. */\r\n shutdown(): void;\r\n}\r\n\r\nexport type NodeUIMiddleware = (\r\n req: IncomingMessage,\r\n res: ServerResponse,\r\n next: () => void,\r\n) => void;\r\n\r\nconst STATIC_ROOT = resolve(__dirname, '..', 'static');\r\n\r\nconst SSE_HEARTBEAT_MS = 15_000;\r\n\r\n/**\r\n * Serializes an API envelope to JSON, applying secret masking so values\r\n * under keys like `TOKEN`/`KEY`/`SECRET`/`PASSWORD` never reach the UI.\r\n */\r\nexport function serializeEnvelope<T>(envelope: ApiEnvelope<T>, mask = true): string {\r\n return JSON.stringify(mask ? maskSecrets(envelope) : envelope);\r\n}\r\n\r\nconst BUILT_IN_PANELS = new Set<string>([\r\n 'health',\r\n 'memory',\r\n 'cpu',\r\n 'event-loop',\r\n 'heap-snapshot',\r\n 'startup',\r\n 'requests',\r\n 'env',\r\n 'routes',\r\n 'logs',\r\n 'metrics',\r\n 'outgoing',\r\n]);\r\nconst PLUGIN_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;\r\nconst RESERVED_API_NAMES = new Set(['config', 'live', 'confirmations']);\r\n\r\nconst SECURITY_HEADERS: Record<string, string> = {\r\n 'X-Content-Type-Options': 'nosniff',\r\n 'X-Frame-Options': 'DENY',\r\n 'Referrer-Policy': 'no-referrer',\r\n 'Cross-Origin-Resource-Policy': 'same-origin',\r\n};\r\nconst CSP =\r\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; \" +\r\n \"img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'\";\r\n\r\nfunction listFrom(value: string[] | undefined, raw: string | undefined): string[] {\r\n if (value) return value;\r\n return (raw ?? '')\r\n .split(',')\r\n .map((v) => v.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nfunction pathnameOf(req: IncomingMessage): string {\r\n const raw = (req as IncomingMessage & { originalUrl?: string }).originalUrl ?? req.url ?? '/';\r\n try {\r\n return new URL(raw, 'http://localhost').pathname;\r\n } catch {\r\n return raw;\r\n }\r\n}\r\n\r\nfunction normalizePath(value: string): string {\r\n const trimmed = value.trim();\r\n if (!trimmed.startsWith('/')) throw new Error(`NODEUI_PATH must start with \"/\", got \"${value}\"`);\r\n return trimmed.replace(/\\/+$/, '') || '/';\r\n}\r\n\r\nfunction positiveInt(value: number | undefined, fallback: number, name: string): number {\r\n const n = value === undefined ? fallback : value;\r\n if (!Number.isInteger(n) || n <= 0) throw new Error(`${name} must be a positive integer`);\r\n return n;\r\n}\r\n\r\nexport function createNodeUI(options: NodeUIOptions = {}): NodeUIServer {\r\n const env = options.env ?? process.env;\r\n\r\n const activation =\r\n options.enabled !== undefined\r\n ? {\r\n active: options.enabled,\r\n reason: options.enabled\r\n ? 'explicitly enabled via options'\r\n : 'explicitly disabled via options',\r\n }\r\n : resolveActivation(env);\r\n\r\n const path = normalizePath(options.path ?? env.NODEUI_PATH ?? '/nodeui');\r\n const host = options.host ?? env.NODEUI_HOST ?? '127.0.0.1';\r\n const config: NodeUIConfig = {\r\n path,\r\n host,\r\n port: options.port ?? (env.NODEUI_PORT ? Number(env.NODEUI_PORT) : 0),\r\n requestLogSize: positiveInt(\r\n options.requestLogSize,\r\n env.NODEUI_REQUEST_LOG_SIZE ? Number(env.NODEUI_REQUEST_LOG_SIZE) : 500,\r\n 'NODEUI_REQUEST_LOG_SIZE',\r\n ),\r\n logSize: positiveInt(\r\n options.logSize,\r\n env.NODEUI_LOG_SIZE ? Number(env.NODEUI_LOG_SIZE) : DEFAULT_LOG_SIZE,\r\n 'NODEUI_LOG_SIZE',\r\n ),\r\n pollIntervalMs: positiveInt(\r\n options.pollIntervalMs,\r\n env.NODEUI_POLL_INTERVAL_MS ? Number(env.NODEUI_POLL_INTERVAL_MS) : 2000,\r\n 'NODEUI_POLL_INTERVAL_MS',\r\n ),\r\n enabled: activation.active,\r\n activationReason: activation.reason,\r\n maskSecrets: options.maskSecrets ?? true,\r\n inactivityTimeoutMs: positiveInt(\r\n options.inactivityTimeoutMs,\r\n env.NODEUI_INACTIVITY_TIMEOUT_MS ? Number(env.NODEUI_INACTIVITY_TIMEOUT_MS) : 60_000,\r\n 'NODEUI_INACTIVITY_TIMEOUT_MS',\r\n ),\r\n confirmTtlMs: positiveInt(\r\n options.confirmTtlMs,\r\n env.NODEUI_CONFIRM_TTL_MS ? Number(env.NODEUI_CONFIRM_TTL_MS) : 60_000,\r\n 'NODEUI_CONFIRM_TTL_MS',\r\n ),\r\n heapSnapshotDir:\r\n options.heapSnapshotDir ?? env.NODEUI_HEAP_SNAPSHOT_DIR ?? join(tmpdir(), 'nodeui-heap'),\r\n allowedHosts: listFrom(options.allowedHosts, env.NODEUI_ALLOWED_HOSTS),\r\n allowedOrigins: listFrom(options.allowedOrigins, env.NODEUI_ALLOWED_ORIGINS),\r\n allowedRemoteAddresses: listFrom(options.allowedRemoteAddresses, env.NODEUI_ALLOWED_REMOTE),\r\n trustProxy: options.trustProxy ?? env.NODEUI_TRUST_PROXY === 'true',\r\n authRequired: Boolean(options.authToken ?? env.NODEUI_TOKEN),\r\n maxSseClients: positiveInt(\r\n options.maxSseClients,\r\n env.NODEUI_MAX_SSE_CLIENTS ? Number(env.NODEUI_MAX_SSE_CLIENTS) : 10,\r\n 'NODEUI_MAX_SSE_CLIENTS',\r\n ),\r\n };\r\n const authToken = options.authToken ?? env.NODEUI_TOKEN;\r\n const guard = createGuard({\r\n host: config.host,\r\n allowedHosts: config.allowedHosts,\r\n allowedOrigins: config.allowedOrigins,\r\n allowedRemoteAddresses: config.allowedRemoteAddresses,\r\n trustProxy: config.trustProxy,\r\n authToken: authToken || undefined,\r\n });\r\n\r\n if (config.enabled && !isLoopbackAddress(config.host)) {\r\n console.warn(\r\n `[nodeui] NODEUI_HOST is set to non-loopback \"${config.host}\". ` +\r\n 'The developer console will be reachable from other hosts; only do this deliberately' +\r\n (config.authRequired ? '.' : ' and set NODEUI_TOKEN (authToken) to require a token.'),\r\n );\r\n }\r\n if (config.enabled && env.NODE_ENV === 'production') {\r\n console.warn(\r\n '[nodeui] running with NODE_ENV=production. The console exposes environment, logs and ' +\r\n 'heap data; keep it behind loopback and an auth token.',\r\n );\r\n }\r\n if (config.enabled && !config.maskSecrets) {\r\n console.warn('[nodeui] secret masking is disabled; panels may show credentials.');\r\n }\r\n\r\n const ctx: ProviderContext = { config, env, store: {} };\r\n if (options.config !== undefined) {\r\n ctx.store['app-config'] = options.config;\r\n }\r\n const registry = new ProviderRegistry();\r\n registry.register(new HealthProvider(options.healthChecks));\r\n registry.register(new MemoryProvider());\r\n registry.register(new CpuProvider());\r\n registry.register(new EventLoopLagProvider());\r\n registry.register(new HeapSnapshotProvider());\r\n\r\n const startupTracker = new StartupTracker();\r\n startupTracker.mark('nodeui.init');\r\n const startupProvider: NodeUIProvider<StartupData> = {\r\n id: 'startup',\r\n get: () => ({ ok: true, data: startupTracker.getData() }),\r\n };\r\n registry.register(startupProvider);\r\n\r\n const requestsProvider = new RequestsProvider(config.requestLogSize);\r\n registry.register(requestsProvider);\r\n\r\n const metricsProvider = new MetricsProvider();\r\n registry.register(metricsProvider);\r\n\r\n const logsProvider = new LogsProvider(config.logSize);\r\n registry.register(logsProvider);\r\n\r\n const envProvider = new EnvProvider();\r\n registry.register(envProvider);\r\n const routesProvider = new RoutesProvider();\r\n registry.register(routesProvider);\r\n registry.register(\r\n new OutgoingProvider(positiveInt(options.outgoingLogSize, 200, 'outgoingLogSize')),\r\n );\r\n\r\n const pluginMeta: Array<{ id: PanelId; title: string }> = [];\r\n for (const plugin of options.plugins ?? []) {\r\n if (\r\n !PLUGIN_ID_PATTERN.test(plugin.id) ||\r\n BUILT_IN_PANELS.has(plugin.id) ||\r\n RESERVED_API_NAMES.has(plugin.id) ||\r\n registry.get(plugin.id)\r\n ) {\r\n throw new Error(\r\n `nodeui plugin id \"${plugin.id}\" is invalid or already taken ` +\r\n '(use lowercase letters, digits and \"-\")',\r\n );\r\n }\r\n registry.register(plugin);\r\n pluginMeta.push({ id: plugin.id, title: plugin.title ?? plugin.id });\r\n }\r\n\r\n const confirmations = new ConfirmationStore(config.confirmTtlMs);\r\n const activeProviders = new Set<PanelId>();\r\n let inactivityTimer: NodeJS.Timeout | null = null;\r\n let sseClients = 0;\r\n\r\n function stopAll(): void {\r\n for (const id of activeProviders) {\r\n registry.get(id)?.stop?.(ctx);\r\n }\r\n activeProviders.clear();\r\n if (inactivityTimer) {\r\n clearTimeout(inactivityTimer);\r\n inactivityTimer = null;\r\n }\r\n }\r\n\r\n function resetInactivityTimer(): void {\r\n if (inactivityTimer) clearTimeout(inactivityTimer);\r\n inactivityTimer = setTimeout(stopAll, config.inactivityTimeoutMs);\r\n if (typeof inactivityTimer.unref === 'function') inactivityTimer.unref();\r\n }\r\n\r\n function ensureActive(id: PanelId): void {\r\n if (!activeProviders.has(id)) {\r\n registry.get(id)?.start?.(ctx);\r\n activeProviders.add(id);\r\n }\r\n resetInactivityTimer();\r\n }\r\n\r\n function sendJson(res: ServerResponse, status: number, envelope: ApiEnvelope<unknown>): void {\r\n const body = serializeEnvelope(envelope, config.maskSecrets);\r\n res.writeHead(status, {\r\n ...SECURITY_HEADERS,\r\n 'Content-Type': 'application/json',\r\n 'Content-Length': Buffer.byteLength(body),\r\n 'Cache-Control': 'no-store',\r\n });\r\n res.end(body);\r\n }\r\n\r\n function notFound(res: ServerResponse): void {\r\n sendJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Not found' } });\r\n }\r\n\r\n function isUnderPath(urlPath: string): boolean {\r\n return urlPath === path || urlPath.startsWith(`${path}/`);\r\n }\r\n\r\n async function servePanel(\r\n res: ServerResponse,\r\n id: PanelId,\r\n query?: Record<string, string>,\r\n ): Promise<void> {\r\n ensureActive(id);\r\n const provider = registry.get(id);\r\n if (!provider) return notFound(res);\r\n const requestCtx = query ? { ...ctx, query } : ctx;\r\n sendJson(res, ...(await readPanel(provider, requestCtx)));\r\n }\r\n\r\n /** Runs a provider, converting a throw or rejection into an error envelope. */\r\n async function readPanel(\r\n provider: NodeUIProvider,\r\n requestCtx: ProviderContext,\r\n ): Promise<[number, ProviderResult<unknown>]> {\r\n try {\r\n const result = (await provider.get(requestCtx)) as ProviderResult<unknown>;\r\n return [result.ok ? 200 : 500, result];\r\n } catch (err) {\r\n return [\r\n 500,\r\n {\r\n ok: false,\r\n error: {\r\n code: 'provider-failed',\r\n message: err instanceof Error ? err.message : 'provider failed',\r\n },\r\n },\r\n ];\r\n }\r\n }\r\n\r\n function configData(): ConfigData {\r\n return {\r\n enabled: config.enabled,\r\n activationReason: config.activationReason,\r\n path: config.path,\r\n host: config.host,\r\n port: config.port,\r\n requestLogSize: config.requestLogSize,\r\n logSize: config.logSize,\r\n pollIntervalMs: config.pollIntervalMs,\r\n panels: registry.ids(),\r\n masking: { enabled: config.maskSecrets, pattern: SECRET_KEY_PATTERN.source },\r\n authRequired: config.authRequired,\r\n plugins: pluginMeta,\r\n };\r\n }\r\n\r\n function issueConfirmation(res: ServerResponse): void {\r\n const issued: ConfirmIssued = confirmations.issue();\r\n sendJson(res, 200, { ok: true, data: issued });\r\n }\r\n\r\n async function takeHeapSnapshot(req: IncomingMessage, res: ServerResponse): Promise<void> {\r\n const header = req.headers['x-nodeui-confirm'];\r\n const nonce = Array.isArray(header) ? header[0] : header;\r\n if (typeof nonce !== 'string' || !confirmations.consume(nonce)) {\r\n sendJson(res, 409, {\r\n ok: false,\r\n error: {\r\n code: 'confirmation-required',\r\n message:\r\n 'This mutating action needs a fresh confirmation nonce. Issue one via ' +\r\n 'POST ' +\r\n `${path}/api/confirmations` +\r\n ' and repeat the request with an x-nodeui-confirm header.',\r\n },\r\n });\r\n return;\r\n }\r\n const heapProvider = registry.get('heap-snapshot');\r\n if (!heapProvider || !('takeSnapshot' in heapProvider)) return notFound(res);\r\n const result = (await (heapProvider as HeapSnapshotProvider).takeSnapshot(\r\n ctx,\r\n )) as ProviderResult<HeapSnapshotData>;\r\n sendJson(res, result.ok ? 200 : 500, result);\r\n }\r\n\r\n async function handleLive(req: IncomingMessage, res: ServerResponse): Promise<void> {\r\n const search = new URL(req.url ?? '/', 'http://localhost').searchParams;\r\n const requested = (search.get('panels') ?? '').split(',').filter(Boolean);\r\n const panels = (\r\n requested.length > 0\r\n ? requested.filter((id): id is PanelId => registry.get(id as PanelId) !== undefined)\r\n : registry.ids()\r\n ).filter((value, index, self) => self.indexOf(value) === index);\r\n\r\n if (panels.length === 0) return notFound(res);\r\n\r\n if (sseClients >= config.maxSseClients) {\r\n res.writeHead(429, { ...SECURITY_HEADERS, 'Content-Type': 'application/json' });\r\n res.end(\r\n JSON.stringify({\r\n ok: false,\r\n error: { code: 'too-many-streams', message: 'too many open live streams' },\r\n }),\r\n );\r\n return;\r\n }\r\n sseClients += 1;\r\n const stream = startSse(res, SECURITY_HEADERS);\r\n const push = async (): Promise<void> => {\r\n for (const id of panels) {\r\n ensureActive(id);\r\n const provider = registry.get(id);\r\n if (!provider) continue;\r\n const [, result] = await readPanel(provider, ctx);\r\n stream.send({ panel: id, envelope: config.maskSecrets ? maskSecrets(result) : result });\r\n }\r\n };\r\n void push().catch(() => undefined);\r\n const pushTimer = setInterval(() => void push().catch(() => undefined), config.pollIntervalMs);\r\n const heartbeatTimer = setInterval(() => stream.heartbeat(), SSE_HEARTBEAT_MS);\r\n if (typeof pushTimer.unref === 'function') pushTimer.unref();\r\n if (typeof heartbeatTimer.unref === 'function') heartbeatTimer.unref();\r\n\r\n let cleaned = false;\r\n const cleanup = (): void => {\r\n if (cleaned) return;\r\n cleaned = true;\r\n sseClients -= 1;\r\n clearInterval(pushTimer);\r\n clearInterval(heartbeatTimer);\r\n stream.close();\r\n };\r\n req.on('close', cleanup);\r\n res.on('close', cleanup);\r\n }\r\n\r\n async function handleApi(\r\n req: IncomingMessage,\r\n res: ServerResponse,\r\n apiPath: string,\r\n ): Promise<void> {\r\n const method = (req.method ?? 'GET').toUpperCase();\r\n if (method === 'GET' && apiPath === '/config') {\r\n sendJson(res, 200, { ok: true, data: configData() });\r\n return;\r\n }\r\n if (method === 'GET' && apiPath === '/live') {\r\n return handleLive(req, res);\r\n }\r\n if (method === 'GET') {\r\n const panel = registry.get(apiPath.slice(1) as PanelId);\r\n if (panel) {\r\n const query = Object.fromEntries(new URL(req.url ?? '/', 'http://localhost').searchParams);\r\n return servePanel(res, panel.id, query);\r\n }\r\n }\r\n if (method === 'POST') {\r\n if (apiPath === '/confirmations') {\r\n issueConfirmation(res);\r\n return;\r\n }\r\n if (apiPath === '/heap-snapshot') {\r\n return takeHeapSnapshot(req, res);\r\n }\r\n }\r\n notFound(res);\r\n }\r\n\r\n function serveStatic(req: IncomingMessage, res: ServerResponse, urlPath: string): void {\r\n const relative = urlPath.slice(path.length);\r\n const asset = resolveStaticAsset(STATIC_ROOT, relative);\r\n if (!asset) {\r\n sendJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Not found' } });\r\n return;\r\n }\r\n res.writeHead(200, {\r\n ...SECURITY_HEADERS,\r\n 'Content-Security-Policy': CSP,\r\n 'Content-Type': asset.contentType,\r\n 'Content-Length': asset.length,\r\n 'Cache-Control': 'no-cache',\r\n });\r\n asset.content.pipe(res);\r\n }\r\n\r\n async function handleNodeUIPath(\r\n req: IncomingMessage,\r\n res: ServerResponse,\r\n urlPath: string,\r\n ): Promise<void> {\r\n const verdict = guard(req);\r\n if (!verdict.ok) {\r\n sendJson(res, verdict.status, {\r\n ok: false,\r\n error: { code: verdict.code, message: verdict.message },\r\n });\r\n return;\r\n }\r\n if (verdict.setToken !== undefined) {\r\n res.writeHead(302, {\r\n ...SECURITY_HEADERS,\r\n 'Set-Cookie': `${AUTH_COOKIE}=${encodeURIComponent(verdict.setToken)}; Path=${path}; HttpOnly; SameSite=Strict`,\r\n Location: `${path}/`,\r\n 'Cache-Control': 'no-store',\r\n });\r\n res.end();\r\n return;\r\n }\r\n const apiBase = `${path}/api`;\r\n if (urlPath === apiBase || urlPath.startsWith(`${apiBase}/`)) {\r\n await handleApi(req, res, urlPath.slice(apiBase.length));\r\n return;\r\n }\r\n serveStatic(req, res, urlPath);\r\n }\r\n\r\n function captureRouter(req: IncomingMessage): void {\r\n if (ctx.store['express-router'] !== undefined) return;\r\n const app = (req as IncomingMessage & { app?: { _router?: unknown; router?: unknown } }).app;\r\n if (!app) return;\r\n let router: unknown;\r\n try {\r\n // Express 4 and 5 both expose the router on `_router` once the app has\r\n // handled a request. Reading it directly avoids Express 4's deprecated\r\n // `app.router` getter, which throws on access.\r\n router = app._router;\r\n } catch {\r\n router = undefined;\r\n }\r\n if (router === undefined) {\r\n try {\r\n router = app.router;\r\n } catch {\r\n router = undefined;\r\n }\r\n }\r\n if (router) {\r\n ctx.store['express-router'] = router;\r\n }\r\n }\r\n\r\n function recordAppRequest(req: IncomingMessage, res: ServerResponse): void {\r\n const started = process.hrtime.bigint();\r\n const timestampMs = Date.now();\r\n res.on('finish', () => {\r\n const durationMs = Number(process.hrtime.bigint() - started) / 1e6;\r\n requestsProvider.record({\r\n method: req.method ?? '?',\r\n path: pathnameOf(req),\r\n status: res.statusCode,\r\n durationMs,\r\n timestampMs,\r\n ip: req.socket.remoteAddress ?? 'unknown',\r\n });\r\n metricsProvider.record(res.statusCode);\r\n });\r\n }\r\n\r\n const server: NodeUIServer = {\r\n get config() {\r\n return config;\r\n },\r\n get active() {\r\n return config.enabled;\r\n },\r\n get activationReason() {\r\n return config.activationReason;\r\n },\r\n middleware(): NodeUIMiddleware {\r\n return (req, res, next) => {\r\n captureRouter(req);\r\n if (!config.enabled) {\r\n next();\r\n return;\r\n }\r\n const urlPath = pathnameOf(req);\r\n if (isUnderPath(urlPath)) {\r\n void handleNodeUIPath(req, res, urlPath);\r\n return;\r\n }\r\n recordAppRequest(req, res);\r\n next();\r\n };\r\n },\r\n async handle(req, res): Promise<void> {\r\n captureRouter(req);\r\n if (!config.enabled) {\r\n notFound(res);\r\n return;\r\n }\r\n const urlPath = pathnameOf(req);\r\n if (isUnderPath(urlPath)) {\r\n await handleNodeUIPath(req, res, urlPath);\r\n return;\r\n }\r\n notFound(res);\r\n },\r\n setRoutes(source): void {\r\n ctx.store['routes-source'] = source;\r\n },\r\n mark(name: string): void {\r\n startupTracker.mark(name);\r\n },\r\n isProviderActive(id: PanelId): boolean {\r\n return activeProviders.has(id);\r\n },\r\n addLogSource(entry: { level: LogLevel; message: string }): void {\r\n logsProvider.addSource(entry);\r\n },\r\n shutdown(): void {\r\n stopAll();\r\n },\r\n };\r\n\r\n return server;\r\n}\r\n"],"mappings":"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","names":["import_node_crypto","stat","ZEROED","defaultClock","ZEROED","resolve","diagnosticsChannel","http","https","import_node_os","import_node_path","RESPONSE_PAGE_SIZE","routes","originals","import_node_os","import_node_path"]}
|