🚨 Latest Research:Tanstack npm Packages Compromised in Ongoing Mini Shai-Hulud Supply-Chain Attack.Learn More
Socket
Book a DemoSign in
Socket

@adonisjs/http-server

Package Overview
Dependencies
Maintainers
3
Versions
169
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@adonisjs/http-server - npm Package Compare versions

Comparing version
8.1.2
to
8.1.3
build/define_config-drp-Wzwx.js

Sorry, the diff of this file is too big to display

+1484
import { t as createURL } from "./helpers-Dqw8abku.js";
import { createRequire } from "node:module";
import { InvalidArgumentsException, RuntimeException } from "@poppinss/utils/exception";
import { debuglog } from "node:util";
import { serialize } from "cookie-es";
import matchit from "@poppinss/matchit";
import string from "@poppinss/utils/string";
import { moduleCaller, moduleImporter, parseBindingReference } from "@adonisjs/fold";
import Cache from "tmp-cache";
import Macroable from "@poppinss/macroable";
import is from "@sindresorhus/is";
import Middleware from "@poppinss/middleware";
import diagnostics_channel from "node:diagnostics_channel";
import StringBuilder from "@poppinss/utils/string_builder";
import encodeUrl from "encodeurl";
import mime from "mime-types";
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
var __exportAll = (all, no_symbols) => {
let target = {};
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: true
});
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
return target;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region src/debug.ts
/**
* Debug logger instance for the AdonisJS HTTP server package.
*
* This debug logger can be enabled by setting the NODE_DEBUG environment variable
* to include 'adonisjs:http'. When enabled, it will output detailed debugging
* information about HTTP server operations including route matching, middleware
* execution, and response generation.
*
* @example
* ```bash
* NODE_DEBUG=adonisjs:http node ace serve
* ```
*/
var debug_default = debuglog("adonisjs:http");
//#endregion
//#region src/router/factories/use_return_value.ts
/**
* Check if the value can be used to write the response body. Returns
* false when the response body has already been set
* @param value - The value to check
* @param ctx - The HTTP context instance
* @returns True if value can be used for response body
*/
function canWriteResponseBody(value, ctx) {
return value !== void 0 && !ctx.response.hasLazyBody && value !== ctx.response;
}
/**
* A factory function that uses the return value of the request
* pipeline as the response
* @param ctx - The HTTP context instance
* @returns Function that handles return values
*/
function useReturnValue(ctx) {
return function(value) {
if (canWriteResponseBody(value, ctx)) ctx.response.send(value);
};
}
//#endregion
//#region src/tracing_channels.ts
var tracing_channels_exports = /* @__PURE__ */ __exportAll({
httpExceptionHandler: () => httpExceptionHandler,
httpMiddleware: () => httpMiddleware,
httpRequest: () => httpRequest,
httpResponseSerializer: () => httpResponseSerializer,
httpRouteHandler: () => httpRouteHandler
});
/**
* Traces every HTTP request handled by the {@link Server} class.
*/
const httpRequest = diagnostics_channel.tracingChannel("adonisjs.http.request");
/**
* Traces middleware executed during the HTTP request
*/
const httpMiddleware = diagnostics_channel.tracingChannel("adonisjs.http.middleware");
/**
* Traces the exception handler that converts errors into HTTP responses
*/
const httpExceptionHandler = diagnostics_channel.tracingChannel("adonisjs.http.exception.handler");
/**
* Traces route handler executed during the HTTP request
*/
const httpRouteHandler = diagnostics_channel.tracingChannel("adonisjs.http.route.handler");
/**
* Traces non-stream and non-file download responses written by the AdonisJS
* response class
*/
const httpResponseSerializer = diagnostics_channel.tracingChannel("adonisjs.http.response.serializer");
//#endregion
//#region src/router/executor.ts
/**
* Executor to execute the route middleware pipeline the route
* handler
* @param route - The route JSON object containing route information
* @param resolver - Container resolver for dependency injection
* @param ctx - The HTTP context instance
* @param errorResponder - Error handler function for handling errors
*/
function execute(route, resolver, ctx, errorResponder) {
return route.middleware.runner().errorHandler((error) => errorResponder(error, ctx)).finalHandler(() => {
if (typeof route.handler === "function") return httpRouteHandler.tracePromise(($ctx) => Promise.resolve(route.handler($ctx)), httpRouteHandler.hasSubscribers ? { route } : void 0, void 0, ctx).then(useReturnValue(ctx));
return httpRouteHandler.tracePromise(route.handler.handle, httpRouteHandler.hasSubscribers ? { route } : void 0, void 0, resolver, ctx).then(useReturnValue(ctx));
}).run(async (middleware, next) => {
if (typeof middleware === "function") return httpMiddleware.tracePromise(middleware, httpMiddleware.hasSubscribers ? { middleware } : void 0, void 0, ctx, next);
return httpMiddleware.tracePromise(middleware.handle, httpMiddleware.hasSubscribers ? { middleware } : void 0, void 0, resolver, ctx, next, middleware.args);
});
}
//#endregion
//#region src/router/route.ts
/**
* The Route class provides a fluent API for constructing and configuring HTTP routes.
*
* Routes define how HTTP requests are handled by mapping URL patterns and HTTP methods
* to controller actions or inline handlers. This class supports middleware application,
* parameter validation, naming, and various other route-specific configurations.
*
* @example
* ```ts
* const route = new Route(app, middleware, {
* pattern: '/users/:id',
* methods: ['GET'],
* handler: 'UsersController.show'
* })
*
* route
* .where('id', /^[0-9]+$/)
* .middleware(['auth'])
* .as('users.show')
* ```
*/
var Route = class extends Macroable {
/**
* The URL pattern for this route. May contain dynamic parameters
* prefixed with a colon (e.g., '/users/:id').
*/
#pattern;
/**
* Array of HTTP methods this route responds to (e.g., ['GET', 'POST']).
*/
#methods;
/**
* A unique identifier for the route, used for URL generation and
* route referencing.
*/
#name;
/**
* Flag indicating whether the route should be excluded from registration
* in the route store. This must be set before calling Router.commit().
*/
#isDeleted = false;
/**
* The handler function or controller method that processes requests
* matching this route.
*/
#handler;
/**
* Route parameter matchers inherited from the global router configuration.
* These are applied to all routes unless overridden locally.
*/
#globalMatchers;
/**
* Reference to the AdonisJS application instance, used for module
* resolution and dependency injection.
*/
#app;
/**
* Global middleware registered on the router that applies to this route.
*/
#routerMiddleware;
/**
* The domain this route belongs to. Defaults to 'root' when no specific
* domain is configured.
*/
#routeDomain = "root";
/**
* Route-specific parameter matchers that validate dynamic segments.
* Populated via the where() method and takes precedence over global matchers.
*/
#matchers = {};
/**
* Stack of URL prefixes applied to this route, typically inherited from
* route groups. Prefixes are applied in reverse order during pattern computation.
*/
#prefixes = [];
/**
* Multi-dimensional array of middleware, where each nested array represents
* a layer in the middleware stack. This structure maintains the order of
* middleware application from groups and direct assignments.
*/
#middleware = [];
/**
* Creates a new Route instance
* @param app - The AdonisJS application instance
* @param routerMiddleware - Array of global middleware registered on the router
* @param options - Configuration options for the route
*/
constructor(app, routerMiddleware, options) {
super();
this.#app = app;
this.#routerMiddleware = routerMiddleware;
this.#pattern = options.pattern;
this.#methods = options.methods;
this.#globalMatchers = options.globalMatchers;
const { handler, routeName } = this.#resolveRouteHandle(options.handler);
this.#handler = handler;
this.#name = routeName;
}
/**
* Resolves the route handler from various input formats into a normalized
* StoreRouteHandler object. Supports string references, inline functions,
* class constructors, and lazy imports.
*
* @param handler - The handler in one of the supported formats:
* - String: 'Controller.method' or 'Controller' (defaults to 'handle')
* - Function: Inline route handler function
* - Tuple: [Controller class or lazy import, optional method name]
*/
#resolveRouteHandle(handler) {
/**
* Convert magic string to handle method call
*/
if (typeof handler === "string") {
const parts = handler.split(".");
const method = parts.length === 1 ? "handle" : parts.pop();
const moduleRefId = parts.join(".");
const controllerName = new StringBuilder(moduleRefId.split("/").pop()).removeSuffix("controller").snakeCase();
return {
handler: {
method,
reference: handler,
importExpression: moduleRefId,
...moduleImporter(() => this.#app.import(moduleRefId), method).toHandleMethod(),
name: handler
},
routeName: method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}`
};
}
/**
* Using a lazily imported controller
*/
if (Array.isArray(handler)) {
const controller = handler[0];
const method = handler[1] ?? "handle";
/**
* The first item of the tuple is a class constructor
*/
if (is.class(controller)) {
const controllerName = new StringBuilder(controller.name).removeSuffix("controller").snakeCase();
return {
handler: {
method,
reference: handler,
importExpression: null,
...moduleCaller(controller, method).toHandleMethod()
},
routeName: method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}`
};
}
const controllerName = controller.name ? new StringBuilder(controller.name).removeSuffix("controller").snakeCase() : void 0;
/**
* The first item of the tuple is a function that lazily
* loads the controller
*/
return {
handler: {
method,
reference: handler,
importExpression: String(controller),
...moduleImporter(controller, method).toHandleMethod()
},
routeName: controllerName ? method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}` : void 0
};
}
return { handler };
}
/**
* Merges global and route-specific parameter matchers into a single object.
* Local matchers take precedence over global matchers when conflicts occur.
*/
#getMatchers() {
return {
...this.#globalMatchers,
...this.#matchers
};
}
/**
* Computes the final route pattern by applying all prefixes from route groups.
* Prefixes are applied in reverse order (innermost group first) and normalized
* to remove leading/trailing slashes.
*/
#computePattern() {
const pattern = dropSlash(this.#pattern);
const prefix = this.#prefixes.slice().reverse().map((one) => dropSlash(one)).join("");
return prefix ? `${prefix}${pattern === "/" ? "" : pattern}` : pattern;
}
/**
* Returns the route's handler configuration object.
*/
getHandler() {
return this.#handler;
}
/**
* Defines a validation matcher for a route parameter. Route-level matchers
* take precedence over group-level matchers to ensure routes can override
* group constraints.
*
* @param param - The name of the route parameter to validate
* @param matcher - The validation pattern as a string, RegExp, or RouteMatcher object
*
* @example
* ```ts
* // Validate that 'id' is numeric
* route.where('id', /^[0-9]+$/)
*
* // Using a string pattern
* route.where('slug', '[a-z0-9-]+')
*
* // Route matcher takes precedence over group matcher
* Route.group(() => {
* Route.get('/:id', 'handler').where('id', /^[0-9]$/)
* }).where('id', /[^a-z$]/)
* // The route's /^[0-9]$/ wins over the group's matcher
* ```
*/
where(param, matcher) {
if (this.#matchers[param]) return this;
if (typeof matcher === "string") this.#matchers[param] = { match: new RegExp(matcher) };
else if (is.regExp(matcher)) this.#matchers[param] = { match: matcher };
else this.#matchers[param] = matcher;
return this;
}
/**
* Adds a URL prefix to the route pattern. Multiple calls stack prefixes
* which are applied in reverse order during pattern computation.
*
* @param prefix - The URL prefix to prepend to the route pattern
*
* @example
* ```ts
* route.prefix('/api').prefix('/v1')
* // Results in pattern: /v1/api/users (for original pattern /users)
* ```
*/
prefix(prefix) {
this.#prefixes.push(prefix);
return this;
}
/**
* Assigns a custom domain to the route. By default, routes belong to the
* 'root' domain. Once set, the domain is not overwritten unless the
* overwrite flag is true.
*
* @param domain - The domain identifier for this route
* @param overwrite - Whether to overwrite an existing non-root domain
*
* @example
* ```ts
* route.domain('api.example.com')
*
* // Overwrite existing domain
* route.domain('new.example.com', true)
* ```
*/
domain(domain, overwrite = false) {
if (this.#routeDomain === "root" || overwrite) this.#routeDomain = domain;
return this;
}
/**
* Registers one or more middleware to execute before the route handler.
* Middleware can be inline functions or named middleware references registered
* with the router's middleware store.
*
* @param middleware - Single middleware or array of middleware to apply
*
* @example
* ```ts
* // Single middleware
* route.use(async (ctx, next) => {
* console.log('Before handler')
* await next()
* })
*
* // Multiple middleware
* route.use(['auth', 'admin'])
* ```
*/
use(middleware) {
this.#middleware.push(Array.isArray(middleware) ? middleware : [middleware]);
return this;
}
/**
* Alias for the {@link Route.use} method.
*
* @param middleware - Single middleware or array of middleware to apply
*/
middleware(middleware) {
return this.use(middleware);
}
/**
* Assigns a unique name to the route for use in URL generation and route
* referencing. Assigning a new name replaces any existing name unless
* prepend is true.
*
* @param name - The route name to assign
* @param prepend - If true, prepends the name to the existing name with a dot separator
*
* @example
* ```ts
* // Set route name
* route.as('users.show')
*
* // Prepend to existing name (typically used by route groups)
* route.as('admin', true) // Results in 'admin.users.show'
* ```
*/
as(name, prepend = false) {
if (prepend) {
if (!this.#name) throw new RuntimeException(`Routes inside a group must have names before calling "router.group.as"`);
this.#name = `${name}.${this.#name}`;
return this;
}
this.#name = name;
return this;
}
/**
* Checks whether the route has been marked for deletion. Deleted routes
* are excluded from the route store during registration.
*/
isDeleted() {
return this.#isDeleted;
}
/**
* Marks the route for deletion. Deleted routes will not be registered
* with the route store when Router.commit() is called.
*
* @example
* ```ts
* const route = Route.get('/admin', 'handler')
* route.markAsDeleted()
* // This route will not be registered
* ```
*/
markAsDeleted() {
this.#isDeleted = true;
}
/**
* Returns the unique name assigned to the route, if any.
*/
getName() {
return this.#name;
}
/**
* Returns the route's URL pattern with dynamic parameters.
*/
getPattern() {
return this.#pattern;
}
/**
* Updates the route's URL pattern.
*
* @param pattern - The new URL pattern to assign to the route
*
* @example
* ```ts
* route.setPattern('/users/:id/posts/:postId')
* ```
*/
setPattern(pattern) {
this.#pattern = pattern;
return this;
}
/**
* Returns the multi-dimensional middleware stack registered on this route.
* The returned value is shared by reference, not a copy.
*/
getMiddleware() {
return this.#middleware;
}
/**
* Constructs a frozen Middleware instance for storage. This combines global
* router middleware with route-specific middleware in the correct execution order.
* The middleware is frozen to prevent modifications after route registration.
*/
#getMiddlewareForStore() {
const middleware = new Middleware();
this.#routerMiddleware.forEach((one) => {
debug_default("adding global middleware to route %s, %O", this.#pattern, one);
middleware.add(one);
});
this.#middleware.flat().forEach((one) => {
debug_default("adding named middleware to route %s, %O", this.#pattern, one);
middleware.add(one);
});
middleware.freeze();
return middleware;
}
/**
* Serializes the route into a JSON representation suitable for storage and
* execution. This includes the computed pattern with prefixes, merged matchers,
* parsed route tokens, and frozen middleware stack.
*
* @example
* ```ts
* const json = route.toJSON()
* console.log(json.pattern) // '/api/users/:id'
* console.log(json.methods) // ['GET']
* console.log(json.name) // 'users.show'
* ```
*/
toJSON() {
const pattern = this.#computePattern();
const matchers = this.#getMatchers();
return {
domain: this.#routeDomain,
pattern,
matchers,
tokens: parseRoute(pattern, matchers),
meta: {},
name: this.#name,
handler: this.#handler,
methods: this.#methods,
middleware: this.#getMiddlewareForStore(),
execute
};
}
};
//#endregion
//#region src/router/brisk.ts
/**
* Brisk routes exposes the API to configure the route handler by chaining
* one of the pre-defined methods.
*
* For example: Instead of defining the redirect logic as a callback, one can
* chain the `.redirect` method.
*
* Brisk routes are always registered under the `GET` HTTP method.
*/
var BriskRoute = class extends Macroable {
/**
* Route pattern
*/
#pattern;
/**
* Matchers inherited from the router
*/
#globalMatchers;
/**
* Reference to the AdonisJS application
*/
#app;
/**
* Middleware registered on the router
*/
#routerMiddleware;
/**
* Reference to route instance. Set after `setHandler` is called
*/
route = null;
/**
* Creates a new BriskRoute instance
* @param app - The AdonisJS application instance
* @param routerMiddleware - Array of global middleware registered on the router
* @param options - Configuration options for the brisk route
*/
constructor(app, routerMiddleware, options) {
super();
this.#app = app;
this.#routerMiddleware = routerMiddleware;
this.#pattern = options.pattern;
this.#globalMatchers = options.globalMatchers;
}
/**
* Set handler for the brisk route
* @param handler - The route handler function
* @returns The created route instance
*/
setHandler(handler) {
this.route = new Route(this.#app, this.#routerMiddleware, {
pattern: this.#pattern,
globalMatchers: this.#globalMatchers,
methods: ["GET", "HEAD"],
handler
});
return this.route;
}
/**
* Redirects to a given route. Params from the original request will
* be used when no custom params are defined.
* @param args - Route identifier, parameters, and options for building the redirect URL
* @returns The created route instance
*/
redirect(...args) {
const [identifier, params, options] = args;
function redirectsToRoute(ctx) {
const redirector = ctx.response.redirect();
if (options?.status) redirector.status(options.status);
return redirector.toRoute(identifier, params || ctx.params, options);
}
Object.defineProperty(redirectsToRoute, "listArgs", {
value: identifier,
writable: false
});
return this.setHandler(redirectsToRoute);
}
/**
* Redirect request to a fixed URL
* @param url - The URL to redirect to
* @param options - Optional redirect options including HTTP status code
* @returns The created route instance
*/
redirectToPath(url, options) {
function redirectsToPath(ctx) {
const redirector = ctx.response.redirect();
if (options?.status) redirector.status(options.status);
return redirector.toPath(url);
}
Object.defineProperty(redirectsToPath, "listArgs", {
value: url,
writable: false
});
return this.setHandler(redirectsToPath);
}
};
//#endregion
//#region src/router/resource.ts
/**
* Route resource exposes the API to register multiple routes for a resource.
*/
var RouteResource = class extends Macroable {
/**
* Resource identifier. Nested resources are separated
* with a dot notation
*/
#resource;
/**
* The controller to handle resource routing requests
*/
#controller;
/**
* Is it a shallow resource? Shallow resources URLs do not have parent
* resource name and id once they can be identified with the id.
*/
#shallow = false;
/**
* Matchers inherited from the router
*/
#globalMatchers;
/**
* Reference to the AdonisJS application
*/
#app;
/**
* Middleware registered on the router
*/
#routerMiddleware;
/**
* Parameter names for the resources. Defaults to `id` for
* a singular resource and `resource_id` for nested
* resources.
*/
#params = {};
/**
* Base name for the routes. We suffix action names
* on top of the base name
*/
#routesBaseName;
/**
* A collection of routes instances that belongs to this resource
*/
routes = [];
/**
* Creates a new RouteResource instance
* @param app - The AdonisJS application instance
* @param routerMiddleware - Array of global middleware registered on the router
* @param options - Configuration options for the route resource
*/
constructor(app, routerMiddleware, options) {
super();
this.#validateResourceName(options.resource);
this.#app = app;
this.#shallow = options.shallow;
this.#routerMiddleware = routerMiddleware;
this.#controller = options.controller;
this.#globalMatchers = options.globalMatchers;
this.#resource = this.#normalizeResourceName(options.resource);
this.#routesBaseName = this.#getRoutesBaseName();
this.#buildRoutes();
}
/**
* Normalizes the resource name to dropping leading and trailing
* slashes.
*/
#normalizeResourceName(resource) {
return resource.replace(/^\//, "").replace(/\/$/, "");
}
/**
* Ensure resource name is not an empty string
*/
#validateResourceName(resource) {
if (!resource || resource === "/") throw new RuntimeException(`Invalid resource name "${resource}"`);
}
/**
* Converting segments of a resource to snake case to
* make the route name.
*/
#getRoutesBaseName() {
return this.#resource.split(".").map((token) => string.snakeCase(token)).join(".");
}
/**
* Create a new route for the given pattern, methods and controller action
*/
#createRoute(pattern, methods, action) {
const route = new Route(this.#app, this.#routerMiddleware, {
pattern,
methods,
handler: typeof this.#controller === "string" ? `${this.#controller}.${action}` : [this.#controller, action],
globalMatchers: this.#globalMatchers
});
route.as(`${this.#routesBaseName}.${action}`);
this.routes.push(route);
}
/**
* Returns the `resource_id` name for a given resource. The
* resource name is converted to singular form and
* transformed to snake case.
*
* photos becomes photo_id
* users becomes user_id
*/
#getResourceId(resource) {
return `${string.snakeCase(string.singular(resource))}_id`;
}
/**
* Build routes for the given resource
*/
#buildRoutes() {
const resources = this.#resource.split(".");
const mainResource = resources.pop();
this.#params[mainResource] = ":id";
const baseURI = `${resources.map((resource) => {
const paramName = `:${this.#getResourceId(resource)}`;
this.#params[resource] = paramName;
return `${resource}/${paramName}`;
}).join("/")}/${mainResource}`;
this.#createRoute(baseURI, ["GET", "HEAD"], "index");
this.#createRoute(`${baseURI}/create`, ["GET", "HEAD"], "create");
this.#createRoute(baseURI, ["POST"], "store");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["GET", "HEAD"], "show");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id/edit`, ["GET", "HEAD"], "edit");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["PUT", "PATCH"], "update");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["DELETE"], "destroy");
}
/**
* Filter the routes based on their partial names
*/
#filter(names, inverse) {
const actions = Array.isArray(names) ? names : [names];
return this.routes.filter((route) => {
const match = actions.find((name) => route.getName().endsWith(name));
return inverse ? !match : match;
});
}
/**
* Register only given routes and remove others
* @param names - Array of action names to keep
* @returns Current RouteResource instance with filtered actions
*/
only(names) {
this.#filter(names, true).forEach((route) => route.markAsDeleted());
return this;
}
/**
* Register all routes, except the one's defined
* @param names - Array of action names to exclude
* @returns Current RouteResource instance with filtered actions
*/
except(names) {
this.#filter(names, false).forEach((route) => route.markAsDeleted());
return this;
}
/**
* Register api only routes. The `create` and `edit` routes, which
* are meant to show forms will not be registered
* @returns Current RouteResource instance without create and edit actions
*/
apiOnly() {
return this.except(["create", "edit"]);
}
/**
* Define matcher for params inside the resource
* @param key - The parameter name to match
* @param matcher - The matcher pattern (RegExp, string, or RouteMatcher)
* @returns Current RouteResource instance for method chaining
*/
where(key, matcher) {
this.routes.forEach((route) => {
route.where(key, matcher);
});
return this;
}
tap(actions, callback) {
if (typeof actions === "function") {
this.routes.forEach((route) => {
if (!route.isDeleted()) actions(route);
});
return this;
}
this.#filter(actions, false).forEach((route) => {
if (!route.isDeleted()) callback(route);
});
return this;
}
/**
* Set the param name for a given resource
* @param resources - Object mapping resource names to parameter names
* @returns Current RouteResource instance for method chaining
*/
params(resources) {
Object.keys(resources).forEach((resource) => {
const param = resources[resource];
const existingParam = this.#params[resource];
this.#params[resource] = `:${param}`;
this.routes.forEach((route) => {
route.setPattern(route.getPattern().replace(`${resource}/${existingParam}`, `${resource}/:${param}`));
});
});
return this;
}
/**
* Define one or more middleware on the routes created by
* the resource.
*
* Calling this method multiple times will append middleware
* to existing list.
* @param actions - Action name(s) or '*' for all actions
* @param middleware - Middleware function(s) to apply
* @returns Current RouteResource instance for method chaining
*/
use(actions, middleware) {
if (actions === "*") this.tap((route) => route.use(middleware));
else this.tap(actions, (route) => route.use(middleware));
return this;
}
/**
* Alias for {@link RouteResource.use}
* @param actions - Action name(s) or '*' for all actions
* @param middleware - Middleware function(s) to apply
* @returns Current RouteResource instance for method chaining
*/
middleware(actions, middleware) {
return this.use(actions, middleware);
}
/**
* Prepend name to all the routes
* @param name - The name to prepend to all route names
* @param normalizeName - Whether to normalize the name to snake_case
* @returns Current RouteResource instance for method chaining
*/
as(name, normalizeName = true) {
name = normalizeName ? string.snakeCase(name) : name;
this.routes.forEach((route) => {
route.as(route.getName().replace(this.#routesBaseName, name), false);
});
this.#routesBaseName = name;
return this;
}
};
//#endregion
//#region src/router/group.ts
/**
* Group class exposes the API to take action on a group of routes.
* The group routes must be pre-defined using the constructor.
*/
var RouteGroup = class RouteGroup extends Macroable {
/**
* Array of middleware registered on the group.
*/
#middleware = [];
/**
* Creates a new RouteGroup instance
* @param routes - Array of routes that belong to this group
*/
constructor(routes) {
super();
this.routes = routes;
}
/**
* Shares midldeware stack with the routes. The method is invoked recursively
* to only register middleware with the route class and not with the
* resource or the child group
*/
#shareMiddlewareStackWithRoutes(route) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#shareMiddlewareStackWithRoutes(child));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.getMiddleware().unshift(this.#middleware));
return;
}
if (route instanceof BriskRoute) {
route.route.getMiddleware().unshift(this.#middleware);
return;
}
route.getMiddleware().unshift(this.#middleware);
}
/**
* Updates the route name. The method is invoked recursively to only update
* the name with the route class and not with the resource or the child
* group.
*/
#updateRouteName(route, name) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#updateRouteName(child, name));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.as(name, true));
return;
}
if (route instanceof BriskRoute) {
route.route.as(name, true);
return;
}
route.as(name, true);
}
/**
* Sets prefix on the route. The method is invoked recursively to only set
* the prefix with the route class and not with the resource or the
* child group.
*/
#setRoutePrefix(route, prefix) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#setRoutePrefix(child, prefix));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.prefix(prefix));
return;
}
if (route instanceof BriskRoute) {
route.route.prefix(prefix);
return;
}
route.prefix(prefix);
}
/**
* Updates domain on the route. The method is invoked recursively to only update
* the domain with the route class and not with the resource or the child
* group.
*/
#updateRouteDomain(route, domain) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#updateRouteDomain(child, domain));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.domain(domain));
return;
}
if (route instanceof BriskRoute) {
route.route.domain(domain, false);
return;
}
route.domain(domain, false);
}
/**
* Updates matchers on the route. The method is invoked recursively to only update
* the matchers with the route class and not with the resource or the child
* group.
*/
#updateRouteMatchers(route, param, matcher) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#updateRouteMatchers(child, param, matcher));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.where(param, matcher));
return;
}
if (route instanceof BriskRoute) {
route.route.where(param, matcher);
return;
}
route.where(param, matcher);
}
/**
* Define route param matcher
*
* ```ts
* Route.group(() => {
* }).where('id', /^[0-9]+/)
* ```
* @param param - The parameter name to match
* @param matcher - The matcher pattern (RegExp, string, or RouteMatcher)
* @returns Current RouteGroup instance for method chaining
*/
where(param, matcher) {
this.routes.forEach((route) => this.#updateRouteMatchers(route, param, matcher));
return this;
}
/**
* Define prefix all the routes in the group.
*
* ```ts
* Route.group(() => {
* }).prefix('v1')
* ```
* @param prefix - The prefix to add to all routes in the group
* @returns Current RouteGroup instance for method chaining
*/
prefix(prefix) {
this.routes.forEach((route) => this.#setRoutePrefix(route, prefix));
return this;
}
/**
* Define domain for all the routes.
*
* ```ts
* Route.group(() => {
* }).domain(':name.adonisjs.com')
* ```
* @param domain - The domain pattern for all routes in the group
* @returns Current RouteGroup instance for method chaining
*/
domain(domain) {
this.routes.forEach((route) => this.#updateRouteDomain(route, domain));
return this;
}
/**
* Prepend name to the routes name.
*
* ```ts
* Route.group(() => {
* }).as('version1')
* ```
* @param name - The name to prepend to all route names in the group
* @returns Current RouteGroup instance for method chaining
*/
as(name) {
this.routes.forEach((route) => this.#updateRouteName(route, name));
return this;
}
/**
* Prepend an array of middleware to all routes middleware.
*
* ```ts
* Route.group(() => {
* }).use(middleware.auth())
* ```
* @param middleware - Middleware function(s) to apply to all routes in the group
* @returns Current RouteGroup instance for method chaining
*/
use(middleware) {
/**
* Register middleware with children. We share the group middleware
* array by reference, therefore have to register it only for the
* first time.
*/
if (!this.#middleware.length) this.routes.forEach((route) => this.#shareMiddlewareStackWithRoutes(route));
if (Array.isArray(middleware)) for (let one of middleware) this.#middleware.push(one);
else this.#middleware.push(middleware);
return this;
}
/**
* Alias for {@link RouteGroup.use}
* @param middleware - Middleware function(s) to apply to all routes in the group
* @returns Current RouteGroup instance for method chaining
*/
middleware(middleware) {
return this.use(middleware);
}
};
//#endregion
//#region src/utils.ts
const proxyCache = new Cache({ max: 200 });
/**
* Makes input string consistent by having only the starting slash.
*
* Removes trailing slashes and ensures the path starts with a forward slash,
* except for the root path '/' which remains unchanged.
*
* @param input - The input path string to normalize
*
* @example
* ```ts
* dropSlash('/users/') // '/users'
* dropSlash('users') // '/users'
* dropSlash('/') // '/'
* ```
*/
function dropSlash(input) {
if (input === "/") return "/";
return `/${input.replace(/^\//, "").replace(/\/$/, "")}`;
}
/**
* Returns a flat list of routes from route groups, resources, and brisk routes.
*
* This function recursively processes route collections, extracting individual routes
* from groups and resources while filtering out any deleted routes.
*
* @param routes - Array containing route groups, individual routes, resources, and brisk routes
*
* @example
* ```ts
* const flatRoutes = toRoutesJSON([
* routeGroup,
* singleRoute,
* resourceRoutes
* ])
* ```
*/
function toRoutesJSON(routes) {
return routes.reduce((list, route) => {
if (route instanceof RouteGroup) {
list = list.concat(toRoutesJSON(route.routes));
return list;
}
if (route instanceof RouteResource) {
list = list.concat(toRoutesJSON(route.routes));
return list;
}
if (route instanceof BriskRoute) {
if (route.route && !route.route.isDeleted()) list.push(route.route.toJSON());
return list;
}
if (!route.isDeleted()) list.push(route.toJSON());
return list;
}, []);
}
/**
* Helper to determine if a remote address should be trusted.
*
* Uses caching to avoid repeated expensive proxy function calls for the same
* remote address. The cache improves performance when the same addresses are
* checked multiple times.
*
* @param remoteAddress - The remote IP address to check
* @param proxyFn - Function that determines if an address should be trusted
*
* @example
* ```ts
* const isTrusted = trustProxy('192.168.1.1', proxyAddr.compile('loopback'))
* ```
*/
function trustProxy(remoteAddress, proxyFn) {
if (proxyCache.has(remoteAddress)) return proxyCache.get(remoteAddress);
const result = proxyFn(remoteAddress, 0);
proxyCache.set(remoteAddress, result);
return result;
}
/**
* Parses a range expression (e.g., '200..299') into an object with numeric keys.
*
* Supports both single values and ranges. For ranges, all numbers between
* the start and end (inclusive) are mapped to the provided value.
*
* @param range - Range expression as a string (e.g., '200', '200..299')
* @param value - Value to assign to each number in the range
*
* @example
* ```ts
* parseRange('200', 'success') // { 200: 'success' }
* parseRange('200..202', 'success') // { 200: 'success', 201: 'success', 202: 'success' }
* ```
*/
function parseRange(range, value) {
const parts = range.split("..");
const min = Number(parts[0]);
const max = Number(parts[1]);
/**
* The ending status code does not exists
*/
if (parts.length === 1 && !Number.isNaN(min)) return { [min]: value };
/**
* The starting status code is not a number
*/
if (Number.isNaN(min) || Number.isNaN(max)) return {};
/**
* Min and max are same
*/
if (min === max) return { [min]: value };
/**
* Max cannot be smaller than min
*/
if (max < min) throw new InvalidArgumentsException(`Invalid range "${range}"`);
/**
* Loop over the range and create a collection
* of status codes
*/
return [...Array(max - min + 1).keys()].reduce((result, step) => {
result[min + step] = value;
return result;
}, {});
}
/**
* Decodes specific percent-encoded characters in URI components.
*
* This function handles decoding of specific character codes that are commonly
* percent-encoded in URIs, such as %, #, $, &, +, etc.
*
* @param highCharCode - The high character code of the hex pair
* @param lowCharCode - The low character code of the hex pair
*/
function decodeComponentChar(highCharCode, lowCharCode) {
if (highCharCode === 50) {
if (lowCharCode === 53) return "%";
if (lowCharCode === 51) return "#";
if (lowCharCode === 52) return "$";
if (lowCharCode === 54) return "&";
if (lowCharCode === 66) return "+";
if (lowCharCode === 98) return "+";
if (lowCharCode === 67) return ",";
if (lowCharCode === 99) return ",";
if (lowCharCode === 70) return "/";
if (lowCharCode === 102) return "/";
return null;
}
if (highCharCode === 51) {
if (lowCharCode === 65) return ":";
if (lowCharCode === 97) return ":";
if (lowCharCode === 66) return ";";
if (lowCharCode === 98) return ";";
if (lowCharCode === 68) return "=";
if (lowCharCode === 100) return "=";
if (lowCharCode === 70) return "?";
if (lowCharCode === 102) return "?";
return null;
}
if (highCharCode === 52 && lowCharCode === 48) return "@";
return null;
}
/**
* Safely decodes a URI path while handling special characters and query strings.
*
* This function carefully parses and decodes URI components, handling edge cases
* like double-encoded characters and non-standard query string delimiters.
* It separates the pathname from query parameters and determines whether
* route parameters should be decoded.
*
* @param path - The URI path to decode
* @param useSemicolonDelimiter - Whether to treat semicolons as query string delimiters
*
* @example
* ```ts
* const result = safeDecodeURI('/users/123?name=john', false)
* // Returns: { pathname: '/users/123', query: 'name=john', shouldDecodeParam: false }
* ```
*/
function safeDecodeURI(path, useSemicolonDelimiter) {
let shouldDecode = false;
let shouldDecodeParam = false;
let querystring = "";
for (let i = 1; i < path.length; i++) {
const charCode = path.charCodeAt(i);
if (charCode === 37) {
const highCharCode = path.charCodeAt(i + 1);
const lowCharCode = path.charCodeAt(i + 2);
if (decodeComponentChar(highCharCode, lowCharCode) === null) shouldDecode = true;
else {
shouldDecodeParam = true;
if (highCharCode === 50 && lowCharCode === 53) {
shouldDecode = true;
path = path.slice(0, i + 1) + "25" + path.slice(i + 1);
i += 2;
}
i += 2;
}
} else if (charCode === 63 || charCode === 35 || charCode === 59 && useSemicolonDelimiter) {
querystring = path.slice(i + 1);
path = path.slice(0, i);
break;
}
}
return {
pathname: shouldDecode ? decodeURI(path) : path,
query: querystring,
shouldDecodeParam
};
}
//#endregion
//#region src/helpers.ts
/**
* Returns the previous URL from the request's `Referer` header,
* validated against the request's `Host` header and an optional
* list of allowed hosts.
*
* The referrer is accepted when its host matches the request's
* `Host` header or is listed in `allowedHosts`. Otherwise the
* `fallback` value is returned.
*
* @param headers - The incoming request headers
* @param allowedHosts - Array of allowed referrer hosts
* @param fallback - URL to return when referrer is missing or invalid
*/
function getPreviousUrl(headers, allowedHosts, fallback) {
let referrer = headers["referer"] || headers["referrer"];
if (!referrer) return fallback;
if (Array.isArray(referrer)) referrer = referrer[0];
try {
const parsed = new URL(referrer);
const host = headers["host"];
if (host && parsed.host === host) return referrer;
if (allowedHosts.length > 0 && allowedHosts.includes(parsed.host)) return referrer;
} catch {}
return fallback;
}
/**
* Parse a route pattern into an array of tokens. These tokes can be used
* to match routes, or print them with semantic information.
*
* Token types
*
* - 0: (static) segment
* - 1: (parameter) segment
* - 2: (wildcard) segment
* - 3: (optional parameter) segment
*
* Value (val) refers to the segment value
*
* end refers to be the suffix or the segment (if any)
*
* @param pattern - The route pattern to parse
* @param matchers - Optional route matchers
* @returns {MatchItRouteToken[]} Array of parsed route tokens
*/
function parseRoute(pattern, matchers) {
return matchit.parse(pattern, matchers);
}
/**
* Makes signed URL for a given route pattern using its parsed tokens. The
* tokens could be generated using the "parseRoute" method.
*
* @param identifier - Route identifier
* @param tokens - Array of parsed route tokens
* @param searchParamsStringifier - Function to stringify query parameters
* @param encryption - Encryption instance for signing
* @param params - Route parameters as array or object
* @param options - Signed URL options
* @returns {string} The generated signed URL
*/
function createSignedURL(identifier, tokens, searchParamsStringifier, encryption, params, options) {
const signature = encryption.getMessageVerifier().sign(createURL(identifier, tokens, searchParamsStringifier, params, {
...options,
prefixUrl: void 0
}), options?.expiresIn, options?.purpose);
return createURL(identifier, tokens, searchParamsStringifier, params, {
...options,
qs: {
...options?.qs,
signature
}
});
}
/**
* Match a given URI with an array of patterns and extract the params
* from the URL. Null value is returned in case of no match
*
* @param url - The URL to match
* @param patterns - Array of route patterns to match against
* @returns {null | Record<string, string>} Extracted parameters or null if no match
*/
function matchRoute(url, patterns) {
const tokensBucket = patterns.map((pattern) => parseRoute(pattern));
const match = matchit.match(url, tokensBucket);
if (!match.length) return null;
return matchit.exec(url, match);
}
/**
* Serialize the value of a cookie to a string you can send via
* set-cookie response header.
*
* @param key - Cookie name
* @param value - Cookie value
* @param options - Cookie options
* @returns {string} Serialized cookie string
*/
function serializeCookie(key, value, options) {
let expires;
let maxAge;
if (options) {
expires = typeof options.expires === "function" ? options.expires() : options.expires;
maxAge = options.maxAge ? string.seconds.parse(options.maxAge) : void 0;
}
return serialize(key, value, {
...options,
maxAge,
expires
});
}
/**
* Returns the info about a middleware handler. In case of lazy imports, the method
* will return the import path
*
* @param middleware - The middleware function or parsed middleware
* @returns {Promise<MiddlewareHandlerInfo>} Promise resolving to middleware handler information
*/
async function middlewareInfo(middleware) {
if (typeof middleware === "function") return {
type: "closure",
name: middleware.name || "closure"
};
if ("args" in middleware) return {
type: "named",
name: middleware.name,
args: middleware.args,
...await parseBindingReference([middleware.reference])
};
return {
type: "global",
name: middleware.name,
...await parseBindingReference([middleware.reference])
};
}
/**
* Returns the info about a route handler. In case of lazy imports, the method
* will return the import path.
*
* @param route - The route JSON object
* @returns {Promise<RouteHandlerInfo>} Promise resolving to route handler information
*/
async function routeInfo(route) {
return "reference" in route.handler ? {
type: "controller",
...await parseBindingReference(route.handler.reference)
} : {
type: "closure",
name: route.handler.name || "closure",
args: "listArgs" in route.handler ? String(route.handler.listArgs) : void 0
};
}
/**
* Appends query string parameters to a URI. Existing query parameters
* in the URI are merged with the new ones.
*
* @param uri - The base URI to append query string to
* @param queryString - Object containing query parameters to append
* @param qsParser - Query string parser instance for stringify/parse operations
*
* @example
* ```ts
* const result = appendQueryString('/users', { page: 1, limit: 10 }, qsParser)
* // Returns: '/users?page=1&limit=10'
*
* const result2 = appendQueryString('/users?sort=name', { page: 1 }, qsParser)
* // Returns: '/users?sort=name&page=1'
* ```
*/
function appendQueryString(uri, queryString, qsParser) {
const { query, pathname } = safeDecodeURI(uri, false);
const mergedQueryString = qsParser.stringify(Object.assign(qsParser.parse(query), queryString));
return mergedQueryString ? `${pathname}?${mergedQueryString}` : pathname;
}
//#endregion
export { tracing_channels_exports as C, __exportAll as D, __commonJSMin as E, __require as O, httpResponseSerializer as S, debug_default as T, BriskRoute as _, matchRoute as a, httpMiddleware as b, parseRoute as c, parseRange as d, safeDecodeURI as f, RouteResource as g, RouteGroup as h, getPreviousUrl as i, __toESM as k, routeInfo as l, trustProxy as m, createSignedURL as n, middlewareInfo as o, toRoutesJSON as p, encodeUrl as r, mime as s, appendQueryString as t, serializeCookie as u, Route as v, canWriteResponseBody as w, httpRequest as x, httpExceptionHandler as y };
//#region src/client/helpers.ts
/**
* Finds a route by its identifier across domains.
*
* Searches for routes by name, pattern, or controller reference. When no domain
* is specified, searches across all domains. Supports legacy lookup strategies
* for backwards compatibility.
*
* @param domainsRoutes - Object mapping domain names to route arrays
* @param routeIdentifier - Route name, pattern, or controller reference to find
* @param domain - Optional domain to limit search scope
* @param method - Optional HTTP method to filter routes
* @param disableLegacyLookup - Whether to disable pattern and controller lookup
*
* @example
* ```ts
* const route = findRoute(routes, 'users.show', 'api', 'GET')
* const route2 = findRoute(routes, '/users/:id', undefined, 'GET')
* ```
*/
function findRoute(domainsRoutes, routeIdentifier, domain, method, disableLegacyLookup) {
/**
* Search for route in all the domains when no domain name is
* mentioned.
*/
if (!domain) {
let route = null;
for (const routeDomain of Object.keys(domainsRoutes)) {
route = findRoute(domainsRoutes, routeIdentifier, routeDomain, method, disableLegacyLookup);
if (route) break;
}
return route;
}
const routes = domainsRoutes[domain];
if (!routes) return null;
/**
* Pattern and controller are supported for legacy reasons. However
* the URL builder only works with names
*/
const lookupByPattern = !disableLegacyLookup;
const lookupByController = !disableLegacyLookup;
return routes.find((route) => {
if (method && !route.methods.includes(method)) return false;
if (route.name === routeIdentifier || lookupByPattern && route.pattern === routeIdentifier) return true;
if (lookupByController && route.handler && typeof route.handler === "object") return "reference" in route.handler && route.handler.reference === routeIdentifier;
return false;
}) || null;
}
/**
* Makes URL for a given route pattern using its parsed tokens. The
* tokens could be generated using the "parseRoute" method.
*
* @param pattern - The route pattern
* @param tokens - Array of parsed route tokens
* @param searchParamsStringifier - Function to stringify query parameters
* @param params - Route parameters as array or object
* @param options - URL options
* @returns {string} The generated URL
*/
function createURL(pattern, tokens, searchParamsStringifier, params, options) {
const uriSegments = [];
const paramsArray = Array.isArray(params) ? params : null;
const paramsObject = !Array.isArray(params) ? params ?? {} : {};
let paramsIndex = 0;
for (const token of tokens) {
/**
* Static param
*/
if (token.type === 0) {
uriSegments.push(token.val === "/" ? "" : `${token.val}${token.end}`);
continue;
}
/**
* Wildcard param. It will always be the last param, hence we will provide
* it all the remaining values
*/
if (token.type === 2) {
const values = paramsArray ? paramsArray.slice(paramsIndex) : paramsObject["*"];
if (!Array.isArray(values) || !values.length) throw new Error(`Cannot make URL for "${pattern}". Invalid value provided for the wildcard param`);
uriSegments.push(`${values.join("/")}${token.end}`);
break;
}
const paramName = token.val;
const value = paramsArray ? paramsArray[paramsIndex] : paramsObject[paramName];
const isDefined = value !== void 0 && value !== null;
/**
* Required param
*/
if (token.type === 1 && !isDefined) throw new Error(`Cannot make URL for "${pattern}". Missing value for the "${paramName}" param`);
if (isDefined) uriSegments.push(`${value}${token.end}`);
paramsIndex++;
}
let URI = `/${uriSegments.join("/")}`;
/**
* Prefix base URL
*/
if (options?.prefixUrl) URI = `${options?.prefixUrl.replace(/\/$/, "")}${URI}`;
/**
* Append query string
*/
if (options?.qs) {
const queryString = searchParamsStringifier(options?.qs);
URI = queryString ? `${URI}?${queryString}` : URI;
}
return URI;
}
//#endregion
export { findRoute as n, createURL as t };
+166
-5

@@ -1,6 +0,4 @@

import "../utils-BjSHKI3s.js";
import { _ as Qs, c as HttpRequest, i as HttpResponse, n as Server, r as HttpContext, s as Router, t as defineConfig } from "../define_config-BRmlbWlB.js";
import { t as createURL } from "../helpers-C_2HouOe.js";
import { parseRoute } from "../src/helpers.js";
import "../types-AUwURgIL.js";
import { c as parseRoute } from "../helpers-CLk8RLHd.js";
import { _ as Qs, c as HttpRequest, i as HttpResponse, n as Server, r as HttpContext, s as Router, t as defineConfig } from "../define_config-drp-Wzwx.js";
import { t as createURL } from "../helpers-Dqw8abku.js";
import { Container } from "@adonisjs/fold";

@@ -17,3 +15,11 @@ import { Socket } from "node:net";

import { LoggerFactory } from "@adonisjs/logger/factories";
//#region factories/qs_parser_factory.ts
/**
* QS Parser factory is used to generate the query string
* parser for testing
*/
var QsParserFactory = class {
/**
* Default configuration options for the QS parser
*/
#options = {

@@ -34,2 +40,6 @@ parse: {

};
/**
* Merge QS parser factory options
* @param options - Partial options to merge with existing configuration
*/
merge(options) {

@@ -40,2 +50,5 @@ Object.assign(this.#options.parse, options.parse);

}
/**
* Create instance of the QS parser class
*/
create() {

@@ -45,10 +58,30 @@ return new Qs(this.#options);

};
//#endregion
//#region factories/router.ts
/**
* Router factory is used to generate router class instances for
* testing
*/
var RouterFactory = class {
/**
* Factory parameters for creating router instances
*/
#parameters = {};
/**
* Returns an instance of the application class
*/
#getApp() {
return this.#parameters.app || new AppFactory().create(new URL("./app/", import.meta.url));
}
/**
* Returns an instance of the encryptor to encrypt
* signed URLs
*/
#createEncryption() {
return this.#parameters.encryption || new EncryptionFactory().create();
}
/**
* Merge factory params
* @param params - Partial factory parameters to merge
*/
merge(params) {

@@ -58,2 +91,5 @@ Object.assign(this.#parameters, params);

}
/**
* Create router instance
*/
create() {

@@ -63,4 +99,16 @@ return new Router(this.#getApp(), this.#createEncryption(), new QsParserFactory().create());

};
//#endregion
//#region factories/request.ts
/**
* Request factory is used to generate request class instances for
* testing
*/
var HttpRequestFactory = class {
/**
* Factory parameters for creating request instances
*/
#parameters = {};
/**
* Returns the config for the request class
*/
#getConfig() {

@@ -78,2 +126,5 @@ return {

}
/**
* Returns the HTTP req object
*/
#createRequest() {

@@ -85,8 +136,20 @@ const req = this.#parameters.req || new IncomingMessage(new Socket());

}
/**
* Returns the HTTP res object
* @param req - The incoming message request object
*/
#createResponse(req) {
return this.#parameters.res || new ServerResponse(req);
}
/**
* Returns an instance of the encryptor to encrypt
* signed URLs
*/
#createEncryption() {
return this.#parameters.encryption || new EncryptionFactory().create();
}
/**
* Merge factory params
* @param params - Partial factory parameters to merge
*/
merge(params) {

@@ -96,2 +159,5 @@ Object.assign(this.#parameters, params);

}
/**
* Create request
*/
create() {

@@ -102,4 +168,16 @@ const req = this.#createRequest();

};
//#endregion
//#region factories/response.ts
/**
* Response factory is used to generate response class instances for
* testing
*/
var HttpResponseFactory = class {
/**
* Factory parameters for creating response instances
*/
#parameters = {};
/**
* Returns the config for the request class
*/
#getConfig() {

@@ -110,2 +188,6 @@ return {

jsonpCallbackName: "callback",
redirect: {
allowedHosts: [],
forwardQueryString: false
},
cookie: {

@@ -121,14 +203,32 @@ maxAge: 90,

}
/**
* Returns the HTTP req object
*/
#createRequest() {
return this.#parameters.req || new IncomingMessage(new Socket());
}
/**
* Returns an instance of the router
*/
#createRouter() {
return this.#parameters.router || new RouterFactory().create();
}
/**
* Returns the HTTP res object
* @param req - The incoming message request object
*/
#createResponse(req) {
return this.#parameters.res || new ServerResponse(req);
}
/**
* Returns an instance of the encryptor to encrypt
* signed URLs
*/
#createEncryption() {
return this.#parameters.encryption || new EncryptionFactory().create();
}
/**
* Merge factory params
* @param params - Partial factory parameters to merge
*/
merge(params) {

@@ -138,2 +238,5 @@ Object.assign(this.#parameters, params);

}
/**
* Create response class instance
*/
create() {

@@ -144,19 +247,48 @@ const req = this.#createRequest();

};
//#endregion
//#region factories/server_factory.ts
/**
* Server factory is used to generate server class instances for
* testing
*/
var ServerFactory = class {
/**
* Factory parameters for creating server instances
*/
#parameters = {};
/**
* Returns the emitter instance
*/
#getEmitter() {
return this.#parameters.emitter || new Emitter(this.#getApp());
}
/**
* Returns the logger instance
*/
#getLogger() {
return this.#parameters.logger || new Logger({ enabled: false });
}
/**
* Returns the config for the server class
*/
#getConfig() {
return defineConfig(this.#parameters.config || {});
}
/**
* Returns an instance of the application class
*/
#getApp() {
return this.#parameters.app || new AppFactory().create(new URL("./app/", import.meta.url));
}
/**
* Returns an instance of the encryptor to encrypt
* signed URLs
*/
#createEncryption() {
return this.#parameters.encryption || new EncryptionFactory().create();
}
/**
* Merge factory params
* @param params - Partial factory parameters to merge
*/
merge(params) {

@@ -166,2 +298,5 @@ Object.assign(this.#parameters, params);

}
/**
* Create server instance
*/
create() {

@@ -171,4 +306,16 @@ return new Server(this.#getApp(), this.#createEncryption(), this.#getEmitter(), this.#getLogger(), this.#getConfig());

};
//#endregion
//#region factories/http_context.ts
/**
* HttpContext factory is used to generate Http context class instances for
* testing
*/
var HttpContextFactory = class {
/**
* Factory parameters for creating HTTP context instances
*/
#parameters = {};
/**
* Returns the request class instance
*/
#createRequest() {

@@ -183,8 +330,18 @@ if (this.#parameters.request) return this.#parameters.request;

}
/**
* Returns the response class instance
*/
#createResponse() {
return this.#parameters.response || new HttpResponseFactory().create();
}
/**
* Returns an instance of the logger class
*/
#createLogger() {
return this.#parameters.logger || new LoggerFactory().create();
}
/**
* Merge factory params
* @param params - Partial factory parameters to merge
*/
merge(params) {

@@ -194,2 +351,5 @@ Object.assign(this.#parameters, params);

}
/**
* Create HTTP context instance
*/
create() {

@@ -199,2 +359,3 @@ return new HttpContext(this.#createRequest(), this.#createResponse(), this.#createLogger(), new Container().createResolver());

};
//#endregion
export { HttpContextFactory, QsParserFactory, HttpRequestFactory as RequestFactory, HttpResponseFactory as ResponseFactory, RouterFactory, ServerFactory };

@@ -1,13 +0,63 @@

import { a as RouteGroup, c as Route, m as canWriteResponseBody, o as RouteResource, p as tracing_channels_exports, s as BriskRoute, t as parseRange } from "./utils-BjSHKI3s.js";
import { _ as Qs, a as CookieSerializer, c as HttpRequest, d as Redirect, f as E_CANNOT_LOOKUP_ROUTE, g as errors_exports, h as E_ROUTE_NOT_FOUND, i as HttpResponse, l as CookieParser, m as E_HTTP_REQUEST_ABORTED, n as Server, o as ResponseStatus, p as E_HTTP_EXCEPTION, r as HttpContext, s as Router, t as defineConfig, u as CookieClient } from "./define_config-BRmlbWlB.js";
import "./helpers-C_2HouOe.js";
import "./types-AUwURgIL.js";
import { C as tracing_channels_exports, _ as BriskRoute, d as parseRange, g as RouteResource, h as RouteGroup, v as Route, w as canWriteResponseBody } from "./helpers-CLk8RLHd.js";
import { _ as Qs, a as CookieSerializer, c as HttpRequest, d as Redirect, f as E_CANNOT_LOOKUP_ROUTE, g as errors_exports, h as E_ROUTE_NOT_FOUND, i as HttpResponse, l as CookieParser, m as E_HTTP_REQUEST_ABORTED, n as Server, o as ResponseStatus, p as E_HTTP_EXCEPTION, r as HttpContext, s as Router, t as defineConfig, u as CookieClient } from "./define_config-drp-Wzwx.js";
import Macroable from "@poppinss/macroable";
import is from "@sindresorhus/is";
//#region src/exception_handler.ts
/**
* The base HTTP exception handler that provides comprehensive error handling capabilities.
*
* This class can be inherited to create custom exception handlers for your application.
* It provides built-in support for:
*
* - Self-handling exceptions via their own render/handle methods
* - Custom status page rendering for different HTTP error codes
* - Debug-friendly error display during development
* - Content negotiation for JSON, JSON API, and HTML error responses
* - Configurable error reporting and logging
* - Validation error handling with field-specific messages
*
* @example
* ```ts
* export default class HttpExceptionHandler extends ExceptionHandler {
* protected debug = app.inDev
* protected renderStatusPages = app.inProduction
*
* protected statusPages = {
* '404': (error, ctx) => ctx.view.render('errors/404')
* }
* }
* ```
*/
var ExceptionHandler = class extends Macroable {
/**
* Cached expanded status pages mapping individual status codes to their renderers
* Computed from the statusPages property when first accessed
*/
#expandedStatusPages;
/**
* Controls whether to include debug information in error responses
* When enabled, errors include complete stack traces and detailed debugging info
* Defaults to true in non-production environments
*/
debug = process.env.NODE_ENV !== "production";
/**
* Controls whether to render custom status pages for unhandled errors
* When enabled, errors with matching status codes use configured status page renderers
* Defaults to true in production environments
*/
renderStatusPages = process.env.NODE_ENV === "production";
/**
* Mapping of HTTP status code ranges to their corresponding page renderers
* Supports ranges like '400-499' or individual codes like '404'
*/
statusPages = {};
/**
* Controls whether errors should be reported to logging systems
* When disabled, errors are handled but not logged or reported
*/
reportErrors = true;
/**
* Array of exception class constructors to exclude from error reporting
* These exceptions are handled but not logged or reported to external systems
*/
ignoreExceptions = [

@@ -19,2 +69,6 @@ E_HTTP_EXCEPTION,

];
/**
* Array of HTTP status codes to exclude from error reporting
* Errors with these status codes are handled but not logged
*/
ignoreStatuses = [

@@ -25,3 +79,12 @@ 400,

];
/**
* Array of custom error codes to exclude from error reporting
* Errors with these codes are handled but not logged
*/
ignoreCodes = [];
/**
* Expands status page ranges into individual status code mappings
* Creates a cached lookup table for faster status page resolution
* @returns Mapping of status codes to renderers
*/
#expandStatusPages() {

@@ -35,2 +98,8 @@ if (!this.#expandedStatusPages) this.#expandedStatusPages = Object.keys(this.statusPages).reduce((result, range) => {

}
/**
* Normalizes any thrown value into a standardized HttpError object
* Ensures the error has required properties like message and status
* @param error - Any thrown value (Error, string, object, etc.)
* @returns {HttpError} Normalized error object with status and message
*/
toHttpError(error) {

@@ -42,2 +111,8 @@ const httpError = is.object(error) ? error : new Error(String(error));

}
/**
* Provides additional context information for error reporting
* Includes request ID when available for correlation across logs
* @param ctx - HTTP context containing request information
* @returns Additional context data for error reporting
*/
context(ctx) {

@@ -47,2 +122,8 @@ const requestId = ctx.request.id();

}
/**
* Determines the appropriate log level based on HTTP status code
* 5xx errors are logged as 'error', 4xx as 'warn', others as 'info'
* @param error - HTTP error object with status code
* @returns {Level} Appropriate logging level for the error
*/
getErrorLogLevel(error) {

@@ -53,5 +134,17 @@ if (error.status >= 500) return "error";

}
/**
* Determines whether debug information should be included in error responses
* Override this method to implement context-specific debug control
* @param _ - HTTP context (unused in base implementation)
* @returns {boolean} True if debugging should be enabled
*/
isDebuggingEnabled(_) {
return this.debug;
}
/**
* Determines whether an error should be reported to logging systems
* Checks against ignore lists for exceptions, status codes, and error codes
* @param error - HTTP error to evaluate for reporting
* @returns {boolean} True if the error should be reported
*/
shouldReport(error) {

@@ -64,2 +157,8 @@ if (this.reportErrors === false) return false;

}
/**
* Renders an error as a JSON response
* In debug mode, includes full stack trace using Youch
* @param error - HTTP error to render
* @param ctx - HTTP context for the request
*/
async renderErrorAsJSON(error, ctx) {

@@ -74,2 +173,8 @@ if (this.isDebuggingEnabled(ctx)) {

}
/**
* Renders an error as a JSON API compliant response
* Follows JSON API specification for error objects
* @param error - HTTP error to render
* @param ctx - HTTP context for the request
*/
async renderErrorAsJSONAPI(error, ctx) {

@@ -88,6 +193,18 @@ if (this.isDebuggingEnabled(ctx)) {

}
/**
* Renders an error as an HTML response
* Uses status pages if configured, otherwise shows debug info or simple message
* @param error - HTTP error to render
* @param ctx - HTTP context for the request
*/
async renderErrorAsHTML(error, ctx) {
/**
* Render status page
*/
const statusPages = this.#expandStatusPages();
if (this.renderStatusPages && statusPages[error.status]) {
const statusPageResponse = await statusPages[error.status](error, ctx);
/**
* Use return value and convert it into a response
*/
if (canWriteResponseBody(statusPageResponse, ctx)) return ctx.response.safeStatus(error.status).send(statusPageResponse);

@@ -107,5 +224,17 @@ return statusPageResponse;

}
/**
* Renders validation error messages as a JSON response
* Returns errors in a simple format with field-specific messages
* @param error - Validation error containing messages array
* @param ctx - HTTP context for the request
*/
async renderValidationErrorAsJSON(error, ctx) {
ctx.response.status(error.status).send({ errors: error.messages });
}
/**
* Renders validation error messages as JSON API compliant response
* Transforms validation messages to JSON API error object format
* @param error - Validation error containing messages array
* @param ctx - HTTP context for the request
*/
async renderValidationErrorAsJSONAPI(error, ctx) {

@@ -121,2 +250,8 @@ ctx.response.status(error.status).send({ errors: error.messages.map((message) => {

}
/**
* Renders validation error messages as an HTML response
* Creates simple HTML list of field errors separated by line breaks
* @param error - Validation error containing messages array
* @param ctx - HTTP context for the request
*/
async renderValidationErrorAsHTML(error, ctx) {

@@ -127,2 +262,8 @@ ctx.response.status(error.status).type("html").send(error.messages.map((message) => {

}
/**
* Renders an error to the appropriate response format based on content negotiation
* Supports HTML, JSON API, and JSON formats based on Accept headers
* @param error - HTTP error to render
* @param ctx - HTTP context for the request
*/
renderError(error, ctx) {

@@ -139,2 +280,8 @@ switch (ctx.request.accepts([

}
/**
* Renders validation errors to the appropriate response format based on content negotiation
* Supports HTML, JSON API, and JSON formats for validation error messages
* @param error - Validation error to render
* @param ctx - HTTP context for the request
*/
renderValidationError(error, ctx) {

@@ -151,2 +298,8 @@ switch (ctx.request.accepts([

}
/**
* Reports an error to logging systems if reporting is enabled
* Allows errors to self-report via their own report method if available
* @param error - Any error object to report
* @param ctx - HTTP context for additional reporting context
*/
async report(error, ctx) {

@@ -159,2 +312,5 @@ const httpError = this.toHttpError(error);

}
/**
* Log the error using the logger
*/
const level = this.getErrorLogLevel(httpError);

@@ -166,9 +322,26 @@ ctx.logger.log(level, {

}
/**
* Handles errors during HTTP request processing
* Delegates to error's own handle method if available, otherwise renders response
* @param error - Any error object to handle
* @param ctx - HTTP context for error handling
*/
async handle(error, ctx) {
const httpError = this.toHttpError(error);
/**
* Self handle exception
*/
if (typeof httpError.handle === "function") return httpError.handle(httpError, ctx);
/**
* Handle validation error using the validation error
* renderers
*/
if (httpError.code === "E_VALIDATION_ERROR" && "messages" in httpError) return this.renderValidationError(httpError, ctx);
/**
* Use the format renderers.
*/
return this.renderError(httpError, ctx);
}
};
//#endregion
export { BriskRoute, CookieClient, CookieParser, CookieSerializer, ExceptionHandler, HttpContext, HttpRequest, HttpResponse, Qs, Redirect, ResponseStatus, Route, RouteGroup, RouteResource, Router, Server, defineConfig, errors_exports as errors, tracing_channels_exports as tracingChannels };

@@ -1,3 +0,9 @@

import { n as findRoute, t as createURL } from "../../helpers-C_2HouOe.js";
import "../../types-AUwURgIL.js";
import { n as findRoute, t as createURL } from "../../helpers-Dqw8abku.js";
//#region src/client/url_builder.ts
/**
* Creates the URLBuilder helper
* @param router - The router instance
* @param searchParamsStringifier - Function to stringify query string parameters
* @returns URL builder function for creating URLs
*/
function createUrlBuilder(routesLoader, searchParamsStringifier, defaultOptions) {

@@ -25,2 +31,6 @@ let domainsList;

}
/**
* The urlFor helper is used to make URLs for pre-existing known routes. You can
* make a URL using the route name or the route pattern.
*/
const urlFor = function route(...[identifier, params, options]) {

@@ -120,2 +130,3 @@ return createUrlForRoute(identifier, params, options);

}
//#endregion
export { createURL, createUrlBuilder, findRoute };

@@ -0,1 +1,2 @@

import type { IncomingHttpHeaders } from 'node:http';
import { type Encryption } from '@boringnode/encryption';

@@ -10,2 +11,16 @@ import { type Qs } from './qs.ts';

/**
* Returns the previous URL from the request's `Referer` header,
* validated against the request's `Host` header and an optional
* list of allowed hosts.
*
* The referrer is accepted when its host matches the request's
* `Host` header or is listed in `allowedHosts`. Otherwise the
* `fallback` value is returned.
*
* @param headers - The incoming request headers
* @param allowedHosts - Array of allowed referrer hosts
* @param fallback - URL to return when referrer is missing or invalid
*/
export declare function getPreviousUrl(headers: IncomingHttpHeaders, allowedHosts: string[], fallback: string): string;
/**
* This function is similar to the intrinsic function encodeURI. However, it will not encode:

@@ -12,0 +27,0 @@ * - The \, ^, or | characters

+3
-76

@@ -1,76 +0,3 @@

import { n as safeDecodeURI } from "../utils-BjSHKI3s.js";
import { t as createURL } from "../helpers-C_2HouOe.js";
import { serialize } from "cookie-es";
import matchit from "@poppinss/matchit";
import string from "@poppinss/utils/string";
import { parseBindingReference } from "@adonisjs/fold";
import encodeUrl from "encodeurl";
import mime from "mime-types";
function parseRoute(pattern, matchers) {
return matchit.parse(pattern, matchers);
}
function createSignedURL(identifier, tokens, searchParamsStringifier, encryption, params, options) {
const signature = encryption.getMessageVerifier().sign(createURL(identifier, tokens, searchParamsStringifier, params, {
...options,
prefixUrl: void 0
}), options?.expiresIn, options?.purpose);
return createURL(identifier, tokens, searchParamsStringifier, params, {
...options,
qs: {
...options?.qs,
signature
}
});
}
function matchRoute(url, patterns) {
const tokensBucket = patterns.map((pattern) => parseRoute(pattern));
const match = matchit.match(url, tokensBucket);
if (!match.length) return null;
return matchit.exec(url, match);
}
function serializeCookie(key, value, options) {
let expires;
let maxAge;
if (options) {
expires = typeof options.expires === "function" ? options.expires() : options.expires;
maxAge = options.maxAge ? string.seconds.parse(options.maxAge) : void 0;
}
return serialize(key, value, {
...options,
maxAge,
expires
});
}
async function middlewareInfo(middleware) {
if (typeof middleware === "function") return {
type: "closure",
name: middleware.name || "closure"
};
if ("args" in middleware) return {
type: "named",
name: middleware.name,
args: middleware.args,
...await parseBindingReference([middleware.reference])
};
return {
type: "global",
name: middleware.name,
...await parseBindingReference([middleware.reference])
};
}
async function routeInfo(route) {
return "reference" in route.handler ? {
type: "controller",
...await parseBindingReference(route.handler.reference)
} : {
type: "closure",
name: route.handler.name || "closure",
args: "listArgs" in route.handler ? String(route.handler.listArgs) : void 0
};
}
function appendQueryString(uri, queryString, qsParser) {
const { query, pathname } = safeDecodeURI(uri, false);
const mergedQueryString = qsParser.stringify(Object.assign(qsParser.parse(query), queryString));
return mergedQueryString ? `${pathname}?${mergedQueryString}` : pathname;
}
export { appendQueryString, createSignedURL, createURL, encodeUrl, matchRoute, middlewareInfo, mime, parseRoute, routeInfo, serializeCookie };
import { a as matchRoute, c as parseRoute, i as getPreviousUrl, l as routeInfo, n as createSignedURL, o as middlewareInfo, r as encodeUrl, s as mime, t as appendQueryString, u as serializeCookie } from "../helpers-CLk8RLHd.js";
import { t as createURL } from "../helpers-Dqw8abku.js";
export { appendQueryString, createSignedURL, createURL, encodeUrl, getPreviousUrl, matchRoute, middlewareInfo, mime, parseRoute, routeInfo, serializeCookie };

@@ -5,3 +5,5 @@ import type { IncomingMessage } from 'node:http';

import type { HttpResponse } from './response.ts';
import type { ResponseConfig } from './types/response.ts';
import type { RoutesList, LookupList, URLOptions, GetRoutesForMethod, RouteBuilderArguments } from './types/url_builder.ts';
import Macroable from '@poppinss/macroable';
/**

@@ -29,5 +31,10 @@ * Provides a fluent API for constructing HTTP redirect responses.

*/
export declare class Redirect {
export declare class Redirect extends Macroable {
#private;
/**
* Array of allowed hosts for referrer-based redirects.
* When empty, only the request's own host is allowed.
*/
allowedHosts: string[];
/**
* Creates a new Redirect instance for handling HTTP redirects

@@ -38,5 +45,17 @@ * @param request - Node.js incoming HTTP request

* @param qs - Query string parser instance
* @param config - Redirect configuration
*/
constructor(request: IncomingMessage, response: HttpResponse, router: Router, qs: Qs);
constructor(request: IncomingMessage, response: HttpResponse, router: Router, qs: Qs, config: ResponseConfig['redirect']);
/**
* Returns the previous URL for redirect back. By default reads
* the `Referer` header and validates the host.
*
* Since `Redirect` extends `Macroable`, this method can be overridden
* to implement custom logic such as session-based previous URL
* resolution.
*
* @param fallback - URL to return when no valid previous URL is found
*/
getPreviousUrl(fallback: string): string;
/**
* Sets a custom HTTP status code for the redirect response

@@ -69,2 +88,19 @@ * @param statusCode - HTTP status code to use (e.g., 301, 302, 307)

/**
* Enables or disables query string forwarding from the current request.
*
* Use this overload to explicitly control query string forwarding,
* especially useful when `forwardQueryString` is enabled by default
* in the redirect config and you want to disable it for a specific redirect.
*
* @param forward - Whether to forward the query string
* @returns The Redirect instance for method chaining
*
* @example
* ```ts
* // Disable query string forwarding for this redirect
* response.redirect().withQs(false).toPath('/dashboard')
* ```
*/
withQs(forward: boolean): this;
/**
* Adds multiple query string parameters to the redirect URL

@@ -103,6 +139,7 @@ *

/**
* Redirects to the previous path using the Referer header
* Falls back to '/' if no referrer is found
* Redirects to the previous URL resolved via `getPreviousUrl`.
*
* @param fallback - URL to redirect to when no valid previous URL is found
*/
back(): void;
back(fallback?: string): void;
/**

@@ -109,0 +146,0 @@ * Redirects to a route using its identifier (name, pattern, or handler reference)

@@ -397,2 +397,14 @@ import Macroable from '@poppinss/macroable';

/**
* Returns the previous URL from the `Referer` header, validated against
* the request's `Host` header and an optional list of allowed hosts.
*
* The referrer is accepted when its host matches the request's `Host`
* header or is listed in `allowedHosts`. Otherwise the `fallback`
* value is returned.
*
* @param allowedHosts - Array of allowed referrer hosts
* @param fallback - URL to return when referrer is missing or invalid
*/
getPreviousUrl(allowedHosts: string[], fallback?: string): string;
/**
* Find if the current HTTP request is for the given route or the routes

@@ -399,0 +411,0 @@ * @param routeIdentifier - Route name, pattern, or handler reference to match

@@ -55,2 +55,21 @@ import { type Readable } from 'node:stream';

cookie: Partial<CookieOptions>;
/**
* Configuration for HTTP redirects
*/
redirect: {
/**
* Array of allowed hosts for referrer-based redirects.
* When empty, only the request's own host is allowed.
*
* Defaults to []
*/
allowedHosts: string[];
/**
* Whether to forward the query string from the current request
* by default on redirects.
*
* Defaults to false
*/
forwardQueryString: boolean;
};
};

@@ -57,0 +76,0 @@ /**

{
"name": "@adonisjs/http-server",
"version": "8.1.2",
"version": "8.1.3",
"description": "AdonisJS HTTP server with support packed with Routing and Cookies",

@@ -48,3 +48,3 @@ "main": "build/index.js",

"@adonisjs/eslint-config": "^3.0.0",
"@adonisjs/events": "^10.1.0",
"@adonisjs/events": "^10.2.0",
"@adonisjs/fold": "^11.0.0",

@@ -71,3 +71,3 @@ "@adonisjs/logger": "^7.1.1",

"@types/mime-types": "^3.0.1",
"@types/node": "^25.5.0",
"@types/node": "^25.5.2",
"@types/on-finished": "^2.3.5",

@@ -79,3 +79,3 @@ "@types/pem": "^1.14.4",

"@types/vary": "^1.1.3",
"@vinejs/vine": "^4.3.0",
"@vinejs/vine": "^4.3.1",
"accepts": "^1.3.8",

@@ -85,6 +85,6 @@ "autocannon": "^8.0.0",

"cross-env": "^10.1.0",
"eslint": "^10.0.3",
"fastify": "^5.8.2",
"eslint": "^10.2.0",
"fastify": "^5.8.4",
"fs-extra": "^11.3.4",
"get-port": "^7.1.0",
"get-port": "^7.2.0",
"http-status-codes": "^2.3.0",

@@ -96,6 +96,6 @@ "pem": "^1.14.8",

"supertest": "^7.2.2",
"tsdown": "0.20.3",
"typedoc": "^0.28.17",
"typescript": "^5.9.3",
"youch": "^4.1.0"
"tsdown": "0.21.7",
"typedoc": "^0.28.18",
"typescript": "^6.0.2",
"youch": "^4.1.1"
},

@@ -109,4 +109,4 @@ "dependencies": {

"@sindresorhus/is": "^7.2.0",
"content-disposition": "^1.0.1",
"cookie-es": "^2.0.0",
"content-disposition": "^1.1.0",
"cookie-es": "^3.1.1",
"destroy": "^1.2.0",

@@ -113,0 +113,0 @@ "encodeurl": "^2.0.0",

Sorry, the diff of this file is too big to display

function findRoute(domainsRoutes, routeIdentifier, domain, method, disableLegacyLookup) {
if (!domain) {
let route = null;
for (const routeDomain of Object.keys(domainsRoutes)) {
route = findRoute(domainsRoutes, routeIdentifier, routeDomain, method, disableLegacyLookup);
if (route) break;
}
return route;
}
const routes = domainsRoutes[domain];
if (!routes) return null;
const lookupByPattern = !disableLegacyLookup;
const lookupByController = !disableLegacyLookup;
return routes.find((route) => {
if (method && !route.methods.includes(method)) return false;
if (route.name === routeIdentifier || lookupByPattern && route.pattern === routeIdentifier) return true;
if (lookupByController && route.handler && typeof route.handler === "object") return "reference" in route.handler && route.handler.reference === routeIdentifier;
return false;
}) || null;
}
function createURL(pattern, tokens, searchParamsStringifier, params, options) {
const uriSegments = [];
const paramsArray = Array.isArray(params) ? params : null;
const paramsObject = !Array.isArray(params) ? params ?? {} : {};
let paramsIndex = 0;
for (const token of tokens) {
if (token.type === 0) {
uriSegments.push(token.val === "/" ? "" : `${token.val}${token.end}`);
continue;
}
if (token.type === 2) {
const values = paramsArray ? paramsArray.slice(paramsIndex) : paramsObject["*"];
if (!Array.isArray(values) || !values.length) throw new Error(`Cannot make URL for "${pattern}". Invalid value provided for the wildcard param`);
uriSegments.push(`${values.join("/")}${token.end}`);
break;
}
const paramName = token.val;
const value = paramsArray ? paramsArray[paramsIndex] : paramsObject[paramName];
const isDefined = value !== void 0 && value !== null;
if (token.type === 1 && !isDefined) throw new Error(`Cannot make URL for "${pattern}". Missing value for the "${paramName}" param`);
if (isDefined) uriSegments.push(`${value}${token.end}`);
paramsIndex++;
}
let URI = `/${uriSegments.join("/")}`;
if (options?.prefixUrl) URI = `${options?.prefixUrl.replace(/\/$/, "")}${URI}`;
if (options?.qs) {
const queryString = searchParamsStringifier(options?.qs);
URI = queryString ? `${URI}?${queryString}` : URI;
}
return URI;
}
export { findRoute as n, createURL as t };
import { parseRoute } from "./src/helpers.js";
import { createRequire } from "node:module";
import { InvalidArgumentsException, RuntimeException } from "@poppinss/utils/exception";
import { debuglog } from "node:util";
import string from "@poppinss/utils/string";
import { moduleCaller, moduleImporter } from "@adonisjs/fold";
import Cache from "tmp-cache";
import Macroable from "@poppinss/macroable";
import is from "@sindresorhus/is";
import Middleware from "@poppinss/middleware";
import diagnostics_channel from "node:diagnostics_channel";
import StringBuilder from "@poppinss/utils/string_builder";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
var __exportAll = (all, no_symbols) => {
let target = {};
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: true
});
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
return target;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
var __require = /* @__PURE__ */ createRequire(import.meta.url);
var debug_default = debuglog("adonisjs:http");
function canWriteResponseBody(value, ctx) {
return value !== void 0 && !ctx.response.hasLazyBody && value !== ctx.response;
}
function useReturnValue(ctx) {
return function(value) {
if (canWriteResponseBody(value, ctx)) ctx.response.send(value);
};
}
var tracing_channels_exports = /* @__PURE__ */ __exportAll({
httpExceptionHandler: () => httpExceptionHandler,
httpMiddleware: () => httpMiddleware,
httpRequest: () => httpRequest,
httpResponseSerializer: () => httpResponseSerializer,
httpRouteHandler: () => httpRouteHandler
});
const httpRequest = diagnostics_channel.tracingChannel("adonisjs.http.request");
const httpMiddleware = diagnostics_channel.tracingChannel("adonisjs.http.middleware");
const httpExceptionHandler = diagnostics_channel.tracingChannel("adonisjs.http.exception.handler");
const httpRouteHandler = diagnostics_channel.tracingChannel("adonisjs.http.route.handler");
const httpResponseSerializer = diagnostics_channel.tracingChannel("adonisjs.http.response.serializer");
function execute(route, resolver, ctx, errorResponder) {
return route.middleware.runner().errorHandler((error) => errorResponder(error, ctx)).finalHandler(() => {
if (typeof route.handler === "function") return httpRouteHandler.tracePromise(($ctx) => Promise.resolve(route.handler($ctx)), httpRouteHandler.hasSubscribers ? { route } : void 0, void 0, ctx).then(useReturnValue(ctx));
return httpRouteHandler.tracePromise(route.handler.handle, httpRouteHandler.hasSubscribers ? { route } : void 0, void 0, resolver, ctx).then(useReturnValue(ctx));
}).run(async (middleware, next) => {
if (typeof middleware === "function") return httpMiddleware.tracePromise(middleware, httpMiddleware.hasSubscribers ? { middleware } : void 0, void 0, ctx, next);
return httpMiddleware.tracePromise(middleware.handle, httpMiddleware.hasSubscribers ? { middleware } : void 0, void 0, resolver, ctx, next, middleware.args);
});
}
var Route = class extends Macroable {
#pattern;
#methods;
#name;
#isDeleted = false;
#handler;
#globalMatchers;
#app;
#routerMiddleware;
#routeDomain = "root";
#matchers = {};
#prefixes = [];
#middleware = [];
constructor(app, routerMiddleware, options) {
super();
this.#app = app;
this.#routerMiddleware = routerMiddleware;
this.#pattern = options.pattern;
this.#methods = options.methods;
this.#globalMatchers = options.globalMatchers;
const { handler, routeName } = this.#resolveRouteHandle(options.handler);
this.#handler = handler;
this.#name = routeName;
}
#resolveRouteHandle(handler) {
if (typeof handler === "string") {
const parts = handler.split(".");
const method = parts.length === 1 ? "handle" : parts.pop();
const moduleRefId = parts.join(".");
const controllerName = new StringBuilder(moduleRefId.split("/").pop()).removeSuffix("controller").snakeCase();
return {
handler: {
method,
reference: handler,
importExpression: moduleRefId,
...moduleImporter(() => this.#app.import(moduleRefId), method).toHandleMethod(),
name: handler
},
routeName: method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}`
};
}
if (Array.isArray(handler)) {
const controller = handler[0];
const method = handler[1] ?? "handle";
if (is.class(controller)) {
const controllerName = new StringBuilder(controller.name).removeSuffix("controller").snakeCase();
return {
handler: {
method,
reference: handler,
importExpression: null,
...moduleCaller(controller, method).toHandleMethod()
},
routeName: method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}`
};
}
const controllerName = controller.name ? new StringBuilder(controller.name).removeSuffix("controller").snakeCase() : void 0;
return {
handler: {
method,
reference: handler,
importExpression: String(controller),
...moduleImporter(controller, method).toHandleMethod()
},
routeName: controllerName ? method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}` : void 0
};
}
return { handler };
}
#getMatchers() {
return {
...this.#globalMatchers,
...this.#matchers
};
}
#computePattern() {
const pattern = dropSlash(this.#pattern);
const prefix = this.#prefixes.slice().reverse().map((one) => dropSlash(one)).join("");
return prefix ? `${prefix}${pattern === "/" ? "" : pattern}` : pattern;
}
getHandler() {
return this.#handler;
}
where(param, matcher) {
if (this.#matchers[param]) return this;
if (typeof matcher === "string") this.#matchers[param] = { match: new RegExp(matcher) };
else if (is.regExp(matcher)) this.#matchers[param] = { match: matcher };
else this.#matchers[param] = matcher;
return this;
}
prefix(prefix) {
this.#prefixes.push(prefix);
return this;
}
domain(domain, overwrite = false) {
if (this.#routeDomain === "root" || overwrite) this.#routeDomain = domain;
return this;
}
use(middleware) {
this.#middleware.push(Array.isArray(middleware) ? middleware : [middleware]);
return this;
}
middleware(middleware) {
return this.use(middleware);
}
as(name, prepend = false) {
if (prepend) {
if (!this.#name) throw new RuntimeException(`Routes inside a group must have names before calling "router.group.as"`);
this.#name = `${name}.${this.#name}`;
return this;
}
this.#name = name;
return this;
}
isDeleted() {
return this.#isDeleted;
}
markAsDeleted() {
this.#isDeleted = true;
}
getName() {
return this.#name;
}
getPattern() {
return this.#pattern;
}
setPattern(pattern) {
this.#pattern = pattern;
return this;
}
getMiddleware() {
return this.#middleware;
}
#getMiddlewareForStore() {
const middleware = new Middleware();
this.#routerMiddleware.forEach((one) => {
debug_default("adding global middleware to route %s, %O", this.#pattern, one);
middleware.add(one);
});
this.#middleware.flat().forEach((one) => {
debug_default("adding named middleware to route %s, %O", this.#pattern, one);
middleware.add(one);
});
middleware.freeze();
return middleware;
}
toJSON() {
const pattern = this.#computePattern();
const matchers = this.#getMatchers();
return {
domain: this.#routeDomain,
pattern,
matchers,
tokens: parseRoute(pattern, matchers),
meta: {},
name: this.#name,
handler: this.#handler,
methods: this.#methods,
middleware: this.#getMiddlewareForStore(),
execute
};
}
};
var BriskRoute = class extends Macroable {
#pattern;
#globalMatchers;
#app;
#routerMiddleware;
route = null;
constructor(app, routerMiddleware, options) {
super();
this.#app = app;
this.#routerMiddleware = routerMiddleware;
this.#pattern = options.pattern;
this.#globalMatchers = options.globalMatchers;
}
setHandler(handler) {
this.route = new Route(this.#app, this.#routerMiddleware, {
pattern: this.#pattern,
globalMatchers: this.#globalMatchers,
methods: ["GET", "HEAD"],
handler
});
return this.route;
}
redirect(...args) {
const [identifier, params, options] = args;
function redirectsToRoute(ctx) {
const redirector = ctx.response.redirect();
if (options?.status) redirector.status(options.status);
return redirector.toRoute(identifier, params || ctx.params, options);
}
Object.defineProperty(redirectsToRoute, "listArgs", {
value: identifier,
writable: false
});
return this.setHandler(redirectsToRoute);
}
redirectToPath(url, options) {
function redirectsToPath(ctx) {
const redirector = ctx.response.redirect();
if (options?.status) redirector.status(options.status);
return redirector.toPath(url);
}
Object.defineProperty(redirectsToPath, "listArgs", {
value: url,
writable: false
});
return this.setHandler(redirectsToPath);
}
};
var RouteResource = class extends Macroable {
#resource;
#controller;
#shallow = false;
#globalMatchers;
#app;
#routerMiddleware;
#params = {};
#routesBaseName;
routes = [];
constructor(app, routerMiddleware, options) {
super();
this.#validateResourceName(options.resource);
this.#app = app;
this.#shallow = options.shallow;
this.#routerMiddleware = routerMiddleware;
this.#controller = options.controller;
this.#globalMatchers = options.globalMatchers;
this.#resource = this.#normalizeResourceName(options.resource);
this.#routesBaseName = this.#getRoutesBaseName();
this.#buildRoutes();
}
#normalizeResourceName(resource) {
return resource.replace(/^\//, "").replace(/\/$/, "");
}
#validateResourceName(resource) {
if (!resource || resource === "/") throw new RuntimeException(`Invalid resource name "${resource}"`);
}
#getRoutesBaseName() {
return this.#resource.split(".").map((token) => string.snakeCase(token)).join(".");
}
#createRoute(pattern, methods, action) {
const route = new Route(this.#app, this.#routerMiddleware, {
pattern,
methods,
handler: typeof this.#controller === "string" ? `${this.#controller}.${action}` : [this.#controller, action],
globalMatchers: this.#globalMatchers
});
route.as(`${this.#routesBaseName}.${action}`);
this.routes.push(route);
}
#getResourceId(resource) {
return `${string.snakeCase(string.singular(resource))}_id`;
}
#buildRoutes() {
const resources = this.#resource.split(".");
const mainResource = resources.pop();
this.#params[mainResource] = ":id";
const baseURI = `${resources.map((resource) => {
const paramName = `:${this.#getResourceId(resource)}`;
this.#params[resource] = paramName;
return `${resource}/${paramName}`;
}).join("/")}/${mainResource}`;
this.#createRoute(baseURI, ["GET", "HEAD"], "index");
this.#createRoute(`${baseURI}/create`, ["GET", "HEAD"], "create");
this.#createRoute(baseURI, ["POST"], "store");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["GET", "HEAD"], "show");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id/edit`, ["GET", "HEAD"], "edit");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["PUT", "PATCH"], "update");
this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["DELETE"], "destroy");
}
#filter(names, inverse) {
const actions = Array.isArray(names) ? names : [names];
return this.routes.filter((route) => {
const match = actions.find((name) => route.getName().endsWith(name));
return inverse ? !match : match;
});
}
only(names) {
this.#filter(names, true).forEach((route) => route.markAsDeleted());
return this;
}
except(names) {
this.#filter(names, false).forEach((route) => route.markAsDeleted());
return this;
}
apiOnly() {
return this.except(["create", "edit"]);
}
where(key, matcher) {
this.routes.forEach((route) => {
route.where(key, matcher);
});
return this;
}
tap(actions, callback) {
if (typeof actions === "function") {
this.routes.forEach((route) => {
if (!route.isDeleted()) actions(route);
});
return this;
}
this.#filter(actions, false).forEach((route) => {
if (!route.isDeleted()) callback(route);
});
return this;
}
params(resources) {
Object.keys(resources).forEach((resource) => {
const param = resources[resource];
const existingParam = this.#params[resource];
this.#params[resource] = `:${param}`;
this.routes.forEach((route) => {
route.setPattern(route.getPattern().replace(`${resource}/${existingParam}`, `${resource}/:${param}`));
});
});
return this;
}
use(actions, middleware) {
if (actions === "*") this.tap((route) => route.use(middleware));
else this.tap(actions, (route) => route.use(middleware));
return this;
}
middleware(actions, middleware) {
return this.use(actions, middleware);
}
as(name, normalizeName = true) {
name = normalizeName ? string.snakeCase(name) : name;
this.routes.forEach((route) => {
route.as(route.getName().replace(this.#routesBaseName, name), false);
});
this.#routesBaseName = name;
return this;
}
};
var RouteGroup = class RouteGroup extends Macroable {
#middleware = [];
constructor(routes) {
super();
this.routes = routes;
}
#shareMiddlewareStackWithRoutes(route) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#shareMiddlewareStackWithRoutes(child));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.getMiddleware().unshift(this.#middleware));
return;
}
if (route instanceof BriskRoute) {
route.route.getMiddleware().unshift(this.#middleware);
return;
}
route.getMiddleware().unshift(this.#middleware);
}
#updateRouteName(route, name) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#updateRouteName(child, name));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.as(name, true));
return;
}
if (route instanceof BriskRoute) {
route.route.as(name, true);
return;
}
route.as(name, true);
}
#setRoutePrefix(route, prefix) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#setRoutePrefix(child, prefix));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.prefix(prefix));
return;
}
if (route instanceof BriskRoute) {
route.route.prefix(prefix);
return;
}
route.prefix(prefix);
}
#updateRouteDomain(route, domain) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#updateRouteDomain(child, domain));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.domain(domain));
return;
}
if (route instanceof BriskRoute) {
route.route.domain(domain, false);
return;
}
route.domain(domain, false);
}
#updateRouteMatchers(route, param, matcher) {
if (route instanceof RouteGroup) {
route.routes.forEach((child) => this.#updateRouteMatchers(child, param, matcher));
return;
}
if (route instanceof RouteResource) {
route.routes.forEach((child) => child.where(param, matcher));
return;
}
if (route instanceof BriskRoute) {
route.route.where(param, matcher);
return;
}
route.where(param, matcher);
}
where(param, matcher) {
this.routes.forEach((route) => this.#updateRouteMatchers(route, param, matcher));
return this;
}
prefix(prefix) {
this.routes.forEach((route) => this.#setRoutePrefix(route, prefix));
return this;
}
domain(domain) {
this.routes.forEach((route) => this.#updateRouteDomain(route, domain));
return this;
}
as(name) {
this.routes.forEach((route) => this.#updateRouteName(route, name));
return this;
}
use(middleware) {
if (!this.#middleware.length) this.routes.forEach((route) => this.#shareMiddlewareStackWithRoutes(route));
if (Array.isArray(middleware)) for (let one of middleware) this.#middleware.push(one);
else this.#middleware.push(middleware);
return this;
}
middleware(middleware) {
return this.use(middleware);
}
};
const proxyCache = new Cache({ max: 200 });
function dropSlash(input) {
if (input === "/") return "/";
return `/${input.replace(/^\//, "").replace(/\/$/, "")}`;
}
function toRoutesJSON(routes) {
return routes.reduce((list, route) => {
if (route instanceof RouteGroup) {
list = list.concat(toRoutesJSON(route.routes));
return list;
}
if (route instanceof RouteResource) {
list = list.concat(toRoutesJSON(route.routes));
return list;
}
if (route instanceof BriskRoute) {
if (route.route && !route.route.isDeleted()) list.push(route.route.toJSON());
return list;
}
if (!route.isDeleted()) list.push(route.toJSON());
return list;
}, []);
}
function trustProxy(remoteAddress, proxyFn) {
if (proxyCache.has(remoteAddress)) return proxyCache.get(remoteAddress);
const result = proxyFn(remoteAddress, 0);
proxyCache.set(remoteAddress, result);
return result;
}
function parseRange(range, value) {
const parts = range.split("..");
const min = Number(parts[0]);
const max = Number(parts[1]);
if (parts.length === 1 && !Number.isNaN(min)) return { [min]: value };
if (Number.isNaN(min) || Number.isNaN(max)) return {};
if (min === max) return { [min]: value };
if (max < min) throw new InvalidArgumentsException(`Invalid range "${range}"`);
return [...Array(max - min + 1).keys()].reduce((result, step) => {
result[min + step] = value;
return result;
}, {});
}
function decodeComponentChar(highCharCode, lowCharCode) {
if (highCharCode === 50) {
if (lowCharCode === 53) return "%";
if (lowCharCode === 51) return "#";
if (lowCharCode === 52) return "$";
if (lowCharCode === 54) return "&";
if (lowCharCode === 66) return "+";
if (lowCharCode === 98) return "+";
if (lowCharCode === 67) return ",";
if (lowCharCode === 99) return ",";
if (lowCharCode === 70) return "/";
if (lowCharCode === 102) return "/";
return null;
}
if (highCharCode === 51) {
if (lowCharCode === 65) return ":";
if (lowCharCode === 97) return ":";
if (lowCharCode === 66) return ";";
if (lowCharCode === 98) return ";";
if (lowCharCode === 68) return "=";
if (lowCharCode === 100) return "=";
if (lowCharCode === 70) return "?";
if (lowCharCode === 102) return "?";
return null;
}
if (highCharCode === 52 && lowCharCode === 48) return "@";
return null;
}
function safeDecodeURI(path, useSemicolonDelimiter) {
let shouldDecode = false;
let shouldDecodeParam = false;
let querystring = "";
for (let i = 1; i < path.length; i++) {
const charCode = path.charCodeAt(i);
if (charCode === 37) {
const highCharCode = path.charCodeAt(i + 1);
const lowCharCode = path.charCodeAt(i + 2);
if (decodeComponentChar(highCharCode, lowCharCode) === null) shouldDecode = true;
else {
shouldDecodeParam = true;
if (highCharCode === 50 && lowCharCode === 53) {
shouldDecode = true;
path = path.slice(0, i + 1) + "25" + path.slice(i + 1);
i += 2;
}
i += 2;
}
} else if (charCode === 63 || charCode === 35 || charCode === 59 && useSemicolonDelimiter) {
querystring = path.slice(i + 1);
path = path.slice(0, i);
break;
}
}
return {
pathname: shouldDecode ? decodeURI(path) : path,
query: querystring,
shouldDecodeParam
};
}
export { __exportAll as _, RouteGroup as a, Route as c, httpRequest as d, httpResponseSerializer as f, __commonJSMin as g, debug_default as h, trustProxy as i, httpExceptionHandler as l, canWriteResponseBody as m, safeDecodeURI as n, RouteResource as o, tracing_channels_exports as p, toRoutesJSON as r, BriskRoute as s, parseRange as t, httpMiddleware as u, __require as v, __toESM as y };