@adonisjs/http-server 6.2.0-0 → 6.3.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,7 +62,7 @@ export class Request extends Macroable {
62
62
  throw new Error('Cannot re-set initial body. Use "request.updateBody" instead');
63
63
  }
64
64
  this.updateBody(body);
65
- this.#originalRequestData = Object.freeze({ ...this.#requestData });
65
+ this.#originalRequestData = Object.freeze(lodash.cloneDeep(this.#requestData));
66
66
  }
67
67
  updateBody(body) {
68
68
  this.#requestBody = body;
@@ -13,7 +13,9 @@ export declare class Response extends Macroable {
13
13
  request: IncomingMessage;
14
14
  response: ServerResponse;
15
15
  get hasLazyBody(): boolean;
16
- get isStreamResponse(): boolean;
16
+ get hasContent(): boolean;
17
+ get hasStream(): boolean;
18
+ get content(): [any, boolean, (string | undefined)?] | undefined;
17
19
  lazyBody: Partial<{
18
20
  content: [any, boolean, string?];
19
21
  stream: [ResponseStream, ((error: NodeJS.ErrnoException) => [string, number?])?];
@@ -28,9 +28,15 @@ export class Response extends Macroable {
28
28
  get hasLazyBody() {
29
29
  return !!(this.lazyBody.content || this.lazyBody.fileToStream || this.lazyBody.stream);
30
30
  }
31
- get isStreamResponse() {
31
+ get hasContent() {
32
+ return !!this.lazyBody.content;
33
+ }
34
+ get hasStream() {
32
35
  return !!(this.lazyBody.stream || this.lazyBody.fileToStream);
33
36
  }
37
+ get content() {
38
+ return this.lazyBody.content;
39
+ }
34
40
  lazyBody = {};
35
41
  ctx;
36
42
  constructor(request, response, encryption, config, router, qs) {
@@ -402,8 +408,8 @@ export class Response extends Macroable {
402
408
  if (!this.isPending) {
403
409
  return;
404
410
  }
405
- if (this.lazyBody.content) {
406
- this.writeBody(...this.lazyBody.content);
411
+ if (this.content) {
412
+ this.writeBody(...this.content);
407
413
  return;
408
414
  }
409
415
  if (this.lazyBody.stream) {
@@ -11,6 +11,10 @@ export declare class BriskRoute extends Macroable {
11
11
  globalMatchers: RouteMatchers;
12
12
  });
13
13
  setHandler(handler: RouteFn): Route;
14
- redirect(identifier: string, params?: any[] | Record<string, any>, options?: MakeUrlOptions): Route;
15
- redirectToPath(url: string): Route;
14
+ redirect(identifier: string, params?: any[] | Record<string, any>, options?: MakeUrlOptions & {
15
+ status: number;
16
+ }): Route;
17
+ redirectToPath(url: string, options?: {
18
+ status: number;
19
+ }): Route;
16
20
  }
@@ -24,12 +24,20 @@ export class BriskRoute extends Macroable {
24
24
  }
25
25
  redirect(identifier, params, options) {
26
26
  return this.setHandler(async (ctx) => {
27
- return ctx.response.redirect().toRoute(identifier, params || ctx.params, options);
27
+ const redirector = ctx.response.redirect();
28
+ if (options?.status) {
29
+ redirector.status(options.status);
30
+ }
31
+ return redirector.toRoute(identifier, params || ctx.params, options);
28
32
  });
29
33
  }
30
- redirectToPath(url) {
34
+ redirectToPath(url, options) {
31
35
  return this.setHandler(async (ctx) => {
32
- return ctx.response.redirect().toPath(url);
36
+ const redirector = ctx.response.redirect();
37
+ if (options?.status) {
38
+ redirector.status(options.status);
39
+ }
40
+ return redirector.toPath(url);
33
41
  });
34
42
  }
35
43
  }
@@ -1,13 +1,14 @@
1
+ import { useReturnValue } from './factories/use_return_value.js';
1
2
  export function execute(route, resolver, ctx) {
2
3
  return route.middleware
3
4
  .runner()
4
- .finalHandler(() => {
5
+ .finalHandler(async () => {
5
6
  if (typeof route.handler === 'function') {
6
- return route.handler(ctx);
7
+ return Promise.resolve(route.handler(ctx)).then(useReturnValue(ctx));
7
8
  }
8
- return route.handler.handle(resolver, ctx);
9
+ return route.handler.handle(resolver, ctx).then(useReturnValue(ctx));
9
10
  })
10
- .run((middleware, next) => {
11
+ .run(async (middleware, next) => {
11
12
  if (typeof middleware === 'function') {
12
13
  return middleware(ctx, next);
13
14
  }
@@ -4,6 +4,7 @@ import type { MiddlewareFn, ParsedNamedMiddleware } from '../types/middleware.js
4
4
  import { Route } from './route.js';
5
5
  import { BriskRoute } from './brisk.js';
6
6
  import { RouteResource } from './resource.js';
7
+ import { OneOrMore } from '../types/base.js';
7
8
  export declare class RouteGroup extends Macroable {
8
9
  #private;
9
10
  routes: (Route | RouteGroup | RouteResource | BriskRoute)[];
@@ -12,6 +13,6 @@ export declare class RouteGroup extends Macroable {
12
13
  prefix(prefix: string): this;
13
14
  domain(domain: string): this;
14
15
  as(name: string): this;
15
- use(middleware: MiddlewareFn | ParsedNamedMiddleware): this;
16
- middleware(middleware: MiddlewareFn | ParsedNamedMiddleware): this;
16
+ use(middleware: OneOrMore<MiddlewareFn | ParsedNamedMiddleware>): this;
17
+ middleware(middleware: OneOrMore<MiddlewareFn | ParsedNamedMiddleware>): this;
17
18
  }
@@ -103,7 +103,14 @@ export class RouteGroup extends Macroable {
103
103
  if (!this.#middleware.length) {
104
104
  this.routes.forEach((route) => this.#shareMiddlewareStackWithRoutes(route));
105
105
  }
106
- this.#middleware.push(middleware);
106
+ if (Array.isArray(middleware)) {
107
+ for (let one of middleware) {
108
+ this.#middleware.push(one);
109
+ }
110
+ }
111
+ else {
112
+ this.#middleware.push(middleware);
113
+ }
107
114
  return this;
108
115
  }
109
116
  middleware(middleware) {
@@ -1,3 +1,4 @@
1
+ import matchit from '@poppinss/matchit';
1
2
  import { RuntimeException } from '@poppinss/utils';
2
3
  export class UrlBuilder {
3
4
  #qsParser;
@@ -27,30 +28,30 @@ export class UrlBuilder {
27
28
  const paramsArray = Array.isArray(this.#params) ? this.#params : null;
28
29
  const paramsObject = !Array.isArray(this.#params) ? this.#params : {};
29
30
  let paramsIndex = 0;
30
- const tokens = pattern.split('/');
31
+ const tokens = matchit.parse(pattern);
31
32
  for (const token of tokens) {
32
- if (token === '*') {
33
+ if (token.type === 0) {
34
+ uriSegments.push(`${token.val}${token.end}`);
35
+ }
36
+ else if (token.type === 2) {
33
37
  const values = paramsArray ? paramsArray.slice(paramsIndex) : paramsObject['*'];
34
38
  this.#ensureHasWildCardValues(pattern, values);
35
- values.forEach((value) => uriSegments.push(value));
39
+ uriSegments.push(`${values.join('/')}${token.end}`);
36
40
  break;
37
41
  }
38
- else if (!token.startsWith(':')) {
39
- uriSegments.push(token);
40
- }
41
42
  else {
42
- const paramName = token.replace(/^:/, '').replace(/\?$/, '');
43
+ const paramName = token.val;
43
44
  const value = paramsArray ? paramsArray[paramsIndex] : paramsObject[paramName];
44
- if (!token.endsWith('?')) {
45
+ if (token.type === 1) {
45
46
  this.#ensureHasParamValue(pattern, paramName, value);
46
47
  }
47
48
  paramsIndex++;
48
49
  if (value !== undefined && value !== null) {
49
- uriSegments.push(value);
50
+ uriSegments.push(`${value}${token.end}`);
50
51
  }
51
52
  }
52
53
  }
53
- return uriSegments.join('/');
54
+ return `/${uriSegments.join('/')}`;
54
55
  }
55
56
  #suffixQueryString(url, qs) {
56
57
  if (qs) {
@@ -21,5 +21,5 @@ export declare class RouteResource extends Macroable {
21
21
  params(resources: {
22
22
  [resource: string]: string;
23
23
  }): this;
24
- as(name: string): this;
24
+ as(name: string, normalizeName?: boolean): this;
25
25
  }
@@ -113,8 +113,8 @@ export class RouteResource extends Macroable {
113
113
  });
114
114
  return this;
115
115
  }
116
- as(name) {
117
- name = string.snakeCase(name);
116
+ as(name, normalizeName = true) {
117
+ name = normalizeName ? string.snakeCase(name) : name;
118
118
  this.routes.forEach((route) => {
119
119
  route.as(route.getName().replace(this.#routesBaseName, name), false);
120
120
  });
@@ -1,6 +1,6 @@
1
1
  import { Macroable } from '@poppinss/macroable';
2
2
  import type { Application } from '@adonisjs/application';
3
- import type { Constructor, LazyImport } from '../types/base.js';
3
+ import type { Constructor, LazyImport, OneOrMore } from '../types/base.js';
4
4
  import type { MiddlewareFn, ParsedNamedMiddleware, ParsedGlobalMiddleware } from '../types/middleware.js';
5
5
  import type { GetControllerHandlers, RouteFn, RouteJSON, RouteMatcher, RouteMatchers, StoreRouteMiddleware } from '../types/route.js';
6
6
  export declare class Route<Controller extends Constructor<any> = any> extends Macroable {
@@ -14,8 +14,8 @@ export declare class Route<Controller extends Constructor<any> = any> extends Ma
14
14
  where(param: string, matcher: RouteMatcher | string | RegExp): this;
15
15
  prefix(prefix: string): this;
16
16
  domain(domain: string, overwrite?: boolean): this;
17
- use(middleware: MiddlewareFn | ParsedNamedMiddleware): this;
18
- middleware(middleware: MiddlewareFn | ParsedNamedMiddleware): this;
17
+ use(middleware: OneOrMore<MiddlewareFn | ParsedNamedMiddleware>): this;
18
+ middleware(middleware: OneOrMore<MiddlewareFn | ParsedNamedMiddleware>): this;
19
19
  as(name: string, prepend?: boolean): this;
20
20
  isDeleted(): boolean;
21
21
  markAsDeleted(): void;
@@ -86,7 +86,7 @@ export class Route extends Macroable {
86
86
  return this;
87
87
  }
88
88
  use(middleware) {
89
- this.#middleware.push([middleware]);
89
+ this.#middleware.push(Array.isArray(middleware) ? middleware : [middleware]);
90
90
  return this;
91
91
  }
92
92
  middleware(middleware) {
@@ -12,6 +12,6 @@ export function finalHandler(router, resolver, ctx) {
12
12
  ctx.routeKey = route.routeKey;
13
13
  return route.route.execute(route.route, resolver, ctx);
14
14
  }
15
- throw new RouteNotFoundException(`Cannot ${method}:${url}`);
15
+ return Promise.reject(new RouteNotFoundException(`Cannot ${method}:${url}`));
16
16
  };
17
17
  }
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
+ import { Emitter } from '@adonisjs/events';
3
4
  import type { Encryption } from '@adonisjs/encryption';
4
5
  import type { Server as HttpsServer } from 'node:https';
5
6
  import type { Application } from '@adonisjs/application';
@@ -8,17 +9,15 @@ import type { LazyImport } from '../types/base.js';
8
9
  import type { MiddlewareAsClass } from '../types/middleware.js';
9
10
  import type { ErrorHandlerAsAClass, ServerConfig } from '../types/server.js';
10
11
  import { Router } from '../router/main.js';
11
- import { HttpContext } from '../http_context/main.js';
12
12
  export declare class Server {
13
13
  #private;
14
14
  get usingAsyncLocalStorage(): boolean;
15
- constructor(app: Application<any, any>, encryption: Encryption, config: ServerConfig);
15
+ constructor(app: Application<any, any>, encryption: Encryption, emitter: Emitter<any>, config: ServerConfig);
16
16
  use(middleware: LazyImport<MiddlewareAsClass>[]): this;
17
17
  errorHandler(handler: LazyImport<ErrorHandlerAsAClass>): this;
18
18
  boot(): Promise<void>;
19
19
  setNodeServer(server: HttpServer | HttpsServer): void;
20
20
  getNodeServer(): HttpServer<typeof IncomingMessage, typeof ServerResponse> | HttpsServer<typeof IncomingMessage, typeof ServerResponse> | undefined;
21
21
  getRouter(): Router;
22
- onRequest(callback: (ctx: HttpContext) => void): this;
23
22
  handle(req: IncomingMessage, res: ServerResponse): Promise<any>;
24
23
  }
@@ -1,3 +1,4 @@
1
+ import onFinished from 'on-finished';
1
2
  import Middleware from '@poppinss/middleware';
2
3
  import { moduleImporter } from '@adonisjs/fold';
3
4
  import { Qs } from '../qs.js';
@@ -8,17 +9,17 @@ import { Router } from '../router/main.js';
8
9
  import { HttpContext } from '../http_context/main.js';
9
10
  import { finalHandler } from './factories/final_handler.js';
10
11
  import { writeResponse } from './factories/write_response.js';
11
- import { useReturnValue } from './factories/use_return_value.js';
12
12
  import { asyncLocalStorage } from '../http_context/local_storage.js';
13
13
  import { middlewareHandler } from './factories/middleware_handler.js';
14
14
  export class Server {
15
- #requestHooks = new Set();
16
- #errorHandler;
17
- #resolvedErrorHandler = {
15
+ #defaultErrorHandler = {
18
16
  handle(error, ctx) {
19
17
  ctx.response.status(error.status || 500).send(error.message || 'Internal server error');
20
18
  },
21
19
  };
20
+ #errorHandler;
21
+ #resolvedErrorHandler = this.#defaultErrorHandler;
22
+ #emitter;
22
23
  #app;
23
24
  #encryption;
24
25
  #config;
@@ -30,8 +31,9 @@ export class Server {
30
31
  get usingAsyncLocalStorage() {
31
32
  return asyncLocalStorage.isEnabled;
32
33
  }
33
- constructor(app, encryption, config) {
34
+ constructor(app, encryption, emitter, config) {
34
35
  this.#app = app;
36
+ this.#emitter = emitter;
35
37
  this.#config = config;
36
38
  this.#encryption = encryption;
37
39
  this.#qsParser = new Qs(this.#config.qs);
@@ -56,10 +58,13 @@ export class Server {
56
58
  }
57
59
  #handleRequest(ctx, resolver) {
58
60
  return this.#serverMiddlewareStack.runner()
61
+ .errorHandler((error) => this.#resolvedErrorHandler.handle(error, ctx))
59
62
  .finalHandler(finalHandler(this.#router, resolver, ctx))
60
63
  .run(middlewareHandler(resolver, ctx))
61
- .then(useReturnValue(ctx))
62
- .catch((error) => this.#resolvedErrorHandler.handle(error, ctx))
64
+ .catch((error) => {
65
+ ctx.logger.fatal({ err: error }, 'Exception raised by error handler');
66
+ return this.#defaultErrorHandler.handle(error, ctx);
67
+ })
63
68
  .finally(writeResponse(ctx));
64
69
  }
65
70
  use(middleware) {
@@ -91,17 +96,20 @@ export class Server {
91
96
  getRouter() {
92
97
  return this.#router;
93
98
  }
94
- onRequest(callback) {
95
- this.#requestHooks.add(callback);
96
- return this;
97
- }
98
99
  handle(req, res) {
100
+ const hasRequestListener = this.#emitter.hasListeners('http:request_finished');
101
+ const startTime = hasRequestListener ? process.hrtime() : null;
99
102
  const resolver = this.#app.container.createResolver();
100
103
  const request = new Request(req, res, this.#encryption, this.#config, this.#qsParser);
101
104
  const response = new Response(req, res, this.#encryption, this.#config, this.#router, this.#qsParser);
102
- const ctx = new HttpContext(request, response, this.#app.logger.child({}), resolver);
103
- for (let hook of this.#requestHooks) {
104
- hook(ctx);
105
+ const ctx = new HttpContext(request, response, this.#app.logger.child({ request_id: request.id() }), resolver);
106
+ if (startTime) {
107
+ onFinished(res, () => {
108
+ this.#emitter.emit('http:request_finished', {
109
+ ctx: ctx,
110
+ duration: process.hrtime(startTime),
111
+ });
112
+ });
105
113
  }
106
114
  if (this.usingAsyncLocalStorage) {
107
115
  return asyncLocalStorage.storage.run(ctx, () => this.#handleRequest(ctx, resolver));
@@ -1,4 +1,5 @@
1
1
  export type { NextFn } from '@poppinss/middleware/types';
2
+ export type OneOrMore<T> = T | T[];
2
3
  export type Constructor<T> = new (...args: any[]) => T;
3
4
  export type LazyImport<DefaultExport> = () => Promise<{
4
5
  default: DefaultExport;
@@ -1,8 +1,12 @@
1
- import type { QSParserConfig } from './qs.js';
2
1
  import type { Constructor } from './base.js';
2
+ import type { QSParserConfig } from './qs.js';
3
3
  import type { RequestConfig } from './request.js';
4
4
  import type { ResponseConfig } from './response.js';
5
5
  import type { HttpContext } from '../http_context/main.js';
6
+ export type HttpRequestFinishedPayload = {
7
+ ctx: HttpContext;
8
+ duration: [number, number];
9
+ };
6
10
  export type ServerErrorHandler = {
7
11
  handle: (error: any, ctx: HttpContext) => any;
8
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonisjs/http-server",
3
- "version": "6.2.0-0",
3
+ "version": "6.3.0-0",
4
4
  "description": "AdonisJS HTTP server with support packed with Routing and Cookies",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
@@ -37,13 +37,14 @@
37
37
  "author": "virk,adonisjs",
38
38
  "license": "MIT",
39
39
  "devDependencies": {
40
- "@adonisjs/application": "^6.4.0-0",
41
- "@adonisjs/config": "^4.1.2-0",
42
- "@adonisjs/encryption": "^5.0.2-0",
43
- "@adonisjs/fold": "^9.8.0-0",
44
- "@adonisjs/logger": "^5.1.0-0",
45
- "@commitlint/cli": "^17.3.0",
46
- "@commitlint/config-conventional": "^17.3.0",
40
+ "@adonisjs/application": "^6.5.1-0",
41
+ "@adonisjs/config": "^4.1.3-0",
42
+ "@adonisjs/encryption": "^5.0.3-0",
43
+ "@adonisjs/events": "^8.3.0-0",
44
+ "@adonisjs/fold": "^9.9.0-0",
45
+ "@adonisjs/logger": "^5.1.1-0",
46
+ "@commitlint/cli": "^17.4.0",
47
+ "@commitlint/config-conventional": "^17.4.0",
47
48
  "@fastify/middie": "^8.1.0",
48
49
  "@japa/api-client": "^1.4.2",
49
50
  "@japa/assert": "^1.3.6",
@@ -51,7 +52,7 @@
51
52
  "@japa/run-failed-tests": "^1.1.0",
52
53
  "@japa/runner": "^2.2.2",
53
54
  "@japa/spec-reporter": "^1.3.2",
54
- "@swc/core": "^1.3.22",
55
+ "@swc/core": "^1.3.25",
55
56
  "@types/accepts": "^1.3.5",
56
57
  "@types/content-disposition": "^0.5.5",
57
58
  "@types/cookie": "^0.5.1",
@@ -73,16 +74,16 @@
73
74
  "c8": "^7.12.0",
74
75
  "cross-env": "^7.0.3",
75
76
  "del-cli": "^5.0.0",
76
- "eslint": "^8.28.0",
77
- "eslint-config-prettier": "^8.5.0",
77
+ "eslint": "^8.31.0",
78
+ "eslint-config-prettier": "^8.6.0",
78
79
  "eslint-plugin-adonis": "^3.0.3",
79
80
  "eslint-plugin-prettier": "^4.2.1",
80
- "fastify": "^4.10.2",
81
+ "fastify": "^4.11.0",
81
82
  "fs-extra": "^11.1.0",
82
83
  "github-label-sync": "^2.2.0",
83
84
  "http-status-codes": "^2.2.0",
84
- "husky": "^8.0.2",
85
- "np": "^7.6.2",
85
+ "husky": "^8.0.3",
86
+ "np": "^7.6.3",
86
87
  "pem": "^1.14.6",
87
88
  "prettier": "^2.8.1",
88
89
  "reflect-metadata": "^0.1.13",
@@ -93,8 +94,8 @@
93
94
  "dependencies": {
94
95
  "@poppinss/macroable": "^1.0.0-0",
95
96
  "@poppinss/matchit": "^3.1.2",
96
- "@poppinss/middleware": "^3.0.0",
97
- "@poppinss/utils": "^6.1.0-0",
97
+ "@poppinss/middleware": "^3.1.0",
98
+ "@poppinss/utils": "^6.3.1-0",
98
99
  "@sindresorhus/is": "^5.3.0",
99
100
  "accepts": "^1.3.8",
100
101
  "content-disposition": "^0.5.4",
@@ -113,9 +114,10 @@
113
114
  "vary": "^1.1.2"
114
115
  },
115
116
  "peerDependencies": {
116
- "@adonisjs/application": "^6.4.0-0",
117
- "@adonisjs/encryption": "^5.0.2-0",
118
- "@adonisjs/fold": "^9.8.0-0"
117
+ "@adonisjs/application": "^6.5.1-0",
118
+ "@adonisjs/events": "^8.3.0-0",
119
+ "@adonisjs/encryption": "^5.0.3-0",
120
+ "@adonisjs/fold": "^9.9.0-0"
119
121
  },
120
122
  "repository": {
121
123
  "type": "git",