@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/dist/index.d.ts CHANGED
@@ -1,31 +1,8 @@
1
- import { OnModuleInit, Type } from "@nestjs/common";
2
- import { ModulesContainer } from "@nestjs/core";
3
1
  import { KeqExecutionContext, KeqMiddleware, KeqNext, KeqRequest } from "keq";
2
+ import { Type } from "@nestjs/common";
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: Array<typeof KEQ_ROUTES.ALL | KeqRouteInfo | KeqModuleClass>): KeqMiddlewareConsumer;
55
+ forRoutes(...routes: KeqRouteTarget[]): KeqMiddlewareConsumer;
52
56
  }
53
- interface KeqMiddlewareConsumer {
54
- apply(...middlewares: Array<Type<KeqNestMiddleware> | KeqMiddleware>): KeqMiddlewareConfigProxy;
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/interfaces/keq-middleware-module.interface.d.ts
58
- interface KeqMiddlewareModule {
59
- configureKeqMiddleware(consumer: KeqMiddlewareConsumer): void;
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
- export { KEQ_ROUTES, type KeqMiddlewareConfigProxy, type KeqMiddlewareConsumer, type KeqMiddlewareModule, KeqModule, type KeqModuleClass, type KeqModuleOptions, type KeqNestMiddleware, type KeqRouteInfo, validateConfigureKeqMiddlewareImports };
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.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/keq.module.ts","../src/constants.ts","../src/interfaces/keq-nest-middleware.interface.ts","../src/interfaces/keq-middleware-consumer.interface.ts","../src/interfaces/keq-middleware-module.interface.ts","../src/interfaces/keq-module-options.interface.ts"],"mappings":";;;;;;;AAoCA;;;;;AAkCA;;;;;;;;;iBAlCgB,qCAAA,CAAsC,gBAAA,EAAkB,gBAAA;AAAA,cAuC3D,SAAA,YAAqB,YAAA;EAAA,iBAEb,gBAAA;EAAA,iBACA,UAAA;cADA,gBAAA,EAAkB,gBAAA,EAClB,UAAA,EAAY,UAAA;EAK/B,YAAA,CAAA;AAAA;;;cCnFW,UAAA;EAAA,SAEH,GAAA;AAAA;;;UCCO,iBAAA;EACf,GAAA,CAAI,GAAA,EAAK,mBAAA,EAAqB,IAAA,EAAM,OAAA,UAAiB,WAAA;AAAA;;;UCEtC,YAAA;EACf,IAAA;EACA,MAAA;EACA,QAAA;AAAA;AAAA,UAGe,cAAA,SAAuB,IAAA;EAAA,SAC7B,WAAA;AAAA;AAAA,UAGM,wBAAA;EACf,OAAA,IAAW,MAAA,EAAQ,YAAA,KAAiB,wBAAA;EACpC,SAAA,IAAa,MAAA,EAAQ,KAAA,QAAa,UAAA,CAAW,GAAA,GAAM,YAAA,GAAe,cAAA,IAAkB,qBAAA;AAAA;AAAA,UAGrE,qBAAA;EACf,KAAA,IAAS,WAAA,EAAa,KAAA,CAAM,IAAA,CAAK,iBAAA,IAAqB,aAAA,IAAiB,wBAAA;AAAA;;;UCnBxD,mBAAA;EACf,sBAAA,CAAuB,QAAA,EAAU,qBAAA;AAAA;;;UCDlB,gBAAA;EACf,WAAA,GAAc,aAAA;EACd,OAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","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"}
package/dist/index.js CHANGED
@@ -22,23 +22,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  }) : target, mod));
23
23
  //#endregion
24
24
  let _nestjs_common = require("@nestjs/common");
25
- let _nestjs_core = require("@nestjs/core");
26
25
  let keq = require("keq");
27
26
  let picomatch = require("picomatch");
28
27
  picomatch = __toESM(picomatch);
29
28
  //#region src/constants.ts
30
29
  const KEQ_ROUTES = { ALL: Symbol("KEQ_ROUTES.ALL") };
31
30
  //#endregion
