diff --git a/opt/runtime.js b/opt/runtime.js index 150fa7d..f8ebf25 100644 --- a/opt/runtime.js +++ b/opt/runtime.js @@ -1 +1,5 @@ +// keep compatibility with old imports +// since this is used by benchmarks, +// its very cheap. + export { createRuntimeOptimizer } from "../src/opt/runtime.js"; diff --git a/rust-native/src/analyzer.rs b/rust-native/src/analyzer.rs index 9e53c82..5ec3649 100644 --- a/rust-native/src/analyzer.rs +++ b/rust-native/src/analyzer.rs @@ -67,6 +67,7 @@ pub struct DynamicValueSource { pub enum DynamicValueSourceKind { Param, Query, + QueryObject, Header, } @@ -480,7 +481,7 @@ fn parse_json_object_fields(payload: &str) -> Option> { let separator = find_top_level(trimmed, ':')?; let key = parse_object_key(trimmed[..separator].trim())?; let value_source = trimmed[separator + 1..].trim(); - let value = if let Some(source) = parse_dynamic_value_source(value_source) { + let value = if let Some(source) = parse_dynamic_json_value_source(value_source) { JsonValueTemplate::Dynamic(source) } else { let literal = parse_literal(value_source)?; @@ -686,6 +687,18 @@ fn parse_dynamic_value_source(raw: &str) -> Option { None } +fn parse_dynamic_json_value_source(raw: &str) -> Option { + let source = raw.trim(); + if source == "req.query" { + return Some(DynamicValueSource { + kind: DynamicValueSourceKind::QueryObject, + key: Box::::from(""), + }); + } + + parse_dynamic_value_source(source) +} + fn parse_function_call_string_arg(source: &str, prefix: &str) -> Option { if !source.starts_with(prefix) || !source.ends_with(')') { return None; diff --git a/rust-native/src/lib.rs b/rust-native/src/lib.rs index b4340cc..4600f55 100644 --- a/rust-native/src/lib.rs +++ b/rust-native/src/lib.rs @@ -1252,6 +1252,7 @@ enum ResolvedDynamicValue { Missing, Single(String), Multi(Vec), + RawJson(Vec), } fn build_dynamic_fast_path_response( @@ -1334,6 +1335,14 @@ fn render_dynamic_json_body( output.push(b']'); wrote_field = true; } + ResolvedDynamicValue::RawJson(raw) => { + if wrote_field { + output.push(b','); + } + output.extend_from_slice(field.key_prefix.as_ref()); + output.extend_from_slice(raw.as_slice()); + wrote_field = true; + } } } } @@ -1373,6 +1382,9 @@ fn render_dynamic_text_body( output.push_str(value.as_str()); } } + ResolvedDynamicValue::RawJson(bytes) => { + output.push_str(String::from_utf8_lossy(bytes.as_slice()).as_ref()); + } }, } } @@ -1404,6 +1416,10 @@ fn resolve_dynamic_value( let entries = query_entries(url, query_cache); lookup_query_value(entries.as_slice(), source.key.as_ref()) } + DynamicValueSourceKind::QueryObject => { + let entries = query_entries(url, query_cache); + ResolvedDynamicValue::RawJson(serialize_query_object_json(entries.as_slice())) + } } } @@ -1459,6 +1475,65 @@ fn lookup_query_value(entries: &[(String, String)], key: &str) -> ResolvedDynami } } +fn serialize_query_object_json(entries: &[(String, String)]) -> Vec { + let mut buckets: Vec<(&str, Vec<&str>)> = Vec::new(); + + for (entry_key, entry_value) in entries.iter() { + if is_dangerous_query_key(entry_key.as_str()) { + continue; + } + + if let Some((_, values)) = buckets + .iter_mut() + .find(|(key, _)| *key == entry_key.as_str()) + { + values.push(entry_value.as_str()); + } else { + buckets.push((entry_key.as_str(), vec![entry_value.as_str()])); + } + } + + let mut output = Vec::with_capacity(entries.len() * 24 + 16); + output.push(b'{'); + + for (index, (key, values)) in buckets.iter().enumerate() { + if index > 0 { + output.push(b','); + } + + append_json_string(&mut output, key); + output.push(b':'); + if values.len() == 1 { + append_json_string(&mut output, values[0]); + } else { + output.push(b'['); + for (value_index, value) in values.iter().enumerate() { + if value_index > 0 { + output.push(b','); + } + append_json_string(&mut output, value); + } + output.push(b']'); + } + } + + output.push(b'}'); + output +} + +fn is_dangerous_query_key(key: &str) -> bool { + matches!( + key, + "__proto__" + | "constructor" + | "prototype" + | "__defineGetter__" + | "__defineSetter__" + | "__lookupGetter__" + | "__lookupSetter__" + ) +} + fn append_json_string(output: &mut Vec, value: &str) { output.push(b'"'); for ch in value.chars() { diff --git a/src/opt/constants.js b/src/opt/constants.js new file mode 100644 index 0000000..6f7d54f --- /dev/null +++ b/src/opt/constants.js @@ -0,0 +1,3 @@ +export const HOT_HIT_THRESHOLD = parseInt(process.env.HOT_HIT_THRESHOLD) || 128; +export const STABLE_RESPONSE_THRESHOLD = parseInt(process.env.STABLE_RESPONSE_THRESHOLD) || 32; +export const DEFAULT_NOTIFY_INTERVAL_MS = parseInt(process.env.DEFAULT_NOTIFY_INTERVAL_MS) || 1000; diff --git a/src/opt/entry.js b/src/opt/entry.js new file mode 100644 index 0000000..061b206 --- /dev/null +++ b/src/opt/entry.js @@ -0,0 +1,159 @@ +export function buildRouteEntry(route, middlewares) { + const hasParams = route.path.includes(":"); + const hasMiddleware = middlewares.some((middleware) => + pathPrefixMatches(middleware.pathPrefix, route.path), + ); + const source = route.handlerSource ?? ""; + const staticFastPath = isStaticFastPathCandidate(route, hasMiddleware, source); + const cacheCandidate = + !staticFastPath && + route.method === "GET" && + !hasParams && + !hasMiddleware && + !source.includes("await") && + !/req\.(params|query|body|headers|url|path|method)\b/.test(source) && + !/Date\.now|new Date|Math\.random|crypto\./.test(source); + + const reasons = []; + if (staticFastPath) { + reasons.push("served by static fast path"); + } else { + reasons.push("served through bridge dispatch"); + } + if (hasMiddleware) { + reasons.push("middleware blocks static promotion"); + } + if (hasParams) { + reasons.push("route params require dynamic dispatch"); + } + if (cacheCandidate) { + reasons.push("runtime-stable responses can be cached later"); + } + + return { + handlerId: route.handlerId, + method: route.method, + path: route.path, + label: `${route.method} ${route.path}`, + stage: "cold", + hits: 0, + lastHitAt: null, + staticFastPath, + binaryBridge: true, + dispatchKind: route.dispatchKind ?? "generic_fallback", + jsonFastPath: route.jsonFastPath ?? "fallback", + bridgeObserved: false, + cacheCandidate, + recommendation: null, + reasons, + stableResponses: 0, + lastResponseKey: null, + settled: false, + }; +} + +function isStaticFastPathCandidate(route, hasMiddleware, source) { + if (route.method !== "GET" || route.path.includes(":") || hasMiddleware) { + return false; + } + + if (source.includes("await")) { + return false; + } + + const body = trimReturnAndSemicolon(extractFunctionBody(source)); + if (!body) { + return false; + } + + return ( + isDirectLiteralCall(body, "res.json(") || + isDirectLiteralCall(body, "res.send(") || + isDirectStatusLiteralCall(body, "json") || + isDirectStatusLiteralCall(body, "send") + ); +} + +function extractFunctionBody(source) { + const arrowIndex = source.indexOf("=>"); + if (arrowIndex >= 0) { + const right = source.slice(arrowIndex + 2).trim(); + if (right.startsWith("{") && right.endsWith("}")) { + return right.slice(1, -1).trim(); + } + return right; + } + + const blockStart = source.indexOf("{"); + const blockEnd = source.lastIndexOf("}"); + if (blockStart >= 0 && blockEnd > blockStart) { + return source.slice(blockStart + 1, blockEnd).trim(); + } + + return source.trim(); +} + +function trimReturnAndSemicolon(body) { + let value = body.trim(); + if (value.startsWith("return ")) { + value = value.slice("return ".length).trim(); + } + if (value.endsWith(";")) { + value = value.slice(0, -1).trim(); + } + return value; +} + +function isDirectLiteralCall(body, prefix) { + if (!body.startsWith(prefix) || !body.endsWith(")")) { + return false; + } + + const payload = body.slice(prefix.length, -1).trim(); + return looksLiteralPayload(payload); +} + +function isDirectStatusLiteralCall(body, method) { + if (!body.startsWith("res.status(") || !body.endsWith(")")) { + return false; + } + + const separator = `).${method}(`; + const separatorIndex = body.indexOf(separator); + if (separatorIndex < 0) { + return false; + } + + const payload = body.slice(separatorIndex + separator.length, -1).trim(); + return looksLiteralPayload(payload); +} + +function looksLiteralPayload(payload) { + if (!payload) { + return false; + } + + if ( + payload.startsWith("{") || + payload.startsWith("[") || + payload.startsWith('"') || + payload.startsWith("'") || + payload.startsWith("`") + ) { + return true; + } + + if (/^-?\d/.test(payload)) { + return true; + } + + return payload === "true" || payload === "false" || payload === "null"; +} + +function pathPrefixMatches(pathPrefix, requestPath) { + if (pathPrefix === "/") { + return true; + } + + return requestPath === pathPrefix || requestPath.startsWith(`${pathPrefix}/`); +} diff --git a/src/opt/notify.js b/src/opt/notify.js new file mode 100644 index 0000000..8640be0 --- /dev/null +++ b/src/opt/notify.js @@ -0,0 +1,142 @@ +// cat-loggr-style logger (native ANSI, no dependencies) +const ANSI = { + reset: "\x1b[0m", + bold: "\x1b[1m", + dim: "\x1b[2m", + + // Foreground colors + black: "\x1b[30m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + cyan: "\x1b[36m", + white: "\x1b[37m", + gray: "\x1b[90m", + + // Background colors + bgRed: "\x1b[41m", + bgGreen: "\x1b[42m", + bgYellow: "\x1b[43m", + bgBlue: "\x1b[44m", + bgMagenta: "\x1b[45m", + bgCyan: "\x1b[46m", +}; + +// Log level styles mimicking cat-loggr +const LOG_LEVELS = { + debug: { bg: ANSI.bgBlue, fg: ANSI.white, label: "debug" }, + info: { bg: ANSI.bgBlue, fg: ANSI.white, label: "info" }, + success: { bg: ANSI.bgGreen, fg: ANSI.white, label: "success" }, + warn: { bg: ANSI.bgYellow, fg: ANSI.black, label: "warn" }, + error: { bg: ANSI.bgRed, fg: ANSI.white, label: "error" }, +}; + +function pad(num, width = 2) { + return String(num).padStart(width, "0"); +} + +function getTimestamp() { + const now = new Date(); + const month = pad(now.getMonth() + 1); + const day = pad(now.getDate()); + const hours = pad(now.getHours()); + const minutes = pad(now.getMinutes()); + const seconds = pad(now.getSeconds()); + return `${month}/${day} ${hours}:${minutes}:${seconds}`; +} + +function formatLog(level, message) { + const style = LOG_LEVELS[level] || LOG_LEVELS.info; + const timestamp = getTimestamp(); + const badge = `${style.bg}${style.fg} ${pad(style.label.length)} ${style.label} ${style.label.length === 5 ? "" : " "}${ANSI.reset}`; + const spacer = `${ANSI.dim}${ANSI.gray}|${ANSI.reset}`; + + return ` ${ANSI.dim}${timestamp}${ANSI.reset} ${badge} ${message}`; +} + +function log(level, message) { + console.log(formatLog(level, message)); +} + +export function createOptimizerNotifier(routeEntries, enabled, intervalMs) { + if (!enabled) { + return { + markDirty() {}, + printStartup() {}, + maybeNotify(_entry, _message) {}, + dispose() {}, + }; + } + + let dirty = false; + const timer = + intervalMs > 0 + ? setInterval(() => { + if (!dirty) { + return; + } + dirty = false; + printLiveRouteHits(routeEntries); + }, intervalMs) + : null; + + if (timer && typeof timer.unref === "function") { + timer.unref(); + } + + return { + markDirty() { + dirty = true; + }, + printStartup() { + log("success", `http-native optimizer enabled (interval=${intervalMs}ms)`); + printRouteCatalog(routeEntries); + }, + maybeNotify(_entry, message) { + log("info", message); + }, + dispose() { + if (timer) { + clearInterval(timer); + } + }, + }; +} + +export function normalizeNotifyInterval(value, fallback) { + const normalized = Number(value); + if (!Number.isFinite(normalized) || normalized <= 0) { + return fallback; + } + return Math.floor(normalized); +} + +function printRouteCatalog(routeEntries) { + if (routeEntries.length === 0) { + log("warn", "no routes registered"); + return; + } + + log("info", "tracking routes:"); + for (const entry of routeEntries) { + log("debug", `${ANSI.magenta}${entry.label}${ANSI.reset} staticFastPath=${entry.staticFastPath} dispatch=${entry.dispatchKind}`); + } +} + +function printLiveRouteHits(routeEntries) { + const active = routeEntries.filter((entry) => entry.hits > 0); + if (active.length === 0) { + log("warn", "no bridge-dispatch hits yet (static fast path bypasses JS dispatch counters)"); + return; + } + + log("info", "live hits:"); + for (const entry of active) { + log( + "success", + `${ANSI.magenta}${entry.label}${ANSI.reset} ${ANSI.green}hits=${entry.hits}${ANSI.reset} stage=${entry.stage} bridgeObserved=${entry.bridgeObserved}`, + ); + } +} diff --git a/src/opt/runtime.js b/src/opt/runtime.js index a70b579..74263de 100644 --- a/src/opt/runtime.js +++ b/src/opt/runtime.js @@ -1,21 +1,17 @@ import { Buffer } from "node:buffer"; -const HOT_HIT_THRESHOLD = 128; -const STABLE_RESPONSE_THRESHOLD = 32; -const DEFAULT_NOTIFY_INTERVAL_MS = 1000; +import { + DEFAULT_NOTIFY_INTERVAL_MS, + HOT_HIT_THRESHOLD, + STABLE_RESPONSE_THRESHOLD, +} from "./constants.js"; +import { buildRouteEntry } from "./entry.js"; +import { + createOptimizerNotifier, + normalizeNotifyInterval, +} from "./notify.js"; +import { snapshotRouteEntries, summarizeRouteEntries } from "./summary.js"; -/** - * Create a runtime optimizer that tracks per-route dispatch metrics, - * detects static-fast-path candidates, and identifies cache-promotable - * routes whose responses remain stable across many invocations. - * - * @param {Object[]} routes - Compiled route descriptors from compileRouteDispatch - * @param {Object[]} middlewares - Compiled middleware descriptors - * @param {Object} [options={}] - Runtime optimization options - * @param {boolean} [options.notify=false] - Emit optimization logs to stdout - * @param {number} [options.notifyIntervalMs=1000] - Interval for periodic hit summaries - * @returns {{ recordDispatch: Function, snapshot: Function, summary: Function, dispose: Function }} - */ export function createRuntimeOptimizer(routes, middlewares, options = {}) { const notifyEnabled = options.notify === true || process.env.HTTP_NATIVE_OPT_NOTIFY === "1"; @@ -28,36 +24,14 @@ export function createRuntimeOptimizer(routes, middlewares, options = {}) { const routesByHandlerId = new Map( routeEntries.map((entry) => [entry.handlerId, entry]), ); - - let dirty = false; - let disposed = false; - const notifyTimer = - notifyEnabled && notifyIntervalMs > 0 - ? startNotifyTimer(routeEntries, notifyIntervalMs, () => { - if (!dirty) { - return; - } - dirty = false; - printLiveRouteHits(routeEntries); - }) - : null; - - if (notifyEnabled) { - console.log( - `[http-native][opt] notify enabled (interval=${notifyIntervalMs}ms)`, - ); - printRouteCatalog(routeEntries); - } + const notifier = createOptimizerNotifier( + routeEntries, + notifyEnabled, + notifyIntervalMs, + ); + notifier.printStartup(); return { - /** - * Record a single dispatch event for the given route and check - * whether the route is eligible for promotion (hot, cache, etc.). - * - * @param {Object} route - The compiled route descriptor - * @param {Object} _request - The request object (unused but reserved) - * @param {Object} snapshot - Response snapshot { status, headers, body } - */ recordDispatch(route, _request, snapshot) { const entry = routesByHandlerId.get(route.handlerId); if (!entry || entry.settled) { @@ -66,14 +40,13 @@ export function createRuntimeOptimizer(routes, middlewares, options = {}) { entry.hits += 1; entry.bridgeObserved = true; - dirty = true; + notifier.markDirty(); if (entry.stage === "cold") { if (entry.hits >= HOT_HIT_THRESHOLD) { entry.stage = "hot"; entry.lastHitAt = Date.now(); - maybeNotify( - notifyEnabled, + notifier.maybeNotify( entry, entry.staticFastPath ? `${entry.label} is serving from the static fast path` @@ -107,358 +80,28 @@ export function createRuntimeOptimizer(routes, middlewares, options = {}) { entry.recommendation = "cache-candidate"; entry.settled = true; entry.lastHitAt = Date.now(); - maybeNotify( - notifyEnabled, + notifier.maybeNotify( entry, `${entry.label} looks stable at runtime; cached values may be safe`, ); } }, - /** - * Return a structured snapshot of every route's optimization state. - * - * @returns {{ generatedAt: string, routes: Object[] }} - */ snapshot() { - return { - generatedAt: new Date().toISOString(), - routes: routeEntries.map((entry) => ({ - method: entry.method, - path: entry.path, - label: entry.label, - stage: entry.stage, - hits: entry.hits, - staticFastPath: entry.staticFastPath, - binaryBridge: entry.binaryBridge, - dispatchKind: entry.dispatchKind, - jsonFastPath: entry.jsonFastPath, - bridgeObserved: entry.bridgeObserved, - cacheCandidate: entry.cacheCandidate, - recommendation: entry.recommendation, - reasons: [...entry.reasons], - lastHitAt: entry.lastHitAt, - })), - }; + return snapshotRouteEntries(routeEntries); }, - /** - * Return a human-readable multi-line summary string of all route - * optimization states, suitable for logging. - * - * @returns {string} - */ summary() { - return routeEntries - .map((entry) => { - const flags = []; - if (entry.staticFastPath) { - flags.push("static-fast-path"); - } else { - flags.push("bridge-dispatch"); - } - if (entry.binaryBridge) { - flags.push("binary-bridge"); - } - if (entry.bridgeObserved) { - flags.push("bridge-observed"); - } - if (entry.cacheCandidate) { - flags.push("cache-candidate"); - } - if (entry.recommendation) { - flags.push(entry.recommendation); - } - const uniqueFlags = [...new Set(flags)]; - return `${entry.label} [${uniqueFlags.join(", ")}] hits=${entry.hits}`; - }) - .join("\n"); + return summarizeRouteEntries(routeEntries); }, - /** Stop the periodic notify timer and release resources. */ dispose() { - if (disposed) { - return; - } - disposed = true; - if (notifyTimer) { - clearInterval(notifyTimer); - } + notifier.dispose(); }, }; } -/** - * Build an internal tracking entry for a single route, pre-classifying - * it as static-fast-path, cache-candidate, or generic bridge-dispatch. - * - * @param {Object} route - Compiled route descriptor - * @param {Object[]} middlewares - Compiled middleware descriptors - * @returns {Object} Route tracking entry - */ -function buildRouteEntry(route, middlewares) { - const hasParams = route.path.includes(":"); - const hasMiddleware = middlewares.some((middleware) => - pathPrefixMatches(middleware.pathPrefix, route.path), - ); - const source = route.handlerSource ?? ""; - const staticFastPath = isStaticFastPathCandidate(route, hasMiddleware, source); - const cacheCandidate = - !staticFastPath && - route.method === "GET" && - !hasParams && - !hasMiddleware && - !source.includes("await") && - !/req\.(params|query|body|headers|url|path|method)\b/.test(source) && - !/Date\.now|new Date|Math\.random|crypto\./.test(source); - - const reasons = []; - if (staticFastPath) { - reasons.push("served by static fast path"); - } else { - reasons.push("served through bridge dispatch"); - } - if (hasMiddleware) { - reasons.push("middleware blocks static promotion"); - } - if (hasParams) { - reasons.push("route params require dynamic dispatch"); - } - if (cacheCandidate) { - reasons.push("runtime-stable responses can be cached later"); - } - - return { - handlerId: route.handlerId, - method: route.method, - path: route.path, - label: `${route.method} ${route.path}`, - stage: "cold", - hits: 0, - lastHitAt: null, - staticFastPath, - binaryBridge: true, - dispatchKind: route.dispatchKind ?? "generic_fallback", - jsonFastPath: route.jsonFastPath ?? "fallback", - bridgeObserved: false, - cacheCandidate, - recommendation: null, - reasons, - stableResponses: 0, - lastResponseKey: null, - settled: false, - }; -} - -/** - * Print the initial route catalog to stdout when notify mode is enabled. - * - * @param {Object[]} routeEntries - */ -function printRouteCatalog(routeEntries) { - if (routeEntries.length === 0) { - console.log("[http-native][opt] no routes registered"); - return; - } - - console.log("[http-native][opt] tracking routes:"); - for (const entry of routeEntries) { - console.log( - `[http-native][opt] ${entry.label} staticFastPath=${entry.staticFastPath} dispatch=${entry.dispatchKind}`, - ); - } -} - -/** - * Print live hit counts for routes that have been dispatched at least once. - * - * @param {Object[]} routeEntries - */ -function printLiveRouteHits(routeEntries) { - const active = routeEntries.filter((entry) => entry.hits > 0); - if (active.length === 0) { - console.log( - "[http-native][opt] no bridge-dispatch hits yet (static fast path bypasses JS dispatch counters)", - ); - return; - } - - console.log("[http-native][opt] live hits:"); - for (const entry of active) { - console.log( - `[http-native][opt] ${entry.label} hits=${entry.hits} stage=${entry.stage} bridgeObserved=${entry.bridgeObserved}`, - ); - } -} - -/** - * @param {*} value - * @param {number} fallback - * @returns {number} - */ -function normalizeNotifyInterval(value, fallback) { - const normalized = Number(value); - if (!Number.isFinite(normalized) || normalized <= 0) { - return fallback; - } - return Math.floor(normalized); -} - -/** - * @param {Object[]} routeEntries - * @param {number} notifyIntervalMs - * @param {Function} onTick - * @returns {NodeJS.Timer} - */ -function startNotifyTimer(routeEntries, notifyIntervalMs, onTick) { - const timer = setInterval(onTick, notifyIntervalMs); - if (typeof timer.unref === "function") { - timer.unref(); - } - return timer; -} - -/** - * Determine whether a route qualifies for the static fast path: - * a GET route with no params, no middleware, no async, whose handler - * is a single res.json() or res.send() call with a literal payload. - * - * @param {Object} route - * @param {boolean} hasMiddleware - * @param {string} source - Handler source code - * @returns {boolean} - */ -function isStaticFastPathCandidate(route, hasMiddleware, source) { - if (route.method !== "GET" || route.path.includes(":") || hasMiddleware) { - return false; - } - - if (source.includes("await")) { - return false; - } - - const body = trimReturnAndSemicolon(extractFunctionBody(source)); - if (!body) { - return false; - } - - return ( - isDirectLiteralCall(body, "res.json(") || - isDirectLiteralCall(body, "res.send(") || - isDirectStatusLiteralCall(body, "json") || - isDirectStatusLiteralCall(body, "send") - ); -} - -/** - * @param {string} source - * @returns {string} - */ -function extractFunctionBody(source) { - const arrowIndex = source.indexOf("=>"); - if (arrowIndex >= 0) { - const right = source.slice(arrowIndex + 2).trim(); - if (right.startsWith("{") && right.endsWith("}")) { - return right.slice(1, -1).trim(); - } - return right; - } - - const blockStart = source.indexOf("{"); - const blockEnd = source.lastIndexOf("}"); - if (blockStart >= 0 && blockEnd > blockStart) { - return source.slice(blockStart + 1, blockEnd).trim(); - } - - return source.trim(); -} - -/** - * @param {string} body - * @returns {string} - */ -function trimReturnAndSemicolon(body) { - let value = body.trim(); - if (value.startsWith("return ")) { - value = value.slice("return ".length).trim(); - } - if (value.endsWith(";")) { - value = value.slice(0, -1).trim(); - } - return value; -} - -/** - * @param {string} body - * @param {string} prefix - * @returns {boolean} - */ -function isDirectLiteralCall(body, prefix) { - if (!body.startsWith(prefix) || !body.endsWith(")")) { - return false; - } - - const payload = body.slice(prefix.length, -1).trim(); - return looksLiteralPayload(payload); -} - -/** - * @param {string} body - * @param {string} method - * @returns {boolean} - */ -function isDirectStatusLiteralCall(body, method) { - if (!body.startsWith("res.status(") || !body.endsWith(")")) { - return false; - } - - const separator = `).${method}(`; - const separatorIndex = body.indexOf(separator); - if (separatorIndex < 0) { - return false; - } - - const payload = body.slice(separatorIndex + separator.length, -1).trim(); - return looksLiteralPayload(payload); -} - -/** - * Check if a payload string looks like a JS literal value - * (object, array, string, number, boolean, or null). - * - * @param {string} payload - * @returns {boolean} - */ -function looksLiteralPayload(payload) { - if (!payload) { - return false; - } - - if ( - payload.startsWith("{") || - payload.startsWith("[") || - payload.startsWith('"') || - payload.startsWith("'") || - payload.startsWith("`") - ) { - return true; - } - - if (/^-?\d/.test(payload)) { - return true; - } - - return payload === "true" || payload === "false" || payload === "null"; -} - -/** - * Build a stable fingerprint for a response snapshot using FNV-1a hashing. - * Avoids the overhead of JSON.stringify + base64 that the previous - * implementation used on every dispatch. - * - * @param {Object} snapshot - Response snapshot { status, headers, body } - * @returns {string} Hash-based cache key - */ +// Faster key than JSON.stringify + base64 on every dispatch. function buildResponseKey(snapshot) { let hash = 0x811c9dc5; hash = fnv1aString(hash, String(snapshot.status ?? 200)); @@ -480,13 +123,6 @@ function buildResponseKey(snapshot) { return `${hash}:${body.length}:${headerNames.length}`; } -/** - * FNV-1a hash over a string (character codes). - * - * @param {number} seed - * @param {string} value - * @returns {number} - */ function fnv1aString(seed, value) { let hash = seed >>> 0; for (let index = 0; index < value.length; index += 1) { @@ -496,13 +132,6 @@ function fnv1aString(seed, value) { return hash >>> 0; } -/** - * FNV-1a hash over a byte buffer. - * - * @param {number} seed - * @param {Buffer|Uint8Array} bytes - * @returns {number} - */ function fnv1aBytes(seed, bytes) { let hash = seed >>> 0; for (let index = 0; index < bytes.length; index += 1) { @@ -511,32 +140,3 @@ function fnv1aBytes(seed, bytes) { } return hash >>> 0; } - -/** - * @param {boolean} notify - * @param {Object} _entry - * @param {string} message - */ -function maybeNotify(notify, _entry, message) { - if (!notify) { - return; - } - - console.log(`[http-native][opt] ${message}`); -} - -/** - * Check whether requestPath starts with the given pathPrefix. - * Duplicated from index.js to avoid circular imports — keep in sync. - * - * @param {string} pathPrefix - * @param {string} requestPath - * @returns {boolean} - */ -function pathPrefixMatches(pathPrefix, requestPath) { - if (pathPrefix === "/") { - return true; - } - - return requestPath === pathPrefix || requestPath.startsWith(`${pathPrefix}/`); -} diff --git a/src/opt/summary.js b/src/opt/summary.js new file mode 100644 index 0000000..6ac7812 --- /dev/null +++ b/src/opt/summary.js @@ -0,0 +1,48 @@ +export function summarizeRouteEntries(routeEntries) { + return routeEntries + .map((entry) => { + const flags = []; + if (entry.staticFastPath) { + flags.push("static-fast-path"); + } else { + flags.push("bridge-dispatch"); + } + if (entry.binaryBridge) { + flags.push("binary-bridge"); + } + if (entry.bridgeObserved) { + flags.push("bridge-observed"); + } + if (entry.cacheCandidate) { + flags.push("cache-candidate"); + } + if (entry.recommendation) { + flags.push(entry.recommendation); + } + const uniqueFlags = [...new Set(flags)]; + return `${entry.label} [${uniqueFlags.join(", ")}] hits=${entry.hits}`; + }) + .join("\n"); +} + +export function snapshotRouteEntries(routeEntries) { + return { + generatedAt: new Date().toISOString(), + routes: routeEntries.map((entry) => ({ + method: entry.method, + path: entry.path, + label: entry.label, + stage: entry.stage, + hits: entry.hits, + staticFastPath: entry.staticFastPath, + binaryBridge: entry.binaryBridge, + dispatchKind: entry.dispatchKind, + jsonFastPath: entry.jsonFastPath, + bridgeObserved: entry.bridgeObserved, + cacheCandidate: entry.cacheCandidate, + recommendation: entry.recommendation, + reasons: [...entry.reasons], + lastHitAt: entry.lastHitAt, + })), + }; +} diff --git a/test/app.ts b/test/app.ts index 7dcfc96..b32a97e 100644 --- a/test/app.ts +++ b/test/app.ts @@ -8,18 +8,14 @@ app.error(async (error, req, res) => { }); app.get("/", (req, res) => { - - res.json({ + res.status(200).json({ ok: true, data: req.query, }); }); - - const server = await app.listen({ port: 8190, - opt: { notify: true} });