@keq-request/nestjs 5.0.0-beta.13 → 5.0.0-beta.15
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/.turbo/turbo-build.log +19 -21
- package/CHANGELOG.md +25 -0
- package/__tests__/keq-module.spec.ts +161 -113
- package/dist/index.d.mts +74 -32
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +74 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -119
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -120
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/decorators/inject-keq-consumer.decorator.ts +26 -0
- package/src/index.ts +3 -0
- package/src/interfaces/index.ts +7 -2
- package/src/interfaces/keq-middleware-consumer.interface.ts +11 -5
- package/src/keq-consumer.ts +34 -0
- package/src/keq-middleware-collector.ts +150 -0
- package/src/keq-middleware-consumer.ts +30 -176
- package/src/keq.module.ts +8 -93
- package/src/interfaces/keq-middleware-module.interface.ts +0 -6
- package/src/utils/has-configure-keq-middleware.ts +0 -10
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
> @keq-request/nestjs@5.0.0-beta.
|
|
2
|
+
> @keq-request/nestjs@5.0.0-beta.15 build /home/runner/work/keq/keq/packages/nestjs
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m [34mtsdown v0.21.10[39m powered by [38;2;255;126;23mrolldown v1.0.0-rc.17[39m
|
|
6
6
|
[34mℹ[39m config file: [4m/home/runner/work/keq/keq/packages/nestjs/tsdown.config.ts[24m
|
|
7
|
-
(node:
|
|
7
|
+
(node:2727) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///home/runner/work/keq/keq/packages/nestjs/tsdown.config.ts?no-cache=b289e4f8-de50-42cc-a1c8-4e34b36675d9 is not specified and it doesn't parse as CommonJS.
|
|
8
8
|
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
|
|
9
9
|
To eliminate this warning, add "type": "module" to /home/runner/work/keq/keq/packages/nestjs/package.json.
|
|
10
10
|
(Use `node --trace-warnings ...` to show where the warning was created)
|
|
@@ -12,25 +12,23 @@ To eliminate this warning, add "type": "module" to /home/runner/work/keq/keq/pac
|
|
|
12
12
|
[34mℹ[39m target: [34mnode20[39m
|
|
13
13
|
[34mℹ[39m tsconfig: [34mtsconfig.lib.json[39m
|
|
14
14
|
[34mℹ[39m Build start
|
|
15
|
-
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[1mindex.js[22m [
|
|
16
|
-
[34mℹ[39m [33m[CJS][39m [2mdist/[22mindex.js.map [
|
|
17
|
-
[34mℹ[39m [33m[CJS][39m 2 files, total:
|
|
18
|
-
[33m[
|
|
19
|
-
|
|
20
|
-
[34mℹ[39m [
|
|
21
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.mjs.map [2m14.98 kB[22m [2m│ gzip: 5.14 kB[22m
|
|
22
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.d.mts.map [2m 1.08 kB[22m [2m│ gzip: 0.47 kB[22m
|
|
23
|
-
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 2.96 kB[22m [2m│ gzip: 1.38 kB[22m
|
|
24
|
-
[34mℹ[39m [34m[ESM][39m 4 files, total: 28.22 kB
|
|
25
|
-
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
26
|
-
|
|
27
|
-
[32m✔[39m Build complete in [32m5612ms[39m
|
|
28
|
-
[34mℹ[39m [33m[CJS][39m [2mdist/[22mindex.d.ts.map [2m1.08 kB[22m [2m│ gzip: 0.47 kB[22m
|
|
29
|
-
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m2.96 kB[22m [2m│ gzip: 1.37 kB[22m
|
|
30
|
-
[34mℹ[39m [33m[CJS][39m 2 files, total: 4.04 kB
|
|
15
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[1mindex.js[22m [2m 8.01 kB[22m [2m│ gzip: 2.93 kB[22m
|
|
16
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22mindex.js.map [2m11.70 kB[22m [2m│ gzip: 3.78 kB[22m
|
|
17
|
+
[34mℹ[39m [33m[CJS][39m 2 files, total: 19.72 kB
|
|
18
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22mindex.d.ts.map [2m1.39 kB[22m [2m│ gzip: 0.55 kB[22m
|
|
19
|
+
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m3.81 kB[22m [2m│ gzip: 1.45 kB[22m
|
|
20
|
+
[34mℹ[39m [33m[CJS][39m 2 files, total: 5.20 kB
|
|
31
21
|
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugins. Here is a breakdown:
|
|
32
|
-
- rolldown-plugin-dts:generate (
|
|
33
|
-
- tsdown:deps (
|
|
22
|
+
- rolldown-plugin-dts:generate (46%)
|
|
23
|
+
- tsdown:deps (41%)
|
|
34
24
|
See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
35
25
|
|
|
36
|
-
[32m✔[39m Build complete in [
|
|
26
|
+
[32m✔[39m Build complete in [32m5374ms[39m
|
|
27
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[1mindex.mjs[22m [2m 6.58 kB[22m [2m│ gzip: 2.45 kB[22m
|
|
28
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.mjs.map [2m11.61 kB[22m [2m│ gzip: 3.75 kB[22m
|
|
29
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22mindex.d.mts.map [2m 1.39 kB[22m [2m│ gzip: 0.55 kB[22m
|
|
30
|
+
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 3.81 kB[22m [2m│ gzip: 1.46 kB[22m
|
|
31
|
+
[34mℹ[39m [34m[ESM][39m 4 files, total: 23.38 kB
|
|
32
|
+
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
33
|
+
|
|
34
|
+
[32m✔[39m Build complete in [32m5376ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @keq-request/nestjs
|
|
2
2
|
|
|
3
|
+
## 5.0.0-beta.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- keq@5.0.0-beta.15
|
|
8
|
+
|
|
9
|
+
## 5.0.0-beta.14
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- 23c971f: **BREAKING CHANGE:** Refactor NestJS middleware configuration from lifecycle hook pattern to dependency injection pattern, introducing `KeqMiddlewareConsumer` as an injectable service and `@InjectKeqConsumer()` decorator with `KeqConsumer<T>` for module-scoped middleware.
|
|
14
|
+
|
|
15
|
+
- Remove `KeqMiddlewareModule` interface and `configureKeqMiddleware()` lifecycle method. Middleware is now registered by injecting `KeqMiddlewareConsumer` or `KeqConsumer<T>` directly into services.
|
|
16
|
+
- Remove `KeqMiddlewareConsumer` interface — it is now a concrete `@Injectable()` class provided by `KeqModule`.
|
|
17
|
+
- Remove `hasConfigureKeqMiddleware()` utility function.
|
|
18
|
+
- Remove `forRoutes()` support for `KeqModuleClass` route targets. Use `KeqConsumer<T>` (injected via `@InjectKeqConsumer(ModuleClass)`) for module-scoped middleware.
|
|
19
|
+
- `KeqModuleClass` now requires a static `KEQ_CONSUMER: symbol` property in addition to `KEQ_REQUEST`.
|
|
20
|
+
- `KeqModule` no longer has a `register()` static method — it is now a bare `@Global()` module. Import `KeqModule` directly.
|
|
21
|
+
- Add `@InjectKeqConsumer(ModuleClass)` decorator, `KeqConsumer<T>` class, `KeqRouteTarget` type, and `KeqScopedConfigProxy<T>` interface.
|
|
22
|
+
- CLI-generated NestJS modules now expose `KEQ_CONSUMER` symbol and provide a `KeqConsumer` provider for `@InjectKeqConsumer()` injection.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- keq@5.0.0-beta.14
|
|
27
|
+
|
|
3
28
|
## 5.0.0-beta.13
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -1,155 +1,203 @@
|
|
|
1
1
|
import 'reflect-metadata'
|
|
2
2
|
import { expect, test } from '@jest/globals'
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* 模拟 NestJS Module wrapper 的运行时结构。
|
|
9
|
-
* NestJS 在扫描阶段会将 DynamicModule 的 imports 解析为 Set<Module>,
|
|
10
|
-
* 因此使用 imports Set 而非静态 Reflect.getMetadata 来校验。
|
|
11
|
-
*/
|
|
12
|
-
function createMockModule(metatype: unknown, importsModules: Array<{ metatype: unknown }> = []) {
|
|
13
|
-
return {
|
|
14
|
-
metatype,
|
|
15
|
-
imports: new Set(importsModules),
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function mockModulesContainer(
|
|
20
|
-
modules: Array<ReturnType<typeof createMockModule>>,
|
|
21
|
-
): ModulesContainer {
|
|
22
|
-
const map = new Map()
|
|
23
|
-
for (const mod of modules) {
|
|
24
|
-
map.set(Symbol('module'), mod)
|
|
25
|
-
}
|
|
26
|
-
return map as unknown as ModulesContainer
|
|
27
|
-
}
|
|
3
|
+
import type { KeqMiddleware } from 'keq'
|
|
4
|
+
import { KeqRequest } from 'keq'
|
|
5
|
+
import { KeqMiddlewareConsumer } from '../src/keq-middleware-consumer.js'
|
|
6
|
+
import { KeqConsumer } from '../src/keq-consumer.js'
|
|
7
|
+
import { KEQ_ROUTES } from '../src/constants.js'
|
|
28
8
|
|
|
29
9
|
|
|
30
|
-
test('
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
configureKeqMiddleware(): void {}
|
|
34
|
-
}
|
|
10
|
+
test('applies global middleware via forRoutes(KEQ_ROUTES.ALL)', () => {
|
|
11
|
+
const keqRequest = new KeqRequest()
|
|
12
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
35
13
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
createMockModule(BadModule),
|
|
39
|
-
])
|
|
14
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
15
|
+
consumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
40
16
|
|
|
41
|
-
expect(
|
|
42
|
-
/\[KeqModule\] 'BadModule' implements configureKeqMiddleware\(\)/,
|
|
43
|
-
)
|
|
17
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
44
18
|
})
|
|
45
19
|
|
|
46
|
-
test('
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
20
|
+
test('applies global middleware when forRoutes receives no arguments', () => {
|
|
21
|
+
const keqRequest = new KeqRequest()
|
|
22
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
23
|
+
|
|
24
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
25
|
+
consumer.apply(middleware).forRoutes()
|
|
51
26
|
|
|
52
|
-
|
|
27
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('applies route-specific middleware via forRoutes with KeqRouteInfo', () => {
|
|
31
|
+
const keqRequest = new KeqRequest()
|
|
32
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
53
33
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
createMockModule(GoodModule, [keqModuleWrapper]),
|
|
57
|
-
])
|
|
34
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
35
|
+
consumer.apply(middleware).forRoutes({ pathname: '/api/*' })
|
|
58
36
|
|
|
59
|
-
expect(
|
|
37
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
60
38
|
})
|
|
61
39
|
|
|
62
|
-
test('
|
|
63
|
-
|
|
64
|
-
|
|
40
|
+
test('supports multiple middleware in single apply()', () => {
|
|
41
|
+
const keqRequest = new KeqRequest()
|
|
42
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
65
43
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
44
|
+
const mw1: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
45
|
+
const mw2: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
46
|
+
consumer.apply(mw1, mw2).forRoutes(KEQ_ROUTES.ALL)
|
|
69
47
|
|
|
70
|
-
expect(
|
|
48
|
+
expect(keqRequest.middlewares.length).toBe(2)
|
|
71
49
|
})
|
|
72
50
|
|
|
73
|
-
test('
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
configureKeqMiddleware(): void {}
|
|
77
|
-
}
|
|
51
|
+
test('supports multiple apply() calls', () => {
|
|
52
|
+
const keqRequest = new KeqRequest()
|
|
53
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
78
54
|
|
|
79
|
-
|
|
80
|
-
const
|
|
55
|
+
const mw1: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
56
|
+
const mw2: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
81
57
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
createMockModule(DynamicImportModule, [keqModuleWrapper]),
|
|
85
|
-
])
|
|
58
|
+
consumer.apply(mw1).forRoutes(KEQ_ROUTES.ALL)
|
|
59
|
+
consumer.apply(mw2).forRoutes({ pathname: '/api/*' })
|
|
86
60
|
|
|
87
|
-
expect(
|
|
61
|
+
expect(keqRequest.middlewares.length).toBe(2)
|
|
88
62
|
})
|
|
89
63
|
|
|
90
|
-
test('
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
64
|
+
test('supports exclude().forRoutes() chaining', () => {
|
|
65
|
+
const keqRequest = new KeqRequest()
|
|
66
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
67
|
+
|
|
68
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
69
|
+
consumer.apply(middleware).exclude({ pathname: '/health' }).forRoutes(KEQ_ROUTES.ALL)
|
|
70
|
+
|
|
71
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('middleware is applied synchronously at forRoutes() call time', () => {
|
|
75
|
+
const keqRequest = new KeqRequest()
|
|
76
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
77
|
+
|
|
78
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
79
|
+
|
|
80
|
+
expect(keqRequest.middlewares.length).toBe(0)
|
|
81
|
+
consumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
82
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('supports chaining multiple apply() calls via forRoutes returning consumer', () => {
|
|
86
|
+
const keqRequest = new KeqRequest()
|
|
87
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
88
|
+
|
|
89
|
+
const mw1: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
90
|
+
const mw2: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
91
|
+
const mw3: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
92
|
+
|
|
93
|
+
consumer
|
|
94
|
+
.apply(mw1).forRoutes(KEQ_ROUTES.ALL)
|
|
95
|
+
.apply(mw2).forRoutes({ pathname: '/api/*' })
|
|
96
|
+
.apply(mw3).exclude({ pathname: '/health' }).forRoutes(KEQ_ROUTES.ALL)
|
|
97
|
+
|
|
98
|
+
expect(keqRequest.middlewares.length).toBe(3)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// ─── KeqConsumer(模块绑定)测试 ──────────────────────────
|
|
96
102
|
|
|
97
|
-
|
|
98
|
-
const
|
|
103
|
+
test('KeqConsumer applies middleware to its own KeqRequest', () => {
|
|
104
|
+
const scopedKeqRequest = new KeqRequest()
|
|
105
|
+
const consumer = new KeqConsumer(scopedKeqRequest)
|
|
99
106
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
createMockModule(ForwardRefModule, [keqModuleWrapper]),
|
|
103
|
-
])
|
|
107
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
108
|
+
consumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
104
109
|
|
|
105
|
-
expect(
|
|
110
|
+
expect(scopedKeqRequest.middlewares.length).toBe(1)
|
|
106
111
|
})
|
|
107
112
|
|
|
108
|
-
test('
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
test('KeqConsumer does not affect other KeqRequest instances', () => {
|
|
114
|
+
const globalKeqRequest = new KeqRequest()
|
|
115
|
+
const scopedKeqRequest = new KeqRequest()
|
|
116
|
+
|
|
117
|
+
const globalConsumer = new KeqMiddlewareConsumer(globalKeqRequest)
|
|
118
|
+
const scopedConsumer = new KeqConsumer(scopedKeqRequest)
|
|
112
119
|
|
|
113
|
-
|
|
120
|
+
const middleware: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
121
|
+
scopedConsumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
122
|
+
|
|
123
|
+
expect(scopedKeqRequest.middlewares.length).toBe(1)
|
|
124
|
+
expect(globalKeqRequest.middlewares.length).toBe(0)
|
|
114
125
|
})
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
// ─── KeqNestMiddleware 实例对象测试 ──────────────────────
|
|
128
|
+
|
|
129
|
+
test('applies KeqNestMiddleware instance via apply()', () => {
|
|
130
|
+
const keqRequest = new KeqRequest()
|
|
131
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
132
|
+
|
|
133
|
+
const middleware = {
|
|
134
|
+
use: async (_ctx: unknown, next: () => Promise<void>) => { await next() },
|
|
122
135
|
}
|
|
136
|
+
consumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
137
|
+
|
|
138
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
139
|
+
})
|
|
123
140
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
141
|
+
test('applies KeqNestMiddleware instance to specific route', () => {
|
|
142
|
+
const keqRequest = new KeqRequest()
|
|
143
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
144
|
+
|
|
145
|
+
const middleware = {
|
|
146
|
+
use: async (_ctx: unknown, next: () => Promise<void>) => { await next() },
|
|
147
|
+
}
|
|
148
|
+
consumer.apply(middleware).forRoutes({ pathname: '/api/*' })
|
|
127
149
|
|
|
128
|
-
expect(
|
|
129
|
-
/\[KeqModule\] 'SomeModule'/,
|
|
130
|
-
)
|
|
150
|
+
expect(keqRequest.middlewares.length).toBe(1)
|
|
131
151
|
})
|
|
132
152
|
|
|
133
|
-
test('
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
153
|
+
test('mixes KeqMiddleware function and KeqNestMiddleware instance', () => {
|
|
154
|
+
const keqRequest = new KeqRequest()
|
|
155
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
156
|
+
|
|
157
|
+
const fnMw: KeqMiddleware = async (_ctx, next) => { await next() }
|
|
158
|
+
const instanceMw = {
|
|
159
|
+
use: async (_ctx: unknown, next: () => Promise<void>) => { await next() },
|
|
137
160
|
}
|
|
161
|
+
consumer.apply(fnMw, instanceMw).forRoutes(KEQ_ROUTES.ALL)
|
|
162
|
+
|
|
163
|
+
expect(keqRequest.middlewares.length).toBe(2)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('KeqNestMiddleware instance receives ctx and next in use()', async () => {
|
|
167
|
+
const keqRequest = new KeqRequest()
|
|
168
|
+
const consumer = new KeqMiddlewareConsumer(keqRequest)
|
|
169
|
+
|
|
170
|
+
let capturedCtx: unknown = null
|
|
171
|
+
let capturedNext: unknown = null
|
|
138
172
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
173
|
+
const middleware = {
|
|
174
|
+
use: async (ctx: unknown, next: () => Promise<void>) => {
|
|
175
|
+
capturedCtx = ctx
|
|
176
|
+
capturedNext = next
|
|
177
|
+
await next()
|
|
178
|
+
},
|
|
142
179
|
}
|
|
180
|
+
consumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
143
181
|
|
|
144
|
-
const
|
|
182
|
+
const middlewareFn = keqRequest.middlewares[0]
|
|
183
|
+
const mockCtx = { request: { url: { host: 'example.com', pathname: '/' }, method: 'GET' } }
|
|
184
|
+
const mockNext = async () => {}
|
|
145
185
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
187
|
+
await (middlewareFn as any)(mockCtx, mockNext)
|
|
188
|
+
|
|
189
|
+
expect(capturedCtx).toBe(mockCtx)
|
|
190
|
+
expect(typeof capturedNext).toBe('function')
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('KeqConsumer accepts KeqNestMiddleware instance', () => {
|
|
194
|
+
const scopedKeqRequest = new KeqRequest()
|
|
195
|
+
const consumer = new KeqConsumer(scopedKeqRequest)
|
|
196
|
+
|
|
197
|
+
const middleware = {
|
|
198
|
+
use: async (_ctx: unknown, next: () => Promise<void>) => { await next() },
|
|
199
|
+
}
|
|
200
|
+
consumer.apply(middleware).forRoutes(KEQ_ROUTES.ALL)
|
|
151
201
|
|
|
152
|
-
expect(
|
|
153
|
-
/'BadModule'/,
|
|
154
|
-
)
|
|
202
|
+
expect(scopedKeqRequest.middlewares.length).toBe(1)
|
|
155
203
|
})
|
package/dist/index.d.mts
CHANGED
|
@@ -1,31 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ModulesContainer } from "@nestjs/core";
|
|
1
|
+
import { Type } from "@nestjs/common";
|
|
3
2
|
import { KeqExecutionContext, KeqMiddleware, KeqNext, KeqRequest } from "keq";
|
|
4
3
|
|
|
5
4
|
//#region src/keq.module.d.ts
|
|
6
|
-
|
|
7
|
-
* 在 NestJS 模块初始化早期阶段校验 `configureKeqMiddleware` 的实现模块
|
|
8
|
-
* 是否显式导入了 `KeqModule`。
|
|
9
|
-
*
|
|
10
|
-
* NestJS 初始化分为两个严格串行的阶段:
|
|
11
|
-
* 1. 全量扫描 — 所有模块 wrapper 加入 ModulesContainer,metatype 已设置,
|
|
12
|
-
* DynamicModule 的 imports 已被解析并写入 moduleWrapper.imports Set
|
|
13
|
-
* 2. 实例创建 — 按依赖顺序调用构造函数 → onModuleInit
|
|
14
|
-
*
|
|
15
|
-
* 此校验在阶段 2 开头(构造函数中)执行,早于所有 onModuleInit,
|
|
16
|
-
* 确保中间件缺失问题在启动期就被捕获,而非在业务模块 onModuleInit 发请求时静默失效。
|
|
17
|
-
*
|
|
18
|
-
* 使用运行时 imports Set 而非 Reflect.getMetadata('imports', metatype) 的原因:
|
|
19
|
-
* - DynamicModule 通过 register() 返回的 imports 不在静态装饰器元数据中
|
|
20
|
-
* - 从 ModulesContainer 获取 KeqModule metatype 可避免 pnpm 重复包实例导致的 === 比对失败
|
|
21
|
-
*/
|
|
22
|
-
declare function validateConfigureKeqMiddlewareImports(modulesContainer: ModulesContainer): void;
|
|
23
|
-
declare class KeqModule implements OnModuleInit {
|
|
24
|
-
private readonly modulesContainer;
|
|
25
|
-
private readonly keqRequest;
|
|
26
|
-
constructor(modulesContainer: ModulesContainer, keqRequest: KeqRequest);
|
|
27
|
-
onModuleInit(): void;
|
|
28
|
-
}
|
|
5
|
+
declare class KeqModule {}
|
|
29
6
|
//#endregion
|
|
30
7
|
//#region src/constants.d.ts
|
|
31
8
|
declare const KEQ_ROUTES: {
|
|
@@ -37,6 +14,30 @@ interface KeqNestMiddleware {
|
|
|
37
14
|
use(ctx: KeqExecutionContext, next: KeqNext): void | PromiseLike<void>;
|
|
38
15
|
}
|
|
39
16
|
//#endregion
|
|
17
|
+
//#region src/keq-consumer.d.ts
|
|
18
|
+
/**
|
|
19
|
+
* 为指定 Keq 模块按路由注册中间件。
|
|
20
|
+
*
|
|
21
|
+
* Keq 模块由 `@keq-request/cli` 生成,通过
|
|
22
|
+
* {@link InjectKeqConsumer} 装饰器注入本实例。
|
|
23
|
+
*
|
|
24
|
+
* @typeParam T - 目标模块类
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* constructor(
|
|
29
|
+
* \@InjectKeqConsumer(GeneratedModule) consumer: KeqConsumer<GeneratedModule>,
|
|
30
|
+
* ) {
|
|
31
|
+
* consumer.apply(authMw).forRoutes('*')
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare class KeqConsumer<T = KeqModuleClass> {
|
|
36
|
+
private readonly keqRequest;
|
|
37
|
+
constructor(keqRequest: KeqRequest);
|
|
38
|
+
apply(...middlewares: Array<KeqNestMiddleware | KeqMiddleware>): KeqScopedConfigProxy<T>;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
40
41
|
//#region src/interfaces/keq-middleware-consumer.interface.d.ts
|
|
41
42
|
interface KeqRouteInfo {
|
|
42
43
|
host?: string;
|
|
@@ -45,18 +46,39 @@ interface KeqRouteInfo {
|
|
|
45
46
|
}
|
|
46
47
|
interface KeqModuleClass extends Type<any> {
|
|
47
48
|
readonly KEQ_REQUEST: symbol;
|
|
49
|
+
readonly KEQ_CONSUMER: symbol;
|
|
48
50
|
}
|
|
51
|
+
/** forRoutes() 接受的路由目标(不再包含 KeqModuleClass,模块绑定通过 @InjectKeqConsumer 实现) */
|
|
52
|
+
type KeqRouteTarget = typeof KEQ_ROUTES.ALL | KeqRouteInfo;
|
|
49
53
|
interface KeqMiddlewareConfigProxy {
|
|
50
54
|
exclude(...routes: KeqRouteInfo[]): KeqMiddlewareConfigProxy;
|
|
51
|
-
forRoutes(...routes:
|
|
55
|
+
forRoutes(...routes: KeqRouteTarget[]): KeqMiddlewareConsumer;
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
/** 模块绑定 Consumer 的配置代理,forRoutes() 返回 KeqConsumer<T> */
|
|
58
|
+
interface KeqScopedConfigProxy<T = KeqModuleClass> {
|
|
59
|
+
exclude(...routes: KeqRouteInfo[]): KeqScopedConfigProxy<T>;
|
|
60
|
+
forRoutes(...routes: KeqRouteTarget[]): KeqConsumer<T>;
|
|
55
61
|
}
|
|
56
62
|
//#endregion
|
|
57
|
-
//#region src/
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
//#region src/keq-middleware-consumer.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* 全局中间件消费者,提供 `.apply().forRoutes()` API。
|
|
66
|
+
*
|
|
67
|
+
* `forRoutes()` 返回 `KeqMiddlewareConsumer` 自身,支持链式注册:
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* consumer
|
|
71
|
+
* .apply(mw1).forRoutes(KEQ_ROUTES.ALL)
|
|
72
|
+
* .apply(mw2).forRoutes({ pathname: '/api/*' })
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* 模块级别的中间件请使用 `@InjectKeqConsumer(GeneratedModule)` 注入
|
|
76
|
+
* `KeqConsumer<GeneratedModule>`,其 `forRoutes()` 直接作用于目标模块。
|
|
77
|
+
*/
|
|
78
|
+
declare class KeqMiddlewareConsumer {
|
|
79
|
+
private readonly keqRequest;
|
|
80
|
+
constructor(keqRequest: KeqRequest);
|
|
81
|
+
apply(...middlewares: Array<KeqNestMiddleware | KeqMiddleware>): KeqMiddlewareConfigProxy;
|
|
60
82
|
}
|
|
61
83
|
//#endregion
|
|
62
84
|
//#region src/interfaces/keq-module-options.interface.d.ts
|
|
@@ -65,5 +87,25 @@ interface KeqModuleOptions {
|
|
|
65
87
|
isolate?: boolean;
|
|
66
88
|
}
|
|
67
89
|
//#endregion
|
|
68
|
-
|
|
90
|
+
//#region src/decorators/inject-keq-consumer.decorator.d.ts
|
|
91
|
+
/**
|
|
92
|
+
* 注入绑定到指定 Keq Module 的 `KeqConsumer<T>`。
|
|
93
|
+
*
|
|
94
|
+
* 被注入的 Module Class 必须同时包含 `KEQ_REQUEST` 和 `KEQ_CONSUMER` 静态属性,
|
|
95
|
+
* 两者均由 keq CLI 生成。
|
|
96
|
+
*
|
|
97
|
+
* 使用方式:
|
|
98
|
+
* ```ts
|
|
99
|
+
* constructor(
|
|
100
|
+
* \@InjectKeqConsumer(GeneratedModule) consumer: KeqConsumer<GeneratedModule>,
|
|
101
|
+
* ) {
|
|
102
|
+
* consumer.apply(mw).forRoutes('*')
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @param moduleClass - 目标 KeqModule 类(需具有 `KEQ_REQUEST` 和 `KEQ_CONSUMER` symbol)
|
|
107
|
+
*/
|
|
108
|
+
declare function InjectKeqConsumer<T extends KeqModuleClass>(moduleClass: T): ParameterDecorator;
|
|
109
|
+
//#endregion
|
|
110
|
+
export { InjectKeqConsumer, KEQ_ROUTES, KeqConsumer, type KeqMiddlewareConfigProxy, KeqMiddlewareConsumer, KeqModule, type KeqModuleClass, type KeqModuleOptions, type KeqNestMiddleware, type KeqRouteInfo, type KeqRouteTarget, type KeqScopedConfigProxy };
|
|
69
111
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/keq.module.ts","../src/constants.ts","../src/interfaces/keq-nest-middleware.interface.ts","../src/interfaces/keq-middleware-consumer.interface.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/keq.module.ts","../src/constants.ts","../src/interfaces/keq-nest-middleware.interface.ts","../src/keq-consumer.ts","../src/interfaces/keq-middleware-consumer.interface.ts","../src/keq-middleware-consumer.ts","../src/interfaces/keq-module-options.interface.ts","../src/decorators/inject-keq-consumer.decorator.ts"],"mappings":";;;;cAaa,SAAA;;;cCbA,UAAA;EAAA,SAEH,GAAA;AAAA;;;UCCO,iBAAA;EACf,GAAA,CAAI,GAAA,EAAK,mBAAA,EAAqB,IAAA,EAAM,OAAA,UAAiB,WAAA;AAAA;;;AFCvD;;;;;;;;ACLA;;;;;;;;ACGA;AFEA,cGmBa,WAAA,KAAgB,cAAA;EAAA,iBACE,UAAA;cAAA,UAAA,EAAY,UAAA;EAEzC,KAAA,CAAA,GACK,WAAA,EAAa,KAAA,CAAM,iBAAA,GAAoB,aAAA,IACzC,oBAAA,CAAqB,CAAA;AAAA;;;UCvBT,YAAA;EACf,IAAA;EACA,MAAA;EACA,QAAA;AAAA;AAAA,UAGe,cAAA,SAAuB,IAAA;EAAA,SAC7B,WAAA;EAAA,SACA,YAAA;AAAA;;KAIC,cAAA,UAAwB,UAAA,CAAW,GAAA,GAAM,YAAA;AAAA,UAEpC,wBAAA;EACf,OAAA,IAAW,MAAA,EAAQ,YAAA,KAAiB,wBAAA;EACpC,SAAA,IAAa,MAAA,EAAQ,cAAA,KAAmB,qBAAA;AAAA;;UAIzB,oBAAA,KAAyB,cAAA;EACxC,OAAA,IAAW,MAAA,EAAQ,YAAA,KAAiB,oBAAA,CAAqB,CAAA;EACzD,SAAA,IAAa,MAAA,EAAQ,cAAA,KAAmB,WAAA,CAAY,CAAA;AAAA;;;AJvBtD;;;;;;;;ACLA;;;;;;ADKA,cKkBa,qBAAA;EAAA,iBACkB,UAAA;cAAA,UAAA,EAAY,UAAA;EAEzC,KAAA,CAAA,GACK,WAAA,EAAa,KAAA,CAAM,iBAAA,GAAoB,aAAA,IACzC,wBAAA;AAAA;;;UCzBY,gBAAA;EACf,WAAA,GAAc,aAAA;EACd,OAAA;AAAA;;;;;;ANAF;;;;;;;;ACLA;;;;;;iBMqBgB,iBAAA,WAA4B,cAAA,CAAA,CAC1C,WAAA,EAAa,CAAA,GACZ,kBAAA"}
|