@singhak/nodeui-core 0.3.1 → 0.5.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.
@@ -1 +1 @@
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":["/**\n * @singhak/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 { createGuard, AUTH_COOKIE, type GuardOptions, type GuardResult } from './guard';\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 =\n /token|key|secret|passw(?:or)?d|credential|auth|cookie|dsn|private|signature|session|bearer|connection.?string|database.?url|conn.?str/i;\n\n/** Secret-looking `name=value` / `\"name\":\"value\"` pairs inside free text. */\nconst TEXT_PAIR_PATTERN =\n /\\b([\\w.-]*(?:token|secret|passw(?:or)?d|api[_-]?key|credential|authorization|cookie|dsn)[\\w.-]*[\"']?)(\\s*[=:]\\s*)(\"[^\"]*\"|'[^']*'|[^\\s,;&\"'}]+)/gi;\nconst URL_CREDENTIALS_PATTERN = /([a-z][a-z0-9+.-]*:\\/\\/[^\\s:/@]+:)[^\\s@/]+(@)/gi;\nconst BEARER_PATTERN = /\\b(Bearer|Basic)\\s+[A-Za-z0-9\\-._~+/]+=*/gi;\nconst JWT_PATTERN = /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]*/g;\n\n/**\n * Redacts secrets embedded in free text: credentials in URLs\n * (`postgres://user:pass@host`), bearer/basic tokens, JWTs and\n * `password=...`-style pairs. Used for log lines and any string value.\n */\nexport function maskSecretText(text: string): string {\n if (text.length < 6) return text;\n return text\n .replace(URL_CREDENTIALS_PATTERN, `$1${SECRET_MASKED}$2`)\n .replace(BEARER_PATTERN, `$1 ${SECRET_MASKED}`)\n .replace(JWT_PATTERN, SECRET_MASKED)\n .replace(TEXT_PAIR_PATTERN, `$1$2${SECRET_MASKED}`);\n}\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/** Strips the IPv4-mapped IPv6 prefix so `::ffff:10.0.0.1` compares as `10.0.0.1`. */\nfunction normalizeIp(address: string): string {\n return address.startsWith('::ffff:') ? address.slice(7) : address;\n}\n\nfunction ipv4ToInt(ip: string): number | null {\n const parts = ip.split('.');\n if (parts.length !== 4) return null;\n let out = 0;\n for (const part of parts) {\n const n = Number(part);\n if (!Number.isInteger(n) || n < 0 || n > 255) return null;\n out = out * 256 + n;\n }\n return out;\n}\n\n/**\n * True when `address` matches one of `allowed`, each an exact IP or an IPv4\n * CIDR (e.g. `172.17.0.0/16`, the default Docker bridge network).\n */\nexport function matchesAddress(address: string | undefined, allowed: readonly string[]): boolean {\n if (!address) return false;\n const ip = normalizeIp(address);\n for (const entry of allowed) {\n if (!entry.includes('/')) {\n if (normalizeIp(entry) === ip) return true;\n continue;\n }\n const [base, bitsRaw] = entry.split('/');\n const bits = Number(bitsRaw);\n const a = ipv4ToInt(ip);\n const b = ipv4ToInt(base ?? '');\n if (a === null || b === null || !Number.isInteger(bits) || bits < 0 || bits > 32) continue;\n const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;\n if ((a & mask) >>> 0 === (b & mask) >>> 0) return true;\n }\n return false;\n}\n\n/** Extracts the lower-cased hostname from a `Host` header value (no port). */\nexport function hostnameFromHostHeader(value: string | undefined): string | null {\n if (!value) return null;\n const trimmed = value.trim().toLowerCase();\n if (trimmed.startsWith('[')) {\n const end = trimmed.indexOf(']');\n return end === -1 ? null : trimmed.slice(1, end);\n }\n return trimmed.split(':')[0] || null;\n}\n\n/** True for the hostnames a browser uses to reach a loopback server. */\nexport function isLoopbackHostname(hostname: string | null): boolean {\n if (!hostname) return false;\n return hostname === 'localhost' || hostname.endsWith('.localhost') || isLoopbackAddress(hostname);\n}\n\n/**\n * Deep-clone a value, replacing any value under a key matching the secret\n * pattern with `[REDACTED]` and scrubbing secrets embedded in string values\n * (URL credentials, bearer tokens, `password=...`). The input is not mutated.\n */\nexport function maskSecrets<T>(value: T, pattern: RegExp = SECRET_KEY_PATTERN): T {\n if (typeof value === 'string') {\n return maskSecretText(value) as T;\n }\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 { createHash, timingSafeEqual } from 'node:crypto';\nimport type { IncomingMessage } from 'node:http';\nimport {\n hostnameFromHostHeader,\n isLoopbackAddress,\n isLoopbackHostname,\n matchesAddress,\n} from './safety';\n\nexport const AUTH_COOKIE = 'nodeui_token';\n\nexport interface GuardOptions {\n /** Configured console host; a loopback value enables the strict checks. */\n host: string;\n /** Extra `Host` header hostnames accepted (e.g. `myapp.local`). */\n allowedHosts: readonly string[];\n /** Extra `Origin` values accepted for cross-origin calls. */\n allowedOrigins: readonly string[];\n /** Extra remote IPs / IPv4 CIDRs accepted (e.g. `172.17.0.0/16` for Docker). */\n allowedRemoteAddresses: readonly string[];\n /** Accept requests carrying `X-Forwarded-*` headers. Default false. */\n trustProxy: boolean;\n /** When set, every request must present this token. */\n authToken?: string;\n}\n\nexport type GuardResult =\n { ok: true; setToken?: string } | { ok: false; status: number; code: string; message: string };\n\nconst FORWARD_HEADERS = ['x-forwarded-for', 'x-forwarded-host', 'forwarded', 'x-real-ip'];\n\nfunction digest(value: string): Buffer {\n return createHash('sha256').update(value).digest();\n}\n\nfunction tokenEquals(a: string, b: string): boolean {\n return timingSafeEqual(digest(a), digest(b));\n}\n\nfunction readCookie(header: string | undefined, name: string): string | undefined {\n if (!header) return undefined;\n for (const part of header.split(';')) {\n const [k, ...rest] = part.trim().split('=');\n if (k === name) {\n try {\n return decodeURIComponent(rest.join('='));\n } catch {\n return undefined;\n }\n }\n }\n return undefined;\n}\n\nfunction firstHeader(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction deny(status: number, code: string, message: string): GuardResult {\n return { ok: false, status, code, message };\n}\n\n/**\n * Builds the request guard for the console. It combines: loopback/remote\n * address policy, `Host` validation (DNS-rebinding defence), `Origin`\n * validation (cross-site request defence) and an optional shared token.\n */\nexport function createGuard(options: GuardOptions): (req: IncomingMessage) => GuardResult {\n const strict = isLoopbackAddress(options.host);\n const allowedHosts = options.allowedHosts.map((h) => h.toLowerCase());\n const allowedOrigins = options.allowedOrigins.map((o) => o.toLowerCase().replace(/\\/+$/, ''));\n\n return (req) => {\n const remote = req.socket.remoteAddress;\n const remoteLoopback = isLoopbackAddress(remote);\n const remoteAllowed = remoteLoopback || matchesAddress(remote, options.allowedRemoteAddresses);\n\n if (strict && !remoteAllowed) {\n return deny(\n 403,\n 'forbidden',\n 'nodeui is loopback-only; requests from other hosts are rejected',\n );\n }\n if (!strict && options.allowedRemoteAddresses.length > 0 && !remoteAllowed) {\n return deny(403, 'forbidden', 'remote address is not in the nodeui allow-list');\n }\n\n if (strict && !options.trustProxy && !matchesAddress(remote, options.allowedRemoteAddresses)) {\n if (FORWARD_HEADERS.some((h) => req.headers[h] !== undefined)) {\n return deny(\n 403,\n 'forbidden',\n 'proxied requests are rejected (set trustProxy to allow forwarded traffic)',\n );\n }\n }\n\n const hostHeader = firstHeader(req.headers.host);\n const hostname = hostnameFromHostHeader(hostHeader);\n if (hostname !== null) {\n const hostOk =\n isLoopbackHostname(hostname) ||\n allowedHosts.includes(hostname) ||\n (!strict && allowedHosts.length === 0);\n if (!hostOk) {\n return deny(\n 403,\n 'forbidden',\n `Host \"${hostname}\" is not allowed (add it to allowedHosts to use nodeui via this name)`,\n );\n }\n }\n\n const origin = firstHeader(req.headers.origin);\n if (origin !== undefined) {\n let originOk = false;\n const normalized = origin.toLowerCase().replace(/\\/+$/, '');\n if (allowedOrigins.includes(normalized)) {\n originOk = true;\n } else {\n try {\n originOk = hostHeader !== undefined && new URL(origin).host === hostHeader.toLowerCase();\n } catch {\n originOk = false;\n }\n }\n if (!originOk) {\n return deny(403, 'forbidden', 'cross-origin requests to nodeui are rejected');\n }\n }\n\n if (options.authToken) {\n const authHeader = firstHeader(req.headers.authorization);\n const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;\n const queryToken = new URL(req.url ?? '/', 'http://localhost').searchParams.get('token');\n const cookieToken = readCookie(firstHeader(req.headers.cookie), AUTH_COOKIE);\n const candidates = [\n bearer,\n firstHeader(req.headers['x-nodeui-token']),\n cookieToken,\n queryToken ?? undefined,\n ];\n const matched = candidates.find((c) => c !== undefined && tokenEquals(c, options.authToken!));\n if (matched === undefined) {\n return deny(401, 'unauthorized', 'a valid nodeui token is required');\n }\n if (queryToken !== null && matched === queryToken) {\n return { ok: true, setToken: queryToken };\n }\n }\n\n return { ok: true };\n };\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(\n private readonly ttlMs: number,\n private readonly maxOutstanding = 100,\n ) {}\n\n issue(): ConfirmIssued {\n this.prune();\n while (this.nonces.size >= this.maxOutstanding) {\n const oldest = this.nonces.keys().next().value;\n if (oldest === undefined) break;\n this.nonces.delete(oldest);\n }\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 HealthCheckResult,\n HealthData,\n MemoryData,\n NodeUIProvider,\n ProviderContext,\n} from '../types';\n\nconst LAG_OK_MS = 50;\nconst LAG_DEGRADED_MS = 200;\nconst CHECK_TIMEOUT_MS = 3000;\nconst CHECK_CACHE_MS = 5000;\n\ntype StoredMemory = Partial<MemoryData>;\ntype StoredEventLoop = Partial<EventLoopSample>;\n\n/**\n * A dependency check (database ping, cache ping, ...). Resolve for healthy,\n * reject/throw (or resolve `false`) for unhealthy.\n */\nexport type HealthCheck = () => unknown | Promise<unknown>;\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err: unknown) => {\n clearTimeout(timer);\n reject(err instanceof Error ? err : new Error(String(err)));\n },\n );\n });\n}\n\n/** Derives process + event-loop health, plus optional dependency checks. */\nexport class HealthProvider implements NodeUIProvider<HealthData> {\n readonly id = 'health' as const;\n\n private cache: { at: number; results: HealthCheckResult[] } | null = null;\n\n constructor(private readonly checks: Record<string, HealthCheck> = {}) {}\n\n private async runChecks(): Promise<HealthCheckResult[]> {\n const names = Object.keys(this.checks);\n if (names.length === 0) return [];\n if (this.cache && Date.now() - this.cache.at < CHECK_CACHE_MS) return this.cache.results;\n const results = await Promise.all(\n names.map(async (name): Promise<HealthCheckResult> => {\n const started = Date.now();\n try {\n const outcome = await withTimeout(\n Promise.resolve().then(() => this.checks[name]!()),\n CHECK_TIMEOUT_MS,\n );\n if (outcome === false) throw new Error('check returned false');\n return { name, status: 'up', durationMs: Date.now() - started };\n } catch (err) {\n return {\n name,\n status: 'down',\n durationMs: Date.now() - started,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }),\n );\n this.cache = { at: Date.now(), results };\n return results;\n }\n\n async get(ctx: ProviderContext): Promise<{ 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 const checks = await this.runChecks();\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 const down = checks.filter((c) => c.status === 'down');\n if (down.length > 0) {\n status = 'critical';\n statusReason = `dependency down: ${down.map((c) => c.name).join(', ')}`;\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 checks,\n },\n };\n }\n}\n","import diagnosticsChannel from 'node:diagnostics_channel';\nimport http from 'node:http';\nimport https from 'node:https';\nimport { syncBuiltinESMExports } from 'node:module';\nimport { RingBuffer } from '../ring-buffer';\nimport type { NodeUIProvider, OutgoingData, OutgoingRequestEntry } from '../types';\n\nconst RESPONSE_PAGE_SIZE = 100;\n\ntype Recorder = (entry: Omit<OutgoingRequestEntry, 'id'>) => void;\ntype AnyFn = (...args: unknown[]) => unknown;\n\nconst recorders = new Set<Recorder>();\nconst originals = new Map<string, AnyFn>();\nlet undiciUnsubscribe: (() => void) | null = null;\n\nfunction describeTarget(\n defaultProtocol: string,\n args: unknown[],\n): { method: string; url: string } | null {\n try {\n let method = 'GET';\n let host = 'unknown';\n let path = '/';\n const [first, second] = args;\n const opts =\n typeof first === 'string' || first instanceof URL\n ? (second as Record<string, unknown> | undefined)\n : (first as Record<string, unknown> | undefined);\n if (typeof first === 'string' || first instanceof URL) {\n const parsed = new URL(first.toString());\n host = parsed.host;\n path = parsed.pathname;\n }\n if (opts && typeof opts === 'object') {\n if (typeof opts.method === 'string') method = opts.method.toUpperCase();\n if (typeof first !== 'string' && !(first instanceof URL)) {\n const name = (opts.hostname ?? opts.host ?? 'localhost') as string;\n host = opts.port ? `${name}:${String(opts.port)}` : name;\n path = typeof opts.path === 'string' ? (opts.path.split('?')[0] ?? '/') : '/';\n const protocol = typeof opts.protocol === 'string' ? opts.protocol : defaultProtocol;\n return { method, url: `${protocol}//${host}${path}` };\n }\n }\n return { method, url: `${defaultProtocol}//${host}${path}` };\n } catch {\n return null;\n }\n}\n\nfunction emit(entry: Omit<OutgoingRequestEntry, 'id'>): void {\n for (const record of recorders) {\n try {\n record(entry);\n } catch {\n // instrumentation must never throw into the host app\n }\n }\n}\n\nfunction wrapModule(mod: typeof http | typeof https, protocol: string): void {\n for (const name of ['request', 'get'] as const) {\n const key = `${protocol}${name}`;\n const original = mod[name] as unknown as AnyFn;\n originals.set(key, original);\n const wrapped = function (this: unknown, ...args: unknown[]): unknown {\n const req = original.apply(this, args) as http.ClientRequest;\n const target = recorders.size > 0 ? describeTarget(protocol, args) : null;\n if (target && req && typeof req.once === 'function') {\n const started = process.hrtime.bigint();\n const timestampMs = Date.now();\n let done = false;\n const finish = (status: number | null, error?: string): void => {\n if (done) return;\n done = true;\n emit({\n ...target,\n status,\n durationMs: Number(process.hrtime.bigint() - started) / 1e6,\n timestampMs,\n ...(error ? { error } : {}),\n });\n };\n req.once('response', (res: http.IncomingMessage) => {\n const status = res.statusCode ?? null;\n res.once('end', () => finish(status));\n res.once('close', () => finish(status));\n });\n req.once('error', (err: Error) => finish(null, err.message));\n }\n return req;\n };\n (mod as unknown as Record<string, AnyFn>)[name] = wrapped;\n }\n // ESM consumers (`import { request } from 'node:http'`) only see the patch after a sync.\n syncBuiltinESMExports();\n}\n\nfunction unwrapModule(mod: typeof http | typeof https, protocol: string): void {\n for (const name of ['request', 'get'] as const) {\n const original = originals.get(`${protocol}${name}`);\n if (original) (mod as unknown as Record<string, AnyFn>)[name] = original;\n originals.delete(`${protocol}${name}`);\n }\n syncBuiltinESMExports();\n}\n\ninterface UndiciRequest {\n origin?: string | URL;\n path?: string;\n method?: string;\n}\n\nfunction subscribeUndici(): () => void {\n const started = new WeakMap<object, { at: bigint; ts: number }>();\n const statuses = new WeakMap<object, number>();\n const target = (req: UndiciRequest): { method: string; url: string } => ({\n method: (req.method ?? 'GET').toUpperCase(),\n url: `${String(req.origin ?? '')}${(req.path ?? '/').split('?')[0]}`,\n });\n const onCreate = (message: unknown): void => {\n const request = (message as { request?: object }).request;\n if (request) started.set(request, { at: process.hrtime.bigint(), ts: Date.now() });\n };\n const onHeaders = (message: unknown): void => {\n const { request, response } = message as {\n request?: object;\n response?: { statusCode?: number };\n };\n if (request && typeof response?.statusCode === 'number') {\n statuses.set(request, response.statusCode);\n }\n };\n const finish = (request: object | undefined, error?: string): void => {\n if (!request) return;\n const begin = started.get(request);\n if (!begin) return;\n started.delete(request);\n emit({\n ...target(request as UndiciRequest),\n status: statuses.get(request) ?? null,\n durationMs: Number(process.hrtime.bigint() - begin.at) / 1e6,\n timestampMs: begin.ts,\n ...(error ? { error } : {}),\n });\n };\n const onTrailers = (message: unknown): void => finish((message as { request?: object }).request);\n const onError = (message: unknown): void => {\n const { request, error } = message as { request?: object; error?: Error };\n finish(request, error?.message ?? 'request failed');\n };\n\n diagnosticsChannel.subscribe('undici:request:create', onCreate);\n diagnosticsChannel.subscribe('undici:request:headers', onHeaders);\n diagnosticsChannel.subscribe('undici:request:trailers', onTrailers);\n diagnosticsChannel.subscribe('undici:request:error', onError);\n return () => {\n diagnosticsChannel.unsubscribe('undici:request:create', onCreate);\n diagnosticsChannel.unsubscribe('undici:request:headers', onHeaders);\n diagnosticsChannel.unsubscribe('undici:request:trailers', onTrailers);\n diagnosticsChannel.unsubscribe('undici:request:error', onError);\n };\n}\n\n/**\n * Refcounted instrumentation of outgoing HTTP calls made via `http`,\n * `https` (axios, got, node-fetch, ...) and global `fetch` (undici). The\n * first consumer installs the hooks, the last release restores everything.\n */\nexport function interceptOutgoing(record: Recorder): () => void {\n const first = recorders.size === 0;\n recorders.add(record);\n if (first) {\n wrapModule(http, 'http:');\n wrapModule(https, 'https:');\n try {\n undiciUnsubscribe = subscribeUndici();\n } catch {\n undiciUnsubscribe = null;\n }\n }\n return () => {\n if (!recorders.delete(record) || recorders.size > 0) return;\n unwrapModule(http, 'http:');\n unwrapModule(https, 'https:');\n undiciUnsubscribe?.();\n undiciUnsubscribe = null;\n };\n}\n\n/** Recent outgoing HTTP calls, recorded lazily while the panel is active. */\nexport class OutgoingProvider implements NodeUIProvider<OutgoingData> {\n readonly id = 'outgoing' as const;\n\n private buffer: RingBuffer<OutgoingRequestEntry>;\n private nextId = 1;\n private failed = 0;\n private release: (() => void) | null = null;\n\n constructor(size: number) {\n this.buffer = new RingBuffer<OutgoingRequestEntry>(size);\n }\n\n start(): void {\n if (this.release) return;\n this.release = interceptOutgoing((entry) => this.record(entry));\n }\n\n stop(): void {\n this.release?.();\n this.release = null;\n }\n\n record(entry: Omit<OutgoingRequestEntry, 'id'>): void {\n this.buffer.push({ id: this.nextId, ...entry });\n this.nextId += 1;\n if (entry.error || (entry.status !== null && entry.status >= 500)) this.failed += 1;\n }\n\n get(): { ok: true; data: OutgoingData } {\n const entries = this.buffer.slice(Math.max(0, this.buffer.length - RESPONSE_PAGE_SIZE));\n return { ok: true, data: { total: this.nextId - 1, failed: this.failed, entries } };\n }\n}\n","import { chmod, 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 ?? join(tmpdir(), 'nodeui-heap');\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 // Heap dumps contain every secret held in memory: keep them owner-only.\n await mkdir(dir, { recursive: true, mode: 0o700 });\n writeHeapSnapshot(filePath);\n await chmod(filePath, 0o600).catch(() => undefined);\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 {\r\n NodeUIProvider,\r\n RequestEntry,\r\n RequestsData,\r\n RequestsSummary,\r\n RouteStat,\r\n} from '../types';\r\nimport { RingBuffer } from '../ring-buffer';\r\n\r\nconst RESPONSE_PAGE_SIZE = 100;\r\nconst MAX_ROUTE_STATS = 8;\r\n\r\nfunction percentile(sorted: number[], p: number): number {\r\n if (sorted.length === 0) return 0;\r\n const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1));\r\n return sorted[index] ?? 0;\r\n}\r\n\r\n/** Collapses numeric/uuid path segments so `/users/1` and `/users/2` aggregate. */\r\nfunction routeKey(path: string): string {\r\n return path\r\n .split('/')\r\n .map((seg) =>\r\n /^\\d+$/.test(seg) || /^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(seg) || /^[0-9a-f]{24}$/i.test(seg)\r\n ? ':id'\r\n : seg,\r\n )\r\n .join('/');\r\n}\r\n\r\nexport function summarizeRequests(entries: readonly RequestEntry[]): RequestsSummary {\r\n const durations = entries.map((e) => e.durationMs).sort((a, b) => a - b);\r\n const byStatus = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0 };\r\n const groups = new Map<string, { method: string; path: string; ms: number[]; errors: number }>();\r\n for (const e of entries) {\r\n const bucket = `${Math.floor(e.status / 100)}xx` as keyof typeof byStatus;\r\n if (bucket in byStatus) byStatus[bucket] += 1;\r\n const path = routeKey(e.path);\r\n const key = `${e.method} ${path}`;\r\n const group = groups.get(key) ?? { method: e.method, path, ms: [], errors: 0 };\r\n group.ms.push(e.durationMs);\r\n if (e.status >= 500) group.errors += 1;\r\n groups.set(key, group);\r\n }\r\n const routes: RouteStat[] = [...groups.values()]\r\n .map((g) => {\r\n const sorted = [...g.ms].sort((a, b) => a - b);\r\n return {\r\n method: g.method,\r\n path: g.path,\r\n count: g.ms.length,\r\n avgMs: g.ms.reduce((a, b) => a + b, 0) / g.ms.length,\r\n p95Ms: percentile(sorted, 0.95),\r\n errors: g.errors,\r\n };\r\n })\r\n .sort((a, b) => b.p95Ms - a.p95Ms)\r\n .slice(0, MAX_ROUTE_STATS);\r\n return {\r\n count: entries.length,\r\n errors: byStatus['5xx'],\r\n errorRate: entries.length === 0 ? 0 : byStatus['5xx'] / entries.length,\r\n avgMs: durations.length === 0 ? 0 : durations.reduce((a, b) => a + b, 0) / durations.length,\r\n p50Ms: percentile(durations, 0.5),\r\n p95Ms: percentile(durations, 0.95),\r\n p99Ms: percentile(durations, 0.99),\r\n byStatus,\r\n routes,\r\n };\r\n}\r\n\r\n/** Fixed-size ring buffer of recent HTTP requests, recorded via middleware hook. */\r\nexport class RequestsProvider implements NodeUIProvider<RequestsData> {\r\n readonly id = 'requests' as const;\r\n\r\n private buffer: RingBuffer<RequestEntry>;\r\n private nextId = 1;\r\n\r\n constructor(size: number) {\r\n this.buffer = new RingBuffer<RequestEntry>(size);\r\n }\r\n\r\n record(entry: Omit<RequestEntry, 'id'>): void {\r\n this.buffer.push({ id: this.nextId, ...entry });\r\n this.nextId += 1;\r\n }\r\n\r\n get length(): number {\r\n return this.buffer.length;\r\n }\r\n\r\n get(): { ok: true; data: RequestsData } {\r\n const entries = this.buffer.slice(Math.max(0, this.buffer.length - RESPONSE_PAGE_SIZE));\r\n const summary = summarizeRequests(this.buffer.toArray());\r\n return { ok: true, data: { total: this.buffer.length, entries, summary } };\r\n }\r\n}\r\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 source = ctx.store['routes-source'] as RouteEntry[] | (() => RouteEntry[]) | undefined;\n if (source !== undefined) {\n const routes = typeof source === 'function' ? source() : source;\n return { ok: true, data: { routes } };\n }\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(\n res: ServerResponse,\n extraHeaders: Record<string, string> = {},\n): SseStream {\n let closed = false;\n res.writeHead(200, {\n ...extraHeaders,\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';\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 locked: 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCO,IAAM,gBAAgB;AAGtB,IAAM,mBAAmB;;;ACAzB,IAAM,aAAN,MAAoB;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACP;AAAA,EAET,YAAY,MAAc;AACxB,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,GAAG;AACxC,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AACA,SAAK,WAAW;AAChB,SAAK,SAAS,IAAI,MAAqB,IAAI;AAAA,EAC7C;AAAA,EAEA,KAAK,MAAe;AAClB,SAAK,QAAQ,KAAK,OAAO,KAAK,SAAS,KAAK,QAAQ,IAAI;AACxD,QAAI,KAAK,QAAQ,KAAK,UAAU;AAC9B,WAAK,SAAS;AAAA,IAChB,OAAO;AACL,WAAK,QAAQ,KAAK,OAAO,KAAK,KAAK;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,IAAI,SAAiB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAe;AACb,WAAO,KAAK,MAAM,GAAG,KAAK,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,GAAG,OAAqB;AACrC,UAAM,SAAS,UAAU,SAAY,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK;AACnE,UAAM,MAAW,CAAC;AAClB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC7D,UAAI,KAAK,OAAQ,KAAI,KAAK,KAAK,QAAQ,KAAK,OAAO,KAAK,KAAK,QAAQ,CAAM;AAAA,IAC7E;AACA,WAAO;AAAA,EACT;AACF;;;AC3CO,IAAM,qBACX;AAGF,IAAM,oBACJ;AACF,IAAM,0BAA0B;AAChC,IAAM,iBAAiB;AACvB,IAAM,cAAc;AAOb,SAAS,eAAe,MAAsB;AACnD,MAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,SAAO,KACJ,QAAQ,yBAAyB,KAAK,aAAa,IAAI,EACvD,QAAQ,gBAAgB,MAAM,aAAa,EAAE,EAC7C,QAAQ,aAAa,aAAa,EAClC,QAAQ,mBAAmB,OAAO,aAAa,EAAE;AACtD;AAYO,SAAS,kBAAkB,KAA6D;AAC7F,MAAI,IAAI,mBAAmB,QAAQ;AACjC,WAAO,EAAE,QAAQ,MAAM,QAAQ,mCAAmC;AAAA,EACpE;AACA,MAAI,IAAI,mBAAmB,SAAS;AAClC,WAAO,EAAE,QAAQ,OAAO,QAAQ,wDAAwD;AAAA,EAC1F;AACA,MAAI,IAAI,aAAa,cAAc;AACjC,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,0BAA0B,IAAI,YAAY,OAAO;AAAA,IAC3D;AAAA,EACF;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;AAGO,SAAS,kBAAkB,SAAsC;AACtE,MAAI,CAAC,QAAS,QAAO;AACrB,SACE,QAAQ,WAAW,MAAM,KACzB,YAAY,SACZ,QAAQ,WAAW,aAAa,KAChC,YAAY;AAEhB;AAGA,SAAS,YAAY,SAAyB;AAC5C,SAAO,QAAQ,WAAW,SAAS,IAAI,QAAQ,MAAM,CAAC,IAAI;AAC5D;AAEA,SAAS,UAAU,IAA2B;AAC5C,QAAM,QAAQ,GAAG,MAAM,GAAG;AAC1B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,OAAO,IAAI;AACrB,QAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,IAAK,QAAO;AACrD,UAAM,MAAM,MAAM;AAAA,EACpB;AACA,SAAO;AACT;AAMO,SAAS,eAAe,SAA6B,SAAqC;AAC/F,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,KAAK,YAAY,OAAO;AAC9B,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,SAAS,GAAG,GAAG;AACxB,UAAI,YAAY,KAAK,MAAM,GAAI,QAAO;AACtC;AAAA,IACF;AACA,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,MAAM,GAAG;AACvC,UAAM,OAAO,OAAO,OAAO;AAC3B,UAAM,IAAI,UAAU,EAAE;AACtB,UAAM,IAAI,UAAU,QAAQ,EAAE;AAC9B,QAAI,MAAM,QAAQ,MAAM,QAAQ,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,GAAI;AAClF,UAAM,OAAO,SAAS,IAAI,IAAK,cAAe,KAAK,SAAW;AAC9D,SAAK,IAAI,UAAU,OAAO,IAAI,UAAU,EAAG,QAAO;AAAA,EACpD;AACA,SAAO;AACT;AAGO,SAAS,uBAAuB,OAA0C;AAC/E,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,KAAK,EAAE,YAAY;AACzC,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,UAAM,MAAM,QAAQ,QAAQ,GAAG;AAC/B,WAAO,QAAQ,KAAK,OAAO,QAAQ,MAAM,GAAG,GAAG;AAAA,EACjD;AACA,SAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK;AAClC;AAGO,SAAS,mBAAmB,UAAkC;AACnE,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,aAAa,eAAe,SAAS,SAAS,YAAY,KAAK,kBAAkB,QAAQ;AAClG;AAOO,SAAS,YAAe,OAAU,UAAkB,oBAAuB;AAChF,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,eAAe,KAAK;AAAA,EAC7B;AACA,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,MAAM,IAAI,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAAA,IACjD;AACA,UAAM,SAAS;AACf,UAAM,OAAO,OAAO,KAAK,MAAM;AAC/B,QACE,KAAK,WAAW,KAChB,KAAK,SAAS,KAAK,KACnB,KAAK,SAAS,OAAO,KACrB,OAAO,OAAO,QAAQ,UACtB;AACA,YAAM,SAAS,QAAQ,KAAK,OAAO,GAAG,IAAI,gBAAgB,OAAO;AACjE,aAAO,EAAE,KAAK,OAAO,KAAK,OAAO,YAAY,QAAQ,OAAO,EAAE;AAAA,IAChE;AACA,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,KAAK,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC5C,UAAI,QAAQ,KAAK,GAAG,GAAG;AACrB,YAAI,GAAG,IAAI;AAAA,MACb,OAAO;AACL,YAAI,GAAG,IAAI,YAAY,GAAG,OAAO;AAAA,MACnC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;AC/JA,yBAA4C;AASrC,IAAM,cAAc;AAoB3B,IAAM,kBAAkB,CAAC,mBAAmB,oBAAoB,aAAa,WAAW;AAExF,SAAS,OAAO,OAAuB;AACrC,aAAO,+BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO;AACnD;AAEA,SAAS,YAAY,GAAW,GAAoB;AAClD,aAAO,oCAAgB,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAC7C;AAEA,SAAS,WAAW,QAA4B,MAAkC;AAChF,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,CAAC,GAAG,GAAG,IAAI,IAAI,KAAK,KAAK,EAAE,MAAM,GAAG;AAC1C,QAAI,MAAM,MAAM;AACd,UAAI;AACF,eAAO,mBAAmB,KAAK,KAAK,GAAG,CAAC;AAAA,MAC1C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAA0D;AAC7E,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC3C;AAEA,SAAS,KAAK,QAAgB,MAAc,SAA8B;AACxE,SAAO,EAAE,IAAI,OAAO,QAAQ,MAAM,QAAQ;AAC5C;AAOO,SAAS,YAAY,SAA8D;AACxF,QAAM,SAAS,kBAAkB,QAAQ,IAAI;AAC7C,QAAM,eAAe,QAAQ,aAAa,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AACpE,QAAM,iBAAiB,QAAQ,eAAe,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,QAAQ,EAAE,CAAC;AAE5F,SAAO,CAAC,QAAQ;AACd,UAAM,SAAS,IAAI,OAAO;AAC1B,UAAM,iBAAiB,kBAAkB,MAAM;AAC/C,UAAM,gBAAgB,kBAAkB,eAAe,QAAQ,QAAQ,sBAAsB;AAE7F,QAAI,UAAU,CAAC,eAAe;AAC5B,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,UAAU,QAAQ,uBAAuB,SAAS,KAAK,CAAC,eAAe;AAC1E,aAAO,KAAK,KAAK,aAAa,gDAAgD;AAAA,IAChF;AAEA,QAAI,UAAU,CAAC,QAAQ,cAAc,CAAC,eAAe,QAAQ,QAAQ,sBAAsB,GAAG;AAC5F,UAAI,gBAAgB,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,MAAS,GAAG;AAC7D,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aAAa,YAAY,IAAI,QAAQ,IAAI;AAC/C,UAAM,WAAW,uBAAuB,UAAU;AAClD,QAAI,aAAa,MAAM;AACrB,YAAM,SACJ,mBAAmB,QAAQ,KAC3B,aAAa,SAAS,QAAQ,KAC7B,CAAC,UAAU,aAAa,WAAW;AACtC,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,SAAS,QAAQ;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,YAAY,IAAI,QAAQ,MAAM;AAC7C,QAAI,WAAW,QAAW;AACxB,UAAI,WAAW;AACf,YAAM,aAAa,OAAO,YAAY,EAAE,QAAQ,QAAQ,EAAE;AAC1D,UAAI,eAAe,SAAS,UAAU,GAAG;AACvC,mBAAW;AAAA,MACb,OAAO;AACL,YAAI;AACF,qBAAW,eAAe,UAAa,IAAI,IAAI,MAAM,EAAE,SAAS,WAAW,YAAY;AAAA,QACzF,QAAQ;AACN,qBAAW;AAAA,QACb;AAAA,MACF;AACA,UAAI,CAAC,UAAU;AACb,eAAO,KAAK,KAAK,aAAa,8CAA8C;AAAA,MAC9E;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW;AACrB,YAAM,aAAa,YAAY,IAAI,QAAQ,aAAa;AACxD,YAAM,SAAS,YAAY,WAAW,SAAS,IAAI,WAAW,MAAM,CAAC,IAAI;AACzE,YAAM,aAAa,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE,aAAa,IAAI,OAAO;AACvF,YAAM,cAAc,WAAW,YAAY,IAAI,QAAQ,MAAM,GAAG,WAAW;AAC3E,YAAM,aAAa;AAAA,QACjB;AAAA,QACA,YAAY,IAAI,QAAQ,gBAAgB,CAAC;AAAA,QACzC;AAAA,QACA,cAAc;AAAA,MAChB;AACA,YAAM,UAAU,WAAW,KAAK,CAAC,MAAM,MAAM,UAAa,YAAY,GAAG,QAAQ,SAAU,CAAC;AAC5F,UAAI,YAAY,QAAW;AACzB,eAAO,KAAK,KAAK,gBAAgB,kCAAkC;AAAA,MACrE;AACA,UAAI,eAAe,QAAQ,YAAY,YAAY;AACjD,eAAO,EAAE,IAAI,MAAM,UAAU,WAAW;AAAA,MAC1C;AAAA,IACF;AAEA,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AACF;;;AC1JA,IAAAA,sBAA4B;AAQrB,IAAM,oBAAN,MAAwB;AAAA,EAG7B,YACmB,OACA,iBAAiB,KAClC;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAJX,SAAS,oBAAI,IAAoB;AAAA,EAOzC,QAAuB;AACrB,SAAK,MAAM;AACX,WAAO,KAAK,OAAO,QAAQ,KAAK,gBAAgB;AAC9C,YAAM,SAAS,KAAK,OAAO,KAAK,EAAE,KAAK,EAAE;AACzC,UAAI,WAAW,OAAW;AAC1B,WAAK,OAAO,OAAO,MAAM;AAAA,IAC3B;AACA,UAAM,YAAQ,iCAAY,EAAE,EAAE,SAAS,KAAK;AAC5C,UAAM,cAAc,KAAK,IAAI,IAAI,KAAK;AACtC,SAAK,OAAO,IAAI,OAAO,WAAW;AAClC,WAAO,EAAE,OAAO,aAAa,OAAO,KAAK,MAAM;AAAA,EACjD;AAAA;AAAA,EAGA,QAAQ,OAAwB;AAC9B,SAAK,MAAM;AACX,UAAM,YAAY,KAAK,OAAO,IAAI,KAAK;AACvC,QAAI,cAAc,OAAW,QAAO;AACpC,SAAK,OAAO,OAAO,KAAK;AACxB,WAAO,KAAK,IAAI,KAAK;AAAA,EACvB;AAAA;AAAA,EAGA,OAAe;AACb,SAAK,MAAM;AACX,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA,EAGA,QAAc;AACZ,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC5C,UAAI,aAAa,IAAK,MAAK,OAAO,OAAO,KAAK;AAAA,IAChD;AAAA,EACF;AACF;;;AChDO,IAAM,mBAAN,MAAuB;AAAA,EACpB,YAAY,oBAAI,IAA6B;AAAA,EAErD,SAAS,UAAgC;AACvC,SAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;AAAA,EAC1C;AAAA,EAEA,IAAI,IAAyC;AAC3C,WAAO,KAAK,UAAU,IAAI,EAAE;AAAA,EAC9B;AAAA,EAEA,MAAiB;AACf,WAAO,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,EAClC;AAAA,EAEA,MAAwB;AACtB,WAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC;AAAA,EACpC;AACF;;;ACrBA,qBAAuD;AAEvD,uBAAmC;AAEnC,IAAM,aAAqC;AAAA,EACzC,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,gBAAgB;AAClB;AAaO,SAAS,mBAAmB,SAAiB,SAAqC;AACvF,QAAM,WAAW,YAAY,KAAK,eAAe,QAAQ,QAAQ,QAAQ,EAAE;AAC3E,QAAM,gBAAY,0BAAQ,SAAS,QAAQ;AAC3C,QAAM,cAAc,QAAQ,SAAS,oBAAG,IAAI,UAAU,UAAU;AAChE,MAAI,cAAc,WAAW,CAAC,UAAU,WAAW,WAAW,GAAG;AAC/D,WAAO;AAAA,EACT;AAEA,MAAI,OAAO;AACX,UAAI,2BAAW,IAAI,SAAK,yBAAS,IAAI,EAAE,YAAY,GAAG;AACpD,eAAO,uBAAK,MAAM,YAAY;AAAA,EAChC;AACA,MAAI,KAAC,2BAAW,IAAI,KAAK,KAAC,yBAAS,IAAI,EAAE,OAAO,GAAG;AACjD,eAAO,uBAAK,SAAS,YAAY;AAAA,EACnC;AACA,MAAI,KAAC,2BAAW,IAAI,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,KAAK,MAAM,KAAK,YAAY,GAAG,CAAC,EAAE,YAAY;AAC1D,QAAMC,YAAO,yBAAS,IAAI;AAC1B,SAAO;AAAA,IACL,aAAS,iCAAiB,IAAI;AAAA,IAC9B,aAAa,WAAW,GAAG,KAAK;AAAA,IAChC,QAAQA,MAAK;AAAA,EACf;AACF;;;AC7CO,IAAM,UAAN,MAAiB;AAAA,EAGtB,YAA6B,MAAyB;AAAzB;AAAA,EAA0B;AAAA,EAA1B;AAAA,EAFrB,QAA+B;AAAA,EAIvC,QAAc;AACZ,QAAI,KAAK,MAAO;AAChB,UAAM,MAAM,MAAY;AACtB,UAAI;AACF,aAAK,KAAK,SAAS,KAAK,KAAK,QAAQ,CAAC;AAAA,MACxC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK,QAAQ,YAAY,KAAK,KAAK,KAAK,UAAU;AAClD,QAAI;AAAA,EACN;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,OAAO;AACd,oBAAc,KAAK,KAAK;AACxB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK,UAAU;AAAA,EACxB;AACF;;;ACpCO,IAAM,iBAAN,MAAqB;AAAA,EAClB,cAAc,KAAK,IAAI;AAAA,EACvB,QAAuB,CAAC;AAAA,EAEhC,KAAK,MAAoB;AACvB,UAAM,OAAO,KAAK,IAAI;AACtB,SAAK,MAAM,KAAK,EAAE,MAAM,MAAM,cAAc,OAAO,KAAK,YAAY,CAAC;AAAA,EACvE;AAAA,EAEA,UAAuB;AACrB,WAAO,EAAE,aAAa,KAAK,aAAa,OAAO,CAAC,GAAG,KAAK,KAAK,EAAE;AAAA,EACjE;AAAA,EAEA,QAAc;AACZ,SAAK,cAAc,KAAK,IAAI;AAC5B,SAAK,QAAQ,CAAC;AAAA,EAChB;AACF;;;ACpBA,qBAAkC;AAIlC,IAAM,SAAqB;AAAA,EACzB,UAAU;AAAA,EACV,WAAW;AAAA,EACX,KAAK;AAAA,EACL,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AACd;AAGO,IAAM,iBAAN,MAA2D;AAAA,EACvD,KAAK;AAAA,EAEN,UAAsC;AAAA,EACtC,SAAqB;AAAA,EAErB,UAAsB;AAC5B,UAAM,QAAQ,QAAQ,YAAY;AAClC,WAAO;AAAA,MACL,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB,KAAK,MAAM;AAAA,MACX,UAAU,MAAM;AAAA,MAChB,cAAU,yBAAS;AAAA,MACnB,aAAS,wBAAQ;AAAA,MACjB,YAAY,KAAK,IAAI;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAM,KAA4B;AAChC,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU,IAAI,QAAQ;AAAA,MACzB,YAAY,IAAI,OAAO;AAAA,MACvB,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,UAAU,CAAC,WAAW;AACpB,aAAK,SAAS;AACd,YAAI,MAAM,KAAK,EAAE,IAAI;AAAA,MACvB;AAAA,IACF,CAAC;AACD,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEA,OAAa;AACX,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAsC;AACpC,WAAO,EAAE,IAAI,MAAM,MAAM,KAAK,OAAO;AAAA,EACvC;AACF;;;ACnCA,IAAM,eAAyB;AAAA,EAC7B,OAAO,MAAM,QAAQ,OAAO,OAAO,IAAI;AAAA,EACvC,UAAU,MAAM,QAAQ,SAAS;AACnC;AAEA,IAAMC,UAAkB,EAAE,aAAa,GAAG,eAAe,GAAG,cAAc,GAAG,YAAY,EAAE;AAO3F,SAAS,aAAa,GAAmB;AACvC,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,CAAC,CAAC;AACrC;AAGO,IAAM,cAAN,MAAqD;AAAA,EAO1D,YAA6B,QAAkB,cAAc;AAAhC;AAAA,EAAiC;AAAA,EAAjC;AAAA,EANpB,KAAK;AAAA,EAEN,UAAmC;AAAA,EACnC,SAAkBA;AAAA,EAClB,OAAwB;AAAA;AAAA,EAKhC,UAAmB;AACjB,UAAM,MAAM,KAAK,MAAM,SAAS;AAChC,UAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,QAAI,SAAkB,EAAE,GAAGA,SAAQ,YAAY,KAAK,IAAI,EAAE;AAC1D,QAAI,KAAK,MAAM;AACb,YAAM,SAAS,IAAI,OAAO,KAAK,KAAK,IAAI;AACxC,YAAM,WAAW,IAAI,SAAS,KAAK,KAAK,IAAI;AAC5C,YAAM,YAAY,OAAO,OAAO,KAAK,KAAK,IAAI;AAC9C,UAAI,YAAY,GAAG;AACjB,iBAAS;AAAA,UACP,aAAa,aAAc,SAAS,YAAa,GAAG;AAAA,UACpD,eAAe,aAAc,WAAW,YAAa,GAAG;AAAA,UACxD,cAAc,cAAe,SAAS,YAAY,YAAa,GAAG;AAAA,UAClE,YAAY,KAAK,IAAI;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AACA,SAAK,OAAO,EAAE,KAAK,KAAK;AACxB,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAA4B;AAChC,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU,IAAI,QAAQ;AAAA,MACzB,YAAY,IAAI,OAAO;AAAA,MACvB,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,UAAU,CAAC,WAAW;AACpB,aAAK,SAAS;AACd,YAAI,MAAM,KAAK,EAAE,IAAI;AAAA,MACvB;AAAA,IACF,CAAC;AACD,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEA,OAAa;AACX,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAmC;AACjC,WAAO,EAAE,IAAI,MAAM,MAAM,KAAK,OAAO;AAAA,EACvC;AACF;;;AClFA,IAAMC,gBAA+B;AAAA,EACnC,OAAO,MAAM,QAAQ,OAAO,OAAO;AACrC;AAEA,IAAM,mBAAmB;AAEzB,IAAMC,UAA0B;AAAA,EAC9B,WAAW;AAAA,EACX,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AACd;AAOO,IAAM,uBAAN,MAAsE;AAAA,EAQ3E,YAA6B,QAAwBD,eAAc;AAAtC;AAAA,EAAuC;AAAA,EAAvC;AAAA,EAPpB,KAAK;AAAA,EAEN,UAA2C;AAAA,EAC3C,UAAoB,CAAC;AAAA,EACrB,SAA0BC;AAAA,EAC1B,aAA4B;AAAA;AAAA,EAKpC,QAAQ,YAAqC;AAC3C,UAAM,QAAQ,KAAK,MAAM,MAAM;AAC/B,QAAI,QAAQ;AACZ,QAAI,KAAK,eAAe,MAAM;AAC5B,YAAM,YAAY,OAAO,QAAQ,KAAK,UAAU,IAAI;AACpD,cAAQ,KAAK,IAAI,GAAG,YAAY,UAAU;AAAA,IAC5C;AACA,SAAK,aAAa;AAElB,SAAK,QAAQ,KAAK,KAAK;AACvB,QAAI,KAAK,QAAQ,SAAS,iBAAkB,MAAK,QAAQ,MAAM;AAE/D,UAAM,QAAQ,KAAK,QAAQ;AAC3B,UAAM,QAAQ,KAAK,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC;AACxD,UAAM,SAA0B;AAAA,MAC9B,WAAW;AAAA,MACX,OAAO,KAAK,IAAI,GAAG,KAAK,OAAO;AAAA,MAC/B,OAAO,QAAQ,IAAI,QAAQ,QAAQ;AAAA,MACnC;AAAA,MACA,YAAY,KAAK,IAAI;AAAA,IACvB;AACA,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAA4B;AAChC,QAAI,KAAK,QAAS;AAClB,SAAK,aAAa,KAAK,MAAM,MAAM;AACnC,SAAK,UAAU,IAAI,QAAQ;AAAA,MACzB,YAAY,IAAI,OAAO;AAAA,MACvB,SAAS,MAAM,KAAK,QAAQ,IAAI,OAAO,cAAc;AAAA,MACrD,UAAU,CAAC,WAAW;AACpB,aAAK,SAAS;AACd,YAAI,MAAM,KAAK,EAAE,IAAI;AAAA,MACvB;AAAA,IACF,CAAC;AACD,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEA,OAAa;AACX,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAA2C;AACzC,WAAO,EAAE,IAAI,MAAM,MAAM,KAAK,OAAO;AAAA,EACvC;AACF;;;AC5EA,IAAM,YAAY;AAClB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AACzB,IAAM,iBAAiB;AAWvB,SAAS,YAAe,SAAqB,IAAwB;AACnE,SAAO,IAAI,QAAW,CAACC,UAAS,WAAW;AACzC,UAAM,QAAQ,WAAW,MAAM,OAAO,IAAI,MAAM,mBAAmB,EAAE,IAAI,CAAC,GAAG,EAAE;AAC/E,YAAQ;AAAA,MACN,CAAC,UAAU;AACT,qBAAa,KAAK;AAClB,QAAAA,SAAQ,KAAK;AAAA,MACf;AAAA,MACA,CAAC,QAAiB;AAChB,qBAAa,KAAK;AAClB,eAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAGO,IAAM,iBAAN,MAA2D;AAAA,EAKhE,YAA6B,SAAsC,CAAC,GAAG;AAA1C;AAAA,EAA2C;AAAA,EAA3C;AAAA,EAJpB,KAAK;AAAA,EAEN,QAA6D;AAAA,EAIrE,MAAc,YAA0C;AACtD,UAAM,QAAQ,OAAO,KAAK,KAAK,MAAM;AACrC,QAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,QAAI,KAAK,SAAS,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,eAAgB,QAAO,KAAK,MAAM;AACjF,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,MAAM,IAAI,OAAO,SAAqC;AACpD,cAAM,UAAU,KAAK,IAAI;AACzB,YAAI;AACF,gBAAM,UAAU,MAAM;AAAA,YACpB,QAAQ,QAAQ,EAAE,KAAK,MAAM,KAAK,OAAO,IAAI,EAAG,CAAC;AAAA,YACjD;AAAA,UACF;AACA,cAAI,YAAY,MAAO,OAAM,IAAI,MAAM,sBAAsB;AAC7D,iBAAO,EAAE,MAAM,QAAQ,MAAM,YAAY,KAAK,IAAI,IAAI,QAAQ;AAAA,QAChE,SAAS,KAAK;AACZ,iBAAO;AAAA,YACL;AAAA,YACA,QAAQ;AAAA,YACR,YAAY,KAAK,IAAI,IAAI;AAAA,YACzB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACxD;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AACA,SAAK,QAAQ,EAAE,IAAI,KAAK,IAAI,GAAG,QAAQ;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,KAA+D;AACvE,UAAM,OAAO,IAAI,MAAM,YAAY;AACnC,UAAM,SAAS,IAAI,MAAM;AACzB,UAAM,QAAQ,OAAO,MAAM,cAAc,WAAW,KAAK,YAAY;AACrE,UAAM,WAAW,OAAO,QAAQ,aAAa,WAAW,OAAO,WAAW;AAC1E,UAAM,MAAM,OAAO,QAAQ,QAAQ,WAAW,OAAO,MAAM;AAC3D,UAAM,oBAAoB,WAAW,IAAK,MAAM,WAAY,MAAM;AAClE,UAAM,SAAS,MAAM,KAAK,UAAU;AAEpC,QAAI;AACJ,QAAI;AACJ,QAAI,UAAU,MAAM;AAClB,eAAS;AACT,qBAAe;AAAA,IACjB,WAAW,QAAQ,WAAW;AAC5B,eAAS;AACT,qBAAe,kBAAkB,MAAM,QAAQ,CAAC,CAAC,YAAY,SAAS;AAAA,IACxE,WAAW,QAAQ,iBAAiB;AAClC,eAAS;AACT,qBAAe,kBAAkB,MAAM,QAAQ,CAAC,CAAC,cAAc,SAAS,UAAU,eAAe;AAAA,IACnG,OAAO;AACL,eAAS;AACT,qBAAe,kBAAkB,MAAM,QAAQ,CAAC,CAAC,YAAY,eAAe;AAAA,IAC9E;AAEA,UAAM,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM;AACrD,QAAI,KAAK,SAAS,GAAG;AACnB,eAAS;AACT,qBAAe,oBAAoB,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IACvE;AAEA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,eAAe,KAAK,MAAM,QAAQ,OAAO,CAAC;AAAA,QAC1C,KAAK,QAAQ;AAAA,QACb,aAAa,QAAQ;AAAA,QACrB,UAAU,QAAQ;AAAA,QAClB,gBAAgB;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACzHA,sCAA+B;AAC/B,uBAAiB;AACjB,wBAAkB;AAClB,yBAAsC;AAItC,IAAM,qBAAqB;AAK3B,IAAM,YAAY,oBAAI,IAAc;AACpC,IAAM,YAAY,oBAAI,IAAmB;AACzC,IAAI,oBAAyC;AAE7C,SAAS,eACP,iBACA,MACwC;AACxC,MAAI;AACF,QAAI,SAAS;AACb,QAAI,OAAO;AACX,QAAI,OAAO;AACX,UAAM,CAAC,OAAO,MAAM,IAAI;AACxB,UAAM,OACJ,OAAO,UAAU,YAAY,iBAAiB,MACzC,SACA;AACP,QAAI,OAAO,UAAU,YAAY,iBAAiB,KAAK;AACrD,YAAM,SAAS,IAAI,IAAI,MAAM,SAAS,CAAC;AACvC,aAAO,OAAO;AACd,aAAO,OAAO;AAAA,IAChB;AACA,QAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAI,OAAO,KAAK,WAAW,SAAU,UAAS,KAAK,OAAO,YAAY;AACtE,UAAI,OAAO,UAAU,YAAY,EAAE,iBAAiB,MAAM;AACxD,cAAM,OAAQ,KAAK,YAAY,KAAK,QAAQ;AAC5C,eAAO,KAAK,OAAO,GAAG,IAAI,IAAI,OAAO,KAAK,IAAI,CAAC,KAAK;AACpD,eAAO,OAAO,KAAK,SAAS,WAAY,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAAO;AAC1E,cAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AACrE,eAAO,EAAE,QAAQ,KAAK,GAAG,QAAQ,KAAK,IAAI,GAAG,IAAI,GAAG;AAAA,MACtD;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,KAAK,GAAG,eAAe,KAAK,IAAI,GAAG,IAAI,GAAG;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,KAAK,OAA+C;AAC3D,aAAW,UAAU,WAAW;AAC9B,QAAI;AACF,aAAO,KAAK;AAAA,IACd,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,SAAS,WAAW,KAAiC,UAAwB;AAC3E,aAAW,QAAQ,CAAC,WAAW,KAAK,GAAY;AAC9C,UAAM,MAAM,GAAG,QAAQ,GAAG,IAAI;AAC9B,UAAM,WAAW,IAAI,IAAI;AACzB,cAAU,IAAI,KAAK,QAAQ;AAC3B,UAAM,UAAU,YAA4B,MAA0B;AACpE,YAAM,MAAM,SAAS,MAAM,MAAM,IAAI;AACrC,YAAM,SAAS,UAAU,OAAO,IAAI,eAAe,UAAU,IAAI,IAAI;AACrE,UAAI,UAAU,OAAO,OAAO,IAAI,SAAS,YAAY;AACnD,cAAM,UAAU,QAAQ,OAAO,OAAO;AACtC,cAAM,cAAc,KAAK,IAAI;AAC7B,YAAI,OAAO;AACX,cAAM,SAAS,CAAC,QAAuB,UAAyB;AAC9D,cAAI,KAAM;AACV,iBAAO;AACP,eAAK;AAAA,YACH,GAAG;AAAA,YACH;AAAA,YACA,YAAY,OAAO,QAAQ,OAAO,OAAO,IAAI,OAAO,IAAI;AAAA,YACxD;AAAA,YACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,UAC3B,CAAC;AAAA,QACH;AACA,YAAI,KAAK,YAAY,CAAC,QAA8B;AAClD,gBAAM,SAAS,IAAI,cAAc;AACjC,cAAI,KAAK,OAAO,MAAM,OAAO,MAAM,CAAC;AACpC,cAAI,KAAK,SAAS,MAAM,OAAO,MAAM,CAAC;AAAA,QACxC,CAAC;AACD,YAAI,KAAK,SAAS,CAAC,QAAe,OAAO,MAAM,IAAI,OAAO,CAAC;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AACA,IAAC,IAAyC,IAAI,IAAI;AAAA,EACpD;AAEA,gDAAsB;AACxB;AAEA,SAAS,aAAa,KAAiC,UAAwB;AAC7E,aAAW,QAAQ,CAAC,WAAW,KAAK,GAAY;AAC9C,UAAM,WAAW,UAAU,IAAI,GAAG,QAAQ,GAAG,IAAI,EAAE;AACnD,QAAI,SAAU,CAAC,IAAyC,IAAI,IAAI;AAChE,cAAU,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE;AAAA,EACvC;AACA,gDAAsB;AACxB;AAQA,SAAS,kBAA8B;AACrC,QAAM,UAAU,oBAAI,QAA4C;AAChE,QAAM,WAAW,oBAAI,QAAwB;AAC7C,QAAM,SAAS,CAAC,SAAyD;AAAA,IACvE,SAAS,IAAI,UAAU,OAAO,YAAY;AAAA,IAC1C,KAAK,GAAG,OAAO,IAAI,UAAU,EAAE,CAAC,IAAI,IAAI,QAAQ,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,EACpE;AACA,QAAM,WAAW,CAAC,YAA2B;AAC3C,UAAM,UAAW,QAAiC;AAClD,QAAI,QAAS,SAAQ,IAAI,SAAS,EAAE,IAAI,QAAQ,OAAO,OAAO,GAAG,IAAI,KAAK,IAAI,EAAE,CAAC;AAAA,EACnF;AACA,QAAM,YAAY,CAAC,YAA2B;AAC5C,UAAM,EAAE,SAAS,SAAS,IAAI;AAI9B,QAAI,WAAW,OAAO,UAAU,eAAe,UAAU;AACvD,eAAS,IAAI,SAAS,SAAS,UAAU;AAAA,IAC3C;AAAA,EACF;AACA,QAAM,SAAS,CAAC,SAA6B,UAAyB;AACpE,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ,QAAQ,IAAI,OAAO;AACjC,QAAI,CAAC,MAAO;AACZ,YAAQ,OAAO,OAAO;AACtB,SAAK;AAAA,MACH,GAAG,OAAO,OAAwB;AAAA,MAClC,QAAQ,SAAS,IAAI,OAAO,KAAK;AAAA,MACjC,YAAY,OAAO,QAAQ,OAAO,OAAO,IAAI,MAAM,EAAE,IAAI;AAAA,MACzD,aAAa,MAAM;AAAA,MACnB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IAC3B,CAAC;AAAA,EACH;AACA,QAAM,aAAa,CAAC,YAA2B,OAAQ,QAAiC,OAAO;AAC/F,QAAM,UAAU,CAAC,YAA2B;AAC1C,UAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,WAAO,SAAS,OAAO,WAAW,gBAAgB;AAAA,EACpD;AAEA,kCAAAC,QAAmB,UAAU,yBAAyB,QAAQ;AAC9D,kCAAAA,QAAmB,UAAU,0BAA0B,SAAS;AAChE,kCAAAA,QAAmB,UAAU,2BAA2B,UAAU;AAClE,kCAAAA,QAAmB,UAAU,wBAAwB,OAAO;AAC5D,SAAO,MAAM;AACX,oCAAAA,QAAmB,YAAY,yBAAyB,QAAQ;AAChE,oCAAAA,QAAmB,YAAY,0BAA0B,SAAS;AAClE,oCAAAA,QAAmB,YAAY,2BAA2B,UAAU;AACpE,oCAAAA,QAAmB,YAAY,wBAAwB,OAAO;AAAA,EAChE;AACF;AAOO,SAAS,kBAAkB,QAA8B;AAC9D,QAAM,QAAQ,UAAU,SAAS;AACjC,YAAU,IAAI,MAAM;AACpB,MAAI,OAAO;AACT,eAAW,iBAAAC,SAAM,OAAO;AACxB,eAAW,kBAAAC,SAAO,QAAQ;AAC1B,QAAI;AACF,0BAAoB,gBAAgB;AAAA,IACtC,QAAQ;AACN,0BAAoB;AAAA,IACtB;AAAA,EACF;AACA,SAAO,MAAM;AACX,QAAI,CAAC,UAAU,OAAO,MAAM,KAAK,UAAU,OAAO,EAAG;AACrD,iBAAa,iBAAAD,SAAM,OAAO;AAC1B,iBAAa,kBAAAC,SAAO,QAAQ;AAC5B,wBAAoB;AACpB,wBAAoB;AAAA,EACtB;AACF;AAGO,IAAM,mBAAN,MAA+D;AAAA,EAC3D,KAAK;AAAA,EAEN;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAA+B;AAAA,EAEvC,YAAY,MAAc;AACxB,SAAK,SAAS,IAAI,WAAiC,IAAI;AAAA,EACzD;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU,kBAAkB,CAAC,UAAU,KAAK,OAAO,KAAK,CAAC;AAAA,EAChE;AAAA,EAEA,OAAa;AACX,SAAK,UAAU;AACf,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,OAAO,OAA+C;AACpD,SAAK,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,GAAG,MAAM,CAAC;AAC9C,SAAK,UAAU;AACf,QAAI,MAAM,SAAU,MAAM,WAAW,QAAQ,MAAM,UAAU,IAAM,MAAK,UAAU;AAAA,EACpF;AAAA,EAEA,MAAwC;AACtC,UAAM,UAAU,KAAK,OAAO,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,SAAS,kBAAkB,CAAC;AACtF,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,OAAO,KAAK,SAAS,GAAG,QAAQ,KAAK,QAAQ,QAAQ,EAAE;AAAA,EACpF;AACF;;;AC/NA,sBAAmC;AACnC,IAAAC,kBAAuB;AACvB,IAAAC,oBAAqB;AACrB,qBAAkC;AASlC,SAAS,qBAA6B;AACpC,SAAO,QAAQ,IAAI,gCAA4B,4BAAK,wBAAO,GAAG,aAAa;AAC7E;AAOO,IAAM,uBAAN,MAA4E;AAAA,EACxE,KAAK;AAAA,EAEN,eAAwC;AAAA,EAEhD,MAAiD;AAC/C,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,WAAW,MAAM,cAAc,KAAK,aAAa,EAAE;AAAA,EAChF;AAAA,EAEA,MAAM,aAAa,KAAiE;AAClF,UAAM,MAAM,IAAI,OAAO,mBAAmB,mBAAmB;AAC7D,UAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AAC3D,UAAM,WAAW,eAAe,QAAQ,GAAG,IAAI,KAAK;AACpD,UAAM,eAAW,wBAAK,KAAK,QAAQ;AACnC,QAAI;AAEF,gBAAM,uBAAM,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACjD,4CAAkB,QAAQ;AAC1B,gBAAM,uBAAM,UAAU,GAAK,EAAE,MAAM,MAAM,MAAS;AAClD,YAAM,WAAW,UAAM,sBAAK,QAAQ;AACpC,YAAM,OAAyB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA,WAAW,SAAS;AAAA,QACpB,aAAa,KAAK,IAAI;AAAA,MACxB;AACA,WAAK,eAAe;AACpB,aAAO,EAAE,IAAI,MAAM,KAAK;AAAA,IAC1B,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AClDA,IAAMC,sBAAqB;AAC3B,IAAM,kBAAkB;AAExB,SAAS,WAAW,QAAkB,GAAmB;AACvD,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAM,QAAQ,KAAK,IAAI,OAAO,SAAS,GAAG,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,OAAO,MAAM,IAAI,CAAC,CAAC;AACvF,SAAO,OAAO,KAAK,KAAK;AAC1B;AAGA,SAAS,SAAS,MAAsB;AACtC,SAAO,KACJ,MAAM,GAAG,EACT;AAAA,IAAI,CAAC,QACJ,QAAQ,KAAK,GAAG,KAAK,+BAA+B,KAAK,GAAG,KAAK,kBAAkB,KAAK,GAAG,IACvF,QACA;AAAA,EACN,EACC,KAAK,GAAG;AACb;AAEO,SAAS,kBAAkB,SAAmD;AACnF,QAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACvE,QAAM,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,EAAE;AAC1D,QAAM,SAAS,oBAAI,IAA4E;AAC/F,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,GAAG,KAAK,MAAM,EAAE,SAAS,GAAG,CAAC;AAC5C,QAAI,UAAU,SAAU,UAAS,MAAM,KAAK;AAC5C,UAAM,OAAO,SAAS,EAAE,IAAI;AAC5B,UAAM,MAAM,GAAG,EAAE,MAAM,IAAI,IAAI;AAC/B,UAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,EAAE,QAAQ,EAAE,QAAQ,MAAM,IAAI,CAAC,GAAG,QAAQ,EAAE;AAC7E,UAAM,GAAG,KAAK,EAAE,UAAU;AAC1B,QAAI,EAAE,UAAU,IAAK,OAAM,UAAU;AACrC,WAAO,IAAI,KAAK,KAAK;AAAA,EACvB;AACA,QAAM,SAAsB,CAAC,GAAG,OAAO,OAAO,CAAC,EAC5C,IAAI,CAAC,MAAM;AACV,UAAM,SAAS,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC7C,WAAO;AAAA,MACL,QAAQ,EAAE;AAAA,MACV,MAAM,EAAE;AAAA,MACR,OAAO,EAAE,GAAG;AAAA,MACZ,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG;AAAA,MAC9C,OAAO,WAAW,QAAQ,IAAI;AAAA,MAC9B,QAAQ,EAAE;AAAA,IACZ;AAAA,EACF,CAAC,EACA,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,eAAe;AAC3B,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,QAAQ,SAAS,KAAK;AAAA,IACtB,WAAW,QAAQ,WAAW,IAAI,IAAI,SAAS,KAAK,IAAI,QAAQ;AAAA,IAChE,OAAO,UAAU,WAAW,IAAI,IAAI,UAAU,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,UAAU;AAAA,IACrF,OAAO,WAAW,WAAW,GAAG;AAAA,IAChC,OAAO,WAAW,WAAW,IAAI;AAAA,IACjC,OAAO,WAAW,WAAW,IAAI;AAAA,IACjC;AAAA,IACA;AAAA,EACF;AACF;AAGO,IAAM,mBAAN,MAA+D;AAAA,EAC3D,KAAK;AAAA,EAEN;AAAA,EACA,SAAS;AAAA,EAEjB,YAAY,MAAc;AACxB,SAAK,SAAS,IAAI,WAAyB,IAAI;AAAA,EACjD;AAAA,EAEA,OAAO,OAAuC;AAC5C,SAAK,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,GAAG,MAAM,CAAC;AAC9C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,IAAI,SAAiB;AACnB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,MAAwC;AACtC,UAAM,UAAU,KAAK,OAAO,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,SAASA,mBAAkB,CAAC;AACtF,UAAM,UAAU,kBAAkB,KAAK,OAAO,QAAQ,CAAC;AACvD,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,OAAO,KAAK,OAAO,QAAQ,SAAS,QAAQ,EAAE;AAAA,EAC3E;AACF;;;AC9FA,IAAM,iBAAiB;AAOhB,IAAM,kBAAN,MAA6D;AAAA,EACzD,KAAK;AAAA,EAEN,UAA2B,CAAC;AAAA,EACnB;AAAA,EAEjB,YAAY,QAAsB,KAAK,KAAK;AAC1C,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAGA,OAAO,QAAsB;AAC3B,UAAM,KAAK,KAAK,MAAM,KAAK,MAAM,IAAI,GAAI,IAAI;AAC7C,UAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC;AACjD,QAAI,QAAQ,KAAK,OAAO,IAAI;AAC1B,WAAK,YAAY;AACjB,UAAI,UAAU,IAAK,MAAK,UAAU;AAClC;AAAA,IACF;AACA,SAAK,QAAQ,KAAK,EAAE,IAAI,UAAU,GAAG,QAAQ,UAAU,MAAM,IAAI,EAAE,CAAC;AACpE,QAAI,KAAK,QAAQ,SAAS,iBAAiB,GAAG;AAC5C,WAAK,QAAQ,OAAO,GAAG,KAAK,QAAQ,SAAS,cAAc;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,MAAuC;AACrC,UAAM,MAAM,KAAK,MAAM,KAAK,MAAM,IAAI,GAAI,IAAI;AAC9C,UAAM,QAAQ,OAAO,iBAAiB,KAAK;AAC3C,UAAM,OAAO,IAAI,IAA2B,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAC9E,UAAM,UAA2B,CAAC;AAClC,aAAS,KAAK,OAAO,MAAM,KAAK,MAAM,KAAM;AAC1C,YAAM,WAAW,KAAK,IAAI,EAAE;AAC5B,cAAQ,KAAK,WAAW,EAAE,GAAG,SAAS,IAAI,EAAE,IAAI,UAAU,GAAG,QAAQ,EAAE,CAAC;AAAA,IAC1E;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,QAAQ,EAAE;AAAA,EACvC;AACF;;;AC3CA,SAAS,UAAU,OAAwB;AACzC,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,UAAU,QAA6C;AAC9D,SAAO,OAAO,QAAQ,MAAM,EACzB,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,EAAE,KAAK,OAAO,UAAU,KAAK,EAAE,EAAE,EACxD,KAAK,CAAC,GAAG,MAAO,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAE;AAChE;AAMO,IAAM,cAAN,MAAqD;AAAA,EACjD,KAAK;AAAA,EAEd,IAAI,KAAmD;AACrD,UAAM,cAAc,UAAU,IAAI,GAA8B;AAChE,UAAM,YAAY,IAAI,MAAM,YAAY;AACxC,QAAI,SAA4B;AAChC,QAAI,cAAc,QAAW;AAC3B,YAAM,WAAW,OAAO,cAAc,aAAa,UAAU,IAAI;AACjE,UAAI,aAAa,UAAa,aAAa,MAAM;AAC/C,iBAAS,UAAU,QAAmC;AAAA,MACxD;AAAA,IACF;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,aAAa,OAAO,EAAE;AAAA,EACnD;AACF;;;ACpBA,SAAS,SAAS,QAAgB,KAAqB;AACrD,MAAI,CAAC,OAAO,QAAQ,IAAK,QAAO,UAAU;AAC1C,SAAO,GAAG,OAAO,QAAQ,QAAQ,EAAE,CAAC,IAAI,IAAI,QAAQ,QAAQ,EAAE,CAAC;AACjE;AAYO,SAAS,cAAc,QAA+B;AAC3D,QAAM,MAAoB,CAAC;AAC3B,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI;AACJ,MAAI;AACF,YAAS,QAA4C;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAElC,QAAM,OAAO,CAAC,QAAuB,WAAyB;AAC5D,eAAW,SAAS,QAAQ;AAC1B,UAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,UAAI,MAAM,SAAS,OAAO,MAAM,MAAM,SAAS,UAAU;AACvD,cAAM,OAAO,SAAS,QAAQ,MAAM,MAAM,IAAI;AAC9C,cAAM,UAAU,OAAO,QAAQ,MAAM,MAAM,WAAW,CAAC,CAAC,EACrD,OAAO,CAAC,CAAC,EAAE,OAAO,MAAM,OAAO,EAC/B,IAAI,CAAC,CAAC,MAAM,MAAM,OAAO,YAAY,CAAC;AACzC,cAAM,UAAU,MAAM,MAAM,QAAQ,CAAC,GAAG,QAAQ,QAAQ;AACxD,YAAI,QAAQ,WAAW,EAAG;AAC1B,mBAAW,UAAU,SAAS;AAC5B,gBAAM,MAAM,GAAG,MAAM,IAAI,IAAI;AAC7B,cAAI,KAAK,IAAI,GAAG,EAAG;AACnB,eAAK,IAAI,GAAG;AACZ,cAAI,KAAK,EAAE,QAAQ,MAAM,QAAQ,CAAC;AAAA,QACpC;AACA;AAAA,MACF;AACA,YAAM,SAAS,MAAM,QAAQ;AAC7B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,aAAK,QAAQ,SAAS,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACF,SAAK,OAAwB,EAAE;AAAA,EACjC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,MAAI,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,cAAc,EAAE,MAAM,CAAC;AACnF,SAAO;AACT;AAQO,IAAM,iBAAN,MAA2D;AAAA,EACvD,KAAK;AAAA,EAEN,QAAQ,oBAAI,QAA8B;AAAA,EAElD,IACE,KAC0F;AAC1F,UAAM,SAAS,IAAI,MAAM,eAAe;AACxC,QAAI,WAAW,QAAW;AACxB,YAAMC,UAAS,OAAO,WAAW,aAAa,OAAO,IAAI;AACzD,aAAO,EAAE,IAAI,MAAM,MAAM,EAAE,QAAAA,QAAO,EAAE;AAAA,IACtC;AACA,UAAM,SAAS,IAAI,MAAM,gBAAgB;AACzC,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO,WAAW,YAAY,OAAO,WAAW,YAAY;AAC9D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,KAAK,MAAM,IAAI,MAAgB;AAC5C,QAAI,CAAC,QAAQ;AACX,eAAS,cAAc,MAAM;AAC7B,WAAK,MAAM,IAAI,QAAkB,MAAM;AAAA,IACzC;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE;AAAA,EACtC;AACF;;;AClHA,IAAM,kBAAoD;AAAA,EACxD,CAAC,SAAS,OAAO;AAAA,EACjB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,OAAO,MAAM;AAAA,EACd,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,SAAS,OAAO;AACnB;AAEA,SAAS,UAAU,KAAsB;AACvC,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,MAAI;AACF,UAAM,OAAO,KAAK,UAAU,GAAG;AAC/B,WAAO,SAAS,SAAY,OAAO,GAAG,IAAI;AAAA,EAC5C,QAAQ;AACN,WAAO,OAAO,GAAG;AAAA,EACnB;AACF;AAEA,IAAI,OAAO;AACX,IAAM,SAAS,oBAAI,IAAkD;AACrE,IAAM,WAAW,oBAAI,IAAiD;AACtE,IAAIC,aAA0E,CAAC;AAE/E,SAAS,aAA2D;AAClE,SAAO;AACT;AAEA,SAAS,kBAAwB;AAC/B,aAAW,CAAC,QAAQ,KAAK,KAAK,iBAAiB;AAC7C,UAAM,WAAW,WAAW,EAAE,MAAM;AACpC,IAAAA,WAAU,MAAM,IAAI;AACpB,UAAM,UAAU,IAAI,SAA0B;AAC5C,iBAAW,QAAQ,QAAQ;AACzB,YAAI;AACF,eAAK,EAAE,OAAO,SAAS,KAAK,IAAI,SAAS,EAAE,KAAK,GAAG,EAAE,CAAC;AAAA,QACxD,QAAQ;AAAA,QAER;AAAA,MACF;AACA,UAAI,SAAU,UAAS,GAAG,IAAI;AAAA,IAChC;AACA,aAAS,IAAI,QAAQ,OAAO;AAC5B,eAAW,EAAE,MAAM,IAAI;AAAA,EACzB;AACF;AAEA,SAAS,oBAA0B;AACjC,aAAW,CAAC,MAAM,KAAK,iBAAiB;AACtC,UAAM,UAAU,WAAW,EAAE,MAAM;AACnC,QAAI,YAAY,SAAS,IAAI,MAAM,GAAG;AACpC,YAAM,WAAWA,WAAU,MAAM;AACjC,UAAI,UAAU;AACZ,mBAAW,EAAE,MAAM,IAAI;AAAA,MACzB,OAAO;AACL,eAAO,WAAW,EAAE,MAAM;AAAA,MAC5B;AAAA,IACF;AAAA,EAGF;AACA,WAAS,MAAM;AACf,EAAAA,aAAY,CAAC;AACf;AASO,SAAS,iBAAiB,MAAgE;AAC/F,UAAQ;AACR,SAAO,IAAI,IAAI;AACf,MAAI,SAAS,EAAG,iBAAgB;AAChC,SAAO,MAAM;AACX,WAAO,OAAO,IAAI;AAClB,WAAO,KAAK,IAAI,GAAG,OAAO,CAAC;AAC3B,QAAI,SAAS,EAAG,mBAAkB;AAAA,EACpC;AACF;AAOO,IAAM,eAAN,MAAuD;AAAA,EACnD,KAAK;AAAA,EAEN;AAAA,EACA,UAA+B;AAAA,EAEvC,YAAY,OAAe,kBAAkB;AAC3C,SAAK,SAAS,IAAI,WAAqB,IAAI;AAAA,EAC7C;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU,iBAAiB,CAAC,EAAE,OAAO,QAAQ,MAAM;AACtD,WAAK,OAAO,KAAK,EAAE,OAAO,SAAS,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IAC5D,CAAC;AAAA,EACH;AAAA,EAEA,OAAa;AACX,SAAK,UAAU;AACf,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,UAAU,OAAmD;AAC3D,SAAK,OAAO,KAAK,EAAE,GAAG,OAAO,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,EACtD;AAAA,EAEA,IAAI,KAAoD;AACtD,QAAI,UAAU,KAAK,OAAO,QAAQ;AAClC,UAAM,QAAQ,IAAI,OAAO;AACzB,QAAI,MAAO,WAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK;AAC5D,UAAM,QAAQ,IAAI,OAAO;AACzB,QAAI,OAAO;AACT,YAAM,SAAS,MAAM,YAAY;AACjC,gBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,QAAQ,YAAY,EAAE,SAAS,MAAM,CAAC;AAAA,IAC1E;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,EAAE,QAAQ,EAAE;AAAA,EACvC;AACF;;;ACrHO,SAAS,SACd,KACA,eAAuC,CAAC,GAC7B;AACX,MAAI,SAAS;AACb,MAAI,UAAU,KAAK;AAAA,IACjB,GAAG;AAAA,IACH,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,YAAY;AAAA,IACZ,qBAAqB;AAAA,EACvB,CAAC;AACD,MAAI,eAAe;AAEnB,SAAO;AAAA,IACL,KAAK,SAAwB;AAC3B,UAAI,UAAU,IAAI,UAAW;AAC7B,UAAI,MAAM,SAAS,KAAK,UAAU,OAAO,CAAC;AAAA;AAAA,CAAM;AAAA,IAClD;AAAA,IACA,YAAkB;AAChB,UAAI,UAAU,IAAI,UAAW;AAC7B,UAAI,MAAM,WAAW;AAAA,IACvB;AAAA,IACA,QAAc;AACZ,UAAI,UAAU,IAAI,UAAW;AAC7B,eAAS;AACT,UAAI,IAAI;AAAA,IACV;AAAA,EACF;AACF;;;ACxCA,IAAAC,kBAAuB;AACvB,IAAAC,oBAA8B;AAmH9B,IAAM,kBAAc,2BAAQ,WAAW,MAAM,QAAQ;AAErD,IAAM,mBAAmB;AAMlB,SAAS,kBAAqB,UAA0B,OAAO,MAAc;AAClF,SAAO,KAAK,UAAU,OAAO,YAAY,QAAQ,IAAI,QAAQ;AAC/D;AAEA,IAAM,kBAAkB,oBAAI,IAAY;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB,oBAAI,IAAI,CAAC,UAAU,QAAQ,eAAe,CAAC;AAEtE,IAAM,mBAA2C;AAAA,EAC/C,0BAA0B;AAAA,EAC1B,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,gCAAgC;AAClC;AACA,IAAM,MACJ;AAGF,SAAS,SAAS,OAA6B,KAAmC;AAChF,MAAI,MAAO,QAAO;AAClB,UAAQ,OAAO,IACZ,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACnB;AAEA,SAAS,WAAW,KAA8B;AAChD,QAAM,MAAO,IAAmD,eAAe,IAAI,OAAO;AAC1F,MAAI;AACF,WAAO,IAAI,IAAI,KAAK,kBAAkB,EAAE;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc,OAAuB;AAC5C,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAQ,WAAW,GAAG,EAAG,OAAM,IAAI,MAAM,yCAAyC,KAAK,GAAG;AAC/F,SAAO,QAAQ,QAAQ,QAAQ,EAAE,KAAK;AACxC;AAEA,SAAS,YAAY,OAA2B,UAAkB,MAAsB;AACtF,QAAM,IAAI,UAAU,SAAY,WAAW;AAC3C,MAAI,CAAC,OAAO,UAAU,CAAC,KAAK,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,6BAA6B;AACxF,SAAO;AACT;AAEO,SAAS,aAAa,UAAyB,CAAC,GAAiB;AACtE,QAAM,MAAM,QAAQ,OAAO,QAAQ;AAEnC,QAAM,aACJ,QAAQ,YAAY,SAChB;AAAA,IACE,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ,UACZ,mCACA;AAAA,EACN,IACA,kBAAkB,GAAG;AAE3B,QAAM,OAAO,cAAc,QAAQ,QAAQ,IAAI,eAAe,SAAS;AACvE,QAAM,OAAO,QAAQ,QAAQ,IAAI,eAAe;AAChD,QAAM,SAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA,MAAM,QAAQ,SAAS,IAAI,cAAc,OAAO,IAAI,WAAW,IAAI;AAAA,IACnE,gBAAgB;AAAA,MACd,QAAQ;AAAA,MACR,IAAI,0BAA0B,OAAO,IAAI,uBAAuB,IAAI;AAAA,MACpE;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,QAAQ;AAAA,MACR,IAAI,kBAAkB,OAAO,IAAI,eAAe,IAAI;AAAA,MACpD;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd,QAAQ;AAAA,MACR,IAAI,0BAA0B,OAAO,IAAI,uBAAuB,IAAI;AAAA,MACpE;AAAA,IACF;AAAA,IACA,SAAS,WAAW;AAAA,IACpB,kBAAkB,WAAW;AAAA,IAC7B,aAAa,QAAQ,eAAe;AAAA,IACpC,qBAAqB;AAAA,MACnB,QAAQ;AAAA,MACR,IAAI,+BAA+B,OAAO,IAAI,4BAA4B,IAAI;AAAA,MAC9E;AAAA,IACF;AAAA,IACA,cAAc;AAAA,MACZ,QAAQ;AAAA,MACR,IAAI,wBAAwB,OAAO,IAAI,qBAAqB,IAAI;AAAA,MAChE;AAAA,IACF;AAAA,IACA,iBACE,QAAQ,mBAAmB,IAAI,gCAA4B,4BAAK,wBAAO,GAAG,aAAa;AAAA,IACzF,cAAc,SAAS,QAAQ,cAAc,IAAI,oBAAoB;AAAA,IACrE,gBAAgB,SAAS,QAAQ,gBAAgB,IAAI,sBAAsB;AAAA,IAC3E,wBAAwB,SAAS,QAAQ,wBAAwB,IAAI,qBAAqB;AAAA,IAC1F,YAAY,QAAQ,cAAc,IAAI,uBAAuB;AAAA,IAC7D,cAAc,QAAQ,QAAQ,aAAa,IAAI,YAAY;AAAA,IAC3D,eAAe;AAAA,MACb,QAAQ;AAAA,MACR,IAAI,yBAAyB,OAAO,IAAI,sBAAsB,IAAI;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,QAAQ,aAAa,IAAI;AAC3C,QAAM,QAAQ,YAAY;AAAA,IACxB,MAAM,OAAO;AAAA,IACb,cAAc,OAAO;AAAA,IACrB,gBAAgB,OAAO;AAAA,IACvB,wBAAwB,OAAO;AAAA,IAC/B,YAAY,OAAO;AAAA,IACnB,WAAW,aAAa;AAAA,EAC1B,CAAC;AAED,MAAI,OAAO,WAAW,CAAC,kBAAkB,OAAO,IAAI,GAAG;AACrD,YAAQ;AAAA,MACN,gDAAgD,OAAO,IAAI,4FAExD,OAAO,eAAe,MAAM;AAAA,IACjC;AAAA,EACF;AACA,MAAI,OAAO,WAAW,IAAI,aAAa,cAAc;AACnD,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AACA,MAAI,OAAO,WAAW,CAAC,OAAO,aAAa;AACzC,YAAQ,KAAK,mEAAmE;AAAA,EAClF;AAEA,QAAM,MAAuB,EAAE,QAAQ,KAAK,OAAO,CAAC,EAAE;AACtD,MAAI,QAAQ,WAAW,QAAW;AAChC,QAAI,MAAM,YAAY,IAAI,QAAQ;AAAA,EACpC;AACA,QAAM,WAAW,IAAI,iBAAiB;AACtC,WAAS,SAAS,IAAI,eAAe,QAAQ,YAAY,CAAC;AAC1D,WAAS,SAAS,IAAI,eAAe,CAAC;AACtC,WAAS,SAAS,IAAI,YAAY,CAAC;AACnC,WAAS,SAAS,IAAI,qBAAqB,CAAC;AAC5C,WAAS,SAAS,IAAI,qBAAqB,CAAC;AAE5C,QAAM,iBAAiB,IAAI,eAAe;AAC1C,iBAAe,KAAK,aAAa;AACjC,QAAM,kBAA+C;AAAA,IACnD,IAAI;AAAA,IACJ,KAAK,OAAO,EAAE,IAAI,MAAM,MAAM,eAAe,QAAQ,EAAE;AAAA,EACzD;AACA,WAAS,SAAS,eAAe;AAEjC,QAAM,mBAAmB,IAAI,iBAAiB,OAAO,cAAc;AACnE,WAAS,SAAS,gBAAgB;AAElC,QAAM,kBAAkB,IAAI,gBAAgB;AAC5C,WAAS,SAAS,eAAe;AAEjC,QAAM,eAAe,IAAI,aAAa,OAAO,OAAO;AACpD,WAAS,SAAS,YAAY;AAE9B,QAAM,cAAc,IAAI,YAAY;AACpC,WAAS,SAAS,WAAW;AAC7B,QAAM,iBAAiB,IAAI,eAAe;AAC1C,WAAS,SAAS,cAAc;AAChC,WAAS;AAAA,IACP,IAAI,iBAAiB,YAAY,QAAQ,iBAAiB,KAAK,iBAAiB,CAAC;AAAA,EACnF;AAEA,QAAM,aAAoD,CAAC;AAC3D,aAAW,UAAU,QAAQ,WAAW,CAAC,GAAG;AAC1C,QACE,CAAC,kBAAkB,KAAK,OAAO,EAAE,KACjC,gBAAgB,IAAI,OAAO,EAAE,KAC7B,mBAAmB,IAAI,OAAO,EAAE,KAChC,SAAS,IAAI,OAAO,EAAE,GACtB;AACA,YAAM,IAAI;AAAA,QACR,qBAAqB,OAAO,EAAE;AAAA,MAEhC;AAAA,IACF;AACA,aAAS,SAAS,MAAM;AACxB,eAAW,KAAK,EAAE,IAAI,OAAO,IAAI,OAAO,OAAO,SAAS,OAAO,GAAG,CAAC;AAAA,EACrE;AAEA,QAAM,gBAAgB,IAAI,kBAAkB,OAAO,YAAY;AAC/D,QAAM,kBAAkB,oBAAI,IAAa;AACzC,MAAI,kBAAyC;AAC7C,MAAI,aAAa;AAEjB,WAAS,UAAgB;AACvB,eAAW,MAAM,iBAAiB;AAChC,eAAS,IAAI,EAAE,GAAG,OAAO,GAAG;AAAA,IAC9B;AACA,oBAAgB,MAAM;AACtB,QAAI,iBAAiB;AACnB,mBAAa,eAAe;AAC5B,wBAAkB;AAAA,IACpB;AAAA,EACF;AAEA,WAAS,uBAA6B;AACpC,QAAI,gBAAiB,cAAa,eAAe;AACjD,sBAAkB,WAAW,SAAS,OAAO,mBAAmB;AAChE,QAAI,OAAO,gBAAgB,UAAU,WAAY,iBAAgB,MAAM;AAAA,EACzE;AAEA,WAAS,aAAa,IAAmB;AACvC,QAAI,CAAC,gBAAgB,IAAI,EAAE,GAAG;AAC5B,eAAS,IAAI,EAAE,GAAG,QAAQ,GAAG;AAC7B,sBAAgB,IAAI,EAAE;AAAA,IACxB;AACA,yBAAqB;AAAA,EACvB;AAEA,WAAS,SAAS,KAAqB,QAAgB,UAAsC;AAC3F,UAAM,OAAO,kBAAkB,UAAU,OAAO,WAAW;AAC3D,QAAI,UAAU,QAAQ;AAAA,MACpB,GAAG;AAAA,MACH,gBAAgB;AAAA,MAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,MACxC,iBAAiB;AAAA,IACnB,CAAC;AACD,QAAI,IAAI,IAAI;AAAA,EACd;AAEA,WAAS,SAAS,KAA2B;AAC3C,aAAS,KAAK,KAAK,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,aAAa,SAAS,YAAY,EAAE,CAAC;AAAA,EACtF;AAEA,WAAS,YAAY,SAA0B;AAC7C,WAAO,YAAY,QAAQ,QAAQ,WAAW,GAAG,IAAI,GAAG;AAAA,EAC1D;AAEA,iBAAe,WACb,KACA,IACA,OACe;AACf,iBAAa,EAAE;AACf,UAAM,WAAW,SAAS,IAAI,EAAE;AAChC,QAAI,CAAC,SAAU,QAAO,SAAS,GAAG;AAClC,UAAM,aAAa,QAAQ,EAAE,GAAG,KAAK,MAAM,IAAI;AAC/C,aAAS,KAAK,GAAI,MAAM,UAAU,UAAU,UAAU,CAAE;AAAA,EAC1D;AAGA,iBAAe,UACb,UACA,YAC4C;AAC5C,QAAI;AACF,YAAM,SAAU,MAAM,SAAS,IAAI,UAAU;AAC7C,aAAO,CAAC,OAAO,KAAK,MAAM,KAAK,MAAM;AAAA,IACvC,SAAS,KAAK;AACZ,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,UAChD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,WAAS,aAAyB;AAChC,WAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,kBAAkB,OAAO;AAAA,MACzB,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,gBAAgB,OAAO;AAAA,MACvB,SAAS,OAAO;AAAA,MAChB,gBAAgB,OAAO;AAAA,MACvB,QAAQ,SAAS,IAAI;AAAA,MACrB,SAAS,EAAE,SAAS,OAAO,aAAa,SAAS,mBAAmB,OAAO;AAAA,MAC3E,QAAQ,OAAO;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AAEA,WAAS,kBAAkB,KAA2B;AACpD,UAAM,SAAwB,cAAc,MAAM;AAClD,aAAS,KAAK,KAAK,EAAE,IAAI,MAAM,MAAM,OAAO,CAAC;AAAA,EAC/C;AAEA,iBAAe,iBAAiB,KAAsB,KAAoC;AACxF,UAAM,SAAS,IAAI,QAAQ,kBAAkB;AAC7C,UAAM,QAAQ,MAAM,QAAQ,MAAM,IAAI,OAAO,CAAC,IAAI;AAClD,QAAI,OAAO,UAAU,YAAY,CAAC,cAAc,QAAQ,KAAK,GAAG;AAC9D,eAAS,KAAK,KAAK;AAAA,QACjB,IAAI;AAAA,QACJ,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SACE,6EAEG,IAAI;AAAA,QAEX;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,UAAM,eAAe,SAAS,IAAI,eAAe;AACjD,QAAI,CAAC,gBAAgB,EAAE,kBAAkB,cAAe,QAAO,SAAS,GAAG;AAC3E,UAAM,SAAU,MAAO,aAAsC;AAAA,MAC3D;AAAA,IACF;AACA,aAAS,KAAK,OAAO,KAAK,MAAM,KAAK,MAAM;AAAA,EAC7C;AAEA,iBAAe,WAAW,KAAsB,KAAoC;AAClF,UAAM,SAAS,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE;AAC3D,UAAM,aAAa,OAAO,IAAI,QAAQ,KAAK,IAAI,MAAM,GAAG,EAAE,OAAO,OAAO;AACxE,UAAM,UACJ,UAAU,SAAS,IACf,UAAU,OAAO,CAAC,OAAsB,SAAS,IAAI,EAAa,MAAM,MAAS,IACjF,SAAS,IAAI,GACjB,OAAO,CAAC,OAAO,OAAO,SAAS,KAAK,QAAQ,KAAK,MAAM,KAAK;AAE9D,QAAI,OAAO,WAAW,EAAG,QAAO,SAAS,GAAG;AAE5C,QAAI,cAAc,OAAO,eAAe;AACtC,UAAI,UAAU,KAAK,EAAE,GAAG,kBAAkB,gBAAgB,mBAAmB,CAAC;AAC9E,UAAI;AAAA,QACF,KAAK,UAAU;AAAA,UACb,IAAI;AAAA,UACJ,OAAO,EAAE,MAAM,oBAAoB,SAAS,6BAA6B;AAAA,QAC3E,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,kBAAc;AACd,UAAM,SAAS,SAAS,KAAK,gBAAgB;AAC7C,UAAM,OAAO,YAA2B;AACtC,iBAAW,MAAM,QAAQ;AACvB,qBAAa,EAAE;AACf,cAAM,WAAW,SAAS,IAAI,EAAE;AAChC,YAAI,CAAC,SAAU;AACf,cAAM,CAAC,EAAE,MAAM,IAAI,MAAM,UAAU,UAAU,GAAG;AAChD,eAAO,KAAK,EAAE,OAAO,IAAI,UAAU,OAAO,cAAc,YAAY,MAAM,IAAI,OAAO,CAAC;AAAA,MACxF;AAAA,IACF;AACA,SAAK,KAAK,EAAE,MAAM,MAAM,MAAS;AACjC,UAAM,YAAY,YAAY,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,MAAS,GAAG,OAAO,cAAc;AAC7F,UAAM,iBAAiB,YAAY,MAAM,OAAO,UAAU,GAAG,gBAAgB;AAC7E,QAAI,OAAO,UAAU,UAAU,WAAY,WAAU,MAAM;AAC3D,QAAI,OAAO,eAAe,UAAU,WAAY,gBAAe,MAAM;AAErE,QAAI,UAAU;AACd,UAAM,UAAU,MAAY;AAC1B,UAAI,QAAS;AACb,gBAAU;AACV,oBAAc;AACd,oBAAc,SAAS;AACvB,oBAAc,cAAc;AAC5B,aAAO,MAAM;AAAA,IACf;AACA,QAAI,GAAG,SAAS,OAAO;AACvB,QAAI,GAAG,SAAS,OAAO;AAAA,EACzB;AAEA,iBAAe,UACb,KACA,KACA,SACe;AACf,UAAM,UAAU,IAAI,UAAU,OAAO,YAAY;AACjD,QAAI,WAAW,SAAS,YAAY,WAAW;AAC7C,eAAS,KAAK,KAAK,EAAE,IAAI,MAAM,MAAM,WAAW,EAAE,CAAC;AACnD;AAAA,IACF;AACA,QAAI,WAAW,SAAS,YAAY,SAAS;AAC3C,aAAO,WAAW,KAAK,GAAG;AAAA,IAC5B;AACA,QAAI,WAAW,OAAO;AACpB,YAAM,QAAQ,SAAS,IAAI,QAAQ,MAAM,CAAC,CAAY;AACtD,UAAI,OAAO;AACT,cAAM,QAAQ,OAAO,YAAY,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB,EAAE,YAAY;AACzF,eAAO,WAAW,KAAK,MAAM,IAAI,KAAK;AAAA,MACxC;AAAA,IACF;AACA,QAAI,WAAW,QAAQ;AACrB,UAAI,YAAY,kBAAkB;AAChC,0BAAkB,GAAG;AACrB;AAAA,MACF;AACA,UAAI,YAAY,kBAAkB;AAChC,eAAO,iBAAiB,KAAK,GAAG;AAAA,MAClC;AAAA,IACF;AACA,aAAS,GAAG;AAAA,EACd;AAEA,WAAS,YAAY,KAAsB,KAAqB,SAAuB;AACrF,UAAM,WAAW,QAAQ,MAAM,KAAK,MAAM;AAC1C,UAAM,QAAQ,mBAAmB,aAAa,QAAQ;AACtD,QAAI,CAAC,OAAO;AACV,eAAS,KAAK,KAAK,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,aAAa,SAAS,YAAY,EAAE,CAAC;AACpF;AAAA,IACF;AACA,QAAI,UAAU,KAAK;AAAA,MACjB,GAAG;AAAA,MACH,2BAA2B;AAAA,MAC3B,gBAAgB,MAAM;AAAA,MACtB,kBAAkB,MAAM;AAAA,MACxB,iBAAiB;AAAA,IACnB,CAAC;AACD,UAAM,QAAQ,KAAK,GAAG;AAAA,EACxB;AAEA,iBAAe,iBACb,KACA,KACA,SACe;AACf,UAAM,UAAU,MAAM,GAAG;AACzB,QAAI,CAAC,QAAQ,IAAI;AACf,eAAS,KAAK,QAAQ,QAAQ;AAAA,QAC5B,IAAI;AAAA,QACJ,OAAO,EAAE,MAAM,QAAQ,MAAM,SAAS,QAAQ,QAAQ;AAAA,MACxD,CAAC;AACD;AAAA,IACF;AACA,QAAI,QAAQ,aAAa,QAAW;AAClC,UAAI,UAAU,KAAK;AAAA,QACjB,GAAG;AAAA,QACH,cAAc,GAAG,WAAW,IAAI,mBAAmB,QAAQ,QAAQ,CAAC,UAAU,IAAI;AAAA,QAClF,UAAU,GAAG,IAAI;AAAA,QACjB,iBAAiB;AAAA,MACnB,CAAC;AACD,UAAI,IAAI;AACR;AAAA,IACF;AACA,UAAM,UAAU,GAAG,IAAI;AACvB,QAAI,YAAY,WAAW,QAAQ,WAAW,GAAG,OAAO,GAAG,GAAG;AAC5D,YAAM,UAAU,KAAK,KAAK,QAAQ,MAAM,QAAQ,MAAM,CAAC;AACvD;AAAA,IACF;AACA,gBAAY,KAAK,KAAK,OAAO;AAAA,EAC/B;AAEA,WAAS,cAAc,KAA4B;AACjD,QAAI,IAAI,MAAM,gBAAgB,MAAM,OAAW;AAC/C,UAAM,MAAO,IAA4E;AACzF,QAAI,CAAC,IAAK;AACV,QAAI;AACJ,QAAI;AAIF,eAAS,IAAI;AAAA,IACf,QAAQ;AACN,eAAS;AAAA,IACX;AACA,QAAI,WAAW,QAAW;AACxB,UAAI;AACF,iBAAS,IAAI;AAAA,MACf,QAAQ;AACN,iBAAS;AAAA,MACX;AAAA,IACF;AACA,QAAI,QAAQ;AACV,UAAI,MAAM,gBAAgB,IAAI;AAAA,IAChC;AAAA,EACF;AAEA,WAAS,iBAAiB,KAAsB,KAA2B;AACzE,UAAM,UAAU,QAAQ,OAAO,OAAO;AACtC,UAAM,cAAc,KAAK,IAAI;AAC7B,QAAI,GAAG,UAAU,MAAM;AACrB,YAAM,aAAa,OAAO,QAAQ,OAAO,OAAO,IAAI,OAAO,IAAI;AAC/D,uBAAiB,OAAO;AAAA,QACtB,QAAQ,IAAI,UAAU;AAAA,QACtB,MAAM,WAAW,GAAG;AAAA,QACpB,QAAQ,IAAI;AAAA,QACZ;AAAA,QACA;AAAA,QACA,IAAI,IAAI,OAAO,iBAAiB;AAAA,MAClC,CAAC;AACD,sBAAgB,OAAO,IAAI,UAAU;AAAA,IACvC,CAAC;AAAA,EACH;AAEA,QAAM,SAAuB;AAAA,IAC3B,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,IAAI,SAAS;AACX,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,IAAI,mBAAmB;AACrB,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,aAA+B;AAC7B,aAAO,CAAC,KAAK,KAAK,SAAS;AACzB,sBAAc,GAAG;AACjB,YAAI,CAAC,OAAO,SAAS;AACnB,eAAK;AACL;AAAA,QACF;AACA,cAAM,UAAU,WAAW,GAAG;AAC9B,YAAI,YAAY,OAAO,GAAG;AACxB,eAAK,iBAAiB,KAAK,KAAK,OAAO;AACvC;AAAA,QACF;AACA,yBAAiB,KAAK,GAAG;AACzB,aAAK;AAAA,MACP;AAAA,IACF;AAAA,IACA,MAAM,OAAO,KAAK,KAAoB;AACpC,oBAAc,GAAG;AACjB,UAAI,CAAC,OAAO,SAAS;AACnB,iBAAS,GAAG;AACZ;AAAA,MACF;AACA,YAAM,UAAU,WAAW,GAAG;AAC9B,UAAI,YAAY,OAAO,GAAG;AACxB,cAAM,iBAAiB,KAAK,KAAK,OAAO;AACxC;AAAA,MACF;AACA,eAAS,GAAG;AAAA,IACd;AAAA,IACA,UAAU,QAAc;AACtB,UAAI,MAAM,eAAe,IAAI;AAAA,IAC/B;AAAA,IACA,KAAK,MAAoB;AACvB,qBAAe,KAAK,IAAI;AAAA,IAC1B;AAAA,IACA,iBAAiB,IAAsB;AACrC,aAAO,gBAAgB,IAAI,EAAE;AAAA,IAC/B;AAAA,IACA,aAAa,OAAmD;AAC9D,mBAAa,UAAU,KAAK;AAAA,IAC9B;AAAA,IACA,WAAiB;AACf,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AACT;","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"]}
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/queries.ts","../src/context.ts","../src/providers/errors.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/request-detail.ts","../src/sse.ts","../src/server.ts","../src/otlp.ts","../src/persistence.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 { DEFAULT_REQUEST_DETAIL } from './request-detail';\r\nexport { currentRequestId, enterRequestContext, runInRequestContext } from './context';\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, (_m, name: string, sep: string, value: string) => {\r\n // Keep quotes so masked JSON-ish text stays well-formed.\r\n const quote = value.startsWith('\"') || value.startsWith(\"'\") ? value[0] : '';\r\n return `${name}${sep}${quote}${SECRET_MASKED}${quote}`;\r\n });\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 * Parses an IPv6 literal (`::1`, `fd00::1`, `64:ff9b::1.2.3.4`, optional `%zone`)\r\n * into its 128-bit value, or null when it is not valid.\r\n */\r\nfunction ipv6ToBigInt(address: string): bigint | null {\r\n const text = address.split('%')[0] ?? '';\r\n if (!text.includes(':')) return null;\r\n let head = text;\r\n let tailGroups: string[] = [];\r\n const doubleColon = text.indexOf('::');\r\n if (doubleColon !== -1) {\r\n if (text.indexOf('::', doubleColon + 1) !== -1) return null;\r\n head = text.slice(0, doubleColon);\r\n const tail = text.slice(doubleColon + 2);\r\n tailGroups = tail === '' ? [] : tail.split(':');\r\n }\r\n const headGroups = doubleColon !== -1 ? (head === '' ? [] : head.split(':')) : text.split(':');\r\n const groups = doubleColon !== -1 ? [...headGroups, ...tailGroups] : headGroups;\r\n\r\n // An embedded IPv4 tail (`::ffff:1.2.3.4`) takes the place of two groups.\r\n const last = groups[groups.length - 1];\r\n const v4 = last?.includes('.') ? ipv4ToInt(last) : null;\r\n if (last?.includes('.')) {\r\n if (v4 === null) return null;\r\n groups.splice(\r\n groups.length - 1,\r\n 1,\r\n ((v4 >>> 16) & 0xffff).toString(16),\r\n (v4 & 0xffff).toString(16),\r\n );\r\n }\r\n const expected = 8;\r\n if (doubleColon === -1 ? groups.length !== expected : groups.length >= expected) return null;\r\n const parseGroup = (g: string): bigint | null =>\r\n /^[0-9a-f]{1,4}$/i.test(g) ? BigInt(`0x${g}`) : null;\r\n\r\n const headLen = doubleColon !== -1 ? headGroups.length : groups.length;\r\n const fill = expected - groups.length;\r\n const full: string[] = [\r\n ...groups.slice(0, headLen),\r\n ...Array<string>(doubleColon !== -1 ? fill : 0).fill('0'),\r\n ...groups.slice(headLen),\r\n ];\r\n let out = 0n;\r\n for (const g of full) {\r\n const n = parseGroup(g);\r\n if (n === null) return null;\r\n out = (out << 16n) | n;\r\n }\r\n return out;\r\n}\r\n\r\n/** True when `entry` is an exact IP or an IPv4/IPv6 CIDR that {@link matchesAddress} understands. */\r\nexport function isValidAddressEntry(entry: string): boolean {\r\n const normalized = normalizeIp(entry);\r\n if (!normalized.includes('/'))\r\n return ipv4ToInt(normalized) !== null || ipv6ToBigInt(normalized) !== null;\r\n const [base = '', bitsRaw = ''] = normalized.split('/');\r\n const bits = Number(bitsRaw);\r\n if (!/^\\d{1,3}$/.test(bitsRaw) || !Number.isInteger(bits)) return false;\r\n if (ipv4ToInt(base) !== null) return bits <= 32;\r\n return ipv6ToBigInt(base) !== null && bits <= 128;\r\n}\r\n\r\n/**\r\n * True when `address` matches one of `allowed`, each an exact IP or a CIDR\r\n * (e.g. `172.17.0.0/16`, the default Docker bridge network, or `fd00::/8`).\r\n * IPv4-mapped IPv6 addresses compare as their IPv4 form; IPv6 literals compare\r\n * by value, so `::1` equals `0:0:0:0:0:0:0:1`.\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 const ip4 = ipv4ToInt(ip);\r\n const ip6 = ip4 === null ? ipv6ToBigInt(ip) : null;\r\n for (const raw of allowed) {\r\n const entry = normalizeIp(raw);\r\n if (!entry.includes('/')) {\r\n if (entry === ip) return true;\r\n const e4 = ipv4ToInt(entry);\r\n if (ip4 !== null && e4 !== null) {\r\n if (ip4 === e4) return true;\r\n continue;\r\n }\r\n const e6 = ipv6ToBigInt(entry);\r\n if (ip6 !== null && e6 !== null && ip6 === e6) return true;\r\n continue;\r\n }\r\n const [base = '', bitsRaw = ''] = entry.split('/');\r\n if (!/^\\d{1,3}$/.test(bitsRaw)) continue;\r\n const bits = Number(bitsRaw);\r\n const b4 = ipv4ToInt(base);\r\n if (b4 !== null) {\r\n if (ip4 === null || bits > 32) continue;\r\n const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;\r\n if ((ip4 & mask) >>> 0 === (b4 & mask) >>> 0) return true;\r\n continue;\r\n }\r\n const b6 = ipv6ToBigInt(base);\r\n if (b6 === null || ip6 === null || bits > 128) continue;\r\n const shift = BigInt(128 - bits);\r\n if (ip6 >> shift === b6 >> shift) 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';\nimport type { IncomingMessage } from 'node:http';\nimport {\n hostnameFromHostHeader,\n isLoopbackAddress,\n isLoopbackHostname,\n matchesAddress,\n} from './safety';\n\nexport const AUTH_COOKIE = 'nodeui_token';\n\nexport interface GuardOptions {\n /** Configured console host; a loopback value enables the strict checks. */\n host: string;\n /** Extra `Host` header hostnames accepted (e.g. `myapp.local`). */\n allowedHosts: readonly string[];\n /** Extra `Origin` values accepted for cross-origin calls. */\n allowedOrigins: readonly string[];\n /** Extra remote IPs / IPv4 CIDRs accepted (e.g. `172.17.0.0/16` for Docker). */\n allowedRemoteAddresses: readonly string[];\n /** Accept requests carrying `X-Forwarded-*` headers. Default false. */\n trustProxy: boolean;\n /** When set, every request must present this token. */\n authToken?: string;\n}\n\nexport type GuardResult =\n { ok: true; setToken?: string } | { ok: false; status: number; code: string; message: string };\n\nconst FORWARD_HEADERS = ['x-forwarded-for', 'x-forwarded-host', 'forwarded', 'x-real-ip'];\n\nfunction digest(value: string): Buffer {\n return createHash('sha256').update(value).digest();\n}\n\nfunction tokenEquals(a: string, b: string): boolean {\n return timingSafeEqual(digest(a), digest(b));\n}\n\nfunction readCookie(header: string | undefined, name: string): string | undefined {\n if (!header) return undefined;\n for (const part of header.split(';')) {\n const [k, ...rest] = part.trim().split('=');\n if (k === name) {\n try {\n return decodeURIComponent(rest.join('='));\n } catch {\n return undefined;\n }\n }\n }\n return undefined;\n}\n\nfunction firstHeader(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction deny(status: number, code: string, message: string): GuardResult {\n return { ok: false, status, code, message };\n}\n\n/**\n * Builds the request guard for the console. It combines: loopback/remote\n * address policy, `Host` validation (DNS-rebinding defence), `Origin`\n * validation (cross-site request defence) and an optional shared token.\n */\nexport function createGuard(options: GuardOptions): (req: IncomingMessage) => GuardResult {\n const strict = isLoopbackAddress(options.host);\n const allowedHosts = options.allowedHosts.map((h) => h.toLowerCase());\n const allowedOrigins = options.allowedOrigins.map((o) => o.toLowerCase().replace(/\\/+$/, ''));\n\n return (req) => {\n const remote = req.socket.remoteAddress;\n const remoteLoopback = isLoopbackAddress(remote);\n const remoteAllowed = remoteLoopback || matchesAddress(remote, options.allowedRemoteAddresses);\n\n if (strict && !remoteAllowed) {\n return deny(\n 403,\n 'forbidden',\n 'nodeui is loopback-only; requests from other hosts are rejected',\n );\n }\n if (!strict && options.allowedRemoteAddresses.length > 0 && !remoteAllowed) {\n return deny(403, 'forbidden', 'remote address is not in the nodeui allow-list');\n }\n\n if (strict && !options.trustProxy && !matchesAddress(remote, options.allowedRemoteAddresses)) {\n if (FORWARD_HEADERS.some((h) => req.headers[h] !== undefined)) {\n return deny(\n 403,\n 'forbidden',\n 'proxied requests are rejected (set trustProxy to allow forwarded traffic)',\n );\n }\n }\n\n const hostHeader = firstHeader(req.headers.host);\n const hostname = hostnameFromHostHeader(hostHeader);\n if (hostname !== null) {\n const hostOk =\n isLoopbackHostname(hostname) ||\n allowedHosts.includes(hostname) ||\n (!strict && allowedHosts.length === 0);\n if (!hostOk) {\n return deny(\n 403,\n 'forbidden',\n `Host \"${hostname}\" is not allowed (add it to allowedHosts to use nodeui via this name)`,\n );\n }\n }\n\n const origin = firstHeader(req.headers.origin);\n if (origin !== undefined) {\n let originOk = false;\n const normalized = origin.toLowerCase().replace(/\\/+$/, '');\n if (allowedOrigins.includes(normalized)) {\n originOk = true;\n } else {\n try {\n originOk = hostHeader !== undefined && new URL(origin).host === hostHeader.toLowerCase();\n } catch {\n originOk = false;\n }\n }\n if (!originOk) {\n return deny(403, 'forbidden', 'cross-origin requests to nodeui are rejected');\n }\n }\n\n if (options.authToken) {\n const authHeader = firstHeader(req.headers.authorization);\n const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined;\n const queryToken = new URL(req.url ?? '/', 'http://localhost').searchParams.get('token');\n const cookieToken = readCookie(firstHeader(req.headers.cookie), AUTH_COOKIE);\n const candidates = [\n bearer,\n firstHeader(req.headers['x-nodeui-token']),\n cookieToken,\n queryToken ?? undefined,\n ];\n const matched = candidates.find((c) => c !== undefined && tokenEquals(c, options.authToken!));\n if (matched === undefined) {\n return deny(401, 'unauthorized', 'a valid nodeui token is required');\n }\n if (queryToken !== null && matched === queryToken) {\n return { ok: true, setToken: queryToken };\n }\n }\n\n return { ok: true };\n };\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(\n private readonly ttlMs: number,\n private readonly maxOutstanding = 100,\n ) {}\n\n issue(): ConfirmIssued {\n this.prune();\n while (this.nonces.size >= this.maxOutstanding) {\n const oldest = this.nonces.keys().next().value;\n if (oldest === undefined) break;\n this.nonces.delete(oldest);\n }\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';\r\nimport type { Readable } from 'node:stream';\r\nimport { join, resolve, sep } from 'node:path';\r\n\r\nconst MIME_TYPES: Record<string, string> = {\r\n '.html': 'text/html; charset=utf-8',\r\n '.js': 'text/javascript; charset=utf-8',\r\n '.mjs': 'text/javascript; charset=utf-8',\r\n '.css': 'text/css; charset=utf-8',\r\n '.svg': 'image/svg+xml',\r\n '.json': 'application/json; charset=utf-8',\r\n '.png': 'image/png',\r\n '.ico': 'image/x-icon',\r\n '.map': 'application/json; charset=utf-8',\r\n '.woff': 'font/woff',\r\n '.woff2': 'font/woff2',\r\n '.webmanifest': 'application/manifest+json',\r\n};\r\n\r\n/**\r\n * Directory holding the prebuilt console (`index.html` and assets). It ships\r\n * inside this package, so the UI always matches the API it talks to. Use it to\r\n * serve the console from your own host or gateway.\r\n */\r\nexport function uiAssetsDir(): string {\r\n return resolve(__dirname, '..', 'static');\r\n}\r\n\r\nexport interface StaticAsset {\r\n content: Readable;\r\n contentType: string;\r\n length: number;\r\n}\r\n\r\n/**\r\n * Resolves a URL path to a file inside `rootDir`, serving `index.html` for\r\n * directories and falling back to `index.html` for unknown paths (SPA).\r\n * Returns null when the path would escape `rootDir`.\r\n */\r\nexport function resolveStaticAsset(rootDir: string, urlPath: string): StaticAsset | null {\r\n const relative = urlPath === '' ? 'index.html' : urlPath.replace(/^\\/+/, '');\r\n const candidate = resolve(rootDir, relative);\r\n const rootWithSep = rootDir.endsWith(sep) ? rootDir : rootDir + sep;\r\n if (candidate !== rootDir && !candidate.startsWith(rootWithSep)) {\r\n return null;\r\n }\r\n\r\n let file = candidate;\r\n if (existsSync(file) && statSync(file).isDirectory()) {\r\n file = join(file, 'index.html');\r\n }\r\n if (!existsSync(file) || !statSync(file).isFile()) {\r\n file = join(rootDir, 'index.html');\r\n }\r\n if (!existsSync(file)) {\r\n return null;\r\n }\r\n\r\n const ext = file.slice(file.lastIndexOf('.')).toLowerCase();\r\n const stat = statSync(file);\r\n return {\r\n content: createReadStream(file),\r\n contentType: MIME_TYPES[ext] ?? 'application/octet-stream',\r\n length: stat.size,\r\n };\r\n}\r\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 HealthCheckResult,\n HealthData,\n MemoryData,\n NodeUIProvider,\n ProviderContext,\n} from '../types';\n\nconst LAG_OK_MS = 50;\nconst LAG_DEGRADED_MS = 200;\nconst CHECK_TIMEOUT_MS = 3000;\nconst CHECK_CACHE_MS = 5000;\n\ntype StoredMemory = Partial<MemoryData>;\ntype StoredEventLoop = Partial<EventLoopSample>;\n\n/**\n * A dependency check (database ping, cache ping, ...). Resolve for healthy,\n * reject/throw (or resolve `false`) for unhealthy.\n */\nexport type HealthCheck = () => unknown | Promise<unknown>;\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err: unknown) => {\n clearTimeout(timer);\n reject(err instanceof Error ? err : new Error(String(err)));\n },\n );\n });\n}\n\n/** Derives process + event-loop health, plus optional dependency checks. */\nexport class HealthProvider implements NodeUIProvider<HealthData> {\n readonly id = 'health' as const;\n\n private cache: { at: number; results: HealthCheckResult[] } | null = null;\n\n constructor(private readonly checks: Record<string, HealthCheck> = {}) {}\n\n private async runChecks(): Promise<HealthCheckResult[]> {\n const names = Object.keys(this.checks);\n if (names.length === 0) return [];\n if (this.cache && Date.now() - this.cache.at < CHECK_CACHE_MS) return this.cache.results;\n const results = await Promise.all(\n names.map(async (name): Promise<HealthCheckResult> => {\n const started = Date.now();\n try {\n const outcome = await withTimeout(\n Promise.resolve().then(() => this.checks[name]!()),\n CHECK_TIMEOUT_MS,\n );\n if (outcome === false) throw new Error('check returned false');\n return { name, status: 'up', durationMs: Date.now() - started };\n } catch (err) {\n return {\n name,\n status: 'down',\n durationMs: Date.now() - started,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }),\n );\n this.cache = { at: Date.now(), results };\n return results;\n }\n\n async get(ctx: ProviderContext): Promise<{ 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 const checks = await this.runChecks();\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 const down = checks.filter((c) => c.status === 'down');\n if (down.length > 0) {\n status = 'critical';\n statusReason = `dependency down: ${down.map((c) => c.name).join(', ')}`;\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 checks,\n },\n };\n }\n}\n","import { createRequire } from 'node:module';\r\nimport { join } from 'node:path';\r\nimport { currentRequestId } from '../context';\r\nimport { RingBuffer } from '../ring-buffer';\r\nimport type { NodeUIProvider, QueriesData, QueryEntry } from '../types';\r\n\r\nconst RESPONSE_PAGE_SIZE = 100;\r\nconst MAX_SQL_CHARS = 2000;\r\n/** Same normalized statement this many times within one request is flagged as N+1. */\r\nexport const N_PLUS_ONE_THRESHOLD = 5;\r\n\r\ntype Recorder = (entry: Omit<QueryEntry, 'id'>) => void;\r\ntype AnyFn = (...args: unknown[]) => unknown;\r\n\r\n/** Loads an optional driver from the host app's dependency tree. */\r\nexport type DriverLoader = (name: string) => unknown;\r\n\r\nconst defaultLoader: DriverLoader = (name) => {\r\n try {\r\n return createRequire(join(process.cwd(), 'noop.js'))(name);\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nconst recorders = new Set<Recorder>();\r\nconst restorers: Array<() => void> = [];\r\n\r\n/** Collapses literals so repeated statements compare equal. */\r\nexport function normalizeSql(sql: string): string {\r\n return sql\r\n .replace(/'(?:[^']|'')*'/g, '?')\r\n .replace(/\\b\\d+(?:\\.\\d+)?\\b/g, '?')\r\n .replace(/\\(\\s*\\?(?:\\s*,\\s*\\?)+\\s*\\)/g, '(?)')\r\n .replace(/\\s+/g, ' ')\r\n .trim()\r\n .toLowerCase();\r\n}\r\n\r\nfunction emit(entry: Omit<QueryEntry, '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 sqlOf(arg: unknown): string | null {\r\n if (typeof arg === 'string') return arg;\r\n if (arg && typeof arg === 'object') {\r\n const candidate =\r\n (arg as { text?: unknown; sql?: unknown }).text ?? (arg as { sql?: unknown }).sql;\r\n if (typeof candidate === 'string') return candidate;\r\n }\r\n return null;\r\n}\r\n\r\nfunction rowCountOf(result: unknown): number | undefined {\r\n if (Array.isArray(result)) {\r\n // mysql2 resolves to [rows, fields]; plain arrays are row lists\r\n const first = result[0];\r\n return Array.isArray(first) ? first.length : result.length;\r\n }\r\n if (result && typeof result === 'object') {\r\n const r = result as { rowCount?: unknown; affectedRows?: unknown };\r\n if (typeof r.rowCount === 'number') return r.rowCount;\r\n if (typeof r.affectedRows === 'number') return r.affectedRows;\r\n }\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Wraps a driver's `query`-style method. Handles callbacks, promises and\r\n * event-emitter results, records exactly once per call and never alters the\r\n * value, errors or timing semantics seen by the app.\r\n */\r\nfunction wrapQueryMethod(\r\n proto: Record<string, unknown>,\r\n method: string,\r\n system: string,\r\n rowCount: (result: unknown, extra?: unknown) => number | undefined,\r\n): void {\r\n const original = proto[method];\r\n if (typeof original !== 'function') return;\r\n const orig = original as AnyFn;\r\n const wrapped = function (this: unknown, ...args: unknown[]): unknown {\r\n if (recorders.size === 0) return orig.apply(this, args);\r\n const sql = sqlOf(args[0]);\r\n if (sql === null) return orig.apply(this, args);\r\n\r\n const started = process.hrtime.bigint();\r\n const timestampMs = Date.now();\r\n const requestId = currentRequestId();\r\n let done = false;\r\n const finish = (error?: unknown, result?: unknown): void => {\r\n if (done) return;\r\n done = true;\r\n emit({\r\n system,\r\n sql: sql.slice(0, MAX_SQL_CHARS),\r\n durationMs: Number(process.hrtime.bigint() - started) / 1e6,\r\n timestampMs,\r\n ...(requestId !== undefined ? { requestId } : {}),\r\n ...(error ? { error: error instanceof Error ? error.message : String(error) } : {}),\r\n ...(!error && rowCount(result) !== undefined ? { rowCount: rowCount(result) } : {}),\r\n });\r\n };\r\n\r\n const last = args.length - 1;\r\n const hasCallback = typeof args[last] === 'function';\r\n if (hasCallback) {\r\n const callback = args[last] as AnyFn;\r\n args[last] = function (this: unknown, err: unknown, result: unknown, ...rest: unknown[]) {\r\n finish(err, result);\r\n return callback.call(this, err, result, ...rest);\r\n };\r\n }\r\n\r\n const ret = orig.apply(this, args) as unknown;\r\n if (!hasCallback && ret && typeof (ret as { then?: unknown }).then === 'function') {\r\n (ret as Promise<unknown>).then(\r\n (result) => finish(undefined, result),\r\n (error: unknown) => finish(error ?? new Error('query failed')),\r\n );\r\n } else if (!hasCallback && ret && typeof (ret as { once?: unknown }).once === 'function') {\r\n const emitter = ret as { once(e: string, fn: (...a: unknown[]) => void): void };\r\n emitter.once('end', (result: unknown) => finish(undefined, result));\r\n emitter.once('error', (error: unknown) => finish(error ?? new Error('query failed')));\r\n }\r\n return ret;\r\n };\r\n proto[method] = wrapped;\r\n restorers.push(() => {\r\n if (proto[method] === wrapped) proto[method] = original;\r\n });\r\n}\r\n\r\nfunction instrumentDrivers(load: DriverLoader): void {\r\n const pg = load('pg') as { Client?: { prototype: Record<string, unknown> } } | null;\r\n if (pg?.Client?.prototype) wrapQueryMethod(pg.Client.prototype, 'query', 'pg', rowCountOf);\r\n\r\n const mysql2 = load('mysql2') as { Connection?: { prototype: Record<string, unknown> } } | null;\r\n const proto = mysql2?.Connection?.prototype;\r\n if (proto) {\r\n wrapQueryMethod(proto, 'query', 'mysql2', rowCountOf);\r\n wrapQueryMethod(proto, 'execute', 'mysql2', rowCountOf);\r\n }\r\n}\r\n\r\n/**\r\n * Refcounted instrumentation of installed database drivers (`pg`, `mysql2`),\r\n * resolved from the host app's dependencies. Drivers that are not installed are\r\n * skipped. The last release restores the original methods.\r\n */\r\nexport function interceptQueries(record: Recorder, load: DriverLoader = defaultLoader): () => void {\r\n const first = recorders.size === 0;\r\n recorders.add(record);\r\n if (first) {\r\n try {\r\n instrumentDrivers(load);\r\n } catch {\r\n // a driver we cannot patch must not break the host app\r\n }\r\n }\r\n return () => {\r\n if (!recorders.delete(record) || recorders.size > 0) return;\r\n while (restorers.length > 0) restorers.pop()?.();\r\n };\r\n}\r\n\r\nexport interface QueriesProviderOptions {\r\n size: number;\r\n slowQueryMs: number;\r\n loader?: DriverLoader;\r\n}\r\n\r\n/** Recent database queries with slow-query and N+1 flags. */\r\nexport class QueriesProvider implements NodeUIProvider<QueriesData> {\r\n readonly id = 'queries' as const;\r\n\r\n private buffer: RingBuffer<QueryEntry>;\r\n private nextId = 1;\r\n private release: (() => void) | null = null;\r\n private failed = 0;\r\n /** Called with every newly recorded entry (persistence hook). */\r\n onRecord?: (entry: QueryEntry) => void;\r\n\r\n constructor(private readonly options: QueriesProviderOptions) {\r\n this.buffer = new RingBuffer<QueryEntry>(options.size);\r\n }\r\n\r\n start(): void {\r\n if (this.release) return;\r\n this.release = interceptQueries((entry) => this.record(entry), this.options.loader);\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<QueryEntry, 'id'>): void {\r\n const full = { id: this.nextId, ...entry };\r\n this.buffer.push(full);\r\n this.nextId += 1;\r\n if (entry.error) this.failed += 1;\r\n this.onRecord?.(full);\r\n }\r\n\r\n /** Re-inserts journaled entries without re-emitting them. */\r\n restore(entries: readonly QueryEntry[]): void {\r\n for (const entry of entries) {\r\n this.buffer.push(entry);\r\n this.nextId = Math.max(this.nextId, entry.id + 1);\r\n if (entry.error) this.failed += 1;\r\n }\r\n }\r\n\r\n get(): { ok: true; data: QueriesData } {\r\n const all = this.buffer.toArray();\r\n // N+1: the same normalized statement repeated within one request.\r\n const counts = new Map<string, number>();\r\n const keyOf = (e: QueryEntry): string | null =>\r\n e.requestId === undefined ? null : `${e.requestId}|${normalizeSql(e.sql)}`;\r\n for (const e of all) {\r\n const key = keyOf(e);\r\n if (key) counts.set(key, (counts.get(key) ?? 0) + 1);\r\n }\r\n const flagged = new Set<string>();\r\n const entries = all.map((e) => {\r\n const key = keyOf(e);\r\n const repeats = key ? (counts.get(key) ?? 0) : 0;\r\n const nPlusOne = repeats >= N_PLUS_ONE_THRESHOLD;\r\n if (nPlusOne && key) flagged.add(key);\r\n return {\r\n ...e,\r\n slow: e.durationMs >= this.options.slowQueryMs,\r\n ...(nPlusOne ? { nPlusOne, repeats } : {}),\r\n };\r\n });\r\n return {\r\n ok: true,\r\n data: {\r\n total: this.nextId - 1,\r\n failed: this.failed,\r\n slow: entries.filter((e) => e.slow).length,\r\n nPlusOneGroups: flagged.size,\r\n slowQueryMs: this.options.slowQueryMs,\r\n entries: entries.slice(Math.max(0, entries.length - RESPONSE_PAGE_SIZE)),\r\n },\r\n };\r\n }\r\n}\r\n","import { AsyncLocalStorage } from 'node:async_hooks';\r\nimport type { IncomingMessage } from 'node:http';\r\n\r\ninterface RequestContext {\r\n requestId: number;\r\n}\r\n\r\nconst storage = new AsyncLocalStorage<RequestContext>();\r\nconst ids = new WeakMap<object, number>();\r\nconst suppression = new AsyncLocalStorage<true>();\r\n\r\n/** Runs `fn` so NodeUI's own outgoing HTTP calls (e.g. OTLP export) are not captured. */\r\nexport function runSuppressed<T>(fn: () => T): T {\r\n return suppression.run(true, fn);\r\n}\r\n\r\nexport function isSuppressed(): boolean {\r\n return suppression.getStore() === true;\r\n}\r\n\r\n/** Id of the app request the current async call chain belongs to, if any. */\r\nexport function currentRequestId(): number | undefined {\r\n return storage.getStore()?.requestId;\r\n}\r\n\r\n/** Associates a request with its id so adapters can re-enter its context. */\r\nexport function bindRequestId(req: IncomingMessage, requestId: number): void {\r\n ids.set(req, requestId);\r\n}\r\n\r\n/** Runs `fn` so outgoing calls and logs made inside are attributed to the request. */\r\nexport function runWithRequestId<T>(requestId: number, fn: () => T): T {\r\n return storage.run({ requestId }, fn);\r\n}\r\n\r\n/**\r\n * Runs `fn` inside the request's context when it was recorded by the NodeUI\r\n * middleware (otherwise just calls `fn`). For adapters that own the `next` call.\r\n */\r\nexport function runInRequestContext<T>(req: IncomingMessage, fn: () => T): T {\r\n const requestId = ids.get(req);\r\n return requestId === undefined ? fn() : storage.run({ requestId }, fn);\r\n}\r\n\r\n/**\r\n * Attributes the rest of the current async chain to the request. For adapters\r\n * (Fastify) whose hooks cannot wrap the downstream handler in `run`.\r\n */\r\nexport function enterRequestContext(req: IncomingMessage): void {\r\n const requestId = ids.get(req);\r\n if (requestId !== undefined) storage.enterWith({ requestId });\r\n}\r\n","import { createHash } from 'node:crypto';\r\nimport { currentRequestId } from '../context';\r\nimport type { ErrorGroup, ErrorSource, ErrorsData, NodeUIProvider } from '../types';\r\n\r\n/** A single occurrence in a serializable form (for the persistence journal). */\r\nexport interface ErrorEvent {\r\n name: string;\r\n message: string;\r\n stack: string;\r\n source: ErrorSource;\r\n at: number;\r\n requestId?: number;\r\n route?: string;\r\n status?: number;\r\n}\r\n\r\nconst MAX_GROUPS = 100;\r\nconst MAX_STACK_CHARS = 4000;\r\nconst MAX_MESSAGE_CHARS = 500;\r\nconst FINGERPRINT_FRAMES = 3;\r\n\r\nfunction toError(value: unknown): { name: string; message: string; stack: string } {\r\n if (value instanceof Error) {\r\n return { name: value.name, message: value.message, stack: value.stack ?? '' };\r\n }\r\n let message: string;\r\n try {\r\n message = typeof value === 'string' ? value : JSON.stringify(value);\r\n } catch {\r\n message = String(value);\r\n }\r\n return { name: 'NonError', message: message ?? String(value), stack: '' };\r\n}\r\n\r\n/** Function + file of the top frames, without line/column so edits don't split a group. */\r\nfunction topFrames(stack: string): string[] {\r\n const frames: string[] = [];\r\n for (const line of stack.split('\\n')) {\r\n const match = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:.*[\\\\/])?([^\\\\/:()]+?)(?::\\d+){0,2}\\)?\\s*$/.exec(\r\n line,\r\n );\r\n if (match) frames.push(`${match[1] ?? ''}@${match[2] ?? ''}`);\r\n if (frames.length >= FINGERPRINT_FRAMES) break;\r\n }\r\n return frames;\r\n}\r\n\r\n/** Groups equal failures: same type, same message shape (numbers and ids collapsed), same origin. */\r\nexport function fingerprintError(name: string, message: string, stack: string): string {\r\n const shape = message\r\n .replace(/\\b[0-9a-f]{8}-[0-9a-f-]{27}\\b/gi, '#')\r\n .replace(/\\b[0-9a-f]{24}\\b/gi, '#')\r\n .replace(/\\d+/g, '#');\r\n return createHash('sha1')\r\n .update([name, shape, ...topFrames(stack)].join('\\n'))\r\n .digest('hex')\r\n .slice(0, 12);\r\n}\r\n\r\n/**\r\n * Groups errors by fingerprint with counts. Process-level failures are observed\r\n * with `uncaughtExceptionMonitor`, which never changes Node's crash behaviour\r\n * (unlike an `uncaughtException` or `unhandledRejection` listener, which would\r\n * suppress the default exit).\r\n */\r\nexport class ErrorsProvider implements NodeUIProvider<ErrorsData> {\r\n readonly id = 'errors' as const;\r\n\r\n private groups = new Map<string, ErrorGroup>();\r\n private total = 0;\r\n private listener: ((err: Error, origin: string) => void) | null = null;\r\n /** Called with every newly recorded occurrence (persistence hook). */\r\n onRecord?: (event: ErrorEvent) => void;\r\n\r\n /** Starts observing process-level failures. Idempotent. */\r\n attach(): void {\r\n if (this.listener) return;\r\n this.listener = (err, origin) =>\r\n this.record(err, origin === 'unhandledRejection' ? 'rejection' : 'uncaught');\r\n process.on('uncaughtExceptionMonitor', this.listener);\r\n }\r\n\r\n detach(): void {\r\n if (!this.listener) return;\r\n process.removeListener('uncaughtExceptionMonitor', this.listener);\r\n this.listener = null;\r\n }\r\n\r\n record(\r\n error: unknown,\r\n source: ErrorSource,\r\n context: { requestId?: number; route?: string; status?: number } = {},\r\n ): void {\r\n try {\r\n const { name, message, stack } = toError(error);\r\n const requestId = context.requestId ?? currentRequestId();\r\n const event: ErrorEvent = {\r\n name,\r\n message,\r\n stack: stack.slice(0, MAX_STACK_CHARS),\r\n source,\r\n at: Date.now(),\r\n ...(requestId !== undefined ? { requestId } : {}),\r\n ...(context.route ? { route: context.route } : {}),\r\n ...(context.status !== undefined ? { status: context.status } : {}),\r\n };\r\n this.apply(event);\r\n this.onRecord?.(event);\r\n } catch {\r\n // recording an error must never raise another one\r\n }\r\n }\r\n\r\n /** Re-applies journaled occurrences without re-emitting them. */\r\n restore(events: readonly ErrorEvent[]): void {\r\n for (const event of events) this.apply(event);\r\n }\r\n\r\n private apply(event: ErrorEvent): void {\r\n const { name, message, stack, source, at: now, requestId } = event;\r\n const context = { route: event.route, status: event.status };\r\n const fingerprint = fingerprintError(name, message, stack);\r\n const existing = this.groups.get(fingerprint);\r\n this.total += 1;\r\n if (existing) {\r\n existing.count += 1;\r\n existing.lastSeenMs = now;\r\n existing.message = message.slice(0, MAX_MESSAGE_CHARS);\r\n existing.stack = stack.slice(0, MAX_STACK_CHARS);\r\n if (requestId !== undefined) existing.lastRequestId = requestId;\r\n if (context.route) existing.lastRoute = context.route;\r\n if (context.status !== undefined) existing.lastStatus = context.status;\r\n return;\r\n }\r\n if (this.groups.size >= MAX_GROUPS) {\r\n let oldest: ErrorGroup | undefined;\r\n for (const g of this.groups.values()) {\r\n if (!oldest || g.lastSeenMs < oldest.lastSeenMs) oldest = g;\r\n }\r\n if (oldest) this.groups.delete(oldest.id);\r\n }\r\n this.groups.set(fingerprint, {\r\n id: fingerprint,\r\n name,\r\n message: message.slice(0, MAX_MESSAGE_CHARS),\r\n stack: stack.slice(0, MAX_STACK_CHARS),\r\n source,\r\n count: 1,\r\n firstSeenMs: now,\r\n lastSeenMs: now,\r\n ...(requestId !== undefined ? { lastRequestId: requestId } : {}),\r\n ...(context.route ? { lastRoute: context.route } : {}),\r\n ...(context.status !== undefined ? { lastStatus: context.status } : {}),\r\n });\r\n }\r\n\r\n get(): { ok: true; data: ErrorsData } {\r\n const groups = [...this.groups.values()].sort((a, b) => b.lastSeenMs - a.lastSeenMs);\r\n return { ok: true, data: { total: this.total, groups } };\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 { currentRequestId, isSuppressed } from '../context';\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\n/** Installed wrappers, so unwrap only removes ours and can retire a buried one. */\r\nconst installed = new Map<string, { fn: AnyFn; state: { live: boolean } }>();\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 state = { live: true };\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 =\r\n state.live && recorders.size > 0 && !isSuppressed() ? 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 const requestId = currentRequestId();\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 ...(requestId !== undefined ? { requestId } : {}),\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 installed.set(key, { fn: wrapped, state });\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 key = `${protocol}${name}`;\r\n const original = originals.get(key);\r\n const ours = installed.get(key);\r\n const target = mod as unknown as Record<string, AnyFn>;\r\n // Another layer (OpenTelemetry, Sentry, an APM agent) may have wrapped on top of\r\n // ours; restoring blindly would drop its wrapper. Restore only if ours is still on\r\n // top, otherwise leave the chain intact and make our wrapper a passthrough.\r\n if (original && ours && target[name] === ours.fn) target[name] = original;\r\n if (ours) ours.state.live = false;\r\n originals.delete(key);\r\n installed.delete(key);\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; requestId?: 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 && !isSuppressed()) {\r\n started.set(request, {\r\n at: process.hrtime.bigint(),\r\n ts: Date.now(),\r\n requestId: currentRequestId(),\r\n });\r\n }\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 ...(begin.requestId !== undefined ? { requestId: begin.requestId } : {}),\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 /** Called with every newly recorded entry (persistence hook). */\r\n onRecord?: (entry: OutgoingRequestEntry) => void;\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 const full = { id: this.nextId, ...entry };\r\n this.buffer.push(full);\r\n this.nextId += 1;\r\n if (entry.error || (entry.status !== null && entry.status >= 500)) this.failed += 1;\r\n this.onRecord?.(full);\r\n }\r\n\r\n /** Re-inserts journaled entries without re-emitting them. */\r\n restore(entries: readonly OutgoingRequestEntry[]): void {\r\n for (const entry of entries) {\r\n this.buffer.push(entry);\r\n this.nextId = Math.max(this.nextId, entry.id + 1);\r\n if (entry.error || (entry.status !== null && entry.status >= 500)) this.failed += 1;\r\n }\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';\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 ?? join(tmpdir(), 'nodeui-heap');\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 // Heap dumps contain every secret held in memory: keep them owner-only.\n await mkdir(dir, { recursive: true, mode: 0o700 });\n writeHeapSnapshot(filePath);\n await chmod(filePath, 0o600).catch(() => undefined);\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 {\r\n NodeUIProvider,\r\n RequestEntry,\r\n RequestsData,\r\n RequestsSummary,\r\n RouteStat,\r\n} from '../types';\r\nimport { RingBuffer } from '../ring-buffer';\r\n\r\nconst RESPONSE_PAGE_SIZE = 100;\r\nconst MAX_ROUTE_STATS = 8;\r\n\r\nfunction percentile(sorted: number[], p: number): number {\r\n if (sorted.length === 0) return 0;\r\n const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1));\r\n return sorted[index] ?? 0;\r\n}\r\n\r\n/** Collapses numeric/uuid path segments so `/users/1` and `/users/2` aggregate. */\r\nfunction routeKey(path: string): string {\r\n return path\r\n .split('/')\r\n .map((seg) =>\r\n /^\\d+$/.test(seg) || /^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(seg) || /^[0-9a-f]{24}$/i.test(seg)\r\n ? ':id'\r\n : seg,\r\n )\r\n .join('/');\r\n}\r\n\r\nexport function summarizeRequests(entries: readonly RequestEntry[]): RequestsSummary {\r\n const durations = entries.map((e) => e.durationMs).sort((a, b) => a - b);\r\n const byStatus = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0 };\r\n const groups = new Map<string, { method: string; path: string; ms: number[]; errors: number }>();\r\n for (const e of entries) {\r\n const bucket = `${Math.floor(e.status / 100)}xx` as keyof typeof byStatus;\r\n if (bucket in byStatus) byStatus[bucket] += 1;\r\n const path = e.route ?? routeKey(e.path);\r\n const key = `${e.method} ${path}`;\r\n const group = groups.get(key) ?? { method: e.method, path, ms: [], errors: 0 };\r\n group.ms.push(e.durationMs);\r\n if (e.status >= 500) group.errors += 1;\r\n groups.set(key, group);\r\n }\r\n const routes: RouteStat[] = [...groups.values()]\r\n .map((g) => {\r\n const sorted = [...g.ms].sort((a, b) => a - b);\r\n return {\r\n method: g.method,\r\n path: g.path,\r\n count: g.ms.length,\r\n avgMs: g.ms.reduce((a, b) => a + b, 0) / g.ms.length,\r\n p95Ms: percentile(sorted, 0.95),\r\n errors: g.errors,\r\n };\r\n })\r\n .sort((a, b) => b.p95Ms - a.p95Ms)\r\n .slice(0, MAX_ROUTE_STATS);\r\n return {\r\n count: entries.length,\r\n errors: byStatus['5xx'],\r\n errorRate: entries.length === 0 ? 0 : byStatus['5xx'] / entries.length,\r\n avgMs: durations.length === 0 ? 0 : durations.reduce((a, b) => a + b, 0) / durations.length,\r\n p50Ms: percentile(durations, 0.5),\r\n p95Ms: percentile(durations, 0.95),\r\n p99Ms: percentile(durations, 0.99),\r\n byStatus,\r\n routes,\r\n };\r\n}\r\n\r\n/** Fixed-size ring buffer of recent HTTP requests, recorded via middleware hook. */\r\nexport class RequestsProvider implements NodeUIProvider<RequestsData> {\r\n readonly id = 'requests' as const;\r\n\r\n private buffer: RingBuffer<RequestEntry>;\r\n private nextId = 1;\r\n /** Called with every newly recorded entry (persistence hook). */\r\n onRecord?: (entry: RequestEntry) => void;\r\n\r\n constructor(size: number) {\r\n this.buffer = new RingBuffer<RequestEntry>(size);\r\n }\r\n\r\n /** Reserves an id up front so in-flight work can be attributed to the request. */\r\n reserveId(): number {\r\n const id = this.nextId;\r\n this.nextId += 1;\r\n return id;\r\n }\r\n\r\n record(entry: Omit<RequestEntry, 'id'>, id: number = this.reserveId()): void {\r\n const full = { id, ...entry };\r\n this.buffer.push(full);\r\n this.onRecord?.(full);\r\n }\r\n\r\n /** Re-inserts journaled entries without re-emitting them. */\r\n restore(entries: readonly RequestEntry[]): void {\r\n for (const entry of entries) {\r\n this.buffer.push(entry);\r\n this.nextId = Math.max(this.nextId, entry.id + 1);\r\n }\r\n }\r\n\r\n get length(): number {\r\n return this.buffer.length;\r\n }\r\n\r\n get(): { ok: true; data: RequestsData } {\r\n const entries = this.buffer.slice(Math.max(0, this.buffer.length - RESPONSE_PAGE_SIZE));\r\n const summary = summarizeRequests(this.buffer.toArray());\r\n return { ok: true, data: { total: this.buffer.length, entries, summary } };\r\n }\r\n}\r\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 source = ctx.store['routes-source'] as RouteEntry[] | (() => RouteEntry[]) | undefined;\n if (source !== undefined) {\n const routes = typeof source === 'function' ? source() : source;\n return { ok: true, data: { routes } };\n }\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';\r\nimport { currentRequestId } from '../context';\r\nimport { RingBuffer } from '../ring-buffer';\r\nimport type { LogEntry, LogLevel, LogsData, NodeUIProvider, ProviderContext } from '../types';\r\n\r\nconst CONSOLE_METHODS: Array<[keyof Console, LogLevel]> = [\r\n ['debug', 'debug'],\r\n ['info', 'info'],\r\n ['log', 'info'],\r\n ['warn', 'warn'],\r\n ['error', 'error'],\r\n];\r\n\r\nfunction formatArg(arg: unknown): string {\r\n if (typeof arg === 'string') return arg;\r\n try {\r\n const json = JSON.stringify(arg);\r\n return json === undefined ? String(arg) : json;\r\n } catch {\r\n return String(arg);\r\n }\r\n}\r\n\r\nlet refs = 0;\r\nconst pushes = new Set<(entry: Omit<LogEntry, 'timestamp'>) => void>();\r\nconst wrappers = new Map<keyof Console, (...args: unknown[]) => void>();\r\nlet originals: Partial<Record<keyof Console, (...args: unknown[]) => void>> = {};\r\n\r\nfunction consoleRef(): Record<string, (...args: unknown[]) => void> {\r\n return console as unknown as Record<string, (...args: unknown[]) => void>;\r\n}\r\n\r\nfunction installWrappers(): void {\r\n for (const [method, level] of CONSOLE_METHODS) {\r\n const original = consoleRef()[method];\r\n originals[method] = original;\r\n const wrapper = (...args: unknown[]): void => {\r\n for (const push of pushes) {\r\n try {\r\n push({ level, message: args.map(formatArg).join(' '), requestId: currentRequestId() });\r\n } catch {\r\n // interception must never throw into the caller\r\n }\r\n }\r\n if (original) original(...args);\r\n };\r\n wrappers.set(method, wrapper);\r\n consoleRef()[method] = wrapper;\r\n }\r\n}\r\n\r\nfunction uninstallWrappers(): void {\r\n for (const [method] of CONSOLE_METHODS) {\r\n const current = consoleRef()[method];\r\n if (current === wrappers.get(method)) {\r\n const original = originals[method];\r\n if (original) {\r\n consoleRef()[method] = original;\r\n } else {\r\n delete consoleRef()[method];\r\n }\r\n }\r\n // If another layer patched console after us, the current method is not\r\n // ours — leave the chain intact so the host app's interceptor survives.\r\n }\r\n wrappers.clear();\r\n originals = {};\r\n}\r\n\r\n/**\r\n * Wraps `console.debug/info/log/warn/error` to tee entries to every active\r\n * `push` callback. Refcounted: the first install replaces the methods, the\r\n * last release restores the originals. Supports concurrent consumers (e.g.\r\n * multiple `createNodeUI()` instances) — each registers its own push and is\r\n * removed independently. Never throws into the caller.\r\n */\r\nexport function interceptConsole(push: (entry: Omit<LogEntry, 'timestamp'>) => void): () => void {\r\n refs += 1;\r\n pushes.add(push);\r\n if (refs === 1) installWrappers();\r\n return () => {\r\n pushes.delete(push);\r\n refs = Math.max(0, refs - 1);\r\n if (refs === 0) uninstallWrappers();\r\n };\r\n}\r\n\r\n/**\r\n * Log viewer provider. While active it intercepts `console.*` output and\r\n * also accepts entries pushed via {@link addSource}. Filtering happens\r\n * server-side via `ctx.query.level` and `ctx.query.query`.\r\n */\r\nexport class LogsProvider implements NodeUIProvider<LogsData> {\r\n readonly id = 'logs' as const;\r\n\r\n private buffer: RingBuffer<LogEntry>;\r\n private release: (() => void) | null = null;\r\n\r\n constructor(size: number = DEFAULT_LOG_SIZE) {\r\n this.buffer = new RingBuffer<LogEntry>(size);\r\n }\r\n\r\n start(): void {\r\n if (this.release) return;\r\n this.release = interceptConsole(({ level, message, requestId }) => {\r\n this.buffer.push({\r\n level,\r\n message,\r\n timestamp: Date.now(),\r\n ...(requestId !== undefined ? { requestId } : {}),\r\n });\r\n });\r\n }\r\n\r\n stop(): void {\r\n this.release?.();\r\n this.release = null;\r\n }\r\n\r\n /** Pushes an external entry (logger adapter). */\r\n addSource(entry: { level: LogLevel; message: string }): void {\r\n this.buffer.push({ ...entry, timestamp: Date.now() });\r\n }\r\n\r\n get(ctx: ProviderContext): { ok: true; data: LogsData } {\r\n let entries = this.buffer.toArray();\r\n const level = ctx.query?.level;\r\n if (level) entries = entries.filter((e) => e.level === level);\r\n const query = ctx.query?.query;\r\n if (query) {\r\n const needle = query.toLowerCase();\r\n entries = entries.filter((e) => e.message.toLowerCase().includes(needle));\r\n }\r\n return { ok: true, data: { entries } };\r\n }\r\n}\r\n","import type { IncomingMessage, ServerResponse } from 'node:http';\r\nimport { SECRET_MASKED } from './constants';\r\nimport { maskSecrets, maskSecretText, SECRET_KEY_PATTERN } from './safety';\r\nimport type { RequestDetail, RequestDetailOptions } from './types';\r\n\r\nexport const DEFAULT_REQUEST_DETAIL: Required<RequestDetailOptions> = {\r\n query: true,\r\n headers: true,\r\n bodies: false,\r\n maxBodyBytes: 4096,\r\n};\r\n\r\n/** Merges user options over the defaults; `false` turns every capture off. */\r\nexport function resolveRequestDetail(\r\n option: boolean | RequestDetailOptions | undefined,\r\n bodiesFromEnv: boolean,\r\n): Required<RequestDetailOptions> {\r\n if (option === false) {\r\n return { query: false, headers: false, bodies: false, maxBodyBytes: 0 };\r\n }\r\n const given = typeof option === 'object' ? option : {};\r\n const maxBodyBytes = given.maxBodyBytes ?? DEFAULT_REQUEST_DETAIL.maxBodyBytes;\r\n if (!Number.isInteger(maxBodyBytes) || maxBodyBytes <= 0 || maxBodyBytes > 1_048_576) {\r\n throw new Error('captureRequestDetail.maxBodyBytes must be an integer in 1..1048576');\r\n }\r\n return {\r\n query: given.query ?? DEFAULT_REQUEST_DETAIL.query,\r\n headers: given.headers ?? DEFAULT_REQUEST_DETAIL.headers,\r\n bodies: given.bodies ?? (bodiesFromEnv || DEFAULT_REQUEST_DETAIL.bodies),\r\n maxBodyBytes,\r\n };\r\n}\r\n\r\n/** Header names whose values are never stored, regardless of `maskSecrets`. */\r\nconst ALWAYS_REDACT = /^(authorization|proxy-authorization|cookie|set-cookie|x-api-key)$/i;\r\nconst TEXTUAL_TYPE = /json|text|xml|x-www-form-urlencoded|javascript|graphql/i;\r\n\r\nfunction queryOf(req: IncomingMessage): Record<string, string> | undefined {\r\n const raw = (req as IncomingMessage & { originalUrl?: string }).originalUrl ?? req.url ?? '';\r\n const index = raw.indexOf('?');\r\n if (index === -1) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [key, value] of new URLSearchParams(raw.slice(index + 1))) {\r\n out[key] = SECRET_KEY_PATTERN.test(key) ? SECRET_MASKED : value;\r\n }\r\n return Object.keys(out).length > 0 ? out : undefined;\r\n}\r\n\r\nfunction headersOf(req: IncomingMessage): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const [name, value] of Object.entries(req.headers)) {\r\n if (value === undefined) continue;\r\n out[name] =\r\n ALWAYS_REDACT.test(name) || SECRET_KEY_PATTERN.test(name)\r\n ? SECRET_MASKED\r\n : Array.isArray(value)\r\n ? value.join(', ')\r\n : value;\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Response content type. `writeHead(status, headers)` bypasses `getHeader`, so\r\n * fall back to the serialized header block once it has been flushed.\r\n */\r\nfunction responseType(res: ServerResponse): string {\r\n const header = res.getHeader('content-type');\r\n if (header !== undefined) return String(header);\r\n const raw = (res as ServerResponse & { _header?: unknown })._header;\r\n const match = typeof raw === 'string' ? /^content-type:[ \\t]*(.*?)\\r?$/im.exec(raw) : null;\r\n return match?.[1] ?? '';\r\n}\r\n\r\n/** Collects up to `max` bytes and notes whether anything was dropped. */\r\nclass BodyCollector {\r\n private chunks: Buffer[] = [];\r\n private size = 0;\r\n total = 0;\r\n\r\n constructor(private readonly max: number) {}\r\n\r\n add(chunk: unknown, encoding?: unknown): void {\r\n let buf: Buffer;\r\n if (Buffer.isBuffer(chunk)) buf = chunk;\r\n else if (typeof chunk === 'string') {\r\n buf = Buffer.from(\r\n chunk,\r\n typeof encoding === 'string' ? (encoding as BufferEncoding) : 'utf8',\r\n );\r\n } else if (chunk instanceof Uint8Array) buf = Buffer.from(chunk);\r\n else return;\r\n this.total += buf.length;\r\n if (this.size >= this.max) return;\r\n const slice = buf.subarray(0, this.max - this.size);\r\n this.chunks.push(slice);\r\n this.size += slice.length;\r\n }\r\n\r\n get truncated(): boolean {\r\n return this.total > this.size;\r\n }\r\n\r\n text(): string {\r\n return Buffer.concat(this.chunks).toString('utf8');\r\n }\r\n}\r\n\r\n/** Masks a captured body: structured JSON by key, everything else by text patterns. */\r\nfunction maskBody(text: string, contentType: string, mask: boolean, truncated: boolean): string {\r\n if (!mask) return text;\r\n if (!truncated && /json/i.test(contentType)) {\r\n try {\r\n return JSON.stringify(maskSecrets(JSON.parse(text) as unknown));\r\n } catch {\r\n // fall through to text masking\r\n }\r\n }\r\n return maskSecretText(text);\r\n}\r\n\r\n/**\r\n * Attaches non-invasive capture to a request/response pair. The request body is\r\n * observed by wrapping `emit('data')` (never by adding a listener, which would\r\n * switch the stream to flowing mode and starve the app's body parser).\r\n * Returns a function that yields the captured detail once the response finishes.\r\n */\r\nexport function captureRequestDetail(\r\n req: IncomingMessage,\r\n res: ServerResponse,\r\n settings: Required<RequestDetailOptions>,\r\n mask: boolean,\r\n): () => RequestDetail {\r\n let reqBody: BodyCollector | null = null;\r\n let resBody: BodyCollector | null = null;\r\n\r\n if (settings.bodies) {\r\n const reqType = String(req.headers['content-type'] ?? '');\r\n if (TEXTUAL_TYPE.test(reqType)) {\r\n const collector = new BodyCollector(settings.maxBodyBytes);\r\n reqBody = collector;\r\n const emit = req.emit.bind(req) as (event: string | symbol, ...args: unknown[]) => boolean;\r\n req.emit = ((event: string | symbol, ...args: unknown[]): boolean => {\r\n if (event === 'data') {\r\n try {\r\n collector.add(args[0]);\r\n } catch {\r\n // capture must never break the app\r\n }\r\n }\r\n return emit(event, ...args);\r\n }) as typeof req.emit;\r\n }\r\n\r\n const collector = new BodyCollector(settings.maxBodyBytes);\r\n resBody = collector;\r\n const write = res.write.bind(res) as (...a: unknown[]) => boolean;\r\n const end = res.end.bind(res) as (...a: unknown[]) => ServerResponse;\r\n const textual = (): boolean => TEXTUAL_TYPE.test(responseType(res));\r\n res.write = ((chunk: unknown, ...rest: unknown[]): boolean => {\r\n try {\r\n if (textual()) collector.add(chunk, rest[0]);\r\n } catch {\r\n // capture must never break the app\r\n }\r\n return write(chunk, ...rest);\r\n }) as typeof res.write;\r\n res.end = ((chunk?: unknown, ...rest: unknown[]): ServerResponse => {\r\n try {\r\n if (chunk && typeof chunk !== 'function' && textual()) collector.add(chunk, rest[0]);\r\n } catch {\r\n // capture must never break the app\r\n }\r\n return end(chunk, ...rest);\r\n }) as typeof res.end;\r\n }\r\n\r\n return () => {\r\n const detail: RequestDetail = {};\r\n if (settings.query) {\r\n const query = queryOf(req);\r\n if (query) detail.query = query;\r\n }\r\n if (settings.headers) detail.headers = headersOf(req);\r\n if (reqBody && reqBody.total > 0) {\r\n detail.requestBody = maskBody(\r\n reqBody.text(),\r\n String(req.headers['content-type'] ?? ''),\r\n mask,\r\n reqBody.truncated,\r\n );\r\n if (reqBody.truncated) detail.requestBodyTruncated = true;\r\n }\r\n if (resBody && resBody.total > 0) {\r\n detail.responseBody = maskBody(resBody.text(), responseType(res), mask, resBody.truncated);\r\n if (resBody.truncated) detail.responseBodyTruncated = true;\r\n }\r\n return detail;\r\n };\r\n}\r\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(\n res: ServerResponse,\n extraHeaders: Record<string, string> = {},\n): SseStream {\n let closed = false;\n res.writeHead(200, {\n ...extraHeaders,\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';\r\nimport { tmpdir } from 'node:os';\r\nimport { join } 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 RequestDetailOptions,\r\n} from './types';\r\nimport {\r\n isLoopbackAddress,\r\n isLoopbackHostname,\r\n maskSecrets,\r\n resolveActivation,\r\n SECRET_KEY_PATTERN,\r\n isValidAddressEntry,\r\n} 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, uiAssetsDir } 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 { bindRequestId, currentRequestId, runWithRequestId } from './context';\r\nimport { captureRequestDetail, resolveRequestDetail } from './request-detail';\r\nimport { OtlpExporter, type OtlpOptions } from './otlp';\r\nimport { DEFAULT_PERSIST_MAX_BYTES, Persistence, type PersistedRecord } from './persistence';\r\nimport { StartupTracker } from './providers/startup-tracker';\r\nimport { RequestsProvider } from './providers/requests';\r\nimport { OutgoingProvider } from './providers/outgoing';\r\nimport { ErrorsProvider } from './providers/errors';\r\nimport { QueriesProvider } from './providers/queries';\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 /**\r\n * Extra detail recorded per request: query, headers (credentials always\r\n * redacted) and, opt-in, textual bodies (or `NODEUI_CAPTURE_BODIES=true`).\r\n * `false` records none of it.\r\n */\r\n captureRequestDetail?: boolean | RequestDetailOptions;\r\n /**\r\n * Keep recent requests, outgoing calls, queries and errors in an NDJSON\r\n * journal so they survive a restart (or `NODEUI_PERSIST_FILE`). Off by\r\n * default. The file is private (0600), masked, and rotated at `maxBytes`\r\n * (default 5 MiB). It holds whatever the console records, so keep it out of\r\n * version control.\r\n */\r\n persist?: string | { file: string; maxBytes?: number };\r\n /**\r\n * When the outgoing-call, query and log instrumentation runs. `'always'`\r\n * (default) records from startup, so you can open the console after a problem\r\n * and still see what led to it. `'lazy'` installs the hooks only while a panel\r\n * is open and removes them after `inactivityTimeoutMs`, for zero cost when\r\n * nobody is looking (or `NODEUI_CAPTURE=lazy`). Buffers are bounded either way.\r\n */\r\n capture?: 'always' | 'lazy';\r\n /**\r\n * Export requests, outgoing calls and queries as OTLP/HTTP JSON spans to a\r\n * collector (or `NODEUI_OTLP_ENDPOINT`). Off by default. Enabling it keeps\r\n * outgoing and query instrumentation running and sends (masked) telemetry to\r\n * the given endpoint, so point it at a local collector.\r\n */\r\n otlp?: string | OtlpOptions;\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 /** Max recorded database queries. Default 200. */\r\n queryLogSize?: number;\r\n /** Queries at or above this duration are flagged slow. Default 100 ms. */\r\n slowQueryMs?: 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 /**\r\n * Records a database query from an ORM or driver hook that NodeUI does not\r\n * patch itself (Sequelize `logging`, TypeORM logger, ...). Parameters are not recorded.\r\n */\r\n recordQuery(query: {\r\n system: string;\r\n sql: string;\r\n durationMs: number;\r\n rowCount?: number;\r\n error?: string;\r\n }): void;\r\n /**\r\n * Subscribes to a Prisma client's query events. Create the client with\r\n * `log: [{ emit: 'event', level: 'query' }]`.\r\n */\r\n trackPrisma(client: {\r\n $on(event: 'query', cb: (e: { query: string; duration: number | bigint }) => void): void;\r\n }): void;\r\n /**\r\n * Records a handled error (e.g. from a framework error hook) so it appears in\r\n * the Errors panel, attributed to the current request when there is one.\r\n */\r\n recordError(error: unknown, context?: { route?: string; status?: number }): 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 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 'errors',\r\n 'queries',\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 otlpHostname(endpoint: string): string {\r\n try {\r\n return new URL(endpoint).hostname.replace(/^\\[|\\]$/g, '');\r\n } catch {\r\n throw new Error(`nodeui otlp endpoint \"${endpoint}\" is not a valid URL`);\r\n }\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 resolveCapture(\r\n option: 'always' | 'lazy' | undefined,\r\n env: string | undefined,\r\n): 'always' | 'lazy' {\r\n const value = option ?? env ?? 'always';\r\n if (value !== 'always' && value !== 'lazy') {\r\n throw new Error(`nodeui capture must be 'always' or 'lazy', got \"${String(value)}\"`);\r\n }\r\n return value;\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 capture: resolveCapture(options.capture, env.NODEUI_CAPTURE),\r\n persistFile:\r\n (typeof options.persist === 'string' ? options.persist : options.persist?.file) ??\r\n env.NODEUI_PERSIST_FILE ??\r\n null,\r\n persistMaxBytes: positiveInt(\r\n typeof options.persist === 'object' ? options.persist.maxBytes : undefined,\r\n DEFAULT_PERSIST_MAX_BYTES,\r\n 'persist.maxBytes',\r\n ),\r\n otlp:\r\n typeof options.otlp === 'string'\r\n ? { endpoint: options.otlp }\r\n : (options.otlp ??\r\n (env.NODEUI_OTLP_ENDPOINT ? { endpoint: env.NODEUI_OTLP_ENDPOINT } : null)),\r\n requestDetail: resolveRequestDetail(\r\n options.captureRequestDetail,\r\n env.NODEUI_CAPTURE_BODIES === 'true',\r\n ),\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) {\r\n for (const entry of config.allowedRemoteAddresses) {\r\n if (!isValidAddressEntry(entry)) {\r\n console.warn(\r\n `[nodeui] ignoring allowedRemoteAddresses entry \"${entry}\": expected an IP or a ` +\r\n 'CIDR such as 172.16.0.0/12 or fd00::/8; it will never match.',\r\n );\r\n }\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 const outgoingProvider = new OutgoingProvider(\r\n positiveInt(options.outgoingLogSize, 200, 'outgoingLogSize'),\r\n );\r\n registry.register(outgoingProvider);\r\n\r\n const queriesProvider = new QueriesProvider({\r\n size: positiveInt(options.queryLogSize, 200, 'queryLogSize'),\r\n slowQueryMs: positiveInt(options.slowQueryMs, 100, 'slowQueryMs'),\r\n });\r\n registry.register(queriesProvider);\r\n\r\n const errorsProvider = new ErrorsProvider();\r\n registry.register(errorsProvider);\r\n if (config.enabled) errorsProvider.attach();\r\n\r\n // Optional sinks fed by every recorded item: a journal that survives restarts\r\n // and an OTLP exporter. Their data comes from the lazy outgoing/query\r\n // instrumentation, so either sink keeps those running (\"pinned\").\r\n const persistence =\r\n config.enabled && config.persistFile\r\n ? new Persistence(config.persistFile, config.persistMaxBytes)\r\n : null;\r\n const exporter = config.enabled && config.otlp ? new OtlpExporter(config.otlp) : null;\r\n const pinned = new Set<PanelId>();\r\n const otlpHost = config.otlp ? otlpHostname(config.otlp.endpoint) : null;\r\n if (exporter && config.otlp && !isLoopbackHostname(otlpHost)) {\r\n console.warn(\r\n `[nodeui] exporting telemetry to non-local OTLP endpoint ${config.otlp.endpoint}; ` +\r\n 'requests, queries and outgoing URLs leave this machine (masked).',\r\n );\r\n }\r\n if (persistence) {\r\n const byKind = (kind: PersistedRecord['kind']): never[] =>\r\n persistence\r\n .load()\r\n .filter((r) => r.kind === kind)\r\n .map((r) => r.data) as never[];\r\n requestsProvider.restore(byKind('request'));\r\n outgoingProvider.restore(byKind('outgoing'));\r\n queriesProvider.restore(byKind('query'));\r\n errorsProvider.restore(byKind('error'));\r\n }\r\n if (persistence || exporter) {\r\n const save = (kind: PersistedRecord['kind'], data: unknown): void =>\r\n persistence?.append(kind, config.maskSecrets ? maskSecrets(data) : data);\r\n requestsProvider.onRecord = (e) => {\r\n save('request', e);\r\n exporter?.request(e);\r\n };\r\n outgoingProvider.onRecord = (e) => {\r\n save('outgoing', e);\r\n exporter?.outgoing(e);\r\n };\r\n queriesProvider.onRecord = (e) => {\r\n save('query', e);\r\n exporter?.query(e);\r\n };\r\n errorsProvider.onRecord = (e) => save('error', e);\r\n }\r\n // Sinks need the outgoing/query hooks running; `capture: 'always'` also pins logs.\r\n const pinnedProviders: Array<NodeUIProvider> = [];\r\n if (persistence || exporter || (config.enabled && config.capture === 'always')) {\r\n pinnedProviders.push(outgoingProvider, queriesProvider);\r\n }\r\n if (config.enabled && config.capture === 'always') pinnedProviders.push(logsProvider);\r\n for (const provider of pinnedProviders) {\r\n provider.start?.(ctx);\r\n pinned.add(provider.id);\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 if (!pinned.has(id)) 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 locked: 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(uiAssetsDir(), 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 /** Matched route pattern: Express exposes `req.route`, adapters may set `nodeuiRoute`. */\r\n function routePatternOf(req: IncomingMessage): string | undefined {\r\n const r = req as IncomingMessage & {\r\n nodeuiRoute?: unknown;\r\n route?: { path?: unknown };\r\n baseUrl?: unknown;\r\n };\r\n if (typeof r.nodeuiRoute === 'string') return r.nodeuiRoute;\r\n const path = r.route?.path;\r\n if (typeof path !== 'string') return undefined;\r\n return `${typeof r.baseUrl === 'string' ? r.baseUrl : ''}${path}` || undefined;\r\n }\r\n\r\n function recordAppRequest(req: IncomingMessage, res: ServerResponse): number {\r\n const started = process.hrtime.bigint();\r\n const timestampMs = Date.now();\r\n const requestId = requestsProvider.reserveId();\r\n bindRequestId(req, requestId);\r\n const collectDetail = captureRequestDetail(req, res, config.requestDetail, config.maskSecrets);\r\n res.on('finish', () => {\r\n const durationMs = Number(process.hrtime.bigint() - started) / 1e6;\r\n requestsProvider.record(\r\n {\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 route: routePatternOf(req),\r\n ...collectDetail(),\r\n },\r\n requestId,\r\n );\r\n metricsProvider.record(res.statusCode);\r\n });\r\n return requestId;\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 const requestId = recordAppRequest(req, res);\r\n runWithRequestId(requestId, 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 recordQuery(query): void {\r\n if (!config.enabled) return;\r\n queriesProvider.record({\r\n ...query,\r\n sql: query.sql.slice(0, 2000),\r\n timestampMs: Date.now() - query.durationMs,\r\n requestId: currentRequestId(),\r\n });\r\n },\r\n trackPrisma(client): void {\r\n if (!config.enabled) return;\r\n client.$on('query', (e) =>\r\n server.recordQuery({ system: 'prisma', sql: e.query, durationMs: Number(e.duration) }),\r\n );\r\n },\r\n recordError(error, context): void {\r\n if (!config.enabled) return;\r\n // Client errors (e.g. http-errors 404) are expected outcomes, not failures.\r\n const e = error as { status?: unknown; statusCode?: unknown } | null;\r\n const status = e?.status ?? e?.statusCode;\r\n if (typeof status === 'number' && status >= 400 && status < 500) return;\r\n errorsProvider.record(error, 'request', context);\r\n },\r\n shutdown(): void {\r\n errorsProvider.detach();\r\n persistence?.close();\r\n exporter?.close();\r\n for (const id of pinned) registry.get(id)?.stop?.(ctx);\r\n pinned.clear();\r\n stopAll();\r\n },\r\n };\r\n\r\n return server;\r\n}\r\n","import { randomBytes } from 'node:crypto';\r\nimport { runSuppressed } from './context';\r\nimport { maskSecretText } from './safety';\r\nimport type { OutgoingRequestEntry, QueryEntry, RequestEntry } from './types';\r\n\r\nexport interface OtlpOptions {\r\n /** Collector base URL (e.g. `http://localhost:4318`) or a full `/v1/traces` URL. */\r\n endpoint: string;\r\n /** `service.name` resource attribute. Default `OTEL_SERVICE_NAME` or `nodeui-app`. */\r\n serviceName?: string;\r\n /** Extra HTTP headers, e.g. an auth header for a hosted collector. */\r\n headers?: Record<string, string>;\r\n /** Export interval. Default 2000 ms. */\r\n intervalMs?: number;\r\n}\r\n\r\ninterface TraceContext {\r\n traceId: string;\r\n spanId: string;\r\n}\r\n\r\ninterface Attribute {\r\n key: string;\r\n value: { stringValue: string } | { intValue: string } | { boolValue: boolean };\r\n}\r\n\r\ninterface Span {\r\n traceId: string;\r\n spanId: string;\r\n parentSpanId?: string;\r\n name: string;\r\n kind: number;\r\n startTimeUnixNano: string;\r\n endTimeUnixNano: string;\r\n attributes: Attribute[];\r\n status?: { code: number; message?: string };\r\n}\r\n\r\nconst KIND_SERVER = 2;\r\nconst KIND_CLIENT = 3;\r\nconst STATUS_ERROR = 2;\r\nconst MAX_QUEUE = 1000;\r\nconst MAX_BATCH = 200;\r\nconst MAX_TRACKED_REQUESTS = 1000;\r\n\r\nconst hex = (bytes: number): string => randomBytes(bytes).toString('hex');\r\nconst nanos = (ms: number): string => (BigInt(Math.round(ms * 1000)) * 1000n).toString();\r\nconst str = (key: string, value: string): Attribute => ({ key, value: { stringValue: value } });\r\nconst int = (key: string, value: number): Attribute => ({\r\n key,\r\n value: { intValue: String(Math.round(value)) },\r\n});\r\n\r\n/** Resolves the traces URL: accepts a base URL or an explicit `/v1/traces` URL. */\r\nexport function tracesUrl(endpoint: string): string {\r\n const trimmed = endpoint.replace(/\\/+$/, '');\r\n return trimmed.endsWith('/v1/traces') ? trimmed : `${trimmed}/v1/traces`;\r\n}\r\n\r\n/**\r\n * Exports recorded requests, outgoing calls and database queries as OTLP/HTTP\r\n * JSON spans. Spans of one request share a trace; outgoing calls and queries\r\n * are children of the request span. No OpenTelemetry SDK is needed. Failures\r\n * never reach the host app, and the exporter's own HTTP calls are excluded\r\n * from outgoing capture.\r\n */\r\nexport class OtlpExporter {\r\n private readonly url: string;\r\n private readonly serviceName: string;\r\n private queue: Span[] = [];\r\n private timer: NodeJS.Timeout | null = null;\r\n private contexts = new Map<number, TraceContext>();\r\n private warned = false;\r\n exported = 0;\r\n dropped = 0;\r\n\r\n constructor(\r\n private readonly options: OtlpOptions,\r\n env: Record<string, string | undefined> = process.env,\r\n ) {\r\n this.url = tracesUrl(options.endpoint);\r\n this.serviceName = options.serviceName ?? env.OTEL_SERVICE_NAME ?? 'nodeui-app';\r\n }\r\n\r\n /** Stable ids for a request so children recorded earlier link to its span. */\r\n private contextFor(requestId: number): TraceContext {\r\n let ctx = this.contexts.get(requestId);\r\n if (!ctx) {\r\n ctx = { traceId: hex(16), spanId: hex(8) };\r\n this.contexts.set(requestId, ctx);\r\n if (this.contexts.size > MAX_TRACKED_REQUESTS) {\r\n const oldest = this.contexts.keys().next().value;\r\n if (oldest !== undefined) this.contexts.delete(oldest);\r\n }\r\n }\r\n return ctx;\r\n }\r\n\r\n request(entry: RequestEntry): void {\r\n const ctx = this.contextFor(entry.id);\r\n const attributes = [\r\n str('http.request.method', entry.method),\r\n str('url.path', maskSecretText(entry.path)),\r\n int('http.response.status_code', entry.status),\r\n str('client.address', entry.ip),\r\n ];\r\n if (entry.route) attributes.push(str('http.route', entry.route));\r\n this.push({\r\n ...ctx,\r\n name: `${entry.method} ${entry.route ?? entry.path}`,\r\n kind: KIND_SERVER,\r\n startTimeUnixNano: nanos(entry.timestampMs),\r\n endTimeUnixNano: nanos(entry.timestampMs + entry.durationMs),\r\n attributes,\r\n ...(entry.status >= 500 ? { status: { code: STATUS_ERROR } } : {}),\r\n });\r\n }\r\n\r\n outgoing(entry: OutgoingRequestEntry): void {\r\n const attributes = [\r\n str('http.request.method', entry.method),\r\n str('url.full', maskSecretText(entry.url)),\r\n ];\r\n if (entry.status !== null) attributes.push(int('http.response.status_code', entry.status));\r\n const failed = Boolean(entry.error) || (entry.status !== null && entry.status >= 500);\r\n this.child(entry.requestId, {\r\n name: `${entry.method} ${entry.url}`,\r\n timestampMs: entry.timestampMs,\r\n durationMs: entry.durationMs,\r\n attributes,\r\n ...(failed ? { status: { code: STATUS_ERROR, message: entry.error } } : {}),\r\n });\r\n }\r\n\r\n query(entry: QueryEntry): void {\r\n const statement = maskSecretText(entry.sql);\r\n this.child(entry.requestId, {\r\n name: `${statement.split(/\\s+/)[0]?.toUpperCase() ?? 'QUERY'} ${entry.system}`,\r\n timestampMs: entry.timestampMs,\r\n durationMs: entry.durationMs,\r\n attributes: [str('db.system.name', entry.system), str('db.query.text', statement)],\r\n ...(entry.error ? { status: { code: STATUS_ERROR, message: entry.error } } : {}),\r\n });\r\n }\r\n\r\n private child(\r\n requestId: number | undefined,\r\n span: {\r\n name: string;\r\n timestampMs: number;\r\n durationMs: number;\r\n attributes: Attribute[];\r\n status?: { code: number; message?: string };\r\n },\r\n ): void {\r\n const parent = requestId === undefined ? undefined : this.contextFor(requestId);\r\n this.push({\r\n traceId: parent?.traceId ?? hex(16),\r\n spanId: hex(8),\r\n ...(parent ? { parentSpanId: parent.spanId } : {}),\r\n name: span.name,\r\n kind: KIND_CLIENT,\r\n startTimeUnixNano: nanos(span.timestampMs),\r\n endTimeUnixNano: nanos(span.timestampMs + span.durationMs),\r\n attributes: span.attributes,\r\n ...(span.status ? { status: span.status } : {}),\r\n });\r\n }\r\n\r\n private push(span: Span): void {\r\n if (this.queue.length >= MAX_QUEUE) {\r\n this.queue.shift();\r\n this.dropped += 1;\r\n }\r\n this.queue.push(span);\r\n if (!this.timer) {\r\n this.timer = setTimeout(() => void this.flush(), this.options.intervalMs ?? 2000);\r\n if (typeof this.timer.unref === 'function') this.timer.unref();\r\n }\r\n }\r\n\r\n body(spans: Span[]): string {\r\n return JSON.stringify({\r\n resourceSpans: [\r\n {\r\n resource: { attributes: [str('service.name', this.serviceName)] },\r\n scopeSpans: [{ scope: { name: '@singhak/nodeui' }, spans }],\r\n },\r\n ],\r\n });\r\n }\r\n\r\n async flush(): Promise<void> {\r\n this.timer = null;\r\n while (this.queue.length > 0) {\r\n const batch = this.queue.splice(0, MAX_BATCH);\r\n try {\r\n const res = await runSuppressed(() =>\r\n fetch(this.url, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json', ...this.options.headers },\r\n body: this.body(batch),\r\n signal: AbortSignal.timeout(5000),\r\n }),\r\n );\r\n if (!res.ok) throw new Error(`collector answered ${res.status}`);\r\n this.exported += batch.length;\r\n } catch (err) {\r\n this.dropped += batch.length;\r\n if (!this.warned) {\r\n this.warned = true;\r\n console.warn(\r\n `[nodeui] OTLP export to ${this.url} failed (${\r\n err instanceof Error ? err.message : String(err)\r\n }); dropping spans.`,\r\n );\r\n }\r\n return; // do not hammer a down collector; the next record re-arms the timer\r\n }\r\n }\r\n }\r\n\r\n /** Cancels the timer and makes a best-effort final export. */\r\n close(): void {\r\n if (this.timer) clearTimeout(this.timer);\r\n this.timer = null;\r\n void this.flush();\r\n }\r\n}\r\n","import {\r\n appendFile,\r\n appendFileSync,\r\n existsSync,\r\n mkdirSync,\r\n readFileSync,\r\n renameSync,\r\n statSync,\r\n} from 'node:fs';\r\nimport { dirname } from 'node:path';\r\n\r\nexport type PersistKind = 'request' | 'outgoing' | 'query' | 'error';\r\n\r\nexport interface PersistedRecord {\r\n kind: PersistKind;\r\n data: unknown;\r\n}\r\n\r\nexport const DEFAULT_PERSIST_MAX_BYTES = 5 * 1024 * 1024;\r\nconst FLUSH_DELAY_MS = 250;\r\n\r\n/**\r\n * Append-only NDJSON journal of recent activity so the console survives a\r\n * restart. Writes are batched and asynchronous (never on the request path),\r\n * the file is created private (0600) and rotated to `<file>.1` when it grows\r\n * past `maxBytes`, so disk use stays under roughly twice that. Failures to\r\n * read or write are swallowed: persistence is a convenience, never a risk to\r\n * the host app.\r\n */\r\nexport class Persistence {\r\n private pending: string[] = [];\r\n private timer: NodeJS.Timeout | null = null;\r\n private writing = false;\r\n\r\n constructor(\r\n readonly file: string,\r\n private readonly maxBytes: number = DEFAULT_PERSIST_MAX_BYTES,\r\n ) {\r\n try {\r\n mkdirSync(dirname(file), { recursive: true });\r\n } catch {\r\n // surfaced as a no-op journal\r\n }\r\n }\r\n\r\n append(kind: PersistKind, data: unknown): void {\r\n try {\r\n this.pending.push(`${JSON.stringify({ kind, data })}\\n`);\r\n } catch {\r\n return;\r\n }\r\n if (!this.timer) {\r\n this.timer = setTimeout(() => this.flush(), FLUSH_DELAY_MS);\r\n if (typeof this.timer.unref === 'function') this.timer.unref();\r\n }\r\n }\r\n\r\n /** Reads the journal (rotated file first) in chronological order. */\r\n load(): PersistedRecord[] {\r\n const out: PersistedRecord[] = [];\r\n for (const path of [`${this.file}.1`, this.file]) {\r\n let text: string;\r\n try {\r\n text = readFileSync(path, 'utf8');\r\n } catch {\r\n continue;\r\n }\r\n for (const line of text.split('\\n')) {\r\n if (!line) continue;\r\n try {\r\n const parsed = JSON.parse(line) as PersistedRecord;\r\n if (parsed && typeof parsed.kind === 'string') out.push(parsed);\r\n } catch {\r\n // a torn last line after a crash is expected\r\n }\r\n }\r\n }\r\n return out;\r\n }\r\n\r\n private rotateIfNeeded(): void {\r\n try {\r\n if (existsSync(this.file) && statSync(this.file).size >= this.maxBytes) {\r\n renameSync(this.file, `${this.file}.1`);\r\n }\r\n } catch {\r\n // keep appending to the current file\r\n }\r\n }\r\n\r\n private flush(): void {\r\n this.timer = null;\r\n if (this.writing || this.pending.length === 0) {\r\n if (this.pending.length > 0) this.timer = setTimeout(() => this.flush(), FLUSH_DELAY_MS);\r\n return;\r\n }\r\n this.rotateIfNeeded();\r\n const chunk = this.pending.join('');\r\n this.pending = [];\r\n this.writing = true;\r\n appendFile(this.file, chunk, { mode: 0o600 }, () => {\r\n this.writing = false;\r\n });\r\n }\r\n\r\n /** Writes anything still buffered, synchronously (used on shutdown). */\r\n close(): void {\r\n if (this.timer) {\r\n clearTimeout(this.timer);\r\n this.timer = null;\r\n }\r\n if (this.pending.length === 0) return;\r\n try {\r\n this.rotateIfNeeded();\r\n appendFileSync(this.file, this.pending.join(''), { mode: 0o600 });\r\n } catch {\r\n // ignore\r\n }\r\n this.pending = [];\r\n }\r\n}\r\n"],"mappings":"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","names":["sep","import_node_crypto","stat","ZEROED","defaultClock","ZEROED","resolve","import_node_path","import_node_crypto","import_node_module","RESPONSE_PAGE_SIZE","recorders","emit","diagnosticsChannel","http","https","import_node_os","import_node_path","RESPONSE_PAGE_SIZE","routes","originals","collector","emit","import_node_os","import_node_path","import_node_crypto","import_node_fs","import_node_path","path"]}