starlight-server 0.0.1

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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/demo/index.d.ts +1 -0
  4. package/dist/demo/index.js +27 -0
  5. package/dist/http/body/form-data.d.ts +35 -0
  6. package/dist/http/body/form-data.js +141 -0
  7. package/dist/http/body/index.d.ts +23 -0
  8. package/dist/http/body/index.js +47 -0
  9. package/dist/http/body/receive.d.ts +7 -0
  10. package/dist/http/body/receive.js +39 -0
  11. package/dist/http/http-status.d.ts +9 -0
  12. package/dist/http/http-status.js +64 -0
  13. package/dist/http/index.d.ts +9 -0
  14. package/dist/http/index.js +9 -0
  15. package/dist/http/mime-types.d.ts +14 -0
  16. package/dist/http/mime-types.js +764 -0
  17. package/dist/http/request.d.ts +25 -0
  18. package/dist/http/request.js +40 -0
  19. package/dist/http/response.d.ts +32 -0
  20. package/dist/http/response.js +66 -0
  21. package/dist/http/server.d.ts +31 -0
  22. package/dist/http/server.js +52 -0
  23. package/dist/http/types.d.ts +26 -0
  24. package/dist/http/types.js +12 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.js +4 -0
  27. package/dist/logging.d.ts +24 -0
  28. package/dist/logging.js +30 -0
  29. package/dist/router/cors.d.ts +24 -0
  30. package/dist/router/cors.js +35 -0
  31. package/dist/router/index.d.ts +38 -0
  32. package/dist/router/index.js +36 -0
  33. package/dist/router/match.d.ts +23 -0
  34. package/dist/router/match.js +172 -0
  35. package/dist/router/parameters.d.ts +51 -0
  36. package/dist/router/parameters.js +118 -0
  37. package/dist/router/router.d.ts +127 -0
  38. package/dist/router/router.js +97 -0
  39. package/dist/swagger/index.d.ts +1 -0
  40. package/dist/swagger/index.js +168 -0
  41. package/dist/swagger/openapi-spec.d.ts +261 -0
  42. package/dist/swagger/openapi-spec.js +5 -0
  43. package/dist/validators/array.d.ts +9 -0
  44. package/dist/validators/array.js +28 -0
  45. package/dist/validators/boolean.d.ts +4 -0
  46. package/dist/validators/boolean.js +28 -0
  47. package/dist/validators/common.d.ts +23 -0
  48. package/dist/validators/common.js +25 -0
  49. package/dist/validators/index.d.ts +20 -0
  50. package/dist/validators/index.js +38 -0
  51. package/dist/validators/number.d.ts +10 -0
  52. package/dist/validators/number.js +30 -0
  53. package/dist/validators/object.d.ts +13 -0
  54. package/dist/validators/object.js +36 -0
  55. package/dist/validators/string.d.ts +11 -0
  56. package/dist/validators/string.js +29 -0
  57. package/package.json +54 -0
  58. package/src/demo/index.ts +33 -0
  59. package/src/http/body/form-data.ts +164 -0
  60. package/src/http/body/index.ts +59 -0
  61. package/src/http/body/receive.ts +49 -0
  62. package/src/http/http-status.ts +65 -0
  63. package/src/http/index.ts +9 -0
  64. package/src/http/mime-types.ts +765 -0
  65. package/src/http/request.ts +44 -0
  66. package/src/http/response.ts +73 -0
  67. package/src/http/server.ts +67 -0
  68. package/src/http/types.ts +31 -0
  69. package/src/index.ts +4 -0
  70. package/src/logging.ts +57 -0
  71. package/src/router/cors.ts +54 -0
  72. package/src/router/index.ts +38 -0
  73. package/src/router/match.ts +194 -0
  74. package/src/router/parameters.ts +172 -0
  75. package/src/router/router.ts +233 -0
  76. package/src/swagger/index.ts +184 -0
  77. package/src/swagger/openapi-spec.ts +312 -0
  78. package/src/validators/array.ts +33 -0
  79. package/src/validators/boolean.ts +23 -0
  80. package/src/validators/common.ts +46 -0
  81. package/src/validators/index.ts +50 -0
  82. package/src/validators/number.ts +36 -0
  83. package/src/validators/object.ts +41 -0
  84. package/src/validators/string.ts +38 -0
