@hazeljs/core 0.2.0-beta.8 → 0.2.0-beta.80

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.
@@ -676,5 +676,508 @@ describe('Router', () => {
676
676
  }));
677
677
  }
678
678
  });
679
+ // -----------------------------------------------------------------------
680
+ // Parameter injection — extended types
681
+ // -----------------------------------------------------------------------
682
+ it('should inject named header via { type: "headers", name }', async () => {
683
+ class TestController {
684
+ getHeader(authHeader) {
685
+ return { auth: authHeader };
686
+ }
687
+ }
688
+ container.register(TestController, new TestController());
689
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
690
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/header-named', propertyKey: 'getHeader' }], TestController);
691
+ Reflect.defineMetadata('hazel:inject', [{ type: 'headers', name: 'authorization' }], TestController, 'getHeader');
692
+ router.registerController(TestController);
693
+ const context = {
694
+ params: {},
695
+ query: {},
696
+ body: {},
697
+ headers: { authorization: 'Bearer token123' },
698
+ method: 'GET',
699
+ url: '/test/header-named',
700
+ };
701
+ const route = await router.match('GET', '/test/header-named', context);
702
+ expect(route).toBeDefined();
703
+ if (route) {
704
+ await route.handler(mockReq, mockRes, context);
705
+ expect(mockRes.json).toHaveBeenCalledWith({ auth: 'Bearer token123' });
706
+ }
707
+ });
708
+ it('should inject all headers when { type: "headers" } has no name', async () => {
709
+ class TestController {
710
+ getAllHeaders(headers) {
711
+ return headers;
712
+ }
713
+ }
714
+ container.register(TestController, new TestController());
715
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
716
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/headers-all', propertyKey: 'getAllHeaders' }], TestController);
717
+ Reflect.defineMetadata('hazel:inject', [{ type: 'headers' }], TestController, 'getAllHeaders');
718
+ router.registerController(TestController);
719
+ const context = {
720
+ params: {},
721
+ query: {},
722
+ body: {},
723
+ headers: { 'x-custom': 'value' },
724
+ method: 'GET',
725
+ url: '/test/headers-all',
726
+ };
727
+ const route = await router.match('GET', '/test/headers-all', context);
728
+ expect(route).toBeDefined();
729
+ if (route) {
730
+ await route.handler(mockReq, mockRes, context);
731
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({ 'x-custom': 'value' }));
732
+ }
733
+ });
734
+ it('should inject user object via { type: "user" } without field', async () => {
735
+ class TestController {
736
+ whoAmI(user) {
737
+ return user;
738
+ }
739
+ }
740
+ container.register(TestController, new TestController());
741
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
742
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/me', propertyKey: 'whoAmI' }], TestController);
743
+ Reflect.defineMetadata('hazel:inject', [{ type: 'user' }], TestController, 'whoAmI');
744
+ router.registerController(TestController);
745
+ const context = {
746
+ params: {},
747
+ query: {},
748
+ body: {},
749
+ headers: {},
750
+ method: 'GET',
751
+ url: '/test/me',
752
+ };
753
+ const reqWithUser = { ...mockReq, user: { sub: 'u1', role: 'admin' } };
754
+ const route = await router.match('GET', '/test/me', context);
755
+ expect(route).toBeDefined();
756
+ if (route) {
757
+ await route.handler(reqWithUser, mockRes, context);
758
+ expect(mockRes.json).toHaveBeenCalledWith({ sub: 'u1', role: 'admin' });
759
+ }
760
+ });
761
+ it('should inject specific user field via { type: "user", field }', async () => {
762
+ class TestController {
763
+ getRole(role) {
764
+ return { role };
765
+ }
766
+ }
767
+ container.register(TestController, new TestController());
768
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
769
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/role', propertyKey: 'getRole' }], TestController);
770
+ Reflect.defineMetadata('hazel:inject', [{ type: 'user', field: 'role' }], TestController, 'getRole');
771
+ router.registerController(TestController);
772
+ const context = {
773
+ params: {},
774
+ query: {},
775
+ body: {},
776
+ headers: {},
777
+ method: 'GET',
778
+ url: '/test/role',
779
+ };
780
+ const reqWithUser = { ...mockReq, user: { sub: 'u1', role: 'manager' } };
781
+ const route = await router.match('GET', '/test/role', context);
782
+ expect(route).toBeDefined();
783
+ if (route) {
784
+ await route.handler(reqWithUser, mockRes, context);
785
+ expect(mockRes.json).toHaveBeenCalledWith({ role: 'manager' });
786
+ }
787
+ });
788
+ it('should invoke a custom resolver via { type: "custom", resolve }', async () => {
789
+ const resolvedValue = { custom: 'injected' };
790
+ const resolveFn = jest.fn().mockReturnValue(resolvedValue);
791
+ class TestController {
792
+ getCustom(val) {
793
+ return val;
794
+ }
795
+ }
796
+ container.register(TestController, new TestController());
797
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
798
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/custom', propertyKey: 'getCustom' }], TestController);
799
+ Reflect.defineMetadata('hazel:inject', [{ type: 'custom', resolve: resolveFn }], TestController, 'getCustom');
800
+ router.registerController(TestController);
801
+ const context = {
802
+ params: {},
803
+ query: {},
804
+ body: {},
805
+ headers: {},
806
+ method: 'GET',
807
+ url: '/test/custom',
808
+ };
809
+ const route = await router.match('GET', '/test/custom', context);
810
+ expect(route).toBeDefined();
811
+ if (route) {
812
+ await route.handler(mockReq, mockRes, context);
813
+ expect(resolveFn).toHaveBeenCalled();
814
+ expect(mockRes.json).toHaveBeenCalledWith(resolvedValue);
815
+ }
816
+ });
817
+ it('should auto-inject RequestContext for undecorated parameters', async () => {
818
+ class TestController {
819
+ contextReceiver(ctx) {
820
+ return { url: ctx.url };
821
+ }
822
+ }
823
+ container.register(TestController, new TestController());
824
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
825
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/ctx', propertyKey: 'contextReceiver' }], TestController);
826
+ // No injection metadata — relies on design:paramtypes auto-inject
827
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'contextReceiver');
828
+ Reflect.defineMetadata('design:paramtypes', [Object], // one undecorated parameter
829
+ TestController.prototype, 'contextReceiver');
830
+ router.registerController(TestController);
831
+ const context = {
832
+ params: {},
833
+ query: {},
834
+ body: {},
835
+ headers: {},
836
+ method: 'GET',
837
+ url: '/test/ctx',
838
+ };
839
+ const route = await router.match('GET', '/test/ctx', context);
840
+ expect(route).toBeDefined();
841
+ if (route) {
842
+ await route.handler(mockReq, mockRes, context);
843
+ expect(mockRes.json).toHaveBeenCalledWith({ url: '/test/ctx' });
844
+ }
845
+ });
846
+ // -----------------------------------------------------------------------
847
+ // @Redirect metadata
848
+ // -----------------------------------------------------------------------
849
+ it('should send a redirect response when @Redirect metadata is set', async () => {
850
+ class TestController {
851
+ goHome() {
852
+ return undefined;
853
+ }
854
+ }
855
+ container.register(TestController, new TestController());
856
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
857
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/redirect', propertyKey: 'goHome' }], TestController);
858
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'goHome');
859
+ // Simulate @Redirect('/home', 301) on the prototype
860
+ Reflect.defineMetadata('hazel:redirect', { url: '/home', statusCode: 301 }, TestController.prototype, 'goHome');
861
+ router.registerController(TestController);
862
+ const context = {
863
+ params: {},
864
+ query: {},
865
+ body: {},
866
+ headers: {},
867
+ method: 'GET',
868
+ url: '/test/redirect',
869
+ };
870
+ const route = await router.match('GET', '/test/redirect', context);
871
+ expect(route).toBeDefined();
872
+ if (route) {
873
+ mockRes.setHeader = jest.fn();
874
+ await route.handler(mockReq, mockRes, context);
875
+ expect(mockRes.status).toHaveBeenCalledWith(301);
876
+ expect(mockRes.setHeader).toHaveBeenCalledWith('Location', '/home');
877
+ }
878
+ });
879
+ // -----------------------------------------------------------------------
880
+ // @Header metadata (custom response headers)
881
+ // -----------------------------------------------------------------------
882
+ it('should set custom response headers when @Header metadata is set', async () => {
883
+ class TestController {
884
+ headered() {
885
+ return { ok: true };
886
+ }
887
+ }
888
+ container.register(TestController, new TestController());
889
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
890
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/headered', propertyKey: 'headered' }], TestController);
891
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'headered');
892
+ Reflect.defineMetadata('hazel:headers', [{ name: 'X-Custom', value: 'hello' }], TestController.prototype, 'headered');
893
+ router.registerController(TestController);
894
+ const context = {
895
+ params: {},
896
+ query: {},
897
+ body: {},
898
+ headers: {},
899
+ method: 'GET',
900
+ url: '/test/headered',
901
+ };
902
+ const route = await router.match('GET', '/test/headered', context);
903
+ expect(route).toBeDefined();
904
+ if (route) {
905
+ await route.handler(mockReq, mockRes, context);
906
+ expect(mockRes.setHeader).toHaveBeenCalledWith('X-Custom', 'hello');
907
+ expect(mockRes.json).toHaveBeenCalledWith({ ok: true });
908
+ }
909
+ });
910
+ // -----------------------------------------------------------------------
911
+ // @HttpCode metadata
912
+ // -----------------------------------------------------------------------
913
+ it('should use custom HTTP status code from @HttpCode when result is defined', async () => {
914
+ class TestController {
915
+ create() {
916
+ return { created: true };
917
+ }
918
+ }
919
+ container.register(TestController, new TestController());
920
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
921
+ Reflect.defineMetadata('hazel:routes', [{ method: 'POST', path: '/http-code', propertyKey: 'create' }], TestController);
922
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'create');
923
+ Reflect.defineMetadata('hazel:http-code', 201, TestController.prototype, 'create');
924
+ router.registerController(TestController);
925
+ const context = {
926
+ params: {},
927
+ query: {},
928
+ body: {},
929
+ headers: {},
930
+ method: 'POST',
931
+ url: '/test/http-code',
932
+ };
933
+ const route = await router.match('POST', '/test/http-code', context);
934
+ expect(route).toBeDefined();
935
+ if (route) {
936
+ await route.handler(mockReq, mockRes, context);
937
+ expect(mockRes.status).toHaveBeenCalledWith(201);
938
+ expect(mockRes.json).toHaveBeenCalledWith({ created: true });
939
+ }
940
+ });
941
+ it('should use custom HTTP status code from @HttpCode when result is undefined', async () => {
942
+ class TestController {
943
+ noContent() {
944
+ return undefined;
945
+ }
946
+ }
947
+ container.register(TestController, new TestController());
948
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
949
+ Reflect.defineMetadata('hazel:routes', [{ method: 'DELETE', path: '/no-content', propertyKey: 'noContent' }], TestController);
950
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'noContent');
951
+ Reflect.defineMetadata('hazel:http-code', 204, TestController.prototype, 'noContent');
952
+ router.registerController(TestController);
953
+ const context = {
954
+ params: {},
955
+ query: {},
956
+ body: {},
957
+ headers: {},
958
+ method: 'DELETE',
959
+ url: '/test/no-content',
960
+ };
961
+ const route = await router.match('DELETE', '/test/no-content', context);
962
+ expect(route).toBeDefined();
963
+ if (route) {
964
+ await route.handler(mockReq, mockRes, context);
965
+ expect(mockRes.status).toHaveBeenCalledWith(204);
966
+ expect(mockRes.end).toHaveBeenCalled();
967
+ }
968
+ });
969
+ // -----------------------------------------------------------------------
970
+ // Guard execution
971
+ // -----------------------------------------------------------------------
972
+ it('should throw UnauthorizedError when guard canActivate returns false', async () => {
973
+ class DenyGuard {
974
+ canActivate() {
975
+ return false;
976
+ }
977
+ }
978
+ container.register(DenyGuard, new DenyGuard());
979
+ class TestController {
980
+ secret() {
981
+ return { secret: true };
982
+ }
983
+ }
984
+ container.register(TestController, new TestController());
985
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
986
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/secret', propertyKey: 'secret' }], TestController);
987
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'secret');
988
+ Reflect.defineMetadata('hazel:guards', [DenyGuard], TestController);
989
+ router.registerController(TestController);
990
+ const context = {
991
+ params: {},
992
+ query: {},
993
+ body: {},
994
+ headers: {},
995
+ method: 'GET',
996
+ url: '/test/secret',
997
+ };
998
+ const route = await router.match('GET', '/test/secret', context);
999
+ expect(route).toBeDefined();
1000
+ if (route) {
1001
+ await route.handler(mockReq, mockRes, context);
1002
+ expect(mockRes.status).toHaveBeenCalledWith(401);
1003
+ }
1004
+ });
1005
+ it('should propagate req.user to context.user after guards pass', async () => {
1006
+ class AllowGuard {
1007
+ canActivate() {
1008
+ return true;
1009
+ }
1010
+ }
1011
+ container.register(AllowGuard, new AllowGuard());
1012
+ class TestController {
1013
+ whoAmI(user) {
1014
+ return user;
1015
+ }
1016
+ }
1017
+ container.register(TestController, new TestController());
1018
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
1019
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/user-propagate', propertyKey: 'whoAmI' }], TestController);
1020
+ Reflect.defineMetadata('hazel:inject', [{ type: 'user' }], TestController, 'whoAmI');
1021
+ Reflect.defineMetadata('hazel:guards', [AllowGuard], TestController);
1022
+ router.registerController(TestController);
1023
+ const context = {
1024
+ params: {},
1025
+ query: {},
1026
+ body: {},
1027
+ headers: {},
1028
+ method: 'GET',
1029
+ url: '/test/user-propagate',
1030
+ };
1031
+ const reqWithUser = { ...mockReq, user: { sub: 'u99' } };
1032
+ const route = await router.match('GET', '/test/user-propagate', context);
1033
+ expect(route).toBeDefined();
1034
+ if (route) {
1035
+ await route.handler(reqWithUser, mockRes, context);
1036
+ expect(mockRes.json).toHaveBeenCalledWith({ sub: 'u99' });
1037
+ }
1038
+ });
1039
+ // -----------------------------------------------------------------------
1040
+ // Legacy string-based injection
1041
+ // -----------------------------------------------------------------------
1042
+ it('should handle legacy string injection "body"', async () => {
1043
+ class TestController {
1044
+ legacyBody(body) {
1045
+ return body;
1046
+ }
1047
+ }
1048
+ container.register(TestController, new TestController());
1049
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
1050
+ Reflect.defineMetadata('hazel:routes', [{ method: 'POST', path: '/legacy-body', propertyKey: 'legacyBody' }], TestController);
1051
+ Reflect.defineMetadata('hazel:inject', ['body'], TestController, 'legacyBody');
1052
+ router.registerController(TestController);
1053
+ const context = {
1054
+ params: {},
1055
+ query: {},
1056
+ body: { name: 'legacy' },
1057
+ headers: {},
1058
+ method: 'POST',
1059
+ url: '/test/legacy-body',
1060
+ };
1061
+ const route = await router.match('POST', '/test/legacy-body', context);
1062
+ if (route) {
1063
+ await route.handler(mockReq, mockRes, context);
1064
+ expect(mockRes.json).toHaveBeenCalledWith({ name: 'legacy' });
1065
+ }
1066
+ });
1067
+ it('should handle legacy string injection "param"', async () => {
1068
+ class TestController {
1069
+ legacyParam(params) {
1070
+ return params;
1071
+ }
1072
+ }
1073
+ container.register(TestController, new TestController());
1074
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
1075
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/legacy-param/:id', propertyKey: 'legacyParam' }], TestController);
1076
+ Reflect.defineMetadata('hazel:inject', ['param'], TestController, 'legacyParam');
1077
+ router.registerController(TestController);
1078
+ const context = {
1079
+ params: { id: '42' },
1080
+ query: {},
1081
+ body: {},
1082
+ headers: {},
1083
+ method: 'GET',
1084
+ url: '/test/legacy-param/42',
1085
+ };
1086
+ const route = await router.match('GET', '/test/legacy-param/42', context);
1087
+ if (route) {
1088
+ await route.handler(mockReq, mockRes, context);
1089
+ expect(mockRes.json).toHaveBeenCalledWith({ id: '42' });
1090
+ }
1091
+ });
1092
+ it('should handle legacy string injection "query"', async () => {
1093
+ class TestController {
1094
+ legacyQuery(query) {
1095
+ return query;
1096
+ }
1097
+ }
1098
+ container.register(TestController, new TestController());
1099
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
1100
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/legacy-query', propertyKey: 'legacyQuery' }], TestController);
1101
+ Reflect.defineMetadata('hazel:inject', ['query'], TestController, 'legacyQuery');
1102
+ router.registerController(TestController);
1103
+ const context = {
1104
+ params: {},
1105
+ query: { filter: 'active' },
1106
+ body: {},
1107
+ headers: {},
1108
+ method: 'GET',
1109
+ url: '/test/legacy-query',
1110
+ };
1111
+ const route = await router.match('GET', '/test/legacy-query', context);
1112
+ if (route) {
1113
+ await route.handler(mockReq, mockRes, context);
1114
+ expect(mockRes.json).toHaveBeenCalledWith({ filter: 'active' });
1115
+ }
1116
+ });
1117
+ it('should handle legacy string injection "headers"', async () => {
1118
+ class TestController {
1119
+ legacyHeaders(headers) {
1120
+ return headers;
1121
+ }
1122
+ }
1123
+ container.register(TestController, new TestController());
1124
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
1125
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/legacy-headers', propertyKey: 'legacyHeaders' }], TestController);
1126
+ Reflect.defineMetadata('hazel:inject', ['headers'], TestController, 'legacyHeaders');
1127
+ router.registerController(TestController);
1128
+ const context = {
1129
+ params: {},
1130
+ query: {},
1131
+ body: {},
1132
+ headers: { 'x-trace': 'abc' },
1133
+ method: 'GET',
1134
+ url: '/test/legacy-headers',
1135
+ };
1136
+ const route = await router.match('GET', '/test/legacy-headers', context);
1137
+ if (route) {
1138
+ await route.handler(mockReq, mockRes, context);
1139
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({ 'x-trace': 'abc' }));
1140
+ }
1141
+ });
1142
+ // -----------------------------------------------------------------------
1143
+ // handleRequest edge cases
1144
+ // -----------------------------------------------------------------------
1145
+ it('should return "Internal Server Error" when a non-Error is thrown in handleRequest', async () => {
1146
+ jest.spyOn(router, 'match').mockRejectedValue('string-error');
1147
+ mockReq.method = 'GET';
1148
+ mockReq.url = '/test';
1149
+ await router.handleRequest(mockReq, mockRes);
1150
+ expect(mockRes.status).toHaveBeenCalledWith(500);
1151
+ expect(mockRes.json).toHaveBeenCalledWith({ error: 'Internal Server Error' });
1152
+ });
1153
+ it('should use production error message when NODE_ENV is production', async () => {
1154
+ const originalEnv = process.env.NODE_ENV;
1155
+ process.env.NODE_ENV = 'production';
1156
+ class TestController {
1157
+ throwInProd() {
1158
+ throw new Error('Sensitive internal details');
1159
+ }
1160
+ }
1161
+ container.register(TestController, new TestController());
1162
+ Reflect.defineMetadata('hazel:controller', { path: '/test' }, TestController);
1163
+ Reflect.defineMetadata('hazel:routes', [{ method: 'GET', path: '/prod-error', propertyKey: 'throwInProd' }], TestController);
1164
+ Reflect.defineMetadata('hazel:inject', [], TestController, 'throwInProd');
1165
+ router.registerController(TestController);
1166
+ const context = {
1167
+ params: {},
1168
+ query: {},
1169
+ body: {},
1170
+ headers: {},
1171
+ method: 'GET',
1172
+ url: '/test/prod-error',
1173
+ };
1174
+ const route = await router.match('GET', '/test/prod-error', context);
1175
+ if (route) {
1176
+ await route.handler(mockReq, mockRes, context);
1177
+ expect(mockRes.status).toHaveBeenCalledWith(500);
1178
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({ message: 'Internal server error' }));
1179
+ }
1180
+ process.env.NODE_ENV = originalEnv;
1181
+ });
679
1182
  });
