@onebun/core 0.1.17 → 0.1.19
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/package.json +2 -2
- package/src/application/application.test.ts +234 -4
- package/src/application/application.ts +109 -12
- package/src/application/multi-service-application.ts +4 -23
- package/src/docs-examples.test.ts +357 -0
- package/src/index.ts +17 -0
- package/src/module/index.ts +1 -0
- package/src/module/lifecycle.ts +197 -0
- package/src/module/module.ts +225 -24
- package/src/module/service.ts +0 -10
- package/src/types.ts +48 -3
|
@@ -26,6 +26,13 @@ import type {
|
|
|
26
26
|
WsExecutionContext,
|
|
27
27
|
WsServerType,
|
|
28
28
|
} from './';
|
|
29
|
+
import type {
|
|
30
|
+
OnModuleInit,
|
|
31
|
+
OnApplicationInit,
|
|
32
|
+
OnModuleDestroy,
|
|
33
|
+
BeforeApplicationDestroy,
|
|
34
|
+
OnApplicationDestroy,
|
|
35
|
+
} from './';
|
|
29
36
|
import type { SseEvent, SseGenerator } from './types';
|
|
30
37
|
import type { ServerWebSocket } from 'bun';
|
|
31
38
|
|
|
@@ -877,6 +884,345 @@ describe('Services API Documentation Examples', () => {
|
|
|
877
884
|
});
|
|
878
885
|
});
|
|
879
886
|
|
|
887
|
+
describe('Lifecycle Hooks API Documentation Examples (docs/api/services.md)', () => {
|
|
888
|
+
describe('OnModuleInit Interface', () => {
|
|
889
|
+
/**
|
|
890
|
+
* @source docs/api/services.md#lifecycle-hooks
|
|
891
|
+
*/
|
|
892
|
+
it('should implement OnModuleInit interface', () => {
|
|
893
|
+
// From docs: OnModuleInit example
|
|
894
|
+
@Service()
|
|
895
|
+
class DatabaseService extends BaseService implements OnModuleInit {
|
|
896
|
+
private connection: unknown = null;
|
|
897
|
+
|
|
898
|
+
async onModuleInit(): Promise<void> {
|
|
899
|
+
// Called after service instantiation and DI
|
|
900
|
+
this.connection = { connected: true };
|
|
901
|
+
this.logger.info('Database connected');
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
isConnected(): boolean {
|
|
905
|
+
return this.connection !== null;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
expect(DatabaseService).toBeDefined();
|
|
910
|
+
// Verify method exists
|
|
911
|
+
const service = new DatabaseService();
|
|
912
|
+
expect(typeof service.onModuleInit).toBe('function');
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
describe('OnApplicationInit Interface', () => {
|
|
917
|
+
/**
|
|
918
|
+
* @source docs/api/services.md#lifecycle-hooks
|
|
919
|
+
*/
|
|
920
|
+
it('should implement OnApplicationInit interface', () => {
|
|
921
|
+
// From docs: OnApplicationInit example
|
|
922
|
+
@Service()
|
|
923
|
+
class CacheService extends BaseService implements OnApplicationInit {
|
|
924
|
+
async onApplicationInit(): Promise<void> {
|
|
925
|
+
// Called after all modules initialized, before HTTP server starts
|
|
926
|
+
this.logger.info('Warming up cache');
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
expect(CacheService).toBeDefined();
|
|
931
|
+
const service = new CacheService();
|
|
932
|
+
expect(typeof service.onApplicationInit).toBe('function');
|
|
933
|
+
});
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
describe('OnModuleDestroy Interface', () => {
|
|
937
|
+
/**
|
|
938
|
+
* @source docs/api/services.md#lifecycle-hooks
|
|
939
|
+
*/
|
|
940
|
+
it('should implement OnModuleDestroy interface', () => {
|
|
941
|
+
// From docs: OnModuleDestroy example
|
|
942
|
+
@Service()
|
|
943
|
+
class ConnectionService extends BaseService implements OnModuleDestroy {
|
|
944
|
+
async onModuleDestroy(): Promise<void> {
|
|
945
|
+
// Called during shutdown, after HTTP server stops
|
|
946
|
+
this.logger.info('Closing connections');
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
expect(ConnectionService).toBeDefined();
|
|
951
|
+
const service = new ConnectionService();
|
|
952
|
+
expect(typeof service.onModuleDestroy).toBe('function');
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
describe('BeforeApplicationDestroy Interface', () => {
|
|
957
|
+
/**
|
|
958
|
+
* @source docs/api/services.md#lifecycle-hooks
|
|
959
|
+
*/
|
|
960
|
+
it('should implement BeforeApplicationDestroy interface', () => {
|
|
961
|
+
// From docs: BeforeApplicationDestroy example
|
|
962
|
+
@Service()
|
|
963
|
+
class GracefulService extends BaseService implements BeforeApplicationDestroy {
|
|
964
|
+
beforeApplicationDestroy(signal?: string): void {
|
|
965
|
+
// Called at the very start of shutdown
|
|
966
|
+
this.logger.info(`Shutdown initiated by signal: ${signal || 'unknown'}`);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
expect(GracefulService).toBeDefined();
|
|
971
|
+
const service = new GracefulService();
|
|
972
|
+
expect(typeof service.beforeApplicationDestroy).toBe('function');
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
describe('OnApplicationDestroy Interface', () => {
|
|
977
|
+
/**
|
|
978
|
+
* @source docs/api/services.md#lifecycle-hooks
|
|
979
|
+
*/
|
|
980
|
+
it('should implement OnApplicationDestroy interface', () => {
|
|
981
|
+
// From docs: OnApplicationDestroy example
|
|
982
|
+
@Service()
|
|
983
|
+
class CleanupService extends BaseService implements OnApplicationDestroy {
|
|
984
|
+
async onApplicationDestroy(signal?: string): Promise<void> {
|
|
985
|
+
// Called at the very end of shutdown
|
|
986
|
+
this.logger.info(`Final cleanup, signal: ${signal || 'unknown'}`);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
expect(CleanupService).toBeDefined();
|
|
991
|
+
const service = new CleanupService();
|
|
992
|
+
expect(typeof service.onApplicationDestroy).toBe('function');
|
|
993
|
+
});
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
describe('Multiple Lifecycle Hooks', () => {
|
|
997
|
+
/**
|
|
998
|
+
* @source docs/api/services.md#lifecycle-hooks
|
|
999
|
+
*/
|
|
1000
|
+
it('should implement multiple lifecycle interfaces', () => {
|
|
1001
|
+
// From docs: Complete lifecycle example
|
|
1002
|
+
@Service()
|
|
1003
|
+
class FullLifecycleService extends BaseService
|
|
1004
|
+
implements OnModuleInit, OnApplicationInit, OnModuleDestroy, BeforeApplicationDestroy, OnApplicationDestroy {
|
|
1005
|
+
|
|
1006
|
+
async onModuleInit(): Promise<void> {
|
|
1007
|
+
this.logger.info('Service initialized');
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
async onApplicationInit(): Promise<void> {
|
|
1011
|
+
this.logger.info('Application initialized');
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
beforeApplicationDestroy(signal?: string): void {
|
|
1015
|
+
this.logger.info(`Shutdown starting: ${signal}`);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
async onModuleDestroy(): Promise<void> {
|
|
1019
|
+
this.logger.info('Module destroying');
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async onApplicationDestroy(signal?: string): Promise<void> {
|
|
1023
|
+
this.logger.info(`Application destroyed: ${signal}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
expect(FullLifecycleService).toBeDefined();
|
|
1028
|
+
const service = new FullLifecycleService();
|
|
1029
|
+
expect(typeof service.onModuleInit).toBe('function');
|
|
1030
|
+
expect(typeof service.onApplicationInit).toBe('function');
|
|
1031
|
+
expect(typeof service.beforeApplicationDestroy).toBe('function');
|
|
1032
|
+
expect(typeof service.onModuleDestroy).toBe('function');
|
|
1033
|
+
expect(typeof service.onApplicationDestroy).toBe('function');
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
describe('Controller Lifecycle Hooks', () => {
|
|
1038
|
+
/**
|
|
1039
|
+
* @source docs/api/controllers.md#lifecycle-hooks
|
|
1040
|
+
*/
|
|
1041
|
+
it('should implement lifecycle hooks in controllers', () => {
|
|
1042
|
+
// From docs: Controller lifecycle hooks example
|
|
1043
|
+
@Controller('/api')
|
|
1044
|
+
class ApiController extends BaseController implements OnModuleInit, OnModuleDestroy {
|
|
1045
|
+
async onModuleInit(): Promise<void> {
|
|
1046
|
+
this.logger.info('Controller initialized');
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
async onModuleDestroy(): Promise<void> {
|
|
1050
|
+
this.logger.info('Controller destroying');
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
@Get('/test')
|
|
1054
|
+
test(): Response {
|
|
1055
|
+
return this.success({ message: 'test' });
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
expect(ApiController).toBeDefined();
|
|
1060
|
+
const controller = new ApiController();
|
|
1061
|
+
expect(typeof controller.onModuleInit).toBe('function');
|
|
1062
|
+
expect(typeof controller.onModuleDestroy).toBe('function');
|
|
1063
|
+
});
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
describe('Lifecycle Helper Functions', () => {
|
|
1067
|
+
/**
|
|
1068
|
+
* Tests for lifecycle helper functions
|
|
1069
|
+
*/
|
|
1070
|
+
it('should detect hasOnModuleInit correctly', () => {
|
|
1071
|
+
const withHook = { onModuleInit: () => Promise.resolve() };
|
|
1072
|
+
const withoutHook = { someOtherMethod: () => 'nothing' };
|
|
1073
|
+
|
|
1074
|
+
expect(hasOnModuleInit(withHook)).toBe(true);
|
|
1075
|
+
expect(hasOnModuleInit(withoutHook)).toBe(false);
|
|
1076
|
+
expect(hasOnModuleInit(null)).toBe(false);
|
|
1077
|
+
expect(hasOnModuleInit(undefined)).toBe(false);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it('should detect hasOnApplicationInit correctly', () => {
|
|
1081
|
+
const withHook = { onApplicationInit: () => Promise.resolve() };
|
|
1082
|
+
const withoutHook = {};
|
|
1083
|
+
|
|
1084
|
+
expect(hasOnApplicationInit(withHook)).toBe(true);
|
|
1085
|
+
expect(hasOnApplicationInit(withoutHook)).toBe(false);
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
it('should detect hasOnModuleDestroy correctly', () => {
|
|
1089
|
+
const withHook = { onModuleDestroy: () => Promise.resolve() };
|
|
1090
|
+
const withoutHook = {};
|
|
1091
|
+
|
|
1092
|
+
expect(hasOnModuleDestroy(withHook)).toBe(true);
|
|
1093
|
+
expect(hasOnModuleDestroy(withoutHook)).toBe(false);
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
it('should detect hasBeforeApplicationDestroy correctly', () => {
|
|
1097
|
+
const withHook = { beforeApplicationDestroy: () => undefined };
|
|
1098
|
+
const withoutHook = {};
|
|
1099
|
+
|
|
1100
|
+
expect(hasBeforeApplicationDestroy(withHook)).toBe(true);
|
|
1101
|
+
expect(hasBeforeApplicationDestroy(withoutHook)).toBe(false);
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
it('should detect hasOnApplicationDestroy correctly', () => {
|
|
1105
|
+
const withHook = { onApplicationDestroy: () => Promise.resolve() };
|
|
1106
|
+
const withoutHook = {};
|
|
1107
|
+
|
|
1108
|
+
expect(hasOnApplicationDestroy(withHook)).toBe(true);
|
|
1109
|
+
expect(hasOnApplicationDestroy(withoutHook)).toBe(false);
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
it('should call lifecycle hooks safely', async () => {
|
|
1113
|
+
const results: string[] = [];
|
|
1114
|
+
|
|
1115
|
+
const service = {
|
|
1116
|
+
async onModuleInit() {
|
|
1117
|
+
results.push('init');
|
|
1118
|
+
},
|
|
1119
|
+
async onApplicationInit() {
|
|
1120
|
+
results.push('appInit');
|
|
1121
|
+
},
|
|
1122
|
+
beforeApplicationDestroy(signal?: string) {
|
|
1123
|
+
results.push(`before:${signal}`);
|
|
1124
|
+
},
|
|
1125
|
+
async onModuleDestroy() {
|
|
1126
|
+
results.push('destroy');
|
|
1127
|
+
},
|
|
1128
|
+
async onApplicationDestroy(signal?: string) {
|
|
1129
|
+
results.push(`appDestroy:${signal}`);
|
|
1130
|
+
},
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
await callOnModuleInit(service);
|
|
1134
|
+
await callOnApplicationInit(service);
|
|
1135
|
+
await callBeforeApplicationDestroy(service, 'SIGTERM');
|
|
1136
|
+
await callOnModuleDestroy(service);
|
|
1137
|
+
await callOnApplicationDestroy(service, 'SIGTERM');
|
|
1138
|
+
|
|
1139
|
+
expect(results).toEqual(['init', 'appInit', 'before:SIGTERM', 'destroy', 'appDestroy:SIGTERM']);
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
it('should not throw when calling hooks on objects without them', async () => {
|
|
1143
|
+
const emptyObj = {};
|
|
1144
|
+
|
|
1145
|
+
// These should not throw
|
|
1146
|
+
await callOnModuleInit(emptyObj);
|
|
1147
|
+
await callOnApplicationInit(emptyObj);
|
|
1148
|
+
await callBeforeApplicationDestroy(emptyObj, 'SIGTERM');
|
|
1149
|
+
await callOnModuleDestroy(emptyObj);
|
|
1150
|
+
await callOnApplicationDestroy(emptyObj, 'SIGTERM');
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
describe('getService API Documentation Examples (docs/api/core.md)', () => {
|
|
1156
|
+
/**
|
|
1157
|
+
* @source docs/api/core.md#accessing-services-outside-of-requests
|
|
1158
|
+
*/
|
|
1159
|
+
it('should have getService method on OneBunApplication', () => {
|
|
1160
|
+
@Module({ controllers: [] })
|
|
1161
|
+
class AppModule {}
|
|
1162
|
+
|
|
1163
|
+
const app = new OneBunApplication(AppModule, {
|
|
1164
|
+
loggerLayer: makeMockLoggerLayer(),
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
expect(typeof app.getService).toBe('function');
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* @source docs/api/core.md#accessing-services-outside-of-requests
|
|
1172
|
+
*/
|
|
1173
|
+
it('should get service instance by class', async () => {
|
|
1174
|
+
@Service()
|
|
1175
|
+
class TaskService extends BaseService {
|
|
1176
|
+
performTask(): string {
|
|
1177
|
+
return 'task completed';
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
@Module({
|
|
1182
|
+
providers: [TaskService],
|
|
1183
|
+
controllers: [],
|
|
1184
|
+
})
|
|
1185
|
+
class AppModule {}
|
|
1186
|
+
|
|
1187
|
+
const app = new OneBunApplication(AppModule, {
|
|
1188
|
+
loggerLayer: makeMockLoggerLayer(),
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
await app.start();
|
|
1192
|
+
|
|
1193
|
+
// From docs: getService usage example
|
|
1194
|
+
const taskService = app.getService(TaskService);
|
|
1195
|
+
expect(taskService).toBeDefined();
|
|
1196
|
+
expect(taskService.performTask()).toBe('task completed');
|
|
1197
|
+
|
|
1198
|
+
await app.stop();
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* @source docs/api/core.md#accessing-services-outside-of-requests
|
|
1203
|
+
*/
|
|
1204
|
+
it('should throw error for non-existent service', async () => {
|
|
1205
|
+
@Service()
|
|
1206
|
+
class NonExistentService extends BaseService {}
|
|
1207
|
+
|
|
1208
|
+
@Module({
|
|
1209
|
+
controllers: [],
|
|
1210
|
+
})
|
|
1211
|
+
class AppModule {}
|
|
1212
|
+
|
|
1213
|
+
const app = new OneBunApplication(AppModule, {
|
|
1214
|
+
loggerLayer: makeMockLoggerLayer(),
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
await app.start();
|
|
1218
|
+
|
|
1219
|
+
// getService throws when service is not found
|
|
1220
|
+
expect(() => app.getService(NonExistentService)).toThrow();
|
|
1221
|
+
|
|
1222
|
+
await app.stop();
|
|
1223
|
+
});
|
|
1224
|
+
});
|
|
1225
|
+
|
|
880
1226
|
describe('Validation API Documentation Examples', () => {
|
|
881
1227
|
describe('validate function (docs/api/validation.md)', () => {
|
|
882
1228
|
/**
|
|
@@ -2936,8 +3282,19 @@ import {
|
|
|
2936
3282
|
createWsClient,
|
|
2937
3283
|
matchPattern,
|
|
2938
3284
|
makeMockLoggerLayer,
|
|
3285
|
+
hasOnModuleInit,
|
|
3286
|
+
hasOnApplicationInit,
|
|
3287
|
+
hasOnModuleDestroy,
|
|
3288
|
+
hasBeforeApplicationDestroy,
|
|
3289
|
+
hasOnApplicationDestroy,
|
|
3290
|
+
callOnModuleInit,
|
|
3291
|
+
callOnApplicationInit,
|
|
3292
|
+
callOnModuleDestroy,
|
|
3293
|
+
callBeforeApplicationDestroy,
|
|
3294
|
+
callOnApplicationDestroy,
|
|
2939
3295
|
} from './';
|
|
2940
3296
|
|
|
3297
|
+
|
|
2941
3298
|
describe('SSE (Server-Sent Events) API Documentation (docs/api/controllers.md)', () => {
|
|
2942
3299
|
describe('SseEvent Type (docs/api/controllers.md)', () => {
|
|
2943
3300
|
/**
|
package/src/index.ts
CHANGED
|
@@ -70,6 +70,23 @@ export {
|
|
|
70
70
|
type IConfig,
|
|
71
71
|
type OneBunAppConfig,
|
|
72
72
|
NotInitializedConfig,
|
|
73
|
+
// Lifecycle hooks interfaces
|
|
74
|
+
type OnModuleInit,
|
|
75
|
+
type OnApplicationInit,
|
|
76
|
+
type OnModuleDestroy,
|
|
77
|
+
type BeforeApplicationDestroy,
|
|
78
|
+
type OnApplicationDestroy,
|
|
79
|
+
// Lifecycle hooks helper functions
|
|
80
|
+
hasOnModuleInit,
|
|
81
|
+
hasOnApplicationInit,
|
|
82
|
+
hasOnModuleDestroy,
|
|
83
|
+
hasBeforeApplicationDestroy,
|
|
84
|
+
hasOnApplicationDestroy,
|
|
85
|
+
callOnModuleInit,
|
|
86
|
+
callOnApplicationInit,
|
|
87
|
+
callOnModuleDestroy,
|
|
88
|
+
callBeforeApplicationDestroy,
|
|
89
|
+
callOnApplicationDestroy,
|
|
73
90
|
} from './module';
|
|
74
91
|
|
|
75
92
|
// Application
|
package/src/module/index.ts
CHANGED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle hooks interfaces for services and controllers.
|
|
3
|
+
* Implement these interfaces to hook into the application lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { Service, BaseService, OnModuleInit, OnModuleDestroy } from '@onebun/core';
|
|
8
|
+
*
|
|
9
|
+
* @Service()
|
|
10
|
+
* export class DatabaseService extends BaseService implements OnModuleInit, OnModuleDestroy {
|
|
11
|
+
* async onModuleInit() {
|
|
12
|
+
* await this.pool.connect();
|
|
13
|
+
* this.logger.info('Database connected');
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* async onModuleDestroy() {
|
|
17
|
+
* await this.pool.end();
|
|
18
|
+
* this.logger.info('Database disconnected');
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Interface for hook called after module initialization.
|
|
26
|
+
* Called after the service/controller is instantiated and dependencies are injected.
|
|
27
|
+
*/
|
|
28
|
+
export interface OnModuleInit {
|
|
29
|
+
/**
|
|
30
|
+
* Called after the module has been initialized.
|
|
31
|
+
* Can be async - the framework will await completion.
|
|
32
|
+
*/
|
|
33
|
+
onModuleInit(): Promise<void> | void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Interface for hook called after application initialization.
|
|
38
|
+
* Called after all modules are initialized and before the HTTP server starts.
|
|
39
|
+
*/
|
|
40
|
+
export interface OnApplicationInit {
|
|
41
|
+
/**
|
|
42
|
+
* Called after the application has been initialized.
|
|
43
|
+
* Can be async - the framework will await completion.
|
|
44
|
+
*/
|
|
45
|
+
onApplicationInit(): Promise<void> | void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Interface for hook called when module is being destroyed.
|
|
50
|
+
* Called during application shutdown, after HTTP server stops.
|
|
51
|
+
*/
|
|
52
|
+
export interface OnModuleDestroy {
|
|
53
|
+
/**
|
|
54
|
+
* Called when the module is being destroyed.
|
|
55
|
+
* Can be async - the framework will await completion.
|
|
56
|
+
*/
|
|
57
|
+
onModuleDestroy(): Promise<void> | void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Interface for hook called before application shutdown begins.
|
|
62
|
+
* Called at the very start of the shutdown process.
|
|
63
|
+
*/
|
|
64
|
+
export interface BeforeApplicationDestroy {
|
|
65
|
+
/**
|
|
66
|
+
* Called before the application shutdown process begins.
|
|
67
|
+
* Can be async - the framework will await completion.
|
|
68
|
+
* @param signal - The signal that triggered the shutdown (e.g., 'SIGTERM', 'SIGINT')
|
|
69
|
+
*/
|
|
70
|
+
beforeApplicationDestroy(signal?: string): Promise<void> | void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Interface for hook called after application shutdown completes.
|
|
75
|
+
* Called at the very end of the shutdown process.
|
|
76
|
+
*/
|
|
77
|
+
export interface OnApplicationDestroy {
|
|
78
|
+
/**
|
|
79
|
+
* Called after the application shutdown process completes.
|
|
80
|
+
* Can be async - the framework will await completion.
|
|
81
|
+
* @param signal - The signal that triggered the shutdown (e.g., 'SIGTERM', 'SIGINT')
|
|
82
|
+
*/
|
|
83
|
+
onApplicationDestroy(signal?: string): Promise<void> | void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// Helper functions for checking if an object implements lifecycle hooks
|
|
88
|
+
// =============================================================================
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if an object has an onModuleInit method
|
|
92
|
+
*/
|
|
93
|
+
export function hasOnModuleInit(obj: unknown): obj is OnModuleInit {
|
|
94
|
+
return (
|
|
95
|
+
typeof obj === 'object' &&
|
|
96
|
+
obj !== null &&
|
|
97
|
+
'onModuleInit' in obj &&
|
|
98
|
+
typeof (obj as OnModuleInit).onModuleInit === 'function'
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if an object has an onApplicationInit method
|
|
104
|
+
*/
|
|
105
|
+
export function hasOnApplicationInit(obj: unknown): obj is OnApplicationInit {
|
|
106
|
+
return (
|
|
107
|
+
typeof obj === 'object' &&
|
|
108
|
+
obj !== null &&
|
|
109
|
+
'onApplicationInit' in obj &&
|
|
110
|
+
typeof (obj as OnApplicationInit).onApplicationInit === 'function'
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check if an object has an onModuleDestroy method
|
|
116
|
+
*/
|
|
117
|
+
export function hasOnModuleDestroy(obj: unknown): obj is OnModuleDestroy {
|
|
118
|
+
return (
|
|
119
|
+
typeof obj === 'object' &&
|
|
120
|
+
obj !== null &&
|
|
121
|
+
'onModuleDestroy' in obj &&
|
|
122
|
+
typeof (obj as OnModuleDestroy).onModuleDestroy === 'function'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if an object has a beforeApplicationDestroy method
|
|
128
|
+
*/
|
|
129
|
+
export function hasBeforeApplicationDestroy(obj: unknown): obj is BeforeApplicationDestroy {
|
|
130
|
+
return (
|
|
131
|
+
typeof obj === 'object' &&
|
|
132
|
+
obj !== null &&
|
|
133
|
+
'beforeApplicationDestroy' in obj &&
|
|
134
|
+
typeof (obj as BeforeApplicationDestroy).beforeApplicationDestroy === 'function'
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if an object has an onApplicationDestroy method
|
|
140
|
+
*/
|
|
141
|
+
export function hasOnApplicationDestroy(obj: unknown): obj is OnApplicationDestroy {
|
|
142
|
+
return (
|
|
143
|
+
typeof obj === 'object' &&
|
|
144
|
+
obj !== null &&
|
|
145
|
+
'onApplicationDestroy' in obj &&
|
|
146
|
+
typeof (obj as OnApplicationDestroy).onApplicationDestroy === 'function'
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// =============================================================================
|
|
151
|
+
// Helper functions to call lifecycle hooks safely
|
|
152
|
+
// =============================================================================
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Call onModuleInit on an object if it implements the hook
|
|
156
|
+
*/
|
|
157
|
+
export async function callOnModuleInit(obj: unknown): Promise<void> {
|
|
158
|
+
if (hasOnModuleInit(obj)) {
|
|
159
|
+
await obj.onModuleInit();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Call onApplicationInit on an object if it implements the hook
|
|
165
|
+
*/
|
|
166
|
+
export async function callOnApplicationInit(obj: unknown): Promise<void> {
|
|
167
|
+
if (hasOnApplicationInit(obj)) {
|
|
168
|
+
await obj.onApplicationInit();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Call onModuleDestroy on an object if it implements the hook
|
|
174
|
+
*/
|
|
175
|
+
export async function callOnModuleDestroy(obj: unknown): Promise<void> {
|
|
176
|
+
if (hasOnModuleDestroy(obj)) {
|
|
177
|
+
await obj.onModuleDestroy();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Call beforeApplicationDestroy on an object if it implements the hook
|
|
183
|
+
*/
|
|
184
|
+
export async function callBeforeApplicationDestroy(obj: unknown, signal?: string): Promise<void> {
|
|
185
|
+
if (hasBeforeApplicationDestroy(obj)) {
|
|
186
|
+
await obj.beforeApplicationDestroy(signal);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Call onApplicationDestroy on an object if it implements the hook
|
|
192
|
+
*/
|
|
193
|
+
export async function callOnApplicationDestroy(obj: unknown, signal?: string): Promise<void> {
|
|
194
|
+
if (hasOnApplicationDestroy(obj)) {
|
|
195
|
+
await obj.onApplicationDestroy(signal);
|
|
196
|
+
}
|
|
197
|
+
}
|