@@ -0,0 +1,25 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type http from 'node:http';
3
+ import { type Logger } from '@anjianshi/utils';
4
+ import { RequestBody, type BodyOptions } from './body/index.js';
5
+ import { type NodeRequest } from './types.js';
6
+ /**
7
+ * 对 Node.js 请求内容的二次封装
8
+ * - 提供经过整理的请求信息
9
+ * - 自带 body 解析(实现了 “body 大小限制” 和 “JSON、form-data 等格式的内容解析”)
10
+ *
11
+ * Secondary encapsulation of the Node.js Request object
12
+ * - Organized request information
13
+ * - Comes with body parsing (implements 'body size limit' and 'content parsing for formats such as JSON and form-data').
14
+ */
15
+ export declare class Request {
16
+ readonly nodeRequest: NodeRequest;
17
+ protected readonly logger: Logger;
18
+ readonly method: string;
19
+ readonly host: string;
20
+ readonly path: string;
21
+ readonly query: URLSearchParams;
22
+ readonly headers: http.IncomingHttpHeaders;
23
+ readonly body: RequestBody;
24
+ constructor(nodeRequest: NodeRequest, logger: Logger, bodyOptions: BodyOptions);
25
+ }
@@ -0,0 +1,40 @@
1
+ import { RequestBody } from './body/index.js';
2
+ import { HTTPError } from './types.js';
3
+ /**
4
+ * 对 Node.js 请求内容的二次封装
5
+ * - 提供经过整理的请求信息
6
+ * - 自带 body 解析(实现了 “body 大小限制” 和 “JSON、form-data 等格式的内容解析”)
7
+ *
8
+ * Secondary encapsulation of the Node.js Request object
9
+ * - Organized request information
10
+ * - Comes with body parsing (implements 'body size limit' and 'content parsing for formats such as JSON and form-data').
11
+ */
12
+ export class Request {
13
+ nodeRequest;
14
+ logger;
15
+ method;
16
+ host;
17
+ path;
18
+ query;
19
+ headers;
20
+ body;
21
+ constructor(nodeRequest, logger, bodyOptions) {
22
+ this.nodeRequest = nodeRequest;
23
+ this.logger = logger;
24
+ if (nodeRequest.method === undefined)
25
+ throw new HTTPError(405);
26
+ this.method = nodeRequest.method;
27
+ try {
28
+ const url = new URL(nodeRequest.url ?? '', `http://${nodeRequest.headers.host ?? ''}`);
29
+ this.host = url.host;
30
+ this.path = url.pathname;
31
+ this.query = url.searchParams;
32
+ }
33
+ catch (e) {
34
+ this.logger.warn('parse url failed', e);
35
+ throw new HTTPError(400, 'url invalid');
36
+ }
37
+ this.headers = nodeRequest.headers;
38
+ this.body = new RequestBody(nodeRequest, bodyOptions);
39
+ }
40
+ }
@@ -0,0 +1,32 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { type Logger } from '@anjianshi/utils';
3
+ import { type NodeResponse } from './types.js';
4
+ /**
5
+ * 封装响应内容输出函数
6
+ * Encapsulate functions for outputting response content.
7
+ */
8
+ export declare class ResponseUtils {
9
+ readonly nodeResponse: NodeResponse;
10
+ readonly logger: Logger;
11
+ constructor(nodeResponse: NodeResponse, logger: Logger);
12
+ header(name: string, value: string): void;
13
+ headers(...items: [string, string][]): void;
14
+ /**
15
+ * Output CORS Headers
16
+ */
17
+ cors(allowOrigin?: string, allowHeaders?: string): void;
18
+ text(content: string | Buffer): void;
19
+ /**
20
+ * Output JSON
21
+ */
22
+ json(data: unknown): void;
23
+ /**
24
+ * 原样输出文件内容,并带上文件类型对应的 MIME Type
25
+ * Output the file content and include the MIME Type corresponding to the file type.
26
+ */
27
+ file(content: Buffer | string, path: string): void;
28
+ /**
29
+ * Output HTTP Error
30
+ */
31
+ error(error: unknown): void;
32
+ }
@@ -0,0 +1,66 @@
1
+ import { path2MIMEType } from './mime-types.js';
2
+ import { HTTPError } from './types.js';
3
+ /**
4
+ * 封装响应内容输出函数
5
+ * Encapsulate functions for outputting response content.
6
+ */
7
+ export class ResponseUtils {
8
+ nodeResponse;
9
+ logger;
10
+ constructor(nodeResponse, logger) {
11
+ this.nodeResponse = nodeResponse;
12
+ this.logger = logger;
13
+ }
14
+ header(name, value) {
15
+ this.nodeResponse.setHeader(name, value);
16
+ }
17
+ headers(...items) {
18
+ items.forEach(([name, value]) => this.header(name, value));
19
+ }
20
+ /**
21
+ * Output CORS Headers
22
+ */
23
+ cors(allowOrigin = '*', allowHeaders = '*') {
24
+ this.headers(['Access-Control-Allow-Origin', allowOrigin], ['Access-Control-Allow-Headers', allowHeaders]);
25
+ }
26
+ text(content) {
27
+ this.nodeResponse.end(content);
28
+ }
29
+ /**
30
+ * Output JSON
31
+ */
32
+ json(data) {
33
+ try {
34
+ const json = JSON.stringify(data);
35
+ this.header('Content-Type', 'application/json; charset=UTF-8');
36
+ this.nodeResponse.end(json);
37
+ }
38
+ catch (e) {
39
+ throw new HTTPError(500, 'Invalid JSON Response');
40
+ }
41
+ }
42
+ /**
43
+ * 原样输出文件内容,并带上文件类型对应的 MIME Type
44
+ * Output the file content and include the MIME Type corresponding to the file type.
45
+ */
46
+ file(content, path) {
47
+ const mimeType = path2MIMEType(path);
48
+ if (mimeType !== null)
49
+ this.header('Content-Type', mimeType);
50
+ this.nodeResponse.end(content);
51
+ }
52
+ /**
53
+ * Output HTTP Error
54
+ */
55
+ error(error) {
56
+ if (error instanceof HTTPError) {
57
+ this.nodeResponse.statusCode = error.status; // eslint-disable-line require-atomic-updates
58
+ this.nodeResponse.end(error.message);
59
+ }
60
+ else {
61
+ this.logger.error(error);
62
+ this.nodeResponse.statusCode = 500; // eslint-disable-line require-atomic-updates
63
+ this.nodeResponse.end(new HTTPError(500).message);
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,31 @@
1
+ import { type Logger } from '@anjianshi/utils';
2
+ import { type BodyOptions } from './body/index.js';
3
+ import { Request } from './request.js';
4
+ import { ResponseUtils } from './response.js';
5
+ export type RequestHandler = (request: Request, response: ResponseUtils) => unknown;
6
+ /**
7
+ * 二次封装 Node.js HTTP Server,实现了更完善的 request 和 response 处理
8
+ * Secondary encapsulation of the Node.js HTTP Server, with more complete request and response handling.
9
+ *
10
+ * 功能点:
11
+ * - 经过整理的 request 信息,且自带 body 解析
12
+ * - 快速输出指定格式的响应内容
13
+ * - 自带异常处理
14
+ * - 任意处抛出 HTTPError,可以指定 HTTP Status 结束请求
15
+ * - 抛出其他 Error,则以 Status 500 结束请求
16
+ *
17
+ * Features:
18
+ * - Organized request information, and built-in body parsing
19
+ * - Quickly output response content in a specified format
20
+ * - Built-in exception handling
21
+ * - Throw HTTPError anywhere, and end the request with the specified HTTP Status
22
+ * - If another Error is thrown, end the request with Status 500
23
+ *
24
+ * Usage:
25
+ * startHTTPServer(handler, options)
26
+ */
27
+ export declare function startHTTPServer(options: BodyOptions & {
28
+ handler?: RequestHandler;
29
+ logger: Logger;
30
+ port: number;
31
+ }): void;
@@ -0,0 +1,52 @@
1
+ import http from 'node:http';
2
+ import { LogLevel } from '@anjianshi/utils';
3
+ import { Request } from './request.js';
4
+ import { ResponseUtils } from './response.js';
5
+ /**
6
+ * 二次封装 Node.js HTTP Server,实现了更完善的 request 和 response 处理
7
+ * Secondary encapsulation of the Node.js HTTP Server, with more complete request and response handling.
8
+ *
9
+ * 功能点:
10
+ * - 经过整理的 request 信息,且自带 body 解析
11
+ * - 快速输出指定格式的响应内容
12
+ * - 自带异常处理
13
+ * - 任意处抛出 HTTPError,可以指定 HTTP Status 结束请求
14
+ * - 抛出其他 Error,则以 Status 500 结束请求
15
+ *
16
+ * Features:
17
+ * - Organized request information, and built-in body parsing
18
+ * - Quickly output response content in a specified format
19
+ * - Built-in exception handling
20
+ * - Throw HTTPError anywhere, and end the request with the specified HTTP Status
21
+ * - If another Error is thrown, end the request with Status 500
22
+ *
23
+ * Usage:
24
+ * startHTTPServer(handler, options)
25
+ */
26
+ export function startHTTPServer(options) {
27
+ const { handler = placeholderHandler, logger, port, ...bodyOptions } = options;
28
+ async function handleRequest(nodeRequest, nodeResponse) {
29
+ const logStart = Date.now();
30
+ const logMethod = nodeRequest.method ?? 'UNKNOWN';
31
+ const logUrl = nodeRequest.url ?? '';
32
+ logger.info('<--', logMethod, logUrl);
33
+ const response = new ResponseUtils(nodeResponse, logger);
34
+ try {
35
+ const request = new Request(nodeRequest, logger, bodyOptions);
36
+ await handler(request, response);
37
+ }
38
+ catch (error) {
39
+ response.error(error);
40
+ }
41
+ const logUsage = Date.now() - logStart;
42
+ const logStatus = nodeResponse.statusCode;
43
+ const logLevel = logStatus >= 400 ? LogLevel.Warning : LogLevel.Info;
44
+ logger.log(logLevel, ['-->', logMethod, logUrl, logStatus, `${logUsage}ms`]);
45
+ }
46
+ const server = http.createServer(handleRequest);
47
+ logger.info('Listening', port);
48
+ server.listen(port);
49
+ }
50
+ function placeholderHandler(request, response) {
51
+ response.json({ message: 'Hello World, from starlight.' });
52
+ }
@@ -0,0 +1,26 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /**
3
+ * 重新定义 HTTP 请求相关类型
4
+ * Redefine HTTP request-related types.
5
+ */
6
+ import type http from 'node:http';
7
+ /**
8
+ * Node.js 原生的请求对象
9
+ * Node.js native request object
10
+ */
11
+ export type NodeRequest = http.IncomingMessage;
12
+ /**
13
+ * Node.js 原生的响应对象
14
+ * Node.js native response object
15
+ */
16
+ export type NodeResponse = http.ServerResponse & {
17
+ req: http.IncomingMessage;
18
+ };
19
+ /**
20
+ * 带有 HTTP Status 的错误对象
21
+ * Error object with HTTP Status
22
+ */
23
+ export declare class HTTPError extends Error {
24
+ readonly status: number;
25
+ constructor(status: number, message?: string);
26
+ }
@@ -0,0 +1,12 @@
1
+ import { HTTPStatusMap } from './http-status.js';
2
+ /**
3
+ * 带有 HTTP Status 的错误对象
4
+ * Error object with HTTP Status
5
+ */
6
+ export class HTTPError extends Error {
7
+ status;
8
+ constructor(status, message) {
9
+ super(message ?? HTTPStatusMap.get(status) ?? status.toString());
10
+ this.status = status;
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ export * from './http/index.js';
2
+ export * from './router/index.js';
3
+ export * from './logging.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './http/index.js';
2
+ export * from './router/index.js';
3
+ export * from './logging.js';
4
+ // export * from './swagger/index.js'
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 实现日志记录
3
+ */
4
+ import { Logger } from '@anjianshi/utils';
5
+ import { type FileHandlerOptions } from '@anjianshi/utils/env-node/logging.js';
6
+ export type { FileHandlerOptions };
7
+ export interface LoggingOptions {
8
+ /**
9
+ * 指定日志等级(debug info warn err)
10
+ * Specify log level.
11
+ */
12
+ level?: string;
13
+ /**
14
+ * 文件日志参数
15
+ * File log options
16
+ */
17
+ file?: Partial<FileHandlerOptions>;
18
+ /**
19
+ * 是否把 debug 库的日志引入进来;可通过字符串指定匹配规则
20
+ * Whether to import logs from 'debug' library; can specify matching rules through strings
21
+ */
22
+ debugLib?: boolean | string;
23
+ }
24
+ export declare function getLogger(options?: LoggingOptions): Logger;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 实现日志记录
3
+ */
4
+ import { truthy, Logger, LogLevel, adaptDebugLib } from '@anjianshi/utils';
5
+ import { ConsoleHandler, FileHandler, } from '@anjianshi/utils/env-node/logging.js';
6
+ import debug from 'debug';
7
+ const levelMap = {
8
+ debug: LogLevel.Debug,
9
+ info: LogLevel.Info,
10
+ warn: LogLevel.Warning,
11
+ warning: LogLevel.Warning,
12
+ err: LogLevel.Error,
13
+ error: LogLevel.Error,
14
+ };
15
+ export function getLogger(options = {}) {
16
+ const logger = new Logger();
17
+ logger.addHandler(new ConsoleHandler());
18
+ if (options.level !== undefined) {
19
+ const level = levelMap[options.level.toLowerCase()];
20
+ if (level !== undefined)
21
+ logger.setLevel(level);
22
+ }
23
+ if (options.file) {
24
+ logger.addHandler(new FileHandler(options.file));
25
+ }
26
+ if (truthy(options.debugLib)) {
27
+ void adaptDebugLib(debug, options.debugLib === true ? '*' : options.debugLib, logger);
28
+ }
29
+ return logger;
30
+ }
@@ -0,0 +1,24 @@
1
+ import { type Request, type ResponseUtils } from '../http/index.js';
2
+ /**
3
+ * CORS 配置
4
+ *
5
+ * - false: 不处理跨域
6
+ * - true: 支持且不设限(等同于 { allowOrigin: '*', allowHeaders: '*', exposeHeaders: '*' })
7
+ * - { allowOrigin, allowHeaders, exposeHeaders }: 手动配置
8
+ *
9
+ * allowHeaders 仅对 Preflight 请求有效
10
+ * exposeHeaders 仅对非 Preflight 请求有效
11
+ */
12
+ export type CORSOptions = boolean | {
13
+ allowOrigin?: string;
14
+ allowHeaders?: string;
15
+ exposeHeaders?: string;
16
+ };
17
+ /**
18
+ * 若当前请求是 CORS Preflight 请求,返回实际请求的 method,否则返回 null
19
+ */
20
+ export declare function getMethodFromCORSPreflight(request: Request): string | null;
21
+ /**
22
+ * 输出 CORS 相关 headers
23
+ */
24
+ export declare function outputCORSHeaders(response: ResponseUtils, cors: CORSOptions, isPreflight: boolean): void;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 若当前请求是 CORS Preflight 请求,返回实际请求的 method,否则返回 null
3
+ */
4
+ export function getMethodFromCORSPreflight(request) {
5
+ if (request.method === 'OPTIONS' &&
6
+ request.headers['Access-Control-Request-Methods'] !== undefined) {
7
+ const rawMethod = request.headers['Access-Control-Request-Methods'];
8
+ return Array.isArray(rawMethod) ? rawMethod[0] : rawMethod;
9
+ }
10
+ return null;
11
+ }
12
+ /**
13
+ * 输出 CORS 相关 headers
14
+ */
15
+ export function outputCORSHeaders(response, cors, isPreflight) {
16
+ if (cors === true) {
17
+ response.headers(['Access-Control-Allow-Origin', '*']);
18
+ if (isPreflight)
19
+ response.headers(['Access-Control-Allow-Headers', '*']);
20
+ else
21
+ response.headers(['Access-Control-Expose-Headers', '*']);
22
+ }
23
+ else if (cors !== false) {
24
+ if (cors.allowOrigin !== undefined)
25
+ response.header('Access-Control-Allow-Origin', cors.allowOrigin);
26
+ if (isPreflight && cors.allowHeaders !== undefined)
27
+ response.header('Access-Control-Allow-Headers', cors.allowHeaders);
28
+ if (!isPreflight && cors.exposeHeaders !== undefined)
29
+ response.header('Access-Control-Expose-Headers', cors.exposeHeaders);
30
+ }
31
+ else {
32
+ // 若指定为“不处理跨域”,则不输出 headers,浏览器会默认服务端不允许跨域请求
33
+ // (后续 route handler 仍有机会自行设置 CORS headers)
34
+ }
35
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 实现路由注册
3
+ * Implement route registration
4
+ *
5
+ * Usage:
6
+ * const router = new DefaultRouter()
7
+ * router.register('GET', '/hello/:name', async ({ request, response }: { request: Request, response: ResponseUtils }, params: { name: string }) => {
8
+ * response.json({ hello: params.name })
9
+ * })
10
+ * startHTTPServer({
11
+ * handler: router.handle
12
+ * ...
13
+ * })
14
+ *
15
+ * Usage With Custom Context:
16
+ * interface MyContext extends BaseContext {
17
+ * foo: () => void
18
+ * }
19
+ * class MyRouter extends BaseRouter<MyContext> {
20
+ * executeWithContext(baseContext: BaseContext, route: Route<MyContext>, params: PathParamaters) {
21
+ * function foo() { console.log('bar') }
22
+ * const context = { ...baseContext, foo }
23
+ * route.handle(context)
24
+ * }
25
+ * }
26
+ * const router = new MyRouter()
27
+ * router.register('GET', '/foobar', async ({ request, response, foo }) => {
28
+ * foo()
29
+ * response.json({})
30
+ * })
31
+ * startHTTPServer({
32
+ * handler: router.handle
33
+ * ...
34
+ * })
35
+ */
36
+ export * from './router.js';
37
+ export { type PathParameters } from './match.js';
38
+ export type { BasicDataType, Parameter, ParameterDataType } from './parameters.js';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 实现路由注册
3
+ * Implement route registration
4
+ *
5
+ * Usage:
6
+ * const router = new DefaultRouter()
7
+ * router.register('GET', '/hello/:name', async ({ request, response }: { request: Request, response: ResponseUtils }, params: { name: string }) => {
8
+ * response.json({ hello: params.name })
9
+ * })
10
+ * startHTTPServer({
11
+ * handler: router.handle
12
+ * ...
13
+ * })
14
+ *
15
+ * Usage With Custom Context:
16
+ * interface MyContext extends BaseContext {
17
+ * foo: () => void
18
+ * }
19
+ * class MyRouter extends BaseRouter<MyContext> {
20
+ * executeWithContext(baseContext: BaseContext, route: Route<MyContext>, params: PathParamaters) {
21
+ * function foo() { console.log('bar') }
22
+ * const context = { ...baseContext, foo }
23
+ * route.handle(context)
24
+ * }
25
+ * }
26
+ * const router = new MyRouter()
27
+ * router.register('GET', '/foobar', async ({ request, response, foo }) => {
28
+ * foo()
29
+ * response.json({})
30
+ * })
31
+ * startHTTPServer({
32
+ * handler: router.handle
33
+ * ...
34
+ * })
35
+ */
36
+ export * from './router.js';
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 标准化路径
3
+ * 标准化后,相同的两个路径一定也是相同的字符串(例如 /Abc/Def 和 abc/def/ 都会变成 abc/def)。
4
+ *
5
+ * - 移除首尾和重复的 '/',完成后有 path 有这几种可能的格式: ''、'abc'、'abc/def'
6
+ * - 统一改为小写
7
+ */
8
+ export declare function normalizePath(path: string): string;
9
+ /**
10
+ * 返回匹配的路由及匹配上的路径参数
11
+ */
12
+ export declare function matchRoutes<R extends BasicRoute>(requestPath: string, routes: R[]): RouteMatch<R>[];
13
+ export type BasicRoute = {
14
+ path: string;
15
+ };
16
+ export interface RouteMatch<R> {
17
+ route: R;
18
+ pathParameters: PathParameters;
19
+ }
20
+ export type PathParameters = {
21
+ [name: string]: string | undefined;
22
+ '*'?: string;
23
+ };