@hile/http 1.0.6

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.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # @hile/http
2
+
3
+ 基于 Koa + find-my-way 的 HTTP 服务框架,支持中间件、路由注册和文件系统自动路由加载。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @hile/http
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ```typescript
14
+ import { Http } from '@hile/http'
15
+
16
+ const http = new Http({ port: 3000 })
17
+
18
+ http.get('/hello', async (ctx) => {
19
+ ctx.body = 'Hello, World!'
20
+ })
21
+
22
+ const close = await http.listen(() => {
23
+ console.log('Server running on http://localhost:3000')
24
+ })
25
+ ```
26
+
27
+ ## 核心概念
28
+
29
+ ### 创建服务
30
+
31
+ ```typescript
32
+ const http = new Http({
33
+ port: 3000,
34
+ keys: ['secret1', 'secret2'], // 可选,不传则自动生成
35
+ })
36
+ ```
37
+
38
+ ### 中间件
39
+
40
+ 通过 `use()` 注册全局中间件,支持链式调用。中间件必须在 `listen()` 之前注册。
41
+
42
+ ```typescript
43
+ http
44
+ .use(async (ctx, next) => {
45
+ const start = Date.now()
46
+ await next()
47
+ ctx.set('X-Response-Time', `${Date.now() - start}ms`)
48
+ })
49
+ .use(async (ctx, next) => {
50
+ console.log(`${ctx.method} ${ctx.url}`)
51
+ await next()
52
+ })
53
+ ```
54
+
55
+ ### 路由
56
+
57
+ 使用快捷方法注册路由,支持路径参数和多个中间件:
58
+
59
+ ```typescript
60
+ http.get('/users', async (ctx) => {
61
+ ctx.body = [{ id: 1, name: 'Alice' }]
62
+ })
63
+
64
+ http.get('/users/:id', async (ctx) => {
65
+ ctx.body = { id: ctx.params.id }
66
+ })
67
+
68
+ http.post('/users', authMiddleware, async (ctx) => {
69
+ ctx.body = { created: true }
70
+ })
71
+ ```
72
+
73
+ 所有路由方法返回注销函数,调用后路由不再匹配:
74
+
75
+ ```typescript
76
+ const off = http.get('/temporary', async (ctx) => {
77
+ ctx.body = 'gone soon'
78
+ })
79
+
80
+ off() // 移除该路由
81
+ ```
82
+
83
+ 支持的快捷方法:`get`、`post`、`put`、`delete`、`trace`,或使用 `route()` 指定任意 HTTP 方法:
84
+
85
+ ```typescript
86
+ http.route('PATCH', '/users/:id', async (ctx) => {
87
+ ctx.body = { patched: true }
88
+ })
89
+ ```
90
+
91
+ ### 启动与关闭
92
+
93
+ ```typescript
94
+ const close = await http.listen((server) => {
95
+ console.log(`Listening on port ${http.port}`)
96
+ })
97
+
98
+ // 关闭服务
99
+ close()
100
+ ```
101
+
102
+ ## 文件系统路由
103
+
104
+ ### 定义控制器
105
+
106
+ 使用 `defineController` 定义路由控制器。返回值自动赋给 `ctx.body`。
107
+
108
+ ```typescript
109
+ // controllers/users/index.controller.ts
110
+ import { defineController } from '@hile/http'
111
+
112
+ export default defineController('GET', async (ctx) => {
113
+ return { users: [] }
114
+ })
115
+ ```
116
+
117
+ 带中间件:
118
+
119
+ ```typescript
120
+ import { defineController } from '@hile/http'
121
+
122
+ const auth = async (ctx, next) => {
123
+ if (!ctx.headers.authorization) ctx.throw(401)
124
+ await next()
125
+ }
126
+
127
+ export default defineController('POST', [auth], async (ctx) => {
128
+ return { created: true }
129
+ })
130
+ ```
131
+
132
+ 同一文件导出多个控制器:
133
+
134
+ ```typescript
135
+ // controllers/users/[id].controller.ts
136
+ import { defineController } from '@hile/http'
137
+
138
+ const getUser = defineController('GET', async (ctx) => {
139
+ return { id: ctx.params.id }
140
+ })
141
+
142
+ const updateUser = defineController('PUT', [auth], async (ctx) => {
143
+ return { updated: true }
144
+ })
145
+
146
+ export default [getUser, updateUser]
147
+ ```
148
+
149
+ ### 加载路由
150
+
151
+ 使用 `load()` 自动扫描目录并注册路由:
152
+
153
+ ```typescript
154
+ await http.load('./src/controllers', {
155
+ suffix: 'controller', // 匹配 *.controller.{ts,js}
156
+ prefix: '/api', // 路由前缀
157
+ defaultSuffix: '/index', // index 文件映射到父路径
158
+ })
159
+ ```
160
+
161
+ ### 路径映射规则
162
+
163
+ | 文件路径 | 路由路径 |
164
+ |---------|---------|
165
+ | `index.controller.ts` | `/api` |
166
+ | `users/index.controller.ts` | `/api/users` |
167
+ | `users/list.controller.ts` | `/api/users/list` |
168
+ | `users/[id].controller.ts` | `/api/users/:id` |
169
+ | `[category]/[id].controller.ts` | `/api/:category/:id` |
170
+
171
+ 路径中的 `[param]` 会自动转换为 `:param` 路由参数。
172
+
173
+ ## 与 hile 集成
174
+
175
+ 配合 `hile` 服务容器管理 HTTP 服务的生命周期:
176
+
177
+ ```typescript
178
+ import { defineService } from 'hile'
179
+ import { Http } from '@hile/http'
180
+
181
+ export const httpService = defineService(async (shutdown) => {
182
+ const http = new Http({ port: 3000 })
183
+
184
+ await http.load('./src/controllers', {
185
+ suffix: 'controller',
186
+ prefix: '/api',
187
+ })
188
+
189
+ const close = await http.listen()
190
+ shutdown(close)
191
+
192
+ return http
193
+ })
194
+ ```
195
+
196
+ ## API
197
+
198
+ ### Http
199
+
200
+ | 方法 | 说明 |
201
+ |------|------|
202
+ | `new Http(props)` | 创建实例,`port` 必填 |
203
+ | `port` | 获取端口号 |
204
+ | `use(middleware)` | 注册全局中间件,返回 `this` |
205
+ | `listen(onListen?)` | 启动服务,返回关闭函数 |
206
+ | `get(url, ...mw)` | 注册 GET 路由,返回注销函数 |
207
+ | `post(url, ...mw)` | 注册 POST 路由,返回注销函数 |
208
+ | `put(url, ...mw)` | 注册 PUT 路由,返回注销函数 |
209
+ | `delete(url, ...mw)` | 注册 DELETE 路由,返回注销函数 |
210
+ | `trace(url, ...mw)` | 注册 TRACE 路由,返回注销函数 |
211
+ | `route(method, url, ...mw)` | 注册任意方法路由,返回注销函数 |
212
+ | `load(dir, options?)` | 加载文件系统路由,返回注销函数 |
213
+
214
+ ### defineController
215
+
216
+ | 调用形式 | 说明 |
217
+ |---------|------|
218
+ | `defineController(method, fn)` | 无中间件 |
219
+ | `defineController(method, [mw...], fn)` | 带中间件 |
220
+
221
+ - 控制器函数接收 `ctx`,返回值非 `undefined` 时自动设为 `ctx.body`
222
+ - 控制器文件必须 `export default` 导出
223
+
224
+ ### load 选项
225
+
226
+ | 选项 | 默认值 | 说明 |
227
+ |------|-------|------|
228
+ | `suffix` | `'controller'` | 文件后缀标记 |
229
+ | `prefix` | — | 路由前缀 |
230
+ | `defaultSuffix` | `'/index'` | 映射到父路径的文件名 |
231
+
232
+ ## License
233
+
234
+ MIT
@@ -0,0 +1,17 @@
1
+ import { HTTPMethod } from 'find-my-way';
2
+ import { Context, Middleware } from 'koa';
3
+ export type ControllerFunction<R> = (ctx: Context) => R | Promise<R>;
4
+ export interface ControllerRegisterProps<R> {
5
+ id: number;
6
+ method: HTTPMethod;
7
+ middlewares: Middleware[];
8
+ data: Record<string, any>;
9
+ }
10
+ /**
11
+ * 定义路由控制器
12
+ * @param method 请求方法
13
+ * @param middlewares 中间件,可以是中间件数组或控制器函数
14
+ * @param fn 控制器函数
15
+ * @returns 路由控制器注册信息
16
+ */
17
+ export declare function defineController<R>(method: HTTPMethod, middlewares: Middleware[] | ControllerFunction<R>, fn?: ControllerFunction<R>): ControllerRegisterProps<R>;
@@ -0,0 +1,37 @@
1
+ let _id = 1;
2
+ /**
3
+ * 定义路由控制器
4
+ * @param method 请求方法
5
+ * @param middlewares 中间件,可以是中间件数组或控制器函数
6
+ * @param fn 控制器函数
7
+ * @returns 路由控制器注册信息
8
+ */
9
+ export function defineController(method, middlewares, fn) {
10
+ let _middlewares = [];
11
+ let _fn = undefined;
12
+ if (typeof middlewares === 'function' && !fn) {
13
+ _fn = middlewares;
14
+ _middlewares = [];
15
+ }
16
+ else {
17
+ _middlewares = middlewares;
18
+ _fn = fn;
19
+ }
20
+ if (!Array.isArray(_middlewares))
21
+ throw new Error('Middlewares must be an array');
22
+ if (!_fn)
23
+ throw new Error('Controller function is required');
24
+ const id = _id++;
25
+ _middlewares.push(async (ctx) => {
26
+ const result = await _fn(ctx);
27
+ if (result !== undefined) {
28
+ ctx.body = result;
29
+ }
30
+ });
31
+ return {
32
+ id,
33
+ method,
34
+ middlewares: _middlewares,
35
+ data: {},
36
+ };
37
+ }
@@ -0,0 +1,11 @@
1
+ import { Config, HTTPVersion, HTTPMethod } from 'find-my-way';
2
+ import { Middleware } from 'koa';
3
+ export { HTTPVersion, };
4
+ export interface Instance {
5
+ on(method: HTTPMethod | HTTPMethod[], path: string, ...middlewares: Middleware[]): Instance;
6
+ off(method: HTTPMethod | HTTPMethod[], path: string): Instance;
7
+ prettyPrint(): string;
8
+ routes(): Middleware;
9
+ }
10
+ declare const _default: <V extends HTTPVersion = HTTPVersion.V1>(options: Config<V>) => Instance;
11
+ export default _default;
@@ -0,0 +1,47 @@
1
+ import compose from 'koa-compose';
2
+ import FindMyWay, { HTTPVersion } from 'find-my-way';
3
+ import { METHODS } from 'node:http';
4
+ export { HTTPVersion, };
5
+ export default (options) => {
6
+ const fmw = FindMyWay(options);
7
+ const r = {};
8
+ function on(method, path, ...middlewares) {
9
+ // optional store argument
10
+ let store;
11
+ if (middlewares.length > 1 && typeof middlewares[middlewares.length - 1] === 'object') {
12
+ store = middlewares.pop();
13
+ }
14
+ // @ts-ignore
15
+ fmw.on(method, path, compose(middlewares), store);
16
+ return r;
17
+ }
18
+ r.on = on;
19
+ // @ts-ignore
20
+ r.all = (path, ...middlewares) => on(METHODS, path, ...middlewares);
21
+ METHODS.forEach((m) => {
22
+ // @ts-ignore
23
+ r[m.toLowerCase()] = (path, ...middlewares) => on(m, path, ...middlewares);
24
+ });
25
+ [
26
+ 'off',
27
+ 'reset',
28
+ 'prettyPrint',
29
+ 'find',
30
+ ].forEach((m) => {
31
+ // @ts-ignore
32
+ r[m] = fmw[m].bind(fmw);
33
+ });
34
+ r.routes = () => (ctx, next) => {
35
+ // @ts-ignore
36
+ const handle = fmw.find(ctx.method, ctx.path);
37
+ if (!handle) {
38
+ // @ts-ignore
39
+ return fmw.defaultRoute && fmw.defaultRoute(ctx, next);
40
+ }
41
+ ctx.params = handle.params;
42
+ ctx.store = handle.store;
43
+ // @ts-ignore
44
+ return handle.handler(ctx, next);
45
+ };
46
+ return r;
47
+ };
package/dist/http.d.ts ADDED
@@ -0,0 +1,109 @@
1
+ import { Middleware } from 'koa';
2
+ import { Config, HTTPMethod, HTTPVersion } from 'find-my-way';
3
+ import { Server } from 'node:http';
4
+ import { LoaderFromOptions } from './loader';
5
+ export type HttpProps = {
6
+ port: number;
7
+ keys?: string[];
8
+ } & Omit<Config<HTTPVersion.V1>, 'defaultRoute'>;
9
+ /**
10
+ * Http 服务类
11
+ * @description - 提供 HTTP 服务的基本功能
12
+ * @example
13
+ * const http = new Http({
14
+ * port: 3000,
15
+ * ignoreDuplicateSlashes: true,
16
+ * ignoreTrailingSlash: true,
17
+ * maxParamLength: +Infinity,
18
+ * allowUnsafeRegex: true,
19
+ * caseSensitive: true,
20
+ * });
21
+ * http.use(async (ctx, next) => {
22
+ * await next();
23
+ * });
24
+ * http.use(async (ctx, next) => {
25
+ * await next();
26
+ * });
27
+ * http.use(async (ctx, next) => {
28
+ * await next();
29
+ * });
30
+ * await http.listen((server) => {
31
+ * console.log('Server is running on port 3000');
32
+ * });
33
+ * console.log('Server is running on port 3000');
34
+ */
35
+ export declare class Http {
36
+ private readonly props;
37
+ private readonly koa;
38
+ private readonly loader;
39
+ private readonly router;
40
+ private server?;
41
+ constructor(props: HttpProps);
42
+ /**
43
+ * 获取服务端口
44
+ * @returns - 服务端口
45
+ */
46
+ get port(): number;
47
+ /**
48
+ * 启动服务前注册中间件
49
+ * @param middleware - 中间件函数
50
+ * @returns - 当前实例
51
+ */
52
+ use(middleware: Middleware): this;
53
+ /**
54
+ * 启动服务
55
+ * @param onListen - 启动服务后回调函数,可选,回调函数返回值为关闭服务回调函数
56
+ * @returns - 关闭服务回调函数
57
+ */
58
+ listen(onListen?: (server: Server) => void | Promise<void>): Promise<() => void>;
59
+ /**
60
+ * 注册 GET 请求路由
61
+ * @param url - 请求路径
62
+ * @param middlewares - 中间件函数
63
+ * @returns - 注销路由回调函数
64
+ */
65
+ get(url: string, ...middlewares: Middleware[]): () => void;
66
+ /**
67
+ * 注册 POST 请求路由
68
+ * @param url - 请求路径
69
+ * @param middlewares - 中间件函数
70
+ * @returns - 注销路由回调函数
71
+ */
72
+ post(url: string, ...middlewares: Middleware[]): () => void;
73
+ /**
74
+ * 注册 PUT 请求路由
75
+ * @param url - 请求路径
76
+ * @param middlewares - 中间件函数
77
+ * @returns - 注销路由回调函数
78
+ */
79
+ put(url: string, ...middlewares: Middleware[]): () => void;
80
+ /**
81
+ * 注册 DELETE 请求路由
82
+ * @param url - 请求路径
83
+ * @param middlewares - 中间件函数
84
+ * @returns - 注销路由回调函数
85
+ */
86
+ delete(url: string, ...middlewares: Middleware[]): () => void;
87
+ /**
88
+ * 注册 TRACE 请求路由
89
+ * @param url - 请求路径
90
+ * @param middlewares - 中间件函数
91
+ * @returns - 注销路由回调函数
92
+ */
93
+ trace(url: string, ...middlewares: Middleware[]): () => void;
94
+ /**
95
+ * 注册路由
96
+ * @param method 请求方法
97
+ * @param url 请求路径
98
+ * @param middlewares 中间件
99
+ * @returns 注销路由回调函数
100
+ */
101
+ route(method: HTTPMethod, url: string, ...middlewares: Middleware[]): () => void;
102
+ /**
103
+ * 绑定文件夹下的所有路由
104
+ * @param directory 文件夹路径
105
+ * @param options 选项
106
+ * @returns 注销回调
107
+ */
108
+ load(directory: string, options?: LoaderFromOptions): Promise<() => void>;
109
+ }
package/dist/http.js ADDED
@@ -0,0 +1,161 @@
1
+ import Koa from 'koa';
2
+ import { randomBytes } from 'node:crypto';
3
+ import FindMyWay from './find-my-way';
4
+ import { createServer } from 'node:http';
5
+ import { Loader } from './loader';
6
+ /**
7
+ * Http 服务类
8
+ * @description - 提供 HTTP 服务的基本功能
9
+ * @example
10
+ * const http = new Http({
11
+ * port: 3000,
12
+ * ignoreDuplicateSlashes: true,
13
+ * ignoreTrailingSlash: true,
14
+ * maxParamLength: +Infinity,
15
+ * allowUnsafeRegex: true,
16
+ * caseSensitive: true,
17
+ * });
18
+ * http.use(async (ctx, next) => {
19
+ * await next();
20
+ * });
21
+ * http.use(async (ctx, next) => {
22
+ * await next();
23
+ * });
24
+ * http.use(async (ctx, next) => {
25
+ * await next();
26
+ * });
27
+ * await http.listen((server) => {
28
+ * console.log('Server is running on port 3000');
29
+ * });
30
+ * console.log('Server is running on port 3000');
31
+ */
32
+ export class Http {
33
+ props;
34
+ koa = new Koa();
35
+ loader = new Loader(this);
36
+ router;
37
+ server;
38
+ constructor(props) {
39
+ this.props = props;
40
+ if (!this.props.keys) {
41
+ this.props.keys = [randomBytes(32).toString(), randomBytes(64).toString()];
42
+ }
43
+ this.koa.keys = this.props.keys;
44
+ this.router = FindMyWay({
45
+ ignoreDuplicateSlashes: this.props.ignoreDuplicateSlashes ?? true,
46
+ ignoreTrailingSlash: this.props.ignoreTrailingSlash ?? true,
47
+ maxParamLength: this.props.maxParamLength ?? +Infinity,
48
+ allowUnsafeRegex: this.props.allowUnsafeRegex ?? true,
49
+ caseSensitive: this.props.caseSensitive ?? true,
50
+ // @ts-ignore
51
+ defaultRoute: async (_, next) => await next(),
52
+ });
53
+ }
54
+ /**
55
+ * 获取服务端口
56
+ * @returns - 服务端口
57
+ */
58
+ get port() {
59
+ return this.props.port;
60
+ }
61
+ /**
62
+ * 启动服务前注册中间件
63
+ * @param middleware - 中间件函数
64
+ * @returns - 当前实例
65
+ */
66
+ use(middleware) {
67
+ this.koa.use(middleware);
68
+ return this;
69
+ }
70
+ /**
71
+ * 启动服务
72
+ * @param onListen - 启动服务后回调函数,可选,回调函数返回值为关闭服务回调函数
73
+ * @returns - 关闭服务回调函数
74
+ */
75
+ async listen(onListen) {
76
+ this.koa.use(this.router.routes());
77
+ this.server = createServer(this.koa.callback());
78
+ if (onListen)
79
+ await onListen(this.server);
80
+ await new Promise((resolve, reject) => {
81
+ this.server.listen(this.props.port, (err) => {
82
+ if (err)
83
+ return reject(err);
84
+ resolve();
85
+ });
86
+ });
87
+ return () => {
88
+ this.server.close();
89
+ };
90
+ }
91
+ /**
92
+ * 注册 GET 请求路由
93
+ * @param url - 请求路径
94
+ * @param middlewares - 中间件函数
95
+ * @returns - 注销路由回调函数
96
+ */
97
+ get(url, ...middlewares) {
98
+ return this.route('GET', url, ...middlewares);
99
+ }
100
+ /**
101
+ * 注册 POST 请求路由
102
+ * @param url - 请求路径
103
+ * @param middlewares - 中间件函数
104
+ * @returns - 注销路由回调函数
105
+ */
106
+ post(url, ...middlewares) {
107
+ return this.route('POST', url, ...middlewares);
108
+ }
109
+ /**
110
+ * 注册 PUT 请求路由
111
+ * @param url - 请求路径
112
+ * @param middlewares - 中间件函数
113
+ * @returns - 注销路由回调函数
114
+ */
115
+ put(url, ...middlewares) {
116
+ return this.route('PUT', url, ...middlewares);
117
+ }
118
+ /**
119
+ * 注册 DELETE 请求路由
120
+ * @param url - 请求路径
121
+ * @param middlewares - 中间件函数
122
+ * @returns - 注销路由回调函数
123
+ */
124
+ delete(url, ...middlewares) {
125
+ return this.route('DELETE', url, ...middlewares);
126
+ }
127
+ /**
128
+ * 注册 TRACE 请求路由
129
+ * @param url - 请求路径
130
+ * @param middlewares - 中间件函数
131
+ * @returns - 注销路由回调函数
132
+ */
133
+ trace(url, ...middlewares) {
134
+ return this.route('TRACE', url, ...middlewares);
135
+ }
136
+ /**
137
+ * 注册路由
138
+ * @param method 请求方法
139
+ * @param url 请求路径
140
+ * @param middlewares 中间件
141
+ * @returns 注销路由回调函数
142
+ */
143
+ route(method, url, ...middlewares) {
144
+ this.router.on(method, url, ...middlewares);
145
+ return () => {
146
+ this.router.off(method, url);
147
+ };
148
+ }
149
+ /**
150
+ * 绑定文件夹下的所有路由
151
+ * @param directory 文件夹路径
152
+ * @param options 选项
153
+ * @returns 注销回调
154
+ */
155
+ load(directory, options = {
156
+ defaultSuffix: '/index',
157
+ suffix: 'controller',
158
+ }) {
159
+ return this.loader.from(directory, options);
160
+ }
161
+ }
@@ -0,0 +1,6 @@
1
+ import { Http } from './http';
2
+ export * from './http';
3
+ export * from './find-my-way';
4
+ export * from './controller';
5
+ export * from './loader';
6
+ export default Http;
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { Http } from './http';
2
+ export * from './http';
3
+ export * from './find-my-way';
4
+ export * from './controller';
5
+ export * from './loader';
6
+ export default Http;
@@ -0,0 +1,28 @@
1
+ import { ControllerRegisterProps } from './controller';
2
+ import { Http } from './http';
3
+ export interface LoaderCompileOptions {
4
+ defaultSuffix?: string;
5
+ prefix?: string;
6
+ }
7
+ export type LoaderFromOptions = {
8
+ suffix?: string;
9
+ } & LoaderCompileOptions;
10
+ export declare class Loader {
11
+ private readonly http;
12
+ constructor(http: Http);
13
+ /**
14
+ * 单个路由绑定编译
15
+ * @param path 路径
16
+ * @param controllers 控制器数组或单个控制器
17
+ * @param options 选项
18
+ * @returns 注销回调
19
+ */
20
+ compile<R>(path: string, controllers: ControllerRegisterProps<R> | ControllerRegisterProps<R>[], options?: LoaderCompileOptions): () => void;
21
+ /**
22
+ * 文件夹批量路由绑定编译
23
+ * @param directory 文件夹路径
24
+ * @param options 选项
25
+ * @returns 注销回调
26
+ */
27
+ from(directory: string, options?: LoaderFromOptions): Promise<() => void>;
28
+ }
package/dist/loader.js ADDED
@@ -0,0 +1,82 @@
1
+ import { glob } from 'glob';
2
+ import { resolve } from 'node:path';
3
+ export class Loader {
4
+ http;
5
+ constructor(http) {
6
+ this.http = http;
7
+ }
8
+ /**
9
+ * 单个路由绑定编译
10
+ * @param path 路径
11
+ * @param controllers 控制器数组或单个控制器
12
+ * @param options 选项
13
+ * @returns 注销回调
14
+ */
15
+ compile(path, controllers, options = {
16
+ defaultSuffix: '/index',
17
+ }) {
18
+ const callbacks = [];
19
+ // 格式化路径,去除默认后缀
20
+ const defaultSuffix = options.defaultSuffix || '/index';
21
+ let url = path.startsWith('/') ? path : '/' + path;
22
+ if (url.endsWith(defaultSuffix)) {
23
+ url = url.substring(0, url.length - defaultSuffix.length);
24
+ }
25
+ if (!url)
26
+ url = '/';
27
+ // 如果导出单个路由,则转化为数组,批量执行
28
+ if (!Array.isArray(controllers)) {
29
+ controllers = [controllers];
30
+ }
31
+ // 添加前缀
32
+ const _url = options.prefix ? options.prefix + url : url;
33
+ // 将路径中的参数转换为路由参数
34
+ const router_url = _url.replace(/\[([^\]]+)\]/g, ':$1');
35
+ // 批量注册路由
36
+ for (let i = 0; i < controllers.length; i++) {
37
+ const controller = controllers[i];
38
+ const { method, middlewares } = controller;
39
+ controller.data.url = router_url;
40
+ callbacks.push(this.http.route(method, router_url, ...middlewares));
41
+ }
42
+ // 销毁路由绑定的回调
43
+ return () => {
44
+ let j = callbacks.length;
45
+ while (j--)
46
+ callbacks[j]();
47
+ };
48
+ }
49
+ /**
50
+ * 文件夹批量路由绑定编译
51
+ * @param directory 文件夹路径
52
+ * @param options 选项
53
+ * @returns 注销回调
54
+ */
55
+ async from(directory, options = {
56
+ defaultSuffix: '/index',
57
+ suffix: 'controller',
58
+ }) {
59
+ const { suffix = 'controller', ...extras } = options;
60
+ // 获取文件夹下的所有文件
61
+ const files = await glob(`**/*.${suffix}.{ts,js}`, { cwd: directory });
62
+ // 批量注册路由
63
+ const callbacks = await Promise.all(files.map(async (file) => {
64
+ // 获取文件绝对路径
65
+ const path = resolve(directory, file);
66
+ // 获取文件地址
67
+ const url = file.substring(0, file.length - suffix.length - 4);
68
+ // 导入文件
69
+ const controller = await import(path);
70
+ // 获取文件导出的默认函数
71
+ const { default: fn } = controller;
72
+ // 注册路由
73
+ return this.compile(url, fn, extras);
74
+ }));
75
+ // 销毁路由绑定的回调
76
+ return () => {
77
+ let i = callbacks.length;
78
+ while (i--)
79
+ callbacks[i]();
80
+ };
81
+ }
82
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@hile/http",
3
+ "description": "Hile http - HTTP service framework",
4
+ "version": "1.0.6",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -b",
10
+ "dev": "tsc -b --watch",
11
+ "test": "vitest run"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "license": "MIT",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "devDependencies": {
21
+ "@types/koa": "^3.0.1",
22
+ "vitest": "^4.0.18"
23
+ },
24
+ "dependencies": {
25
+ "find-my-way": "^9.5.0",
26
+ "glob": "^13.0.6",
27
+ "koa": "^3.1.2",
28
+ "koa-compose": "^4.1.0"
29
+ },
30
+ "gitHead": "16396508dcd3a71359fd8371feefb2f621a92a76"
31
+ }