@e22m4u/ts-rest-router 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README-ru.md +227 -0
  2. package/README.md +227 -0
  3. package/dist/cjs/index.cjs +448 -153
  4. package/dist/esm/controller-registry.d.ts +53 -11
  5. package/dist/esm/controller-registry.js +242 -104
  6. package/dist/esm/debuggable-service.js +1 -1
  7. package/dist/esm/decorators/action/action-decorator.d.ts +7 -8
  8. package/dist/esm/decorators/action/action-decorator.js +1 -5
  9. package/dist/esm/decorators/action/action-decorator.spec.js +16 -16
  10. package/dist/esm/decorators/action/action-metadata.d.ts +0 -1
  11. package/dist/esm/decorators/after/after-decorator.d.ts +9 -0
  12. package/dist/esm/decorators/after/after-decorator.js +22 -0
  13. package/dist/esm/decorators/after/after-decorator.spec.d.ts +1 -0
  14. package/dist/esm/decorators/after/after-decorator.spec.js +115 -0
  15. package/dist/esm/decorators/after/after-metadata.d.ts +13 -0
  16. package/dist/esm/decorators/after/after-metadata.js +5 -0
  17. package/dist/esm/decorators/after/after-reflector.d.ts +22 -0
  18. package/dist/esm/decorators/after/after-reflector.js +29 -0
  19. package/dist/esm/decorators/after/after-reflector.spec.d.ts +1 -0
  20. package/dist/esm/decorators/after/after-reflector.spec.js +102 -0
  21. package/dist/esm/decorators/after/index.d.ts +3 -0
  22. package/dist/esm/decorators/after/index.js +3 -0
  23. package/dist/esm/decorators/before/before-decorator.d.ts +9 -0
  24. package/dist/esm/decorators/before/before-decorator.js +22 -0
  25. package/dist/esm/decorators/before/before-decorator.spec.d.ts +1 -0
  26. package/dist/esm/decorators/before/before-decorator.spec.js +115 -0
  27. package/dist/esm/decorators/before/before-metadata.d.ts +13 -0
  28. package/dist/esm/decorators/before/before-metadata.js +5 -0
  29. package/dist/esm/decorators/before/before-reflector.d.ts +22 -0
  30. package/dist/esm/decorators/before/before-reflector.js +29 -0
  31. package/dist/esm/decorators/before/before-reflector.spec.d.ts +1 -0
  32. package/dist/esm/decorators/before/before-reflector.spec.js +102 -0
  33. package/dist/esm/decorators/before/index.d.ts +3 -0
  34. package/dist/esm/decorators/before/index.js +3 -0
  35. package/dist/esm/decorators/controller/controller-decorator.d.ts +2 -1
  36. package/dist/esm/decorators/controller/controller-decorator.js +27 -6
  37. package/dist/esm/decorators/controller/controller-decorator.spec.js +37 -15
  38. package/dist/esm/decorators/controller/controller-metadata.d.ts +0 -1
  39. package/dist/esm/decorators/index.d.ts +2 -0
  40. package/dist/esm/decorators/index.js +2 -0
  41. package/dist/esm/decorators/request-context/request-context-decorator.d.ts +2 -3
  42. package/dist/esm/decorators/request-context/request-context-decorator.js +3 -6
  43. package/dist/esm/decorators/request-context/request-context-decorator.spec.js +2 -17
  44. package/dist/esm/decorators/request-context/request-context-metadata.d.ts +0 -1
  45. package/dist/esm/decorators/request-data/request-data-decorator.d.ts +7 -3
  46. package/dist/esm/decorators/request-data/request-data-decorator.js +16 -16
  47. package/dist/esm/decorators/request-data/request-data-decorator.spec.js +12 -10
  48. package/dist/esm/decorators/request-data/request-data-metadata.d.ts +0 -1
  49. package/dist/esm/utils/create-debugger.d.ts +35 -2
  50. package/dist/esm/utils/create-debugger.js +71 -5
  51. package/package.json +17 -17
  52. package/src/controller-registry.spec.ts +601 -275
  53. package/src/controller-registry.ts +263 -128
  54. package/src/debuggable-service.ts +1 -1
  55. package/src/decorators/action/action-decorator.spec.ts +16 -16
  56. package/src/decorators/action/action-decorator.ts +10 -12
  57. package/src/decorators/action/action-metadata.ts +0 -1
  58. package/src/decorators/after/after-decorator.spec.ts +92 -0
  59. package/src/decorators/after/after-decorator.ts +40 -0
  60. package/src/decorators/after/after-metadata.ts +17 -0
  61. package/src/decorators/after/after-reflector.spec.ts +107 -0
  62. package/src/decorators/after/after-reflector.ts +45 -0
  63. package/src/decorators/after/index.ts +3 -0
  64. package/src/decorators/before/before-decorator.spec.ts +92 -0
  65. package/src/decorators/before/before-decorator.ts +40 -0
  66. package/src/decorators/before/before-metadata.ts +17 -0
  67. package/src/decorators/before/before-reflector.spec.ts +111 -0
  68. package/src/decorators/before/before-reflector.ts +50 -0
  69. package/src/decorators/before/index.ts +3 -0
  70. package/src/decorators/controller/controller-decorator.spec.ts +33 -16
  71. package/src/decorators/controller/controller-decorator.ts +33 -6
  72. package/src/decorators/controller/controller-metadata.ts +0 -1
  73. package/src/decorators/index.ts +2 -0
  74. package/src/decorators/request-context/request-context-decorator.spec.ts +2 -15
  75. package/src/decorators/request-context/request-context-decorator.ts +3 -8
  76. package/src/decorators/request-context/request-context-metadata.ts +0 -1
  77. package/src/decorators/request-data/request-data-decorator.spec.ts +12 -11
  78. package/src/decorators/request-data/request-data-decorator.ts +41 -16
  79. package/src/decorators/request-data/request-data-metadata.ts +0 -1
  80. package/src/utils/create-debugger.ts +84 -7
