starlight-server 1.7.1 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,12 @@
1
+ import { type MaySuccess } from '@anjianshi/utils';
1
2
  import { type Definition as ValidatorDefinition } from '@anjianshi/utils/validators/index.js';
2
- import { type BasicContext } from './index.js';
3
- import { ResponseUtils } from '../http/response.js';
4
- export declare class Helpers {
5
- readonly context: BasicContext;
6
- constructor(context: BasicContext);
3
+ import { type Request } from '../http/request.js';
4
+ import { type ResponseUtils } from '../http/response.js';
5
+ import { type PathParameters } from './match-path.js';
6
+ declare class RequestHelpers {
7
+ readonly request: Request;
8
+ readonly pathParameters: PathParameters;
9
+ constructor(request: Request, pathParameters: PathParameters);
7
10
  validatePathParameters<Definition extends Record<string, ValidatorDefinition>>(struct: Definition): import("@anjianshi/utils/validators/base.js").Validated<{
8
11
  readonly type: "struct";
9
12
  readonly struct: Definition;
@@ -56,9 +59,18 @@ export declare class Helpers {
56
59
  record: import("@anjianshi/utils/validators/factory.js").ValidatorForDefinition<T_1["record"]>;
57
60
  } : T_1 : never : never>>;
58
61
  }
59
- export declare class ResponseUtilsWithHelpers extends ResponseUtils {
62
+ export type RequestWithHelpers = Request & RequestHelpers;
63
+ export declare function getRequestWithHelpers(request: Request, pathParameters: PathParameters): RequestWithHelpers;
64
+ declare class ResponseUtilsHelpers {
65
+ readonly response: ResponseUtils;
60
66
  constructor(response: ResponseUtils);
61
- success(data?: unknown): void;
62
- failed(message: string, code?: string | number): void;
63
- failed<T>(message: string, code: string | number | undefined, data: T): void;
67
+ success(data?: unknown): never;
68
+ failed(message: string, code?: string | number): never;
69
+ failed<T>(message: string, code: string | number | undefined, data: T): never;
70
+ maySuccess(result: MaySuccess<unknown, unknown>): never;
64
71
  }
72
+ export declare class ResponseFinish {
73
+ }
74
+ export type ResponseUtilsWithHelpers = ResponseUtils & ResponseUtilsHelpers;
75
+ export declare function getResponseUtilsWithHelpers(response: ResponseUtils): ResponseUtilsWithHelpers;
76
+ export {};
@@ -1,26 +1,27 @@
1
1
  import { success, failed } from '@anjianshi/utils';
2
2
  import { getValidator, } from '@anjianshi/utils/validators/index.js';
3
3
  import { HTTPError } from '../index.js';
4
- import { ResponseUtils } from '../http/response.js';
5
- export class Helpers {
6
- context;
7
- constructor(context) {
8
- this.context = context;
4
+ class RequestHelpers {
5
+ request;
6
+ pathParameters;
7
+ constructor(request, pathParameters) {
8
+ this.request = request;
9
+ this.pathParameters = pathParameters;
9
10
  }
10
11
  validatePathParameters(struct) {
11
- const result = getValidator({ type: 'struct', struct })('path', this.context.pathParameters);
12
+ const result = getValidator({ type: 'struct', struct })('path', this.pathParameters);
12
13
  if (result.success)
13
14
  return result.data;
14
15
  throw new HTTPError(400, result.message);
15
16
  }
16
17
  validateQuery(struct) {
17
- const result = getValidator({ type: 'struct', struct })('query', this.context.request.query);
18
+ const result = getValidator({ type: 'struct', struct })('query', this.request.query);
18
19
  if (result.success)
19
20
  return result.data;
20
21
  throw new HTTPError(400, result.message);
21
22
  }
22
23
  async validateBody(struct) {
23
- const body = await this.context.request.body.json();
24
+ const body = await this.request.body.json();
24
25
  if (typeof body !== 'object' || body === null || Array.isArray(body))
25
26
  throw new HTTPError(400, 'Invalid JSON body, should be an object.');
26
27
  const result = getValidator({ type: 'struct', struct })('body', body);
@@ -29,14 +30,46 @@ export class Helpers {
29
30
  throw new HTTPError(400, result.message);
30
31
  }
31
32
  }
32
- export class ResponseUtilsWithHelpers extends ResponseUtils {
33
+ export function getRequestWithHelpers(request, pathParameters) {
34
+ const helpers = new RequestHelpers(request, pathParameters);
35
+ return new Proxy(request, {
36
+ get(target, prop) {
37
+ if (prop in helpers)
38
+ return helpers[prop];
39
+ return request[prop];
40
+ },
41
+ });
42
+ }
43
+ // -----------------------------
44
+ class ResponseUtilsHelpers {
45
+ response;
33
46
  constructor(response) {
34
- super(response.nodeResponse, response.logger, response.jsonReplacer);
47
+ this.response = response;
35
48
  }
49
+ // 输出成功结果,并结束响应处理
36
50
  success(data) {
37
- this.json(success(data));
51
+ this.maySuccess(success(data));
38
52
  }
39
53
  failed(message, code, data) {
40
- this.json(failed(message, code, data));
54
+ this.maySuccess(failed(message, code, data));
55
+ }
56
+ // 输出 MaySuccess 对象,并结束响应处理
57
+ maySuccess(result) {
58
+ this.response.json(result);
59
+ throw new ResponseFinish();
41
60
  }
42
61
  }
62
+ // 通过捕获此对象实现提前结束响应处理
63
+ // eslint-disable-next-line @typescript-eslint/no-extraneous-class
64
+ export class ResponseFinish {
65
+ }
66
+ export function getResponseUtilsWithHelpers(response) {
67
+ const helpers = new ResponseUtilsHelpers(response);
68
+ return new Proxy(response, {
69
+ get(target, prop) {
70
+ if (prop in helpers)
71
+ return helpers[prop];
72
+ return response[prop];
73
+ },
74
+ });
75
+ }
@@ -2,16 +2,12 @@ import { type OptionalFields } from '@anjianshi/utils';
2
2
  import { type CORSRule } from '../http/cors.js';
3
3
  import { type Request, type ResponseUtils } from '../http/index.js';
4
4
  import { Swagger, type Method } from '../swagger/index.js';
5
- import { Helpers, ResponseUtilsWithHelpers } from './helpers.js';
5
+ import { type RequestWithHelpers, type ResponseUtilsWithHelpers, ResponseFinish } from './helpers.js';
6
6
  import { type PathParameters } from './match-path.js';
7
- type HelpersInst = InstanceType<typeof Helpers>;
8
7
  export interface BasicContext {
9
- request: Request;
10
- response: ResponseUtilsWithHelpers;
11
8
  pathParameters: PathParameters;
12
- validatePathParameters: HelpersInst['validatePathParameters'];
13
- validateQuery: HelpersInst['validateQuery'];
14
- validateBody: HelpersInst['validateBody'];
9
+ request: RequestWithHelpers;
10
+ response: ResponseUtilsWithHelpers;
15
11
  }
16
12
  /** 使用者可自行补充 Context 定义 */
17
13
  export interface Context extends BasicContext {
@@ -25,6 +21,8 @@ export interface Route {
25
21
  /** 此接口的 CORS 规则 */
26
22
  cors?: CORSRule;
27
23
  }
24
+ /** handler 里抛出此异常可提前结束请求处理 */
25
+ export { ResponseFinish };
28
26
  export declare class Router {
29
27
  /**
30
28
  * ----------------------
@@ -65,6 +63,5 @@ export declare class Router {
65
63
  * 请求处理
66
64
  * ----------------------
67
65
  */
68
- readonly handle: (request: Request, originResponse: ResponseUtils) => Promise<void>;
66
+ readonly handle: (request: Request, response: ResponseUtils) => Promise<void>;
69
67
  }
70
- export {};
@@ -2,8 +2,10 @@ import { joinPath } from '@anjianshi/utils';
2
2
  import { getPreflightRequestMethod, handleCORS } from '../http/cors.js';
3
3
  import { HTTPError } from '../http/index.js';
4
4
  import { Swagger } from '../swagger/index.js';
5
- import { Helpers, ResponseUtilsWithHelpers } from './helpers.js';
5
+ import { getRequestWithHelpers, getResponseUtilsWithHelpers, ResponseFinish, } from './helpers.js';
6
6
  import { matchPath } from './match-path.js';
7
+ /** handler 里抛出此异常可提前结束请求处理 */
8
+ export { ResponseFinish };
7
9
  export class Router {
8
10
  /**
9
11
  * ----------------------
@@ -78,8 +80,7 @@ export class Router {
78
80
  * 请求处理
79
81
  * ----------------------
80
82
  */
81
- handle = async (request, originResponse) => {
82
- const response = new ResponseUtilsWithHelpers(originResponse);
83
+ handle = async (request, response) => {
83
84
  const pathMatchedRoutes = matchPath(this.routes.map(route => route.path), request.path).map(result => ({ route: this.routes[result.index], parameters: result.parameters }));
84
85
  if (!pathMatchedRoutes.length)
85
86
  throw new HTTPError(404); // 没有路径匹配的路由
@@ -100,18 +101,21 @@ export class Router {
100
101
  const corsRule = matched.route.cors ?? this.cors;
101
102
  handleCORS(request, response, typeof corsRule === 'function' ? corsRule(request) : corsRule);
102
103
  }
104
+ const requestWithHelpers = getRequestWithHelpers(request, matched.parameters);
105
+ const responseWithHelpers = getResponseUtilsWithHelpers(response);
103
106
  const basicContext = {};
104
- const helpers = new Helpers(basicContext);
105
107
  Object.assign(basicContext, {
106
- request,
107
- response,
108
- pathParameters: matched.parameters,
109
- validatePathParameters: helpers.validatePathParameters.bind(helpers),
110
- validateQuery: helpers.validateQuery.bind(helpers),
111
- validateBody: helpers.validateBody.bind(helpers),
108
+ request: requestWithHelpers,
109
+ response: responseWithHelpers,
112
110
  });
113
- const result = await this.executor(basicContext, matched.route);
114
- if (result !== undefined)
115
- throw new Error('route handler 不应该有返回值');
111
+ try {
112
+ const result = await this.executor(basicContext, matched.route);
113
+ if (result !== undefined)
114
+ throw new Error('route handler 不应该有返回值');
115
+ }
116
+ catch (error) {
117
+ if (!(error instanceof ResponseFinish))
118
+ throw error;
119
+ }
116
120
  };
117
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-server",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "Simple But Powerful Node.js HTTP Server",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -1,34 +1,38 @@
1
- import { success, failed } from '@anjianshi/utils'
1
+ import { type MaySuccess, success, failed } from '@anjianshi/utils'
2
2
  import {
3
3
  getValidator,
4
4
  type Definition as ValidatorDefinition,
5
5
  } from '@anjianshi/utils/validators/index.js'
6
6
  import { HTTPError } from '@/index.js'
7
- import { type BasicContext } from './index.js'
8
- import { ResponseUtils } from '@/http/response.js'
7
+ import { type Request } from '@/http/request.js'
8
+ import { type ResponseUtils } from '@/http/response.js'
9
+ import { type PathParameters } from './match-path.js'
9
10
 
10
- export class Helpers {
11
- constructor(readonly context: BasicContext) {}
11
+ class RequestHelpers {
12
+ constructor(
13
+ readonly request: Request,
14
+ readonly pathParameters: PathParameters,
15
+ ) {}
12
16
 
13
17
  validatePathParameters<Definition extends Record<string, ValidatorDefinition>>(
14
18
  struct: Definition,
15
19
  ) {
16
20
  const result = getValidator({ type: 'struct', struct })(
17
21
  'path',
18
- this.context.pathParameters as Record<string, string>,
22
+ this.pathParameters as Record<string, string>,
19
23
  )
20
24
  if (result.success) return result.data
21
25
  throw new HTTPError(400, result.message)
22
26
  }
23
27
 
24
28
  validateQuery<Definition extends Record<string, ValidatorDefinition>>(struct: Definition) {
25
- const result = getValidator({ type: 'struct', struct })('query', this.context.request.query)
29
+ const result = getValidator({ type: 'struct', struct })('query', this.request.query)
26
30
  if (result.success) return result.data
27
31
  throw new HTTPError(400, result.message)
28
32
  }
29
33
 
30
34
  async validateBody<Definition extends Record<string, ValidatorDefinition>>(struct: Definition) {
31
- const body = await this.context.request.body.json()
35
+ const body = await this.request.body.json()
32
36
  if (typeof body !== 'object' || body === null || Array.isArray(body))
33
37
  throw new HTTPError(400, 'Invalid JSON body, should be an object.')
34
38
 
@@ -38,18 +42,54 @@ export class Helpers {
38
42
  }
39
43
  }
40
44
 
41
- export class ResponseUtilsWithHelpers extends ResponseUtils {
42
- constructor(response: ResponseUtils) {
43
- super(response.nodeResponse, response.logger, response.jsonReplacer)
44
- }
45
+ export type RequestWithHelpers = Request & RequestHelpers
46
+
47
+ export function getRequestWithHelpers(request: Request, pathParameters: PathParameters) {
48
+ const helpers = new RequestHelpers(request, pathParameters)
49
+ return new Proxy(request, {
50
+ get(target, prop): unknown {
51
+ if (prop in helpers) return helpers[prop as keyof RequestHelpers]
52
+ return request[prop as keyof Request]
53
+ },
54
+ }) as RequestWithHelpers
55
+ }
45
56
 
46
- success(data?: unknown) {
47
- this.json(success(data))
57
+ // -----------------------------
58
+
59
+ class ResponseUtilsHelpers {
60
+ constructor(readonly response: ResponseUtils) {}
61
+
62
+ // 输出成功结果,并结束响应处理
63
+ success(data?: unknown): never {
64
+ this.maySuccess(success(data))
48
65
  }
49
66
 
50
- failed(message: string, code?: string | number): void
51
- failed<T>(message: string, code: string | number | undefined, data: T): void
67
+ // 输出失败结果,并结束响应处理
68
+ failed(message: string, code?: string | number): never
69
+ failed<T>(message: string, code: string | number | undefined, data: T): never
52
70
  failed<T>(message: string, code?: string | number, data?: T) {
53
- this.json(failed(message, code, data))
71
+ this.maySuccess(failed(message, code, data))
54
72
  }
73
+
74
+ // 输出 MaySuccess 对象,并结束响应处理
75
+ maySuccess(result: MaySuccess<unknown, unknown>): never {
76
+ this.response.json(result)
77
+ throw new ResponseFinish()
78
+ }
79
+ }
80
+
81
+ // 通过捕获此对象实现提前结束响应处理
82
+ // eslint-disable-next-line @typescript-eslint/no-extraneous-class
83
+ export class ResponseFinish {}
84
+
85
+ export type ResponseUtilsWithHelpers = ResponseUtils & ResponseUtilsHelpers
86
+
87
+ export function getResponseUtilsWithHelpers(response: ResponseUtils) {
88
+ const helpers = new ResponseUtilsHelpers(response)
89
+ return new Proxy(response, {
90
+ get(target, prop) {
91
+ if (prop in helpers) return helpers[prop as keyof ResponseUtilsHelpers]
92
+ return response[prop as keyof ResponseUtils]
93
+ },
94
+ }) as ResponseUtilsWithHelpers
55
95
  }
@@ -2,17 +2,19 @@ import { type OptionalFields, joinPath } from '@anjianshi/utils'
2
2
  import { getPreflightRequestMethod, handleCORS, type CORSRule } from '@/http/cors.js'
3
3
  import { HTTPError, type Request, type ResponseUtils } from '@/http/index.js'
4
4
  import { Swagger, type Method } from '@/swagger/index.js'
5
- import { Helpers, ResponseUtilsWithHelpers } from './helpers.js'
5
+ import {
6
+ getRequestWithHelpers,
7
+ getResponseUtilsWithHelpers,
8
+ type RequestWithHelpers,
9
+ type ResponseUtilsWithHelpers,
10
+ ResponseFinish,
11
+ } from './helpers.js'
6
12
  import { matchPath, type PathParameters } from './match-path.js'
7
13
 
8
- type HelpersInst = InstanceType<typeof Helpers>
9
14
  export interface BasicContext {
10
- request: Request
11
- response: ResponseUtilsWithHelpers
12
15
  pathParameters: PathParameters
13
- validatePathParameters: HelpersInst['validatePathParameters']
14
- validateQuery: HelpersInst['validateQuery']
15
- validateBody: HelpersInst['validateBody']
16
+ request: RequestWithHelpers
17
+ response: ResponseUtilsWithHelpers
16
18
  }
17
19
 
18
20
  /** 使用者可自行补充 Context 定义 */
@@ -28,6 +30,9 @@ export interface Route {
28
30
  cors?: CORSRule
29
31
  }
30
32
 
33
+ /** handler 里抛出此异常可提前结束请求处理 */
34
+ export { ResponseFinish }
35
+
31
36
  export class Router {
32
37
  /**
33
38
  * ----------------------
@@ -107,9 +112,7 @@ export class Router {
107
112
  * 请求处理
108
113
  * ----------------------
109
114
  */
110
- readonly handle = async (request: Request, originResponse: ResponseUtils) => {
111
- const response = new ResponseUtilsWithHelpers(originResponse)
112
-
115
+ readonly handle = async (request: Request, response: ResponseUtils) => {
113
116
  const pathMatchedRoutes = matchPath(
114
117
  this.routes.map(route => route.path),
115
118
  request.path,
@@ -135,18 +138,20 @@ export class Router {
135
138
  handleCORS(request, response, typeof corsRule === 'function' ? corsRule(request) : corsRule)
136
139
  }
137
140
 
141
+ const requestWithHelpers = getRequestWithHelpers(request, matched.parameters)
142
+ const responseWithHelpers = getResponseUtilsWithHelpers(response)
143
+
138
144
  const basicContext = {} as BasicContext
139
- const helpers = new Helpers(basicContext)
140
145
  Object.assign(basicContext, {
141
- request,
142
- response,
143
- pathParameters: matched.parameters,
144
- validatePathParameters: helpers.validatePathParameters.bind(helpers),
145
- validateQuery: helpers.validateQuery.bind(helpers),
146
- validateBody: helpers.validateBody.bind(helpers),
146
+ request: requestWithHelpers,
147
+ response: responseWithHelpers,
147
148
  })
148
149
 
149
- const result = await (this.executor(basicContext, matched.route) as Promise<unknown>)
150
- if (result !== undefined) throw new Error('route handler 不应该有返回值')
150
+ try {
151
+ const result = await (this.executor(basicContext, matched.route) as Promise<unknown>)
152
+ if (result !== undefined) throw new Error('route handler 不应该有返回值')
153
+ } catch (error) {
154
+ if (!(error instanceof ResponseFinish)) throw error
155
+ }
151
156
  }
152
157
  }