From 96c85a19c1bb786d6f417667a6be072c62d28065 Mon Sep 17 00:00:00 2001 From: "Nadhi-(Kushi)" Date: Wed, 1 Apr 2026 01:14:27 +0530 Subject: [PATCH] chore: implementing status support --- .github/tests/test.js | 77 ++++++++++++++++- rsrc/src/manifest.rs | 13 +++ rsrc/src/router.rs | 37 +++++++- src/index.d.ts | 15 ++++ src/index.js | 196 +++++++++++++++++++++++++++++++++++++++++- src/opt/entry.js | 4 +- 6 files changed, 335 insertions(+), 7 deletions(-) diff --git a/.github/tests/test.js b/.github/tests/test.js index 145a91a..7ff26b6 100644 --- a/.github/tests/test.js +++ b/.github/tests/test.js @@ -124,6 +124,22 @@ async function main() { const app = createApp(); assert.equal(typeof app.error, "function"); + assert.throws( + () => app.static("/users/:id", "invalid"), + /app\.static\(\) only supports exact GET paths without params/, + ); + + const middlewareBlockedApp = createApp(); + middlewareBlockedApp.use(async (_req, _res, next) => { + await next(); + }); + middlewareBlockedApp.static("/blocked", "blocked"); + + await assert.rejects( + () => middlewareBlockedApp.listen({ port: 0 }), + /app\.static\(\"\/blocked\"\) cannot be used with applicable middleware/, + ); + httpServerConfig.tls = { cert: "/tst/cert.pem", key: "/tst/key.pem" @@ -163,9 +179,36 @@ async function main() { engine: "rust", }); }); + app.static( + "/ssr", + "
ssr
", + { + objects: { + page: "ssr", + safe: "nope", + }, + }, + ); + app.static( + "/ssr-tail", + "
tail
", + { + objects: { + page: "tail", + }, + }, + ); app.get("/stable", (req, res) => { res.json(stablePayload); }); + app.get("/html-helper", (_req, res) => { + res.html("
helper
", { + status: 201, + objects: { + helper: true, + }, + }); + }); app.get("/native/:id", (req, res) => { res.json({ id: req.params.id, @@ -271,6 +314,29 @@ async function main() { engine: "rust", }); + const ssrResponse = await fetch(new URL("/ssr", server.url)); + assert.equal(ssrResponse.status, 200); + assert.equal(ssrResponse.headers.get("content-type"), "text/html; charset=utf-8"); + const ssrMarkup = await ssrResponse.text(); + assert.match( + ssrMarkup, + /
ssr<\/main>`; + const bodyCloseMatch = /<\/body\s*>/i.exec(markup); + + if (!bodyCloseMatch || bodyCloseMatch.index === undefined) { + return `${markup}${script}`; + } + + return `${markup.slice(0, bodyCloseMatch.index)}${script}${markup.slice(bodyCloseMatch.index)}`; +} + +function buildHtmlResponsePayload(html, options = {}) { + const normalized = normalizeHtmlResponseOptions(options); + const headers = { + ...normalized.headers, + }; + + if (!headers["content-type"]) { + headers["content-type"] = "text/html; charset=utf-8"; + } + + return { + status: normalized.status, + headers, + body: injectHtmlObjectsScript(html, normalized.objects), + }; +} + +function createStaticHtmlFallbackHandler(staticResponse) { + return (_req, res) => { + res.status(staticResponse.status); + for (const [name, value] of Object.entries(staticResponse.headers)) { + res.header(name, value); + } + res.send(staticResponse.body); + }; +} + const RESPONSE_POOL_MAX = 512; @@ -251,6 +351,22 @@ const RESPONSE_PROTO = { return this; }, + html(html, options = {}) { + const state = this._state; + if (state.finished) { + return this; + } + + const payload = buildHtmlResponsePayload(html, options); + state.status = payload.status; + for (const [name, value] of Object.entries(payload.headers)) { + state.headers[name] = value; + } + state.body = Buffer.from(payload.body, "utf8"); + state.finished = true; + return this; + }, + send(data) { const state = this._state; if (state.finished) { @@ -791,6 +907,28 @@ function normalizeRouteRegistration(method, path, handler, options = {}) { }; } +function normalizeStaticRouteRegistration(path, html, options = {}) { + if (typeof html !== "string") { + throw new TypeError("app.static(path, html, options) expects html to be a string"); + } + + const normalizedPath = normalizeRoutePath("GET", path); + if (normalizedPath.includes(":")) { + throw new Error("app.static() only supports exact GET paths without params"); + } + + const staticResponse = buildHtmlResponsePayload(html, options); + return { + method: "GET", + path: normalizedPath, + handler: createStaticHtmlFallbackHandler(staticResponse), + cache: null, + staticResponse, + sourceLocation: options.sourceLocation ?? null, + syntheticHandlerSource: "(req, res) => res.html(\"\")", + }; +} + function captureRouteRegistrationLocation() { const stack = new Error().stack; if (!stack) { @@ -852,6 +990,10 @@ function compileMiddlewareRegistration(middleware) { } function createRouteResponseCache(route, applicableMiddlewares, requestPlan, optConfig) { + if (route.staticResponse) { + return null; + } + if (optConfig?.cache !== true) { return null; } @@ -1405,10 +1547,15 @@ function buildCompiledApplication(app, normalizedOptions) { ); const routes = app._routes.map((route) => { - let handlerSource = Function.prototype.toString.call(route.handler); - const probedSource = probeHandlerForFastPath(route, handlerSource); - if (probedSource) { - handlerSource = probedSource; + let handlerSource = + route.syntheticHandlerSource ?? + Function.prototype.toString.call(route.handler); + + if (!route.staticResponse) { + const probedSource = probeHandlerForFastPath(route, handlerSource); + if (probedSource) { + handlerSource = probedSource; + } } return { @@ -1418,6 +1565,22 @@ function buildCompiledApplication(app, normalizedOptions) { ...compileRouteShape(route.method, route.path), }; }); + + for (const route of routes) { + if (!route.staticResponse) { + continue; + } + + const hasMiddleware = compiledMiddlewares.some((middleware) => + pathPrefixMatches(middleware.pathPrefix, route.path), + ); + if (hasMiddleware) { + throw new Error( + `app.static(${JSON.stringify(route.path)}) cannot be used with applicable middleware`, + ); + } + } + const compiledRoutes = routes.map((route) => compileRouteDispatch( route, @@ -1455,6 +1618,13 @@ function buildCompiledApplication(app, normalizedOptions) { route.requestPlan.queryKeys.size > 0, cache: normalizeManifestCache(route.cache), needsSession: /\breq\.session\b|\breq\.sessionId\b/.test(route.handlerSource), + staticResponse: route.staticResponse + ? { + status: route.staticResponse.status, + headers: route.staticResponse.headers, + body: route.staticResponse.body, + } + : null, })), wsRoutes: app._wsRoutes.map((ws) => ({ path: ws.path, @@ -1748,6 +1918,7 @@ export function createApp(config = {}) { patch: undefined, options: undefined, all: undefined, + static: undefined, reload(options = {}) { this._reloadConfig = normalizeApplicationReloadConfig(options); @@ -1934,6 +2105,23 @@ export function createApp(config = {}) { app.patch = createMethodRegistrar(app, "PATCH"); app.options = createMethodRegistrar(app, "OPTIONS"); app.all = createMethodRegistrar(app, "ALL"); + app.static = (routePath, html, options = {}) => { + const groupPrefix = app._groupPrefix ?? "/"; + const scopedPath = + typeof routePath === "string" + ? applyGroupPrefixToRoutePath(routePath, groupPrefix) + : routePath; + const sourceLocation = captureRouteRegistrationLocation(); + const routeOptions = sourceLocation + ? { ...options, sourceLocation } + : options; + + app._routes.push({ + ...normalizeStaticRouteRegistration(scopedPath, html, routeOptions), + handlerId: app._allocateHandlerId(), + }); + return app; + }; return app; } diff --git a/src/opt/entry.js b/src/opt/entry.js index b72e315..ffb19a1 100644 --- a/src/opt/entry.js +++ b/src/opt/entry.js @@ -5,7 +5,9 @@ export function buildRouteEntry(route, middlewares) { ); const source = route.handlerSource ?? ""; const nativeCache = source.includes("res.ncache("); - const staticFastPath = !nativeCache && isStaticFastPathCandidate(route, hasMiddleware, source); + const staticFastPath = + route.staticResponse != null || + (!nativeCache && isStaticFastPathCandidate(route, hasMiddleware, source)); const cacheCandidate = !staticFastPath && route.method === "GET" &&