32
- //#region src/keq-middleware-consumer.ts
33
- var KeqMiddlewareConsumerImpl = class {
31
+ //#region src/keq-middleware-collector.ts
32
+ /**
33
+ * 中间件收集器,收集中间件配置条目并应用到指定的 {@link KeqRequest} 实例。
34
+ *
35
+ * 通过 `apply()` → `forRoutes()` 链式 API 收集多组中间件条目,
36
+ * 在 `forRoutes()` 被调用时立即将中间件绑定到目标请求。
37
+ *
38
+ * 注意:该类并非 `@Injectable()`,由 Consumer 内部手动创建。
39
+ *
40
+ * @typeParam TConsumer - Consumer 类型(`KeqMiddlewareConsumer` 或 `KeqConsumer<T>`)
41
+ */
42
+ var KeqMiddlewareCollector = class {
34
43
  entries = [];
35
- apply(...middlewares) {
44
+ constructor(keqRequest, consumer) {
45
+ this.keqRequest = keqRequest;
46
+ this.consumer = consumer;
47
+ }
48
+ apply(middlewares) {
36
49
  const entry = {
37
50
  middlewares,
38
51
  routes: []
39
52
  };
40
53
  const excludedRouteInfos = [];
41
54
  this.entries.push(entry);
55
+ const { keqRequest, consumer } = this;
42
56
  const proxy = {
43
57
  exclude: (...routes) => {
44
58
  excludedRouteInfos.push(...routes);
@@ -47,14 +61,18 @@ var KeqMiddlewareConsumerImpl = class {
47
61
  forRoutes: (...routes) => {
48
62
  entry.routes = routes;
49
63
  if (excludedRouteInfos.length > 0) entry.excludedRouteInfos = excludedRouteInfos;
50
- return this;
64
+ this.applyTo(keqRequest);
65
+ return consumer;
51
66
  }
52
67
  };
53
68
  return proxy;
54
69
  }
55
- applyTo(keqRequest, modulesContainer) {
70
+ /**
71
+ * 将收集的所有中间件条目解析并应用到指定的 {@link KeqRequest} 实例。
72
+ */
73
+ applyTo(keqRequest) {
56
74
  for (const entry of this.entries) {
57
- let resolved = entry.middlewares.map((m) => this.resolveMiddleware(m, modulesContainer));
75
+ let resolved = entry.middlewares.map((m) => this.normalizeMiddleware(m));
58
76
  if (entry.excludedRouteInfos && entry.excludedRouteInfos.length > 0) {
59
77
  const excludedRoutes = entry.excludedRouteInfos.map((info) => this.buildRoute(info));
60
78
  resolved = resolved.map((mw) => this.wrapWithExclusion(mw, excludedRoutes));
@@ -62,56 +80,24 @@ var KeqMiddlewareConsumerImpl = class {
62
80
  if (entry.routes.length === 0 || entry.routes.some((r) => r === KEQ_ROUTES.ALL)) for (const mw of resolved) keqRequest.use(mw);
63
81
  else for (const routeTarget of entry.routes) {
64
82
  if (routeTarget === KEQ_ROUTES.ALL) continue;
65
- if (this.isKeqModuleClass(routeTarget)) {
66
- const targetRequest = this.findInModulesContainer(routeTarget, modulesContainer);
67
- if (targetRequest) for (const mw of resolved) targetRequest.use(mw);
68
- } else {
69
- const route = this.buildRoute(routeTarget);
70
- keqRequest.useRouter().route(route, resolved);
71
- }
83
+ const route = this.buildRoute(routeTarget);
84
+ keqRequest.useRouter().route(route, resolved);
72
85
  }
73
86
  }
74
87
  }
75
- isKeqModuleClass(route) {
76
- return typeof route === "function" && "KEQ_REQUEST" in route && typeof route.KEQ_REQUEST === "symbol";
77
- }
78
- isNestMiddlewareClass(m) {
79
- return typeof m === "function" && m.prototype !== void 0 && "use" in m.prototype;
80
- }
81
- findMiddlewareInstance(middlewareClass, modulesContainer) {
82
- for (const [, moduleWrapper] of modulesContainer) {
83
- const provider = moduleWrapper.providers.get(middlewareClass);
84
- if (provider?.instance) return provider.instance;
85
- }
86
- }
87
- resolveMiddleware(m, modulesContainer) {
88
- if (this.isNestMiddlewareClass(m)) {
89
- const instance = this.findMiddlewareInstance(m, modulesContainer);
90
- if (!instance) throw new Error(`[KeqModule] Nest middleware class '${m.name}' is not registered as a provider in any module. Ensure the middleware class is added to the providers array of a @Module() decorator.`);
91
- return async (ctx, next) => {
92
- await instance.use(ctx, next);
93
- };
94
- }
95
- return m;
96
- }
97
88
  /**
98
- * 从 ModulesContainer 中查找目标 KeqModule 的 KeqRequest 实例
99
- *
100
- * 通过 metatype 精确匹配模块,从模块 providers 中直接获取 KeqRequest。
101
- * 不依赖 ModuleRef 的作用域机制,直接从 NestJS 全局模块容器中查找。
102
- *
103
- * @param routeTarget - 目标 KeqModule 类,必须具有 KEQ_REQUEST symbol 属性
104
- * @param modulesContainer - NestJS 全局模块容器
105
- * @returns KeqRequest 实例,未找到时返回 undefined
89
+ * 标准化中间件:将不同形式的中间件统一为 {@link KeqMiddleware} 函数。
90
+ * - 若已是 {@link KeqMiddleware} 函数 → 直接返回
91
+ * - 若为 {@link KeqNestMiddleware} 实例对象 → 包装 `use` 方法为中间件函数
106
92
  */
107
- findInModulesContainer(routeTarget, modulesContainer) {
108
- const token = routeTarget.KEQ_REQUEST;
109
- for (const [, moduleWrapper] of modulesContainer) {
110
- if (moduleWrapper.metatype !== routeTarget) continue;
111
- const provider = moduleWrapper.providers.get(token);
112
- if (!provider) continue;
113
- if (provider.instance) return provider.instance;
114
- }
93
+ normalizeMiddleware(m) {
94
+ if (this.isNestMiddlewareInstance(m)) return async (ctx, next) => {
95
+ await m.use(ctx, next);
96
+ };
97
+ return m;
98
+ }
99
+ isNestMiddlewareInstance(m) {
100
+ return typeof m === "object" && m !== null && "use" in m;
115
101
  }
116
102
  buildRoute(info) {
117
103
  const routes = [];
@@ -143,11 +129,6 @@ var KeqMiddlewareConsumerImpl = class {
143
129
  }
144
130
  };
145
131
  //#endregion
146
- //#region src/utils/has-configure-keq-middleware.ts
147
- function hasConfigureKeqMiddleware(instance) {
148
- return instance !== null && instance !== void 0 && typeof instance.configureKeqMiddleware === "function";
149
- }
150
- //#endregion
151
132
  //#region \0@oxc-project+runtime@0.127.0/helpers/decorateMetadata.js
152
133
  function __decorateMetadata(k, v) {
153
134
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
@@ -161,86 +142,91 @@ function __decorate(decorators, target, key, desc) {
161
142
  return c > 3 && r && Object.defineProperty(target, key, r), r;
162
143
  }
163
144
  //#endregion
145
+ //#region src/keq-middleware-consumer.ts
146
+ var _ref;
147
+ let KeqMiddlewareConsumer = class KeqMiddlewareConsumer {
148
+ constructor(keqRequest) {
149
+ this.keqRequest = keqRequest;
150
+ }
151
+ apply(...middlewares) {
152
+ return new KeqMiddlewareCollector(this.keqRequest, this).apply(middlewares);
153
+ }
154
+ };
155
+ KeqMiddlewareConsumer = __decorate([(0, _nestjs_common.Injectable)(), __decorateMetadata("design:paramtypes", [typeof (_ref = typeof keq.KeqRequest !== "undefined" && keq.KeqRequest) === "function" ? _ref : Object])], KeqMiddlewareConsumer);
156
+ //#endregion
164
157
  //#region src/keq.module.ts
165
- var _ref, _ref2;
158
+ let KeqModule = class KeqModule {};
159
+ KeqModule = __decorate([(0, _nestjs_common.Global)(), (0, _nestjs_common.Module)({
160
+ providers: [{
161
+ provide: keq.KeqRequest,
162
+ useFactory: () => new keq.KeqRequest()
163
+ }, KeqMiddlewareConsumer],
164
+ exports: [keq.KeqRequest, KeqMiddlewareConsumer]
165
+ })], KeqModule);
166
+ //#endregion
167
+ //#region src/keq-consumer.ts
166
168
  /**
167
- * 全局标记,用于跨 KeqModule 实例去重
169
+ * 为指定 Keq 模块按路由注册中间件。
168
170
  *
169
- * 当 pnpm 等包管理器因为 peerDependencies 版本差异生成多个
170
- * `@keq-request/nestjs` 的 `.pnpm` 目录时,Node.js 会将它们视为不同模块,
171
- * 产生多个 `KeqModule` 类引用,导致 NestJS 创建多个 `KeqModule` 实例。
171
+ * Keq 模块由 `@keq-request/cli` 生成,通过
172
+ * {@link InjectKeqConsumer} 装饰器注入本实例。
172
173
  *
173
- * 每个实例的 `onModuleInit()` 都会遍历 `ModulesContainer` 并调用所有
174
- * 实现了 `configureKeqMiddleware` 的模块。通过 `Symbol.for()`(全局 Symbol
175
- * 注册表)在 `moduleWrapper.metatype` 上做标记,确保同一个模块类
176
- * 只被处理一次,无论有多少个 `KeqModule` 实例。
174
+ * @typeParam T - 目标模块类
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * constructor(
179
+ * \@InjectKeqConsumer(GeneratedModule) consumer: KeqConsumer<GeneratedModule>,
180
+ * ) {
181
+ * consumer.apply(authMw).forRoutes('*')
182
+ * }
183
+ * ```
177
184
  */
178
- const CONFIGURED_SYMBOL = Symbol.for("__keq_middleware_configured__");
185
+ var KeqConsumer = class {
186
+ constructor(keqRequest) {
187
+ this.keqRequest = keqRequest;
188
+ }
189
+ apply(...middlewares) {
190
+ return new KeqMiddlewareCollector(this.keqRequest, this).apply(middlewares);
191
+ }
192
+ };
193
+ //#endregion
194
+ //#region src/decorators/inject-keq-consumer.decorator.ts
179
195
  /**
180
- * 在 NestJS 模块初始化早期阶段校验 `configureKeqMiddleware` 的实现模块
181
- * 是否显式导入了 `KeqModule`。
196
+ * 注入绑定到指定 Keq Module 的 `KeqConsumer<T>`。
182
197
  *
183
- * NestJS 初始化分为两个严格串行的阶段:
184
- * 1. 全量扫描 — 所有模块 wrapper 加入 ModulesContainer,metatype 已设置,
185
- * DynamicModule 的 imports 已被解析并写入 moduleWrapper.imports Set
186
- * 2. 实例创建 — 按依赖顺序调用构造函数 → onModuleInit
198
+ * 被注入的 Module Class 必须同时包含 `KEQ_REQUEST` 和 `KEQ_CONSUMER` 静态属性,
199
+ * 两者均由 keq CLI 生成。
187
200
  *
188
- * 此校验在阶段 2 开头(构造函数中)执行,早于所有 onModuleInit,
189
- * 确保中间件缺失问题在启动期就被捕获,而非在业务模块 onModuleInit 发请求时静默失效。
201
+ * 使用方式:
202
+ * ```ts
203
+ * constructor(
204
+ * \@InjectKeqConsumer(GeneratedModule) consumer: KeqConsumer<GeneratedModule>,
205
+ * ) {
206
+ * consumer.apply(mw).forRoutes('*')
207
+ * }
208
+ * ```
190
209
  *
191
- * 使用运行时 imports Set 而非 Reflect.getMetadata('imports', metatype) 的原因:
192
- * - DynamicModule 通过 register() 返回的 imports 不在静态装饰器元数据中
193
- * - 从 ModulesContainer 获取 KeqModule metatype 可避免 pnpm 重复包实例导致的 === 比对失败
210
+ * @param moduleClass - 目标 KeqModule 类(需具有 `KEQ_REQUEST` 和 `KEQ_CONSUMER` symbol)
194
211
  */
195
- function validateConfigureKeqMiddlewareImports(modulesContainer) {
196
- let keqModuleMetatype;
197
- for (const [, moduleWrapper] of modulesContainer) if (moduleWrapper.metatype?.name === "KeqModule") {
198
- keqModuleMetatype = moduleWrapper.metatype;
199
- break;
200
- }
201
- for (const [, moduleWrapper] of modulesContainer) {
202
- const metatype = moduleWrapper.metatype;
203
- if (typeof metatype?.prototype?.configureKeqMiddleware !== "function") continue;
204
- if (![...moduleWrapper.imports].some((importedModule) => importedModule.metatype === keqModuleMetatype)) throw new Error(`[KeqModule] '${metatype.name}' implements configureKeqMiddleware() but does not explicitly import KeqModule. Add 'imports: [KeqModule]' to its @Module() decorator to ensure middleware is registered before the module's onModuleInit() runs.`);
205
- }
212
+ function InjectKeqConsumer(moduleClass) {
213
+ return (0, _nestjs_common.Inject)(moduleClass.KEQ_CONSUMER);
206
214
  }
207
- let KeqModule = class KeqModule {
208
- constructor(modulesContainer, keqRequest) {
209
- this.modulesContainer = modulesContainer;
210
- this.keqRequest = keqRequest;
211
- validateConfigureKeqMiddlewareImports(modulesContainer);
212
- }
213
- onModuleInit() {
214
- for (const [, moduleWrapper] of this.modulesContainer) {
215
- const instance = moduleWrapper.instance;
216
- if (!hasConfigureKeqMiddleware(instance)) continue;
217
- if (moduleWrapper.metatype[CONFIGURED_SYMBOL]) continue;
218
- moduleWrapper.metatype[CONFIGURED_SYMBOL] = true;
219
- const consumer = new KeqMiddlewareConsumerImpl();
220
- instance.configureKeqMiddleware(consumer);
221
- consumer.applyTo(this.keqRequest, this.modulesContainer);
222
- }
223
- }
224
- };
225
- KeqModule = __decorate([
226
- (0, _nestjs_common.Global)(),
227
- (0, _nestjs_common.Module)({
228
- providers: [{
229
- provide: keq.KeqRequest,
230
- useFactory: () => new keq.KeqRequest()
231
- }],
232
- exports: [keq.KeqRequest]
233
- }),
234
- __decorateMetadata("design:paramtypes", [typeof (_ref = typeof _nestjs_core.ModulesContainer !== "undefined" && _nestjs_core.ModulesContainer) === "function" ? _ref : Object, typeof (_ref2 = typeof keq.KeqRequest !== "undefined" && keq.KeqRequest) === "function" ? _ref2 : Object])
235
- ], KeqModule);
236
215
  //#endregion
216
+ exports.InjectKeqConsumer = InjectKeqConsumer;
237
217
  exports.KEQ_ROUTES = KEQ_ROUTES;
218
+ exports.KeqConsumer = KeqConsumer;
219
+ Object.defineProperty(exports, "KeqMiddlewareConsumer", {
220
+ enumerable: true,
221
+ get: function() {
222
+ return KeqMiddlewareConsumer;
223
+ }
224
+ });
238
225
  Object.defineProperty(exports, "KeqModule", {
239
226
  enumerable: true,
240
227
  get: function() {
241
228
  return KeqModule;
242
229
  }
243
230
  });
244
- exports.validateConfigureKeqMiddlewareImports = validateConfigureKeqMiddlewareImports;
245
231
 
246
232
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["KeqRequest"],"sources":["../src/constants.ts","../src/keq-middleware-consumer.ts","../src/utils/has-configure-keq-middleware.ts","../src/keq.module.ts"],"sourcesContent":["export const KEQ_ROUTES = {\n ALL: Symbol('KEQ_ROUTES.ALL'),\n} as const\n","import type { Type } from '@nestjs/common'\nimport type { ModulesContainer } from '@nestjs/core'\nimport type { KeqExecutionContext, KeqMiddleware, KeqRoute } from 'keq'\nimport { composeRoute, KeqRequest } from 'keq'\nimport picomatch from 'picomatch'\nimport { KEQ_ROUTES } from './constants.js'\nimport type { KeqMiddlewareConfigProxy, KeqMiddlewareConsumer, KeqModuleClass, KeqRouteInfo } from './interfaces/keq-middleware-consumer.interface.js'\nimport type { KeqNestMiddleware } from './interfaces/keq-nest-middleware.interface.js'\n\n\ninterface MiddlewareEntry {\n middlewares: Array<Type<KeqNestMiddleware> | KeqMiddleware>\n routes: Array<typeof KEQ_ROUTES.ALL | KeqRouteInfo | KeqModuleClass>\n excludedRouteInfos?: KeqRouteInfo[]\n}\n\nexport class KeqMiddlewareConsumerImpl implements KeqMiddlewareConsumer {\n private entries: MiddlewareEntry[] = []\n\n apply(...middlewares: Array<Type<KeqNestMiddleware> | KeqMiddleware>): KeqMiddlewareConfigProxy {\n const entry: MiddlewareEntry = { middlewares, routes: [] }\n const excludedRouteInfos: KeqRouteInfo[] = []\n this.entries.push(entry)\n\n const proxy: KeqMiddlewareConfigProxy = {\n exclude: (...routes) => {\n excludedRouteInfos.push(...routes)\n return proxy\n },\n forRoutes: (...routes) => {\n entry.routes = routes\n if (excludedRouteInfos.length > 0) {\n entry.excludedRouteInfos = excludedRouteInfos\n }\n return this\n },\n }\n return proxy\n }\n\n applyTo(keqRequest: KeqRequest, modulesContainer: ModulesContainer): void {\n for (const entry of this.entries) {\n let resolved = entry.middlewares.map((m) => this.resolveMiddleware(m, modulesContainer))\n\n if (entry.excludedRouteInfos && entry.excludedRouteInfos.length > 0) {\n const excludedRoutes = entry.excludedRouteInfos.map((info) => this.buildRoute(info))\n resolved = resolved.map((mw) => this.wrapWithExclusion(mw, excludedRoutes))\n }\n\n const isGlobal = entry.routes.length === 0\n || entry.routes.some((r) => r === KEQ_ROUTES.ALL)\n\n if (isGlobal) {\n for (const mw of resolved) keqRequest.use(mw)\n } else {\n for (const routeTarget of entry.routes) {\n if (routeTarget === KEQ_ROUTES.ALL) continue\n\n if (this.isKeqModuleClass(routeTarget)) {\n const targetRequest = this.findInModulesContainer(routeTarget, modulesContainer)\n if (targetRequest) {\n for (const mw of resolved) targetRequest.use(mw)\n }\n } else {\n const route = this.buildRoute(routeTarget as KeqRouteInfo)\n keqRequest.useRouter().route(route, resolved)\n }\n }\n }\n }\n }\n\n private isKeqModuleClass(route: unknown): route is KeqModuleClass {\n return (\n typeof route === 'function'\n && 'KEQ_REQUEST' in route\n && typeof (route as Record<string, unknown>).KEQ_REQUEST === 'symbol'\n )\n }\n\n private isNestMiddlewareClass(m: Type<KeqNestMiddleware> | KeqMiddleware): m is Type<KeqNestMiddleware> {\n return typeof m === 'function' && m.prototype !== undefined && 'use' in m.prototype\n }\n\n private findMiddlewareInstance(\n middlewareClass: Type<KeqNestMiddleware>,\n modulesContainer: ModulesContainer,\n ): KeqNestMiddleware | undefined {\n for (const [, moduleWrapper] of modulesContainer) {\n const provider = moduleWrapper.providers.get(middlewareClass)\n if (provider?.instance) {\n return provider.instance as KeqNestMiddleware\n }\n }\n return undefined\n }\n\n private resolveMiddleware(\n m: Type<KeqNestMiddleware> | KeqMiddleware,\n modulesContainer: ModulesContainer,\n ): KeqMiddleware {\n if (this.isNestMiddlewareClass(m)) {\n const instance = this.findMiddlewareInstance(m, modulesContainer)\n if (!instance) {\n throw new Error(\n `[KeqModule] Nest middleware class '${m.name}' is not registered as a provider in any module. `\n + 'Ensure the middleware class is added to the providers array of a @Module() decorator.',\n )\n }\n return async (ctx, next) => {\n await instance.use(ctx, next)\n }\n }\n return m\n }\n\n /**\n * 从 ModulesContainer 中查找目标 KeqModule 的 KeqRequest 实例\n *\n * 通过 metatype 精确匹配模块,从模块 providers 中直接获取 KeqRequest。\n * 不依赖 ModuleRef 的作用域机制,直接从 NestJS 全局模块容器中查找。\n *\n * @param routeTarget - 目标 KeqModule 类,必须具有 KEQ_REQUEST symbol 属性\n * @param modulesContainer - NestJS 全局模块容器\n * @returns KeqRequest 实例,未找到时返回 undefined\n */\n private findInModulesContainer(\n routeTarget: KeqModuleClass,\n modulesContainer: ModulesContainer,\n ): KeqRequest | undefined {\n const token = routeTarget.KEQ_REQUEST\n for (const [, moduleWrapper] of modulesContainer) {\n if (moduleWrapper.metatype !== routeTarget) continue\n\n const provider = moduleWrapper.providers.get(token)\n if (!provider) continue\n\n if (provider.instance) {\n return provider.instance as KeqRequest\n }\n }\n return undefined\n }\n\n private buildRoute(info: KeqRouteInfo): KeqRoute {\n const routes: KeqRoute[] = []\n if (info.host) {\n const host = info.host\n routes.push((ctx) => ctx.request.url.host === host)\n }\n if (info.method) {\n const method = info.method.toLowerCase()\n routes.push((ctx) => ctx.request.method.toLowerCase() === method)\n }\n if (info.pathname) {\n const isMatch: (test: string) => boolean = picomatch(info.pathname)\n routes.push((ctx) => isMatch(ctx.request.url.pathname))\n }\n if (routes.length === 1) return routes[0]\n return composeRoute(routes)\n }\n\n private wrapWithExclusion(middleware: KeqMiddleware, excludedRoutes: KeqRoute[]): KeqMiddleware {\n const excluded: KeqMiddleware = async (ctx, next) => {\n if (await this.checkExcluded(ctx, excludedRoutes)) {\n return next()\n }\n return middleware(ctx, next)\n }\n return excluded\n }\n\n private async checkExcluded(ctx: KeqExecutionContext, excludedRoutes: KeqRoute[]): Promise<boolean> {\n for (const route of excludedRoutes) {\n if (await route(ctx)) return true\n }\n return false\n }\n}\n","import type { KeqMiddlewareModule } from '../interfaces/keq-middleware-module.interface.js'\n\n\nexport function hasConfigureKeqMiddleware(instance: unknown): instance is KeqMiddlewareModule {\n return (\n instance !== null\n && instance !== undefined\n && typeof (instance as Record<string, unknown>).configureKeqMiddleware === 'function'\n )\n}\n","import { Global, Module, OnModuleInit, Type } from '@nestjs/common'\nimport { ModulesContainer } from '@nestjs/core'\nimport { KeqRequest } from 'keq'\nimport { KeqMiddlewareConsumerImpl } from './keq-middleware-consumer.js'\nimport { hasConfigureKeqMiddleware } from './utils/has-configure-keq-middleware.js'\n\n/**\n * 全局标记,用于跨 KeqModule 实例去重\n *\n * 当 pnpm 等包管理器因为 peerDependencies 版本差异生成多个\n * `@keq-request/nestjs` 的 `.pnpm` 目录时,Node.js 会将它们视为不同模块,\n * 产生多个 `KeqModule` 类引用,导致 NestJS 创建多个 `KeqModule` 实例。\n *\n * 每个实例的 `onModuleInit()` 都会遍历 `ModulesContainer` 并调用所有\n * 实现了 `configureKeqMiddleware` 的模块。通过 `Symbol.for()`(全局 Symbol\n * 注册表)在 `moduleWrapper.metatype` 上做标记,确保同一个模块类\n * 只被处理一次,无论有多少个 `KeqModule` 实例。\n */\nconst CONFIGURED_SYMBOL = Symbol.for('__keq_middleware_configured__')\n\n/**\n * 在 NestJS 模块初始化早期阶段校验 `configureKeqMiddleware` 的实现模块\n * 是否显式导入了 `KeqModule`。\n *\n * NestJS 初始化分为两个严格串行的阶段:\n * 1. 全量扫描 — 所有模块 wrapper 加入 ModulesContainer,metatype 已设置,\n * DynamicModule 的 imports 已被解析并写入 moduleWrapper.imports Set\n * 2. 实例创建 — 按依赖顺序调用构造函数 → onModuleInit\n *\n * 此校验在阶段 2 开头(构造函数中)执行,早于所有 onModuleInit,\n * 确保中间件缺失问题在启动期就被捕获,而非在业务模块 onModuleInit 发请求时静默失效。\n *\n * 使用运行时 imports Set 而非 Reflect.getMetadata('imports', metatype) 的原因:\n * - DynamicModule 通过 register() 返回的 imports 不在静态装饰器元数据中\n * - 从 ModulesContainer 获取 KeqModule metatype 可避免 pnpm 重复包实例导致的 === 比对失败\n */\nexport function validateConfigureKeqMiddlewareImports(modulesContainer: ModulesContainer): void {\n // 从 ModulesContainer 中查找 KeqModule 的 metatype,\n // 解决 pnpm 下 @keq-request/nestjs 存在多个包实例导致 === 引用比对失败的问题\n let keqModuleMetatype: Type<any> | undefined\n for (const [, moduleWrapper] of modulesContainer) {\n if (moduleWrapper.metatype?.name === 'KeqModule') {\n keqModuleMetatype = moduleWrapper.metatype\n break\n }\n }\n\n for (const [, moduleWrapper] of modulesContainer) {\n const metatype = moduleWrapper.metatype\n if (typeof metatype?.prototype?.configureKeqMiddleware !== 'function') continue\n\n // 使用运行时 imports Set(NestJS 扫描阶段已解析 DynamicModule 的 imports),\n // 替代 Reflect.getMetadata('imports', metatype),解决:\n // 1. DynamicModule 的 imports 无法通过静态元数据检测\n // 2. 重复包实例导致的类引用不匹配\n const hasKeqModule = [...moduleWrapper.imports].some(\n (importedModule) => importedModule.metatype === keqModuleMetatype,\n )\n\n if (!hasKeqModule) {\n throw new Error(\n `[KeqModule] '${metatype.name}' implements configureKeqMiddleware() `\n + 'but does not explicitly import KeqModule. '\n + 'Add \\'imports: [KeqModule]\\' to its @Module() decorator to ensure '\n + 'middleware is registered before the module\\'s onModuleInit() runs.',\n )\n }\n }\n}\n\n@Global()\n@Module({\n providers: [{ provide: KeqRequest, useFactory: () => new KeqRequest() }],\n exports: [KeqRequest],\n})\nexport class KeqModule implements OnModuleInit {\n constructor(\n private readonly modulesContainer: ModulesContainer,\n private readonly keqRequest: KeqRequest,\n ) {\n validateConfigureKeqMiddlewareImports(modulesContainer)\n }\n\n onModuleInit(): void {\n for (const [, moduleWrapper] of this.modulesContainer) {\n const instance = moduleWrapper.instance\n if (!hasConfigureKeqMiddleware(instance)) continue\n\n // 跨 KeqModule 实例去重:\n // moduleWrapper.metatype 是同一个类引用,Symbol.for 是全局共享 key\n if (moduleWrapper.metatype[CONFIGURED_SYMBOL]) continue\n moduleWrapper.metatype[CONFIGURED_SYMBOL] = true\n\n const consumer = new KeqMiddlewareConsumerImpl()\n instance.configureKeqMiddleware(consumer)\n consumer.applyTo(this.keqRequest, this.modulesContainer)\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["KeqRequest"],"sources":["../src/constants.ts","../src/keq-middleware-collector.ts","../src/keq-middleware-consumer.ts","../src/keq.module.ts","../src/keq-consumer.ts","../src/decorators/inject-keq-consumer.decorator.ts"],"sourcesContent":["export const KEQ_ROUTES = {\n ALL: Symbol('KEQ_ROUTES.ALL'),\n} as const\n","import type { KeqExecutionContext, KeqMiddleware, KeqRoute } from 'keq'\nimport { composeRoute, KeqRequest } from 'keq'\nimport picomatch from 'picomatch'\nimport { KEQ_ROUTES } from './constants.js'\nimport type { KeqMiddlewareConfigProxy, KeqRouteTarget, KeqRouteInfo } from './interfaces/keq-middleware-consumer.interface.js'\nimport type { KeqNestMiddleware } from './interfaces/keq-nest-middleware.interface.js'\n\n\n/** 单条中间件注册条目 */\ninterface MiddlewareEntry {\n middlewares: Array<KeqNestMiddleware | KeqMiddleware>\n routes: KeqRouteTarget[]\n excludedRouteInfos?: KeqRouteInfo[]\n}\n\n/**\n * 中间件收集器,收集中间件配置条目并应用到指定的 {@link KeqRequest} 实例。\n *\n * 通过 `apply()` → `forRoutes()` 链式 API 收集多组中间件条目,\n * 在 `forRoutes()` 被调用时立即将中间件绑定到目标请求。\n *\n * 注意:该类并非 `@Injectable()`,由 Consumer 内部手动创建。\n *\n * @typeParam TConsumer - Consumer 类型(`KeqMiddlewareConsumer` 或 `KeqConsumer<T>`)\n */\nexport class KeqMiddlewareCollector<TConsumer extends object = object> {\n private entries: MiddlewareEntry[] = []\n\n constructor(\n private readonly keqRequest: KeqRequest,\n private readonly consumer: TConsumer,\n ) {}\n\n apply(\n middlewares: Array<KeqNestMiddleware | KeqMiddleware>,\n ): KeqMiddlewareConfigProxy {\n const entry: MiddlewareEntry = { middlewares, routes: [] }\n const excludedRouteInfos: KeqRouteInfo[] = []\n this.entries.push(entry)\n\n const { keqRequest, consumer } = this\n\n const proxy = {\n exclude: (...routes: KeqRouteInfo[]) => {\n excludedRouteInfos.push(...routes)\n return proxy\n },\n forRoutes: (...routes: KeqRouteTarget[]) => {\n entry.routes = routes\n if (excludedRouteInfos.length > 0) {\n entry.excludedRouteInfos = excludedRouteInfos\n }\n this.applyTo(keqRequest)\n return consumer\n },\n }\n return proxy as unknown as KeqMiddlewareConfigProxy\n }\n\n /**\n * 将收集的所有中间件条目解析并应用到指定的 {@link KeqRequest} 实例。\n */\n applyTo(keqRequest: KeqRequest): void {\n for (const entry of this.entries) {\n let resolved = entry.middlewares.map((m) => this.normalizeMiddleware(m))\n\n if (entry.excludedRouteInfos && entry.excludedRouteInfos.length > 0) {\n const excludedRoutes = entry.excludedRouteInfos.map((info) => this.buildRoute(info))\n resolved = resolved.map((mw) => this.wrapWithExclusion(mw, excludedRoutes))\n }\n\n const isGlobal\n = entry.routes.length === 0 || entry.routes.some((r) => r === KEQ_ROUTES.ALL)\n\n if (isGlobal) {\n for (const mw of resolved) keqRequest.use(mw)\n } else {\n for (const routeTarget of entry.routes) {\n if (routeTarget === KEQ_ROUTES.ALL) continue\n const route = this.buildRoute(routeTarget as KeqRouteInfo)\n keqRequest.useRouter().route(route, resolved)\n }\n }\n }\n }\n\n // ─── 中间件标准化 ────────────────────────────────────\n\n /**\n * 标准化中间件:将不同形式的中间件统一为 {@link KeqMiddleware} 函数。\n * - 若已是 {@link KeqMiddleware} 函数 → 直接返回\n * - 若为 {@link KeqNestMiddleware} 实例对象 → 包装 `use` 方法为中间件函数\n */\n private normalizeMiddleware(\n m: KeqNestMiddleware | KeqMiddleware,\n ): KeqMiddleware {\n if (this.isNestMiddlewareInstance(m)) {\n return async (ctx, next) => {\n await m.use(ctx, next)\n }\n }\n return m\n }\n\n private isNestMiddlewareInstance(\n m: KeqNestMiddleware | KeqMiddleware,\n ): m is KeqNestMiddleware {\n return typeof m === 'object' && m !== null && 'use' in m\n }\n\n // ─── 路由构建 ─────────────────────────────────────────\n\n private buildRoute(info: KeqRouteInfo): KeqRoute {\n const routes: KeqRoute[] = []\n if (info.host) {\n const host = info.host\n routes.push((ctx) => ctx.request.url.host === host)\n }\n if (info.method) {\n const method = info.method.toLowerCase()\n routes.push((ctx) => ctx.request.method.toLowerCase() === method)\n }\n if (info.pathname) {\n const isMatch: (test: string) => boolean = picomatch(info.pathname)\n routes.push((ctx) => isMatch(ctx.request.url.pathname))\n }\n if (routes.length === 1) return routes[0]\n return composeRoute(routes)\n }\n\n private wrapWithExclusion(middleware: KeqMiddleware, excludedRoutes: KeqRoute[]): KeqMiddleware {\n const excluded: KeqMiddleware = async (ctx, next) => {\n if (await this.checkExcluded(ctx, excludedRoutes)) {\n return next()\n }\n return middleware(ctx, next)\n }\n return excluded\n }\n\n private async checkExcluded(\n ctx: KeqExecutionContext,\n excludedRoutes: KeqRoute[],\n ): Promise<boolean> {\n for (const route of excludedRoutes) {\n if (await route(ctx)) return true\n }\n return false\n }\n}\n","import { Injectable } from '@nestjs/common'\nimport type { KeqMiddleware } from 'keq'\nimport { KeqRequest } from 'keq'\nimport type { KeqMiddlewareConfigProxy } from './interfaces/keq-middleware-consumer.interface.js'\nimport type { KeqNestMiddleware } from './interfaces/keq-nest-middleware.interface.js'\nimport { KeqMiddlewareCollector } from './keq-middleware-collector.js'\n\n\n/**\n * 全局中间件消费者,提供 `.apply().forRoutes()` API。\n *\n * `forRoutes()` 返回 `KeqMiddlewareConsumer` 自身,支持链式注册:\n *\n * ```ts\n * consumer\n * .apply(mw1).forRoutes(KEQ_ROUTES.ALL)\n * .apply(mw2).forRoutes({ pathname: '/api/*' })\n * ```\n *\n * 模块级别的中间件请使用 `@InjectKeqConsumer(GeneratedModule)` 注入\n * `KeqConsumer<GeneratedModule>`,其 `forRoutes()` 直接作用于目标模块。\n */\n@Injectable()\nexport class KeqMiddlewareConsumer {\n constructor(private readonly keqRequest: KeqRequest) {}\n\n apply(\n ...middlewares: Array<KeqNestMiddleware | KeqMiddleware>\n ): KeqMiddlewareConfigProxy {\n const collector = new KeqMiddlewareCollector(this.keqRequest, this)\n return collector.apply(middlewares)\n }\n}\n","import { Global, Module } from '@nestjs/common'\nimport { KeqRequest } from 'keq'\nimport { KeqMiddlewareConsumer } from './keq-middleware-consumer.js'\n\n\n@Global()\n@Module({\n providers: [\n { provide: KeqRequest, useFactory: () => new KeqRequest() },\n KeqMiddlewareConsumer,\n ],\n exports: [KeqRequest, KeqMiddlewareConsumer],\n})\nexport class KeqModule {}\n","import type { KeqMiddleware } from 'keq'\nimport { KeqRequest } from 'keq'\nimport type { KeqModuleClass, KeqScopedConfigProxy } from './interfaces/keq-middleware-consumer.interface.js'\nimport type { KeqNestMiddleware } from './interfaces/keq-nest-middleware.interface.js'\nimport { KeqMiddlewareCollector } from './keq-middleware-collector.js'\n\n\n/**\n * 为指定 Keq 模块按路由注册中间件。\n *\n * Keq 模块由 `@keq-request/cli` 生成,通过\n * {@link InjectKeqConsumer} 装饰器注入本实例。\n *\n * @typeParam T - 目标模块类\n *\n * @example\n * ```ts\n * constructor(\n * \\@InjectKeqConsumer(GeneratedModule) consumer: KeqConsumer<GeneratedModule>,\n * ) {\n * consumer.apply(authMw).forRoutes('*')\n * }\n * ```\n */\nexport class KeqConsumer<T = KeqModuleClass> {\n constructor(private readonly keqRequest: KeqRequest) {}\n\n apply(\n ...middlewares: Array<KeqNestMiddleware | KeqMiddleware>\n ): KeqScopedConfigProxy<T> {\n const collector = new KeqMiddlewareCollector(this.keqRequest, this)\n return collector.apply(middlewares) as unknown as KeqScopedConfigProxy<T>\n }\n}\n","import { Inject } from '@nestjs/common'\nimport type { KeqModuleClass } from '../interfaces/keq-middleware-consumer.interface.js'\n\n\n/**\n * 注入绑定到指定 Keq Module 的 `KeqConsumer<T>`。\n *\n * 被注入的 Module Class 必须同时包含 `KEQ_REQUEST` 和 `KEQ_CONSUMER` 静态属性,\n * 两者均由 keq CLI 生成。\n *\n * 使用方式:\n * ```ts\n * constructor(\n * \\@InjectKeqConsumer(GeneratedModule) consumer: KeqConsumer<GeneratedModule>,\n * ) {\n * consumer.apply(mw).forRoutes('*')\n * }\n * ```\n *\n * @param moduleClass - 目标 KeqModule 类(需具有 `KEQ_REQUEST` 和 `KEQ_CONSUMER` symbol)\n */\nexport function InjectKeqConsumer<T extends KeqModuleClass>(\n moduleClass: T,\n): ParameterDecorator {\n return Inject(moduleClass.KEQ_CONSUMER)\n}\n"],"mappings":"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"}