@@ -32,12 +32,8 @@ export function action<T extends object>(options: ActionOptions) {
32
32
  throw new Error(
33
33
  '@action decorator is only supported on an instance method.',
34
34
  );
35
- const metadata = {
36
- ...options,
37
- propertyKey,
38
- } as ActionMetadata;
39
35
  ActionReflector.setMetadata(
40
- metadata,
36
+ {...options, propertyKey},
41
37
  target.constructor as Constructor<T>,
42
38
  propertyKey,
43
39
  );
@@ -45,9 +41,11 @@ export function action<T extends object>(options: ActionOptions) {
45
41
  }
46
42
 
47
43
  /**
48
- * Action alias options.
44
+ * Action method options.
49
45
  */
50
- type ActionAliasOptions = Flatten<Omit<ActionOptions, 'method' | 'path'>>;
46
+ export type ActionMethodOptions = Flatten<
47
+ Omit<ActionOptions, 'method' | 'path'>
48
+ >;
51
49
 
52
50
  /**
53
51
  * Get decorator.
@@ -55,7 +53,7 @@ type ActionAliasOptions = Flatten<Omit<ActionOptions, 'method' | 'path'>>;
55
53
  * @param path
56
54
  * @param options
57
55
  */
58
- export const get = (path: string, options?: ActionAliasOptions) => {
56
+ export const get = (path: string, options?: ActionMethodOptions) => {
59
57
  return action({...options, path, method: HttpMethod.GET});
60
58
  };
61
59
 
@@ -65,7 +63,7 @@ export const get = (path: string, options?: ActionAliasOptions) => {
65
63
  * @param path
66
64
  * @param options
67
65
  */
68
- export const post = (path: string, options?: ActionAliasOptions) => {
66
+ export const post = (path: string, options?: ActionMethodOptions) => {
69
67
  return action({...options, path, method: HttpMethod.POST});
70
68
  };
71
69
 
@@ -75,7 +73,7 @@ export const post = (path: string, options?: ActionAliasOptions) => {
75
73
  * @param path
76
74
  * @param options
77
75
  */
78
- export const put = (path: string, options?: ActionAliasOptions) => {
76
+ export const put = (path: string, options?: ActionMethodOptions) => {
79
77
  return action({...options, path, method: HttpMethod.PUT});
80
78
  };
81
79
 
@@ -85,7 +83,7 @@ export const put = (path: string, options?: ActionAliasOptions) => {
85
83
  * @param path
86
84
  * @param options
87
85
  */
88
- export const patch = (path: string, options?: ActionAliasOptions) => {
86
+ export const patch = (path: string, options?: ActionMethodOptions) => {
89
87
  return action({...options, path, method: HttpMethod.PATCH});
90
88
  };
91
89
 
@@ -95,6 +93,6 @@ export const patch = (path: string, options?: ActionAliasOptions) => {
95
93
  * @param path
96
94
  * @param options
97
95
  */
98
- export const del = (path: string, options?: ActionAliasOptions) => {
96
+ export const del = (path: string, options?: ActionMethodOptions) => {
99
97
  return action({...options, path, method: HttpMethod.DELETE});
100
98
  };
@@ -12,7 +12,6 @@ export type ActionMetadata = {
12
12
  path: string;
13
13
  before?: RoutePreHandler | RoutePreHandler[];
14
14
  after?: RoutePostHandler | RoutePostHandler[];
15
- [option: string]: unknown | undefined;
16
15
  };
17
16
 
18
17
  /**
@@ -0,0 +1,92 @@
1
+ /* eslint mocha/no-sibling-hooks: 0 */
2
+ import {expect} from 'chai';
3
+ import {after} from './after-decorator.js';
4
+ import {AfterReflector} from './after-reflector.js';
5
+
6
+ const MIDDLEWARE_1 = () => undefined;
7
+ const MIDDLEWARE_2 = () => undefined;
8
+ const MIDDLEWARE_3 = () => undefined;
9
+
10
+ describe('after', function () {
11
+ describe('class target', function () {
12
+ it('sets given middleware to the target metadata', function () {
13
+ @after(MIDDLEWARE_1)
14
+ class Target {
15
+ method() {}
16
+ }
17
+ const res = AfterReflector.getMetadata(Target);
18
+ expect(res).to.be.eql([{middleware: MIDDLEWARE_1}]);
19
+ });
20
+
21
+ it('sets miltiple middlewares to the target metadata', function () {
22
+ @after([MIDDLEWARE_1, MIDDLEWARE_2])
23
+ class Target {
24
+ method() {}
25
+ }
26
+ const res = AfterReflector.getMetadata(Target);
27
+ expect(res).to.be.eql([{middleware: [MIDDLEWARE_1, MIDDLEWARE_2]}]);
28
+ });
29
+
30
+ it('allows to use the decorator multiple times', function () {
31
+ @after(MIDDLEWARE_1)
32
+ @after([MIDDLEWARE_2, MIDDLEWARE_3])
33
+ class Target {
34
+ method() {}
35
+ }
36
+ const res = AfterReflector.getMetadata(Target);
37
+ expect(res).to.be.eql([
38
+ {middleware: MIDDLEWARE_1},
39
+ {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]},
40
+ ]);
41
+ });
42
+ });
43
+
44
+ describe('method target', function () {
45
+ it('sets given middleware to the target metadata', function () {
46
+ class Target {
47
+ @after(MIDDLEWARE_1)
48
+ method() {}
49
+ }
50
+ const res = AfterReflector.getMetadata(Target, 'method');
51
+ expect(res).to.be.eql([
52
+ {
53
+ propertyKey: 'method',
54
+ middleware: MIDDLEWARE_1,
55
+ },
56
+ ]);
57
+ });
58
+
59
+ it('sets miltiple middlewares to the target metadata', function () {
60
+ class Target {
61
+ @after([MIDDLEWARE_1, MIDDLEWARE_2])
62
+ method() {}
63
+ }
64
+ const res = AfterReflector.getMetadata(Target, 'method');
65
+ expect(res).to.be.eql([
66
+ {
67
+ propertyKey: 'method',
68
+ middleware: [MIDDLEWARE_1, MIDDLEWARE_2],
69
+ },
70
+ ]);
71
+ });
72
+
73
+ it('allows to use the decorator multiple times', function () {
74
+ class Target {
75
+ @after(MIDDLEWARE_1)
76
+ @after([MIDDLEWARE_2, MIDDLEWARE_3])
77
+ method() {}
78
+ }
79
+ const res = AfterReflector.getMetadata(Target, 'method');
80
+ expect(res).to.be.eql([
81
+ {
82
+ propertyKey: 'method',
83
+ middleware: MIDDLEWARE_1,
84
+ },
85
+ {
86
+ propertyKey: 'method',
87
+ middleware: [MIDDLEWARE_2, MIDDLEWARE_3],
88
+ },
89
+ ]);
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,40 @@
1
+ import {Prototype} from '../../types.js';
2
+ import {Constructor} from '../../types.js';
3
+ import {AfterMetadata} from './after-metadata.js';
4
+ import {AfterReflector} from './after-reflector.js';
5
+ import {DecoratorTargetType} from '@e22m4u/ts-reflector';
6
+ import {getDecoratorTargetType} from '@e22m4u/ts-reflector';
7
+
8
+ /**
9
+ * After decorator.
10
+ *
11
+ * @param options
12
+ */
13
+ export function after<T extends object>(
14
+ middleware: AfterMetadata['middleware'],
15
+ ) {
16
+ return function (
17
+ target: Constructor<T> | Prototype<T>,
18
+ propertyKey?: string,
19
+ descriptor?: PropertyDescriptor,
20
+ ) {
21
+ const decoratorType = getDecoratorTargetType(
22
+ target,
23
+ propertyKey,
24
+ descriptor,
25
+ );
26
+ if (decoratorType === DecoratorTargetType.CONSTRUCTOR) {
27
+ AfterReflector.addMetadata({middleware}, target as Constructor<T>);
28
+ } else if (decoratorType === DecoratorTargetType.INSTANCE_METHOD) {
29
+ AfterReflector.addMetadata(
30
+ {propertyKey, middleware},
31
+ target.constructor as Constructor<T>,
32
+ propertyKey,
33
+ );
34
+ } else {
35
+ throw new Error(
36
+ '@after decorator is only supported on a class or an instance method.',
37
+ );
38
+ }
39
+ };
40
+ }
@@ -0,0 +1,17 @@
1
+ import {MetadataKey} from '@e22m4u/ts-reflector';
2
+ import {RoutePostHandler} from '@e22m4u/js-trie-router';
3
+
4
+ /**
5
+ * After metadata.
6
+ */
7
+ export type AfterMetadata = {
8
+ propertyKey?: string;
9
+ middleware: RoutePostHandler | RoutePostHandler[];
10
+ };
11
+
12
+ /**
13
+ * After metadata key.
14
+ */
15
+ export const AFTER_METADATA_KEY = new MetadataKey<AfterMetadata[]>(
16
+ 'afterMetadataKey',
17
+ );
@@ -0,0 +1,107 @@
1
+ import {expect} from 'chai';
2
+ import {describe} from 'mocha';
3
+ import {Reflector} from '@e22m4u/ts-reflector';
4
+ import {AfterReflector} from './after-reflector.js';
5
+ import {AFTER_METADATA_KEY} from './after-metadata.js';
6
+
7
+ const MIDDLEWARE_1 = () => undefined;
8
+ const MIDDLEWARE_2 = () => undefined;
9
+ const MIDDLEWARE_3 = () => undefined;
10
+
11
+ describe('AfterReflector', function () {
12
+ describe('class target', function () {
13
+ describe('addMetadata', function () {
14
+ it('adds a given value to the target metadata', function () {
15
+ class Target {}
16
+ const md1 = {middleware: MIDDLEWARE_1};
17
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
18
+ AfterReflector.addMetadata(md1, Target);
19
+ AfterReflector.addMetadata(md2, Target);
20
+ const res = Reflector.getOwnMetadata(AFTER_METADATA_KEY, Target);
21
+ expect(res).to.be.eql([md2, md1]);
22
+ });
23
+ });
24
+
25
+ describe('getMetadata', function () {
26
+ it('returns an empty array if no metadata', function () {
27
+ class Target {}
28
+ const res = AfterReflector.getMetadata(Target);
29
+ expect(res).to.be.eql([]);
30
+ });
31
+
32
+ it('returns existing metadata by the target', function () {
33
+ class Target {}
34
+ const md1 = {middleware: MIDDLEWARE_1};
35
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
36
+ const mdArray = [md1, md2];
37
+ Reflector.defineMetadata(AFTER_METADATA_KEY, mdArray, Target);
38
+ const res = AfterReflector.getMetadata(Target);
39
+ expect(res).to.be.eql(mdArray);
40
+ });
41
+ });
42
+ });
43
+
44
+ describe('method target', function () {
45
+ describe('addMetadata', function () {
46
+ it('adds a given value to the target metadata', function () {
47
+ class Target {}
48
+ const md1 = {middleware: MIDDLEWARE_1};
49
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
50
+ AfterReflector.addMetadata(md1, Target, 'prop');
51
+ AfterReflector.addMetadata(md2, Target, 'prop');
52
+ const res = Reflector.getOwnMetadata(
53
+ AFTER_METADATA_KEY,
54
+ Target,
55
+ 'prop',
56
+ );
57
+ expect(res).to.be.eql([md2, md1]);
58
+ });
59
+ });
60
+
61
+ describe('getMetadata', function () {
62
+ it('returns an empty array if no metadata', function () {
63
+ class Target {}
64
+ const res = AfterReflector.getMetadata(Target, 'prop');
65
+ expect(res).to.be.eql([]);
66
+ });
67
+
68
+ it('returns existing metadata by the target', function () {
69
+ class Target {}
70
+ const md1 = {middleware: MIDDLEWARE_1};
71
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
72
+ const mdArray = [md1, md2];
73
+ Reflector.defineMetadata(AFTER_METADATA_KEY, mdArray, Target, 'prop');
74
+ const res = AfterReflector.getMetadata(Target, 'prop');
75
+ expect(res).to.be.eql(mdArray);
76
+ });
77
+ });
78
+ });
79
+
80
+ describe('addMetadata', function () {
81
+ it('can distinguish class and method metadata', function () {
82
+ class Target {}
83
+ const md1 = {middleware: MIDDLEWARE_1};
84
+ const md2 = {middleware: MIDDLEWARE_2};
85
+ AfterReflector.addMetadata(md1, Target);
86
+ AfterReflector.addMetadata(md2, Target, 'prop');
87
+ const res1 = Reflector.getOwnMetadata(AFTER_METADATA_KEY, Target);
88
+ const res2 = Reflector.getOwnMetadata(AFTER_METADATA_KEY, Target, 'prop');
89
+ expect(res1).to.be.eql([md1]);
90
+ expect(res2).to.be.eql([md2]);
91
+ });
92
+ });
93
+
94
+ describe('getMetadata', function () {
95
+ it('can distinguish class and method metadata', function () {
96
+ class Target {}
97
+ const md1 = {middleware: MIDDLEWARE_1};
98
+ const md2 = {middleware: MIDDLEWARE_2};
99
+ Reflector.defineMetadata(AFTER_METADATA_KEY, [md1], Target);
100
+ Reflector.defineMetadata(AFTER_METADATA_KEY, [md2], Target, 'prop');
101
+ const res1 = AfterReflector.getMetadata(Target);
102
+ const res2 = AfterReflector.getMetadata(Target, 'prop');
103
+ expect(res1).to.be.eql([md1]);
104
+ expect(res2).to.be.eql([md2]);
105
+ });
106
+ });
107
+ });
@@ -0,0 +1,45 @@
1
+ import {Constructor} from '../../types.js';
2
+ import {Reflector} from '@e22m4u/ts-reflector';
3
+ import {AfterMetadata} from './after-metadata.js';
4
+ import {AFTER_METADATA_KEY} from './after-metadata.js';
5
+
6
+ /**
7
+ * After reflector.
8
+ */
9
+ export class AfterReflector {
10
+ /**
11
+ * Set metadata.
12
+ *
13
+ * @param metadata
14
+ * @param target
15
+ * @param propertyKey
16
+ */
17
+ static addMetadata(
18
+ metadata: AfterMetadata,
19
+ target: Constructor,
20
+ propertyKey?: string,
21
+ ) {
22
+ const oldArray =
23
+ Reflector.getOwnMetadata(AFTER_METADATA_KEY, target, propertyKey) ?? [];
24
+ const newArray = [metadata, ...oldArray];
25
+ Reflector.defineMetadata(AFTER_METADATA_KEY, newArray, target, propertyKey);
26
+ }
27
+
28
+ /**
29
+ * Get metadata.
30
+ *
31
+ * @param target
32
+ * @param propertyKey
33
+ */
34
+ static getMetadata(
35
+ target: Constructor,
36
+ propertyKey?: string,
37
+ ): AfterMetadata[] {
38
+ const metadata = Reflector.getOwnMetadata(
39
+ AFTER_METADATA_KEY,
40
+ target,
41
+ propertyKey,
42
+ );
43
+ return metadata ?? [];
44
+ }
45
+ }
@@ -0,0 +1,3 @@
1
+ export * from './after-metadata.js';
2
+ export * from './after-reflector.js';
3
+ export * from './after-decorator.js';
@@ -0,0 +1,92 @@
1
+ /* eslint mocha/no-sibling-hooks: 0 */
2
+ import {expect} from 'chai';
3
+ import {before} from './before-decorator.js';
4
+ import {BeforeReflector} from './before-reflector.js';
5
+
6
+ const MIDDLEWARE_1 = () => undefined;
7
+ const MIDDLEWARE_2 = () => undefined;
8
+ const MIDDLEWARE_3 = () => undefined;
9
+
10
+ describe('before', function () {
11
+ describe('class target', function () {
12
+ it('sets given middleware to the target metadata', function () {
13
+ @before(MIDDLEWARE_1)
14
+ class Target {
15
+ method() {}
16
+ }
17
+ const res = BeforeReflector.getMetadata(Target);
18
+ expect(res).to.be.eql([{middleware: MIDDLEWARE_1}]);
19
+ });
20
+
21
+ it('sets miltiple middlewares to the target metadata', function () {
22
+ @before([MIDDLEWARE_1, MIDDLEWARE_2])
23
+ class Target {
24
+ method() {}
25
+ }
26
+ const res = BeforeReflector.getMetadata(Target);
27
+ expect(res).to.be.eql([{middleware: [MIDDLEWARE_1, MIDDLEWARE_2]}]);
28
+ });
29
+
30
+ it('allows to use the decorator multiple times', function () {
31
+ @before(MIDDLEWARE_1)
32
+ @before([MIDDLEWARE_2, MIDDLEWARE_3])
33
+ class Target {
34
+ method() {}
35
+ }
36
+ const res = BeforeReflector.getMetadata(Target);
37
+ expect(res).to.be.eql([
38
+ {middleware: MIDDLEWARE_1},
39
+ {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]},
40
+ ]);
41
+ });
42
+ });
43
+
44
+ describe('method target', function () {
45
+ it('sets given middleware to the target metadata', function () {
46
+ class Target {
47
+ @before(MIDDLEWARE_1)
48
+ method() {}
49
+ }
50
+ const res = BeforeReflector.getMetadata(Target, 'method');
51
+ expect(res).to.be.eql([
52
+ {
53
+ propertyKey: 'method',
54
+ middleware: MIDDLEWARE_1,
55
+ },
56
+ ]);
57
+ });
58
+
59
+ it('sets miltiple middlewares to the target metadata', function () {
60
+ class Target {
61
+ @before([MIDDLEWARE_1, MIDDLEWARE_2])
62
+ method() {}
63
+ }
64
+ const res = BeforeReflector.getMetadata(Target, 'method');
65
+ expect(res).to.be.eql([
66
+ {
67
+ propertyKey: 'method',
68
+ middleware: [MIDDLEWARE_1, MIDDLEWARE_2],
69
+ },
70
+ ]);
71
+ });
72
+
73
+ it('allows to use the decorator multiple times', function () {
74
+ class Target {
75
+ @before(MIDDLEWARE_1)
76
+ @before([MIDDLEWARE_2, MIDDLEWARE_3])
77
+ method() {}
78
+ }
79
+ const res = BeforeReflector.getMetadata(Target, 'method');
80
+ expect(res).to.be.eql([
81
+ {
82
+ propertyKey: 'method',
83
+ middleware: MIDDLEWARE_1,
84
+ },
85
+ {
86
+ propertyKey: 'method',
87
+ middleware: [MIDDLEWARE_2, MIDDLEWARE_3],
88
+ },
89
+ ]);
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,40 @@
1
+ import {Prototype} from '../../types.js';
2
+ import {Constructor} from '../../types.js';
3
+ import {BeforeMetadata} from './before-metadata.js';
4
+ import {BeforeReflector} from './before-reflector.js';
5
+ import {DecoratorTargetType} from '@e22m4u/ts-reflector';
6
+ import {getDecoratorTargetType} from '@e22m4u/ts-reflector';
7
+
8
+ /**
9
+ * Before decorator.
10
+ *
11
+ * @param options
12
+ */
13
+ export function before<T extends object>(
14
+ middleware: BeforeMetadata['middleware'],
15
+ ) {
16
+ return function (
17
+ target: Constructor<T> | Prototype<T>,
18
+ propertyKey?: string,
19
+ descriptor?: PropertyDescriptor,
20
+ ) {
21
+ const decoratorType = getDecoratorTargetType(
22
+ target,
23
+ propertyKey,
24
+ descriptor,
25
+ );
26
+ if (decoratorType === DecoratorTargetType.CONSTRUCTOR) {
27
+ BeforeReflector.addMetadata({middleware}, target as Constructor<T>);
28
+ } else if (decoratorType === DecoratorTargetType.INSTANCE_METHOD) {
29
+ BeforeReflector.addMetadata(
30
+ {propertyKey, middleware},
31
+ target.constructor as Constructor<T>,
32
+ propertyKey,
33
+ );
34
+ } else {
35
+ throw new Error(
36
+ '@before decorator is only supported on a class or an instance method.',
37
+ );
38
+ }
39
+ };
40
+ }
@@ -0,0 +1,17 @@
1
+ import {MetadataKey} from '@e22m4u/ts-reflector';
2
+ import {RoutePreHandler} from '@e22m4u/js-trie-router';
3
+
4
+ /**
5
+ * Before metadata.
6
+ */
7
+ export type BeforeMetadata = {
8
+ propertyKey?: string;
9
+ middleware: RoutePreHandler | RoutePreHandler[];
10
+ };
11
+
12
+ /**
13
+ * Before metadata key.
14
+ */
15
+ export const BEFORE_METADATA_KEY = new MetadataKey<BeforeMetadata[]>(
16
+ 'beforeMetadataKey',
17
+ );
@@ -0,0 +1,111 @@
1
+ import {expect} from 'chai';
2
+ import {describe} from 'mocha';
3
+ import {Reflector} from '@e22m4u/ts-reflector';
4
+ import {BeforeReflector} from './before-reflector.js';
5
+ import {BEFORE_METADATA_KEY} from './before-metadata.js';
6
+
7
+ const MIDDLEWARE_1 = () => undefined;
8
+ const MIDDLEWARE_2 = () => undefined;
9
+ const MIDDLEWARE_3 = () => undefined;
10
+
11
+ describe('BeforeReflector', function () {
12
+ describe('class target', function () {
13
+ describe('addMetadata', function () {
14
+ it('adds a given value to the target metadata', function () {
15
+ class Target {}
16
+ const md1 = {middleware: MIDDLEWARE_1};
17
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
18
+ BeforeReflector.addMetadata(md1, Target);
19
+ BeforeReflector.addMetadata(md2, Target);
20
+ const res = Reflector.getOwnMetadata(BEFORE_METADATA_KEY, Target);
21
+ expect(res).to.be.eql([md2, md1]);
22
+ });
23
+ });
24
+
25
+ describe('getMetadata', function () {
26
+ it('returns an empty array if no metadata', function () {
27
+ class Target {}
28
+ const res = BeforeReflector.getMetadata(Target);
29
+ expect(res).to.be.eql([]);
30
+ });
31
+
32
+ it('returns existing metadata by the target', function () {
33
+ class Target {}
34
+ const md1 = {middleware: MIDDLEWARE_1};
35
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
36
+ const mdArray = [md1, md2];
37
+ Reflector.defineMetadata(BEFORE_METADATA_KEY, mdArray, Target);
38
+ const res = BeforeReflector.getMetadata(Target);
39
+ expect(res).to.be.eql(mdArray);
40
+ });
41
+ });
42
+ });
43
+
44
+ describe('method target', function () {
45
+ describe('addMetadata', function () {
46
+ it('adds a given value to the target metadata', function () {
47
+ class Target {}
48
+ const md1 = {middleware: MIDDLEWARE_1};
49
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
50
+ BeforeReflector.addMetadata(md1, Target, 'prop');
51
+ BeforeReflector.addMetadata(md2, Target, 'prop');
52
+ const res = Reflector.getOwnMetadata(
53
+ BEFORE_METADATA_KEY,
54
+ Target,
55
+ 'prop',
56
+ );
57
+ expect(res).to.be.eql([md2, md1]);
58
+ });
59
+ });
60
+
61
+ describe('getMetadata', function () {
62
+ it('returns an empty array if no metadata', function () {
63
+ class Target {}
64
+ const res = BeforeReflector.getMetadata(Target, 'prop');
65
+ expect(res).to.be.eql([]);
66
+ });
67
+
68
+ it('returns existing metadata by the target', function () {
69
+ class Target {}
70
+ const md1 = {middleware: MIDDLEWARE_1};
71
+ const md2 = {middleware: [MIDDLEWARE_2, MIDDLEWARE_3]};
72
+ const mdArray = [md1, md2];
73
+ Reflector.defineMetadata(BEFORE_METADATA_KEY, mdArray, Target, 'prop');
74
+ const res = BeforeReflector.getMetadata(Target, 'prop');
75
+ expect(res).to.be.eql(mdArray);
76
+ });
77
+ });
78
+ });
79
+
80
+ describe('addMetadata', function () {
81
+ it('can distinguish class and method metadata', function () {
82
+ class Target {}
83
+ const md1 = {middleware: MIDDLEWARE_1};
84
+ const md2 = {middleware: MIDDLEWARE_2};
85
+ BeforeReflector.addMetadata(md1, Target);
86
+ BeforeReflector.addMetadata(md2, Target, 'prop');
87
+ const res1 = Reflector.getOwnMetadata(BEFORE_METADATA_KEY, Target);
88
+ const res2 = Reflector.getOwnMetadata(
89
+ BEFORE_METADATA_KEY,
90
+ Target,
91
+ 'prop',
92
+ );
93
+ expect(res1).to.be.eql([md1]);
94
+ expect(res2).to.be.eql([md2]);
95
+ });
96
+ });
97
+
98
+ describe('getMetadata', function () {
99
+ it('can distinguish class and method metadata', function () {
100
+ class Target {}
101
+ const md1 = {middleware: MIDDLEWARE_1};
102
+ const md2 = {middleware: MIDDLEWARE_2};
103
+ Reflector.defineMetadata(BEFORE_METADATA_KEY, [md1], Target);
104
+ Reflector.defineMetadata(BEFORE_METADATA_KEY, [md2], Target, 'prop');
105
+ const res1 = BeforeReflector.getMetadata(Target);
106
+ const res2 = BeforeReflector.getMetadata(Target, 'prop');
107
+ expect(res1).to.be.eql([md1]);
108
+ expect(res2).to.be.eql([md2]);
109
+ });
110
+ });
111
+ });