680
1183
  });
package/dist/container.js CHANGED
@@ -17,7 +17,7 @@ class Container {
17
17
  constructor() {
18
18
  this.providers = new Map();
19
19
  this.requestScopedProviders = new Map();
20
- logger_1.default.info('Container initialized');
20
+ logger_1.default.debug('Container initialized');
21
21
  }
22
22
  static getInstance() {
23
23
  if (!Container.instance) {
@@ -36,7 +36,7 @@ class Container {
36
36
  */
37
37
  register(token, provider, scope = Scope.SINGLETON) {
38
38
  const tokenName = this.getTokenName(token);
39
- logger_1.default.info(`Registering provider: ${tokenName} with scope: ${scope}`);
39
+ logger_1.default.debug(`Registering provider: ${tokenName} with scope: ${scope}`);
40
40
  if (this.isProvider(provider)) {
41
41
  this.registerProvider(provider);
42
42
  }
@@ -216,7 +216,7 @@ class Container {
216
216
  // Create instance with dependencies
217
217
  const instance = new token(...dependencies);
218
218
  if (logger_1.default.isDebugEnabled()) {
219
- logger_1.default.info(`Created instance of: ${this.getTokenName(token)}`);
219
+ logger_1.default.debug(`Created instance of: ${this.getTokenName(token)}`);
220
220
  }
221
221
  return instance;
222
222
  }
@@ -233,7 +233,7 @@ class Container {
233
233
  * Clear all providers
234
234
  */
235
235
  clear() {
236
- logger_1.default.info('Clearing container');
236
+ logger_1.default.debug('Clearing container');
237
237
  this.providers.clear();
238
238
  this.requestScopedProviders.clear();
239
239
  }
@@ -1,8 +1,10 @@
1
1
  import 'reflect-metadata';
2
- import { Type } from './types';
2
+ import { Type, RequestContext, Request } from './types';
3
3
  import { PipeTransform, PipeMetadata } from './pipes/pipe';
4
4
  import { Interceptor, InterceptorMetadata } from './interceptors/interceptor';
5
5
  import { HazelApp } from './hazel-app';
6
+ import type { Container } from './container';
7
+ export declare const CUSTOM_METADATA_PREFIX = "hazel:meta:";
6
8
  export interface ControllerMetadata {
7
9
  path: string;
8
10
  interceptors?: InterceptorMetadata[];
@@ -32,6 +34,7 @@ export interface InjectableOptions {
32
34
  }
33
35
  export interface RepositoryOptions {
34
36
  model: string;
37
+ scope?: 'singleton' | 'transient' | 'request';
35
38
  }
36
39
  export interface OnModuleInit {
37
40
  onModuleInit(): Promise<void>;
@@ -43,6 +46,12 @@ export interface ExecutionContext {
43
46
  switchToHttp(): {
44
47
  getRequest(): unknown;
45
48
  getResponse(): unknown;
49
+ /**
50
+ * Returns the fully parsed RequestContext for this request.
51
+ * Gives guards access to `params`, `query`, `headers`, `user`, and `body`
52
+ * without having to re-parse the raw Node.js IncomingMessage.
53
+ */
54
+ getContext(): RequestContext;
46
55
  };
47
56
  }
48
57
  export interface CanActivate {
@@ -88,5 +97,70 @@ export declare function HttpCode(statusCode: number): MethodDecorator;
88
97
  export declare function Header(name: string, value: string): MethodDecorator;
89
98
  export declare function Redirect(url: string, statusCode?: number): MethodDecorator;
90
99
  export declare function Res(): ParameterDecorator;
100
+ export declare function Ip(): ParameterDecorator;
101
+ export declare function Host(): ParameterDecorator;
102
+ /**
103
+ * Marks a controller or route as public (no auth required).
104
+ * Guards should check Reflect.getMetadata(PUBLIC_METADATA_KEY, target, propertyKey)
105
+ * or Reflect.getMetadata(PUBLIC_METADATA_KEY, target) and allow the request when true.
106
+ */
107
+ export declare function Public(): ClassDecorator & MethodDecorator;
108
+ /** Alias for @Public(). Use when you want to skip auth for specific routes. */
109
+ export declare const SkipAuth: typeof Public;
110
+ export declare function Timeout(ms: number): MethodDecorator;
111
+ export declare function Optional(): ParameterDecorator;
112
+ export declare function Session(): ParameterDecorator;
113
+ export interface RetryDecoratorOptions {
114
+ count: number;
115
+ delay?: number;
116
+ retryIf?: (err: Error) => boolean;
117
+ }
118
+ export declare function Retry(options: RetryDecoratorOptions): MethodDecorator;
119
+ export declare function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator;
120
+ export interface ApiOperationOptions {
121
+ summary?: string;
122
+ description?: string;
123
+ operationId?: string;
124
+ }
125
+ export declare function ApiOperation(options: ApiOperationOptions | string): MethodDecorator;
126
+ /**
127
+ * Sets arbitrary metadata on a class or method.
128
+ * Guards, interceptors, and other components can read it via getMetadata(key, target, propertyKey?).
129
+ *
130
+ * @param key - Metadata key (stored under hazel:meta:<key> to avoid collisions)
131
+ * @param value - Value to store (any serializable or object)
132
+ * @example
133
+ * SetMetadata('roles', ['admin'])(MyController)
134
+ * SetMetadata('roles', ['user'])(MyController.prototype, 'getProfile')
135
+ */
136
+ export declare function SetMetadata(key: string, value: unknown): ClassDecorator & MethodDecorator;
137
+ /**
138
+ * Reads custom metadata set with SetMetadata.
139
+ *
140
+ * @param key - Key passed to SetMetadata(key, value)
141
+ * @param target - Class or prototype
142
+ * @param propertyKey - Optional method name (for method-level metadata)
143
+ */
144
+ export declare function getMetadata<T = unknown>(key: string, target: object, propertyKey?: string | symbol): T | undefined;
145
+ /**
146
+ * Context passed to custom parameter decorator resolvers.
147
+ * The router calls the resolver with (req, context, container) when invoking the handler.
148
+ */
149
+ export interface ParamDecoratorContext {
150
+ req: Request;
151
+ context: RequestContext;
152
+ container: Container;
153
+ }
154
+ /**
155
+ * Creates a custom parameter decorator that injects a value computed from the request.
156
+ * The resolver receives the raw request, parsed request context, and the DI container.
157
+ * Return value can be a Promise for async resolution (e.g. loading the current user from DB).
158
+ *
159
+ * @param resolve - Function (req, context, container) => value | Promise<value>
160
+ * @example
161
+ * const CurrentUser = createParamDecorator(async (req, ctx, container) => ctx.user ?? req.user);
162
+ * // In controller: getProfile(@CurrentUser() user: User) { ... }
163
+ */
164
+ export declare function createParamDecorator<T = unknown>(resolve: (req: Request, context: RequestContext, container: Container) => T | Promise<T>): ParameterDecorator;
91
165
  export { HazelApp };
92
166
  //# sourceMappingURL=decorators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}