vona-module-a-web 5.0.9 → 5.0.11

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.
@@ -0,0 +1,89 @@
1
+ import type { BeanScopeUtil, TypeModuleConfig } from 'vona';
2
+ import type { IDecoratorStartupOptions } from 'vona-module-a-startup';
3
+ /** bean: end */
4
+ /** bean: begin */
5
+ import type { BeanRouter } from '../bean/bean.router.ts';
6
+ import type { config } from '../config/config.ts';
7
+ /** service: end */
8
+ /** service: begin */
9
+ import type { ServiceWeb } from '../service/web.ts';
10
+ /** main: end */
11
+ /** scope: begin */
12
+ import { BeanScopeBase } from 'vona';
13
+ /** service: end */
14
+ /** service: begin */
15
+ /** bean: begin */
16
+ import 'vona';
17
+ import 'vona';
18
+ import 'vona';
19
+ import 'vona';
20
+ import 'vona';
21
+ export * from '../bean/bean.router.ts';
22
+ declare module 'vona' {
23
+ }
24
+ declare module 'vona-module-a-web' {
25
+ interface BeanRouter {
26
+ }
27
+ }
28
+ declare module 'vona' {
29
+ interface IBeanRecordGlobal {
30
+ router: BeanRouter;
31
+ }
32
+ }
33
+ /** bean: end */
34
+ /** startup: begin */
35
+ export * from '../bean/startup.listen.ts';
36
+ declare module 'vona-module-a-startup' {
37
+ interface IStartupRecord {
38
+ 'a-web:listen': IDecoratorStartupOptions;
39
+ }
40
+ }
41
+ declare module 'vona-module-a-web' {
42
+ interface StartupListen {
43
+ }
44
+ }
45
+ /** service: end */
46
+ /** config: begin */
47
+ export * from '../config/config.ts';
48
+ declare module 'vona-module-a-web' {
49
+ interface IServiceRecord {
50
+ 'a-web:web': never;
51
+ }
52
+ }
53
+ declare module 'vona-module-a-web' {
54
+ interface ServiceWeb {
55
+ }
56
+ }
57
+ export interface IModuleService {
58
+ web: ServiceWeb;
59
+ }
60
+ declare module 'vona' {
61
+ interface IBeanRecordGeneral {
62
+ 'a-web.service.web': ServiceWeb;
63
+ }
64
+ }
65
+ /** config: end */
66
+ /** main: begin */
67
+ export * from '../main.ts';
68
+ /** startup: end */
69
+ /** service: begin */
70
+ export * from '../service/web.ts';
71
+ export declare class ScopeModuleAWeb extends BeanScopeBase {
72
+ }
73
+ export interface ScopeModuleAWeb {
74
+ util: BeanScopeUtil;
75
+ config: TypeModuleConfig<typeof config>;
76
+ service: IModuleService;
77
+ }
78
+ declare module 'vona' {
79
+ interface IBeanScopeRecord {
80
+ 'a-web': ScopeModuleAWeb;
81
+ }
82
+ interface IBeanScopeContainer {
83
+ web: ScopeModuleAWeb;
84
+ }
85
+ interface IBeanScopeConfig {
86
+ 'a-web': ReturnType<typeof config>;
87
+ }
88
+ }
89
+ /** scope: end */
@@ -0,0 +1,2 @@
1
+ export declare const __ThisModule__ = "a-web";
2
+ export { ScopeModuleAWeb as ScopeModule } from './index.ts';
@@ -0,0 +1,12 @@
1
+ import type { Constructable, VonaContext } from 'vona';
2
+ import * as ModuleInfo from '@cabloy/module-info';
3
+ import Router from 'find-my-way';
4
+ import { BeanBase } from 'vona';
5
+ export declare class BeanRouter extends BeanBase {
6
+ registerController(moduleName: string, controller: Constructable): void;
7
+ register(method: Router.HTTPMethod, moduleName: ModuleInfo.IModuleInfo | string, path: string | undefined, simplify: boolean, fn: Router.Handler<Router.HTTPVersion.V1>): void;
8
+ unRegister(method: Router.HTTPMethod, moduleName: ModuleInfo.IModuleInfo | string, path: string | undefined, simplify: boolean): void;
9
+ findByPath(method: Router.HTTPMethod, moduleName: ModuleInfo.IModuleInfo | string, path: string | undefined, simplify: boolean): any;
10
+ private _registerControllerAction;
11
+ _registerComposeMiddlewares(ctx: VonaContext): (context: any, next?: any) => any;
12
+ }
@@ -0,0 +1,7 @@
1
+ import type { IStartupExecute } from 'vona-module-a-startup';
2
+ import { BeanBase } from 'vona';
3
+ export declare class StartupListen extends BeanBase implements IStartupExecute {
4
+ execute(): Promise<void>;
5
+ private _listen;
6
+ private _callback;
7
+ }
@@ -0,0 +1,6 @@
1
+ import type Router from 'find-my-way';
2
+ import type { Config } from 'find-my-way';
3
+ import type { VonaApplication } from 'vona';
4
+ export declare function config(_app: VonaApplication): {
5
+ router: Config<Router.HTTPVersion.V1>;
6
+ };
@@ -0,0 +1,3 @@
1
+ export * from './.metadata/index.ts';
2
+ export * from './lib/index.ts';
3
+ export * from './types/index.ts';
package/dist/index.js ADDED
@@ -0,0 +1,614 @@
1
+ import { appMetadata, BeanInfo, BeanBase, appResource, deepExtend, compose, cast, BeanSimple, BeanScopeBase, createBeanDecorator } from 'vona';
2
+ import { Bean, Scope } from 'vona-module-a-bean';
3
+ import * as ModuleInfo from '@cabloy/module-info';
4
+ import { SymbolUseOnionOptions } from 'vona-module-a-onion';
5
+ import { SymbolRouteHandlersArgumentsValue, SymbolRouteHandlersArgumentsMeta, SymbolOpenApiOptions, mergeFieldsOpenAPIMetadata } from 'vona-module-a-openapi';
6
+ import { valid } from 'vona-module-a-validation';
7
+ import { SymbolUploadValue } from 'vona-module-a-upload';
8
+ import http from 'node:http';
9
+ import { Startup } from 'vona-module-a-startup';
10
+ import Router from 'find-my-way';
11
+ import { SymbolRouterMiddleware } from 'vona-module-a-executor';
12
+ import { Service as Service$1, SymbolRequestMappingHandler as SymbolRequestMappingHandler$1 } from 'vona-module-a-web';
13
+ import { combineParamsAndQuery } from '@cabloy/utils';
14
+
15
+ async function middlewareGuard(ctx, next) {
16
+ // check handler
17
+ const handler = ctx.getHandler();
18
+ if (!handler) return next();
19
+ // compose
20
+ const result = await ctx.app.bean.onion.guard.compose(ctx)(ctx);
21
+ if (result === false) ctx.app.throw(403);
22
+ // next
23
+ return next();
24
+ }
25
+
26
+ async function middlewareInterceptor(ctx, next) {
27
+ // check handler
28
+ const handler = ctx.getHandler();
29
+ if (!handler) return next();
30
+ // compose
31
+ return await ctx.app.bean.onion.interceptor.compose(ctx)(ctx, next);
32
+ }
33
+
34
+ function extractValue(ctx, argMeta) {
35
+ return exchangeKeyForValue(ctx, argMeta.type, argMeta.field);
36
+ }
37
+ function exchangeKeyForValue(ctx, type, field) {
38
+ const req = ctx.request;
39
+ const res = ctx.response;
40
+ const modes = {
41
+ request: () => req,
42
+ response: () => res,
43
+ body: () => field && req.body ? req.body[field] : req.body,
44
+ query: () => field ? req.query[field] : req.query,
45
+ param: () => field ? req.params[field] : req.params,
46
+ headers: () => field ? req.headers[field.toLowerCase()] : req.headers,
47
+ host: () => {
48
+ const hosts = req.hosts || {};
49
+ return field ? hosts[field] : hosts;
50
+ },
51
+ ip: () => req.ip,
52
+ rawBody: () => req.rawBody,
53
+ user: () => ctx.app.bean.passport.getCurrentUser(),
54
+ fields: () => {
55
+ const uploadValue = ctx[SymbolUploadValue];
56
+ if (!uploadValue) throw new Error('should use interceptor: a-upload:upload');
57
+ return field ? uploadValue.fields.filter(item => item.name === field).map(item => item.value) : uploadValue.fields;
58
+ },
59
+ field: () => {
60
+ const uploadValue = ctx[SymbolUploadValue];
61
+ if (!uploadValue) throw new Error('should use interceptor: a-upload:upload');
62
+ return field ? uploadValue.fields.find(item => item.name === field)?.value : uploadValue.fields[0]?.value;
63
+ },
64
+ files: () => {
65
+ const uploadValue = ctx[SymbolUploadValue];
66
+ if (!uploadValue) throw new Error('should use interceptor: a-upload:upload');
67
+ return field ? uploadValue.files.filter(item => item.name === field) : uploadValue.files;
68
+ },
69
+ file: () => {
70
+ const uploadValue = ctx[SymbolUploadValue];
71
+ if (!uploadValue) throw new Error('should use interceptor: a-upload:upload');
72
+ return field ? uploadValue.files.find(item => item.name === field) : uploadValue.files[0];
73
+ }
74
+ };
75
+ return modes[type]?.();
76
+ }
77
+
78
+ async function middlewarePipe(ctx, next) {
79
+ // check handler
80
+ const handler = ctx.getHandler();
81
+ if (!handler) return next();
82
+ // body parser
83
+ await ctx.app.bean._getBean('a-body.service.body').parse(true);
84
+ // arguments
85
+ ctx[SymbolRouteHandlersArgumentsValue] = await _transformArguments(ctx, ctx.getController(), handler);
86
+ // next
87
+ return next();
88
+ }
89
+ async function _transformArguments(ctx, controller, handler) {
90
+ const paramtypes = appMetadata.getMetadata('design:paramtypes', controller.prototype, handler.name);
91
+ if (!paramtypes) return;
92
+
93
+ // meta
94
+ const argsMeta = appMetadata.getMetadata(SymbolRouteHandlersArgumentsMeta, controller.prototype, handler.name);
95
+ if (!argsMeta) return;
96
+
97
+ // args
98
+ const args = Array.from({
99
+ length: paramtypes.length
100
+ });
101
+ for (let index = args.length - 1; index >= 0; index--) {
102
+ const argMeta = argsMeta.find(item => item.index === index);
103
+ if (!argMeta) continue;
104
+ // extractValue
105
+ const value = await _extractArgumentValue(ctx, argMeta);
106
+ // metadata
107
+ const metadata = {
108
+ type: argMeta.type,
109
+ field: argMeta.field,
110
+ metaType: paramtypes[index],
111
+ controller,
112
+ method: handler.name,
113
+ index: argMeta.index
114
+ };
115
+ // transform
116
+ args[index] = await _transformArgument(ctx, argMeta, metadata, value);
117
+ }
118
+ return args;
119
+ }
120
+ async function _transformArgument(ctx, argMeta, metadata, value) {
121
+ // pipes
122
+ const pipes = composePipes(ctx, argMeta, (beanInstance, value, options, _next) => {
123
+ return beanInstance.transform(value, metadata, options);
124
+ });
125
+ if (pipes.length === 0) return value;
126
+ // apply
127
+ for (const pipe of pipes) {
128
+ value = await pipe(value, _pipeNextDefault);
129
+ }
130
+ return value;
131
+ }
132
+ function _pipeNextDefault(value) {
133
+ return value;
134
+ }
135
+ async function _extractArgumentValue(ctx, argMeta) {
136
+ if (argMeta.extractValue) {
137
+ return await argMeta.extractValue(ctx, argMeta);
138
+ }
139
+ return extractValue(ctx, argMeta);
140
+ }
141
+ const SymbolCacheMiddlewaresArgument = Symbol('SymbolCacheMiddlewaresArgument');
142
+ function composePipes(ctx, argMeta, executeCustom) {
143
+ if (!ctx.app.meta[SymbolCacheMiddlewaresArgument]) ctx.app.meta[SymbolCacheMiddlewaresArgument] = {};
144
+ const __cacheMiddlewaresArgument = ctx.app.meta[SymbolCacheMiddlewaresArgument];
145
+ const onionPipe = ctx.app.bean.onion.pipe;
146
+ const beanFullName = ctx.getControllerBeanFullName();
147
+ const handlerName = ctx.getHandler().name;
148
+ const key = `${beanFullName}:${handlerName}:${argMeta.index}`;
149
+ if (!__cacheMiddlewaresArgument[key]) {
150
+ const middlewares = [];
151
+ // pipes: global
152
+ for (const item of onionPipe.onionsGlobal) {
153
+ middlewares.push(onionPipe._wrapOnion(item, executeCustom));
154
+ }
155
+ // pipes: route
156
+ const middlewaresLocal = onionPipe._collectOnionsHandler(ctx);
157
+ for (const item of middlewaresLocal) {
158
+ middlewares.push(onionPipe._wrapOnion(item, executeCustom));
159
+ }
160
+ // pipes: arguments
161
+ const middlewaresArgument = _collectArgumentMiddlewares(onionPipe, argMeta);
162
+ if (middlewaresArgument) {
163
+ for (const item of middlewaresArgument) {
164
+ middlewares.push(onionPipe._wrapOnion(item, executeCustom));
165
+ }
166
+ }
167
+ __cacheMiddlewaresArgument[key] = middlewares;
168
+ }
169
+ return __cacheMiddlewaresArgument[key];
170
+ }
171
+ function _collectArgumentMiddlewares(onionPipe, argMeta) {
172
+ if (!argMeta.pipes) return;
173
+ return argMeta.pipes.map(pipe => {
174
+ if (typeof pipe !== 'function') {
175
+ pipe = valid({
176
+ schema: pipe
177
+ });
178
+ }
179
+ const {
180
+ pipeName,
181
+ options
182
+ } = pipe();
183
+ const item = onionPipe.onionsNormal[pipeName];
184
+ if (!item) throw new Error(`${onionPipe.sceneName} not found: ${pipeName}`);
185
+ return {
186
+ ...item,
187
+ argumentPipe: {
188
+ options
189
+ }
190
+ };
191
+ });
192
+ }
193
+
194
+ const SymbolRequestMappingHandler = Symbol('SymbolRequestMappingHandler');
195
+ let RequestMethod = /*#__PURE__*/function (RequestMethod) {
196
+ RequestMethod["GET"] = "get";
197
+ RequestMethod["POST"] = "post";
198
+ RequestMethod["PUT"] = "put";
199
+ RequestMethod["DELETE"] = "delete";
200
+ RequestMethod["PATCH"] = "patch";
201
+ RequestMethod["OPTIONS"] = "options";
202
+ RequestMethod["HEAD"] = "head";
203
+ return RequestMethod;
204
+ }({});
205
+
206
+ var _dec$3, _dec2$3, _class$3;
207
+ const SymbolRouteComposeMiddlewaresCache = Symbol('SymbolRouteComposeMiddlewaresCache');
208
+ let BeanRouter = (_dec$3 = Bean(), _dec2$3 = BeanInfo({
209
+ module: "a-web"
210
+ }), _dec$3(_class$3 = _dec2$3(_class$3 = class BeanRouter extends BeanBase {
211
+ registerController(moduleName, controller) {
212
+ // info
213
+ const info = ModuleInfo.parseInfo(moduleName);
214
+ // controller options
215
+ const beanOptions = appResource.getBean(controller);
216
+ if (!beanOptions) return;
217
+ const controllerBeanFullName = beanOptions.beanFullName;
218
+ const controllerOptions = beanOptions.options;
219
+ const controllerPath = controllerOptions.path;
220
+ const controllerMiddlewaresOptions = appMetadata.getMetadata(SymbolUseOnionOptions, controller);
221
+ // descs
222
+ const descs = Object.getOwnPropertyDescriptors(controller.prototype);
223
+ for (const actionKey in descs) {
224
+ const desc = descs[actionKey];
225
+ if (['constructor'].includes(actionKey)) continue;
226
+ if (!desc.value || typeof desc.value !== 'function') continue;
227
+ this._registerControllerAction(info, controller, controllerBeanFullName, controllerPath, controllerMiddlewaresOptions, actionKey, desc);
228
+ }
229
+ }
230
+ register(method, moduleName, path, simplify, fn) {
231
+ const app = this.app;
232
+ const _path = app.util.combineApiPath(path, moduleName, true, simplify);
233
+ app.router.on(method, _path, fn);
234
+ }
235
+ unRegister(method, moduleName, path, simplify) {
236
+ const app = this.app;
237
+ const _path = app.util.combineApiPath(path, moduleName, true, simplify);
238
+ app.router.off(method, _path);
239
+ }
240
+ findByPath(method, moduleName, path, simplify) {
241
+ const app = this.app;
242
+ const _path = app.util.combineApiPath(path, moduleName, true, simplify);
243
+ return app.router.findRoute(method, _path);
244
+ }
245
+ _registerControllerAction(info, controller, controllerBeanFullName, controllerPath, controllerMiddlewaresOptions, actionKey, desc) {
246
+ // app
247
+ const app = this.app;
248
+
249
+ // actionPath/actionMethod
250
+ if (!appMetadata.hasMetadata(SymbolRequestMappingHandler, controller.prototype, actionKey)) return;
251
+ const handlerMetadata = appMetadata.getMetadata(SymbolRequestMappingHandler, controller.prototype, actionKey);
252
+ const actionPath = handlerMetadata.path || '';
253
+ const actionMethod = handlerMetadata.method || RequestMethod.GET;
254
+ // routePath
255
+ const routePath = app.util.combineApiPathControllerAndAction(info.relativeName, controllerPath, actionPath, true, true);
256
+ const routePathRaw = app.util.combineApiPathControllerAndActionRaw(info.relativeName, controllerPath, actionPath, true);
257
+
258
+ // middlewares options
259
+ const actionMiddlewaresOptions = appMetadata.getMetadata(SymbolUseOnionOptions, controller.prototype, actionKey);
260
+
261
+ // route
262
+ const route = {
263
+ meta: deepExtend({}, controllerMiddlewaresOptions, actionMiddlewaresOptions)
264
+ };
265
+
266
+ // route
267
+ const _route = {
268
+ pid: info.pid,
269
+ module: info.name,
270
+ controller,
271
+ actionDescriptor: desc,
272
+ controllerBeanFullName,
273
+ action: actionKey,
274
+ route,
275
+ routeMethod: actionMethod,
276
+ routePath,
277
+ routePathRaw
278
+ };
279
+
280
+ // fn
281
+ const self = this;
282
+ const fn = function (_req, _res, params, _store, searchParams) {
283
+ const ctx = this;
284
+ ctx.route = _route;
285
+ ctx.request.params = params;
286
+ ctx.request.query = searchParams;
287
+ if (!_route[SymbolRouteComposeMiddlewaresCache]) {
288
+ _route[SymbolRouteComposeMiddlewaresCache] = self._registerComposeMiddlewares(ctx);
289
+ }
290
+ return _route[SymbolRouteComposeMiddlewaresCache](ctx);
291
+ };
292
+
293
+ // register
294
+ app.router.on(_route.routeMethod.toUpperCase(), _route.routePath.toString(), fn);
295
+ }
296
+ _registerComposeMiddlewares(ctx) {
297
+ // start
298
+ const fnStart = routeStartMiddleware;
299
+ // mid: guard/interceptor/pipes/tail
300
+ const fnMid = [];
301
+ fnMid.push(middlewareGuard);
302
+ fnMid.push(middlewareInterceptor);
303
+ fnMid.push(middlewarePipe);
304
+ fnMid.push(routeTailDoneMiddleware);
305
+ // end: controller
306
+ const fnEnd = classControllerMiddleware;
307
+ // compose
308
+ return this.app.bean.onion.middleware.compose(ctx, fnStart, fnMid, fnEnd);
309
+ }
310
+ }) || _class$3) || _class$3);
311
+ function classControllerMiddleware(ctx) {
312
+ const beanFullName = ctx.getControllerBeanFullName();
313
+ const handlerName = ctx.getHandler().name;
314
+ const controller = ctx.app.bean._getBean(beanFullName);
315
+ return controller[handlerName](...(ctx[SymbolRouteHandlersArgumentsValue] || []));
316
+ }
317
+ async function routeStartMiddleware(ctx, next) {
318
+ // next
319
+ const res = await next();
320
+ // invoke callbackes: handle secondly
321
+ await ctx.commitDone();
322
+ // ok
323
+ return res;
324
+ }
325
+ async function routeTailDoneMiddleware(ctx, next) {
326
+ // next
327
+ const res = await next();
328
+ // invoke callbackes: handle firstly
329
+ await ctx.commitDone();
330
+ // ok
331
+ return res;
332
+ }
333
+
334
+ // _registerInner(route, middlewaresLocal) {
335
+ // // app
336
+ // const app = this.app;
337
+ // // args
338
+ // let args: any[] = [];
339
+ // // middlewares: start
340
+ // const fnStart = async (ctx: VonaContext, next: Next) => {
341
+ // // route
342
+ // ctx.route = route;
343
+ // // next
344
+ // const res = await next();
345
+ // // invoke callbackes: handle secondly
346
+ // await ctx.commitDone();
347
+ // // ok
348
+ // return res;
349
+ // };
350
+ // fnStart._name = 'start';
351
+ // args.push(fnStart);
352
+
353
+ // // middlewares: globals
354
+ // args.push(...app.bean.onion.middleware.composedOnionsGlobal);
355
+ // // middlewares: guard/interceptor/pipes
356
+ // args.push(middlewareGuard);
357
+ // args.push(middlewareInterceptor);
358
+ // args.push(middlewarePipe);
359
+
360
+ // // middlewares: tailDone
361
+ // const fnTailDone = async (ctx, next) => {
362
+ // // next
363
+ // const res = await next();
364
+ // // invoke callbackes: handle firstly
365
+ // await ctx.commitDone();
366
+ // // ok
367
+ // return res;
368
+ // };
369
+ // fnTailDone._name = 'tailDone';
370
+ // args.push(fnTailDone);
371
+
372
+ // // middlewares
373
+ // if (middlewaresLocal.length > 0) {
374
+ // args = args.concat(middlewaresLocal);
375
+ // }
376
+
377
+ // // load
378
+ // if (route.routeName) {
379
+ // app.router[route.routeMethod](route.routeName, route.routePath, ...args);
380
+ // } else {
381
+ // app.router[route.routeMethod](route.routePath, ...args);
382
+ // }
383
+ // }
384
+
385
+ var _dec$2, _dec2$2, _class$2;
386
+ let StartupListen = (_dec$2 = Startup({
387
+ after: true
388
+ }), _dec2$2 = BeanInfo({
389
+ module: "a-web"
390
+ }), _dec$2(_class$2 = _dec2$2(_class$2 = class StartupListen extends BeanBase {
391
+ async execute() {
392
+ if (!this.app.config.server.listen.disable) {
393
+ this.app.server = this._listen(this.app.config.server.listen.port, this.app.config.server.listen.hostname);
394
+ }
395
+ }
396
+ _listen(port, hostname) {
397
+ const server = http.createServer(this._callback());
398
+ return server.listen(port, hostname);
399
+ }
400
+ _callback() {
401
+ const fn = compose(this.app.middleware);
402
+ if (!this.app.listenerCount('error')) this.app.on('error', this.app.onerror);
403
+ return (req, res) => {
404
+ return this.app.bean.executor.newCtx(() => {
405
+ return cast(this.app).handleRequest(this.app.ctx, fn);
406
+ }, {
407
+ dbInfo: {
408
+ level: 0
409
+ },
410
+ innerAccess: false,
411
+ req,
412
+ res
413
+ }); // not set instanceName
414
+ };
415
+ }
416
+ }) || _class$2) || _class$2);
417
+
418
+ function config(_app) {
419
+ return {
420
+ router: {
421
+ maxParamLength: 500,
422
+ defaultRoute: (_req, _res) => {}
423
+ }
424
+ };
425
+ }
426
+
427
+ const __ThisModule__ = 'a-web';
428
+
429
+ const SymbolRouter = Symbol('SymbolRouter');
430
+ class Main extends BeanSimple {
431
+ constructor(...args) {
432
+ super(...args);
433
+ this[SymbolRouter] = void 0;
434
+ }
435
+ async moduleLoading() {}
436
+ async moduleLoaded() {
437
+ const config = this.bean.scope(__ThisModule__).config;
438
+ const self = this;
439
+ // router
440
+ Object.defineProperty(this.app, 'router', {
441
+ get() {
442
+ if (!self[SymbolRouter]) {
443
+ self[SymbolRouter] = Router(config.router);
444
+ }
445
+ return self[SymbolRouter];
446
+ }
447
+ });
448
+ // register controllers
449
+ for (const controller of this.bean.onion.controller.getOnionsEnabled()) {
450
+ this.bean.router.registerController(controller.beanOptions.module, controller.beanOptions.beanClass);
451
+ }
452
+ // middleware: system
453
+ const middlewares = this.bean.onion.middlewareSystem.getOnionsEnabledWrapped(item => {
454
+ return this._wrapOnion(item);
455
+ });
456
+ this.app.use(compose(middlewares));
457
+ // middleware: router
458
+ this.app[SymbolRouterMiddleware] = routerMiddleware(this[SymbolRouter]);
459
+ this.app.use(this.app[SymbolRouterMiddleware]);
460
+ }
461
+ async configLoaded(_config) {}
462
+ _wrapOnion(item) {
463
+ const fn = (_ctx, next) => {
464
+ const options = item.beanOptions.options;
465
+ if (!this.bean.onion.checkOnionOptionsEnabled(options, this.ctx.path)) {
466
+ return next();
467
+ }
468
+ // execute
469
+ const beanFullName = item.beanOptions.beanFullName;
470
+ const beanInstance = this.app.bean._getBean(beanFullName);
471
+ if (!beanInstance) {
472
+ throw new Error(`middlewareSystem bean not found: ${beanFullName}`);
473
+ }
474
+ return beanInstance.execute(options, next);
475
+ };
476
+ fn._name = item.name;
477
+ return fn;
478
+ }
479
+ }
480
+ function routerMiddleware(router) {
481
+ return function (ctx) {
482
+ return new Promise((resolve, reject) => {
483
+ router.lookup(ctx.req, ctx.res, ctx, (err, result) => {
484
+ if (err) reject(err);
485
+ resolve(result);
486
+ });
487
+ });
488
+ };
489
+ }
490
+
491
+ var _dec$1, _dec2$1, _class$1;
492
+ let ServiceWeb = (_dec$1 = Service$1(), _dec2$1 = BeanInfo({
493
+ module: "a-web"
494
+ }), _dec$1(_class$1 = _dec2$1(_class$1 = class ServiceWeb extends BeanBase {
495
+ combineControllerActionApiPath(controller, actionKey, prefix, simplify) {
496
+ // beanOptions
497
+ const beanOptions = appResource.getBean(controller);
498
+ if (!beanOptions) return undefined;
499
+ // controllerPath
500
+ const controllerOptions = beanOptions.options;
501
+ const controllerPath = controllerOptions.path;
502
+ // actionPath
503
+ const handlerMetadata = appMetadata.getMetadata(SymbolRequestMappingHandler$1, controller.prototype, actionKey);
504
+ const actionPath = handlerMetadata.path || '';
505
+ // combine
506
+ return this.app.util.combineApiPathControllerAndAction(beanOptions.module, controllerPath, actionPath, prefix, simplify);
507
+ }
508
+ }) || _class$1) || _class$1);
509
+
510
+ var _dec, _dec2, _class;
511
+ let ScopeModuleAWeb = (_dec = Scope(), _dec2 = BeanInfo({
512
+ module: "a-web"
513
+ }), _dec(_class = _dec2(_class = class ScopeModuleAWeb extends BeanScopeBase {}) || _class) || _class);
514
+
515
+ /** scope: end */
516
+
517
+ function Controller(path, options) {
518
+ if (typeof path === 'string') {
519
+ options = Object.assign({}, options, {
520
+ path
521
+ });
522
+ } else {
523
+ options = path || {};
524
+ }
525
+ return createBeanDecorator('controller', options, false, false, target => {
526
+ // IOpenApiOptions
527
+ const optionsMeta = appMetadata.getOwnMetadataMap(false, SymbolOpenApiOptions, target);
528
+ for (const key of ['exclude', 'tags']) {
529
+ if (options[key] !== undefined) optionsMeta[key] = options[key];
530
+ }
531
+ // IOpenApiOptions
532
+ mergeActionsOpenAPIMetadata(target);
533
+ });
534
+ }
535
+ function Service() {
536
+ return createBeanDecorator('service');
537
+ }
538
+ function Dto(options) {
539
+ return createBeanDecorator('dto', options, false, false, target => {
540
+ mergeFieldsOpenAPIMetadata(target);
541
+ });
542
+ }
543
+ function mergeActionsOpenAPIMetadata(target) {
544
+ // beanOptions
545
+ const beanOptions = appResource.getBean(target);
546
+ const actions = cast(beanOptions?.options)?.actions;
547
+ if (!actions) return;
548
+ for (const key in actions) {
549
+ const action = actions[key];
550
+ if (!action) continue;
551
+ const options = appMetadata.getOwnMetadataMap(false, SymbolOpenApiOptions, target.prototype, key);
552
+ deepExtend(options, action);
553
+ }
554
+ }
555
+
556
+ const defaultMetadata = {
557
+ method: RequestMethod.GET,
558
+ path: '',
559
+ options: undefined
560
+ };
561
+ function RequestMapping(metadata = defaultMetadata) {
562
+ const path = metadata.path || '';
563
+ const method = metadata.method || RequestMethod.GET;
564
+ const options = metadata.options;
565
+ return (target, prop, descriptor) => {
566
+ appMetadata.defineMetadata(SymbolRequestMappingHandler, {
567
+ path,
568
+ method
569
+ }, target, prop);
570
+ if (options) {
571
+ const optionsMeta = appMetadata.getOwnMetadataMap(false, SymbolOpenApiOptions, target, prop);
572
+ Object.assign(optionsMeta, options);
573
+ }
574
+ return descriptor;
575
+ };
576
+ }
577
+ function createMappingDecorator(method) {
578
+ return (path, options) => {
579
+ if (path && typeof path === 'object') {
580
+ options = path;
581
+ path = undefined;
582
+ }
583
+ return RequestMapping({
584
+ method,
585
+ path,
586
+ options
587
+ });
588
+ };
589
+ }
590
+ const Post = createMappingDecorator(RequestMethod.POST);
591
+ const Get = createMappingDecorator(RequestMethod.GET);
592
+ const Delete = createMappingDecorator(RequestMethod.DELETE);
593
+ const Put = createMappingDecorator(RequestMethod.PUT);
594
+ const Patch = createMappingDecorator(RequestMethod.PATCH);
595
+ const Options = createMappingDecorator(RequestMethod.OPTIONS);
596
+ const Head = createMappingDecorator(RequestMethod.HEAD);
597
+ const Web = {
598
+ post: Post,
599
+ get: Get,
600
+ delete: Delete,
601
+ put: Put,
602
+ patch: Patch,
603
+ options: Options,
604
+ head: Head
605
+ };
606
+
607
+ function $apiPath(path) {
608
+ return path;
609
+ }
610
+ function $apiPathAndCombineParamsAndQuery(path, options) {
611
+ return combineParamsAndQuery(path, options);
612
+ }
613
+
614
+ export { $apiPath, $apiPathAndCombineParamsAndQuery, BeanRouter, Controller, Dto, Main, RequestMapping, RequestMethod, ScopeModuleAWeb, Service, ServiceWeb, StartupListen, SymbolRequestMappingHandler, Web, config, mergeActionsOpenAPIMetadata };
@@ -0,0 +1,8 @@
1
+ import type { Constructable } from 'vona';
2
+ import type { IDecoratorControllerOptions } from '../../types/controller.ts';
3
+ import type { IDecoratorDtoOptions } from '../../types/dto.ts';
4
+ export declare function Controller<T extends IDecoratorControllerOptions>(options?: T): ClassDecorator;
5
+ export declare function Controller<T extends IDecoratorControllerOptions>(path?: string, options?: Omit<T, 'path'>): ClassDecorator;
6
+ export declare function Service(): ClassDecorator;
7
+ export declare function Dto<T extends IDecoratorDtoOptions<any>>(options?: T): ClassDecorator;
8
+ export declare function mergeActionsOpenAPIMetadata(target: Constructable): void;
@@ -0,0 +1,3 @@
1
+ export * from './bean.ts';
2
+ export * from './request.ts';
3
+ export * from './response.ts';
@@ -0,0 +1,17 @@
1
+ import type { IOpenApiOptions } from 'vona-module-a-openapi';
2
+ import { RequestMethod } from '../../types/request.ts';
3
+ export interface RequestMappingMetadata {
4
+ path?: string;
5
+ method?: RequestMethod;
6
+ options?: IOpenApiOptions;
7
+ }
8
+ export declare function RequestMapping(metadata?: RequestMappingMetadata): MethodDecorator;
9
+ export declare const Web: {
10
+ post: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
11
+ get: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
12
+ delete: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
13
+ put: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
14
+ patch: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
15
+ options: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
16
+ head: (path?: IOpenApiOptions | string, options?: IOpenApiOptions) => MethodDecorator;
17
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './decorator/index.ts';
2
+ export * from './utils.ts';
@@ -0,0 +1,4 @@
1
+ import type { VonaContext } from 'vona';
2
+ import type { RouteHandlerArgumentMetaDecorator, RouteHandlerArgumentType } from 'vona-module-a-openapi';
3
+ export declare function extractValue(ctx: VonaContext, argMeta: RouteHandlerArgumentMetaDecorator): any;
4
+ export declare function exchangeKeyForValue(ctx: VonaContext, type: RouteHandlerArgumentType, field: string | undefined): any;
@@ -0,0 +1,2 @@
1
+ import type { Next, VonaContext } from 'vona';
2
+ export declare function middlewareGuard(ctx: VonaContext, next: Next): Promise<any>;
@@ -0,0 +1,2 @@
1
+ import type { Next, VonaContext } from 'vona';
2
+ export declare function middlewareInterceptor(ctx: VonaContext, next: Next): Promise<any>;
@@ -0,0 +1,2 @@
1
+ import type { Next, VonaContext } from 'vona';
2
+ export declare function middlewarePipe(ctx: VonaContext, next: Next): Promise<any>;
@@ -0,0 +1,6 @@
1
+ import type { IApiPathRecord } from '../types/controller.ts';
2
+ export declare function $apiPath<K extends keyof IApiPathRecord>(path: K): K;
3
+ export declare function $apiPathAndCombineParamsAndQuery<K extends keyof IApiPathRecord>(path: K, options?: {
4
+ params?: object;
5
+ query?: object;
6
+ }): string;
package/dist/main.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { IModuleMain } from 'vona';
2
+ import { BeanSimple } from 'vona';
3
+ declare const SymbolRouter: unique symbol;
4
+ export declare class Main extends BeanSimple implements IModuleMain {
5
+ private [SymbolRouter];
6
+ moduleLoading(): Promise<void>;
7
+ moduleLoaded(): Promise<void>;
8
+ configLoaded(_config: any): Promise<void>;
9
+ private _wrapOnion;
10
+ }
11
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { Constructable, MetadataKey } from 'vona';
2
+ import { BeanBase } from 'vona';
3
+ export declare class ServiceWeb extends BeanBase {
4
+ combineControllerActionApiPath(controller: Constructable, actionKey: MetadataKey, prefix?: string | boolean, simplify?: boolean): string | undefined;
5
+ }
@@ -0,0 +1,42 @@
1
+ import type { OmitNever } from 'vona';
2
+ import type { ServiceOnion } from 'vona-module-a-onion';
3
+ import type { IOnionOptionsEnable } from 'vona-module-a-onion';
4
+ export interface IApiPathRecordMethodMap {
5
+ get: IApiPathGetRecord;
6
+ post: IApiPathPostRecord;
7
+ delete: IApiPathDeleteRecord;
8
+ put: IApiPathPutRecord;
9
+ patch: IApiPathPatchRecord;
10
+ }
11
+ export interface IApiPathRecord extends IApiPathGetRecord, IApiPathPostRecord, IApiPathDeleteRecord, IApiPathPutRecord, IApiPathPatchRecord {
12
+ }
13
+ export interface IApiPathGetRecord {
14
+ }
15
+ export interface IApiPathPostRecord {
16
+ }
17
+ export interface IApiPathDeleteRecord {
18
+ }
19
+ export interface IApiPathPutRecord {
20
+ }
21
+ export interface IApiPathPatchRecord {
22
+ }
23
+ export interface IControllerRecord {
24
+ }
25
+ export interface IDecoratorControllerOptions extends IOnionOptionsEnable {
26
+ path?: string;
27
+ exclude?: boolean;
28
+ tags?: string[];
29
+ }
30
+ declare module 'vona-module-a-onion' {
31
+ interface BeanOnion {
32
+ controller: ServiceOnion<IDecoratorControllerOptions, keyof IControllerRecord>;
33
+ }
34
+ }
35
+ declare module 'vona' {
36
+ interface ConfigOnions {
37
+ controller: OmitNever<IControllerRecord>;
38
+ }
39
+ interface IBeanSceneRecord {
40
+ controller: never;
41
+ }
42
+ }
@@ -0,0 +1,23 @@
1
+ import type { OmitNever } from 'vona';
2
+ import type { ServiceOnion } from 'vona-module-a-onion';
3
+ import type { TypeOpenAPIMetadata } from 'vona-module-a-openapi';
4
+ export interface IDtoRecord {
5
+ }
6
+ export interface IDecoratorDtoOptions<FieldsMore = never> {
7
+ independent?: boolean;
8
+ openapi?: TypeOpenAPIMetadata;
9
+ fieldsMore?: FieldsMore;
10
+ }
11
+ declare module 'vona-module-a-onion' {
12
+ interface BeanOnion {
13
+ dto: ServiceOnion<IDecoratorDtoOptions, keyof IDtoRecord>;
14
+ }
15
+ }
16
+ declare module 'vona' {
17
+ interface ConfigOnions {
18
+ dto: OmitNever<IDtoRecord>;
19
+ }
20
+ interface IBeanSceneRecord {
21
+ dto: never;
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ export * from './controller.ts';
2
+ export * from './dto.ts';
3
+ export * from './request.ts';
4
+ export * from './response.ts';
5
+ export * from './router.ts';
6
+ export * from './service.ts';
@@ -0,0 +1,11 @@
1
+ export declare const SymbolRequestMappingHandler: unique symbol;
2
+ export type TypeRequestMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
3
+ export declare enum RequestMethod {
4
+ GET = "get",
5
+ POST = "post",
6
+ PUT = "put",
7
+ DELETE = "delete",
8
+ PATCH = "patch",
9
+ OPTIONS = "options",
10
+ HEAD = "head"
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import type Router from 'find-my-way';
2
+ import type { Constructable } from 'vona';
3
+ import type { RequestMethod } from './request.ts';
4
+ export interface ContextRouteMetadata {
5
+ meta: any;
6
+ }
7
+ export interface ContextRoute {
8
+ pid: string;
9
+ module: string;
10
+ controller: Constructable;
11
+ actionDescriptor: PropertyDescriptor;
12
+ controllerBeanFullName: string;
13
+ action: string;
14
+ route: ContextRouteMetadata;
15
+ routeMethod: RequestMethod;
16
+ routePath: string | RegExp;
17
+ routePathRaw: string | RegExp;
18
+ }
19
+ declare module 'vona' {
20
+ interface VonaApplication {
21
+ router: Router.Instance<Router.HTTPVersion.V1>;
22
+ }
23
+ interface VonaContext {
24
+ route: ContextRoute;
25
+ }
26
+ }
27
+ declare module 'koa' {
28
+ interface Request {
29
+ params: {
30
+ [key: string]: string;
31
+ };
32
+ query: {
33
+ [key: string]: string;
34
+ };
35
+ }
36
+ }
@@ -0,0 +1,13 @@
1
+ import type { ServiceOnion } from 'vona-module-a-onion';
2
+ export interface IServiceRecord {
3
+ }
4
+ declare module 'vona-module-a-onion' {
5
+ interface BeanOnion {
6
+ service: ServiceOnion<never, keyof IServiceRecord>;
7
+ }
8
+ }
9
+ declare module 'vona' {
10
+ interface IBeanSceneRecord {
11
+ service: never;
12
+ }
13
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vona-module-a-web",
3
3
  "type": "module",
4
- "version": "5.0.9",
4
+ "version": "5.0.11",
5
5
  "title": "a-web",
6
6
  "vonaModule": {
7
7
  "capabilities": {
@@ -55,8 +55,12 @@
55
55
  "dependencies": {
56
56
  "find-my-way": "^9.2.0"
57
57
  },
58
+ "devDependencies": {
59
+ "clean-package": "^2.2.0",
60
+ "rimraf": "^6.0.1"
61
+ },
58
62
  "scripts": {
59
- "clean": "rimraf dist tsconfig.tsbuildinfo",
60
- "tsc:publish": "npm run clean && tsc"
63
+ "clean": "rimraf dist tsconfig.build.tsbuildinfo",
64
+ "tsc:publish": "npm run clean && vona :bin:buildModule && tsc -p tsconfig.build.json"
61
65
  }
62
66
  }