@dangao/bun-server 2.1.0 → 2.2.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.
- package/dist/core/application.d.ts.map +1 -1
- package/dist/di/container.d.ts +16 -0
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/lifecycle.d.ts +48 -0
- package/dist/di/lifecycle.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +10 -6
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -67
- package/docs/lifecycle.md +74 -4
- package/docs/zh/lifecycle.md +47 -8
- package/package.json +1 -1
- package/src/core/application.ts +36 -23
- package/src/di/container.ts +55 -1
- package/src/di/lifecycle.ts +114 -0
- package/src/di/module-registry.ts +58 -10
- package/src/index.ts +4 -0
- package/tests/di/lifecycle.test.ts +102 -1
- package/tests/di/scoped-lifecycle.test.ts +61 -0
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
callOnModuleDestroy,
|
|
17
17
|
callOnApplicationBootstrap,
|
|
18
18
|
callOnApplicationShutdown,
|
|
19
|
+
callComponentBeforeCreate,
|
|
20
|
+
callOnAfterCreate,
|
|
21
|
+
callOnBeforeDestroy,
|
|
22
|
+
callOnAfterDestroy,
|
|
19
23
|
} from '../../src/di/lifecycle';
|
|
20
24
|
|
|
21
25
|
describe('Lifecycle Hooks', () => {
|
|
@@ -68,6 +72,54 @@ describe('Lifecycle Hooks', () => {
|
|
|
68
72
|
]);
|
|
69
73
|
});
|
|
70
74
|
|
|
75
|
+
test('callComponentBeforeCreate and callOnAfterCreate should invoke component creation hooks', () => {
|
|
76
|
+
const calls: string[] = [];
|
|
77
|
+
class TestComponent {
|
|
78
|
+
public static onBeforeCreate(): void {
|
|
79
|
+
calls.push('beforeCreate');
|
|
80
|
+
}
|
|
81
|
+
public onAfterCreate(): void {
|
|
82
|
+
calls.push('afterCreate');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
callComponentBeforeCreate(TestComponent);
|
|
87
|
+
callOnAfterCreate(new TestComponent());
|
|
88
|
+
expect(calls).toEqual(['beforeCreate', 'afterCreate']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('callOnBeforeDestroy and callOnAfterDestroy should invoke in reverse order', async () => {
|
|
92
|
+
const calls: string[] = [];
|
|
93
|
+
const instances = [
|
|
94
|
+
{
|
|
95
|
+
onBeforeDestroy: () => {
|
|
96
|
+
calls.push('before:first');
|
|
97
|
+
},
|
|
98
|
+
onAfterDestroy: () => {
|
|
99
|
+
calls.push('after:first');
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
onBeforeDestroy: () => {
|
|
104
|
+
calls.push('before:second');
|
|
105
|
+
},
|
|
106
|
+
onAfterDestroy: () => {
|
|
107
|
+
calls.push('after:second');
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
await callOnBeforeDestroy(instances);
|
|
113
|
+
await callOnAfterDestroy(instances);
|
|
114
|
+
|
|
115
|
+
expect(calls).toEqual([
|
|
116
|
+
'before:second',
|
|
117
|
+
'before:first',
|
|
118
|
+
'after:second',
|
|
119
|
+
'after:first',
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
|
|
71
123
|
test('should skip non-hook instances', async () => {
|
|
72
124
|
const calls: string[] = [];
|
|
73
125
|
const instances = [
|
|
@@ -90,12 +142,24 @@ describe('Lifecycle Hooks', () => {
|
|
|
90
142
|
|
|
91
143
|
@Injectable()
|
|
92
144
|
class LifecycleService implements OnModuleInit, OnModuleDestroy, OnApplicationBootstrap, OnApplicationShutdown {
|
|
145
|
+
public static onBeforeCreate(): void {
|
|
146
|
+
calls.push('service:onBeforeCreate');
|
|
147
|
+
}
|
|
148
|
+
public onAfterCreate(): void {
|
|
149
|
+
calls.push('service:onAfterCreate');
|
|
150
|
+
}
|
|
151
|
+
public onBeforeDestroy(): void {
|
|
152
|
+
calls.push('service:onBeforeDestroy');
|
|
153
|
+
}
|
|
93
154
|
public onModuleInit(): void {
|
|
94
155
|
calls.push('onModuleInit');
|
|
95
156
|
}
|
|
96
157
|
public onModuleDestroy(): void {
|
|
97
158
|
calls.push('onModuleDestroy');
|
|
98
159
|
}
|
|
160
|
+
public onAfterDestroy(): void {
|
|
161
|
+
calls.push('service:onAfterDestroy');
|
|
162
|
+
}
|
|
99
163
|
public onApplicationBootstrap(): void {
|
|
100
164
|
calls.push('onApplicationBootstrap');
|
|
101
165
|
}
|
|
@@ -105,7 +169,31 @@ describe('Lifecycle Hooks', () => {
|
|
|
105
169
|
}
|
|
106
170
|
|
|
107
171
|
@Controller('/test')
|
|
108
|
-
class TestController {
|
|
172
|
+
class TestController implements OnModuleInit, OnModuleDestroy, OnApplicationBootstrap, OnApplicationShutdown {
|
|
173
|
+
public static onBeforeCreate(): void {
|
|
174
|
+
calls.push('controller:onBeforeCreate');
|
|
175
|
+
}
|
|
176
|
+
public onAfterCreate(): void {
|
|
177
|
+
calls.push('controller:onAfterCreate');
|
|
178
|
+
}
|
|
179
|
+
public onBeforeDestroy(): void {
|
|
180
|
+
calls.push('controller:onBeforeDestroy');
|
|
181
|
+
}
|
|
182
|
+
public onModuleInit(): void {
|
|
183
|
+
calls.push('controller:onModuleInit');
|
|
184
|
+
}
|
|
185
|
+
public onModuleDestroy(): void {
|
|
186
|
+
calls.push('controller:onModuleDestroy');
|
|
187
|
+
}
|
|
188
|
+
public onAfterDestroy(): void {
|
|
189
|
+
calls.push('controller:onAfterDestroy');
|
|
190
|
+
}
|
|
191
|
+
public onApplicationBootstrap(): void {
|
|
192
|
+
calls.push('controller:onApplicationBootstrap');
|
|
193
|
+
}
|
|
194
|
+
public onApplicationShutdown(signal?: string): void {
|
|
195
|
+
calls.push(`controller:onApplicationShutdown:${signal ?? 'none'}`);
|
|
196
|
+
}
|
|
109
197
|
@GET('/')
|
|
110
198
|
public get(): string {
|
|
111
199
|
return 'ok';
|
|
@@ -122,8 +210,14 @@ describe('Lifecycle Hooks', () => {
|
|
|
122
210
|
app.registerModule(TestModule);
|
|
123
211
|
await app.listen(0);
|
|
124
212
|
|
|
213
|
+
expect(calls).toContain('service:onBeforeCreate');
|
|
214
|
+
expect(calls).toContain('service:onAfterCreate');
|
|
215
|
+
expect(calls).toContain('controller:onBeforeCreate');
|
|
216
|
+
expect(calls).toContain('controller:onAfterCreate');
|
|
125
217
|
expect(calls).toContain('onModuleInit');
|
|
126
218
|
expect(calls).toContain('onApplicationBootstrap');
|
|
219
|
+
expect(calls).toContain('controller:onModuleInit');
|
|
220
|
+
expect(calls).toContain('controller:onApplicationBootstrap');
|
|
127
221
|
|
|
128
222
|
const initIdx = calls.indexOf('onModuleInit');
|
|
129
223
|
const bootIdx = calls.indexOf('onApplicationBootstrap');
|
|
@@ -131,10 +225,17 @@ describe('Lifecycle Hooks', () => {
|
|
|
131
225
|
|
|
132
226
|
await app.stop();
|
|
133
227
|
|
|
228
|
+
expect(calls).toContain('service:onBeforeDestroy');
|
|
134
229
|
expect(calls).toContain('onModuleDestroy');
|
|
230
|
+
expect(calls).toContain('service:onAfterDestroy');
|
|
231
|
+
expect(calls).toContain('controller:onBeforeDestroy');
|
|
232
|
+
expect(calls).toContain('controller:onModuleDestroy');
|
|
233
|
+
expect(calls).toContain('controller:onAfterDestroy');
|
|
135
234
|
// onApplicationShutdown with no signal when using stop() directly
|
|
136
235
|
const shutdownEntry = calls.find((c) => c.startsWith('onApplicationShutdown'));
|
|
137
236
|
expect(shutdownEntry).toBeDefined();
|
|
237
|
+
const controllerShutdownEntry = calls.find((c) => c.startsWith('controller:onApplicationShutdown'));
|
|
238
|
+
expect(controllerShutdownEntry).toBeDefined();
|
|
138
239
|
});
|
|
139
240
|
|
|
140
241
|
test('should call onModuleInit once for duplicated provider instance', async () => {
|
|
@@ -219,5 +219,66 @@ describe('Lifecycle.Scoped', () => {
|
|
|
219
219
|
// 工厂函数应该被调用 2 次(每个请求一次)
|
|
220
220
|
expect(factoryCallCount).toBe(2);
|
|
221
221
|
});
|
|
222
|
+
|
|
223
|
+
test('should call scoped destroy hooks on context dispose', async () => {
|
|
224
|
+
const calls: string[] = [];
|
|
225
|
+
|
|
226
|
+
@Injectable({ lifecycle: Lifecycle.Scoped })
|
|
227
|
+
class ScopedService {
|
|
228
|
+
public onBeforeDestroy(): void {
|
|
229
|
+
calls.push('before');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public onModuleDestroy(): void {
|
|
233
|
+
calls.push('destroy');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
public onAfterDestroy(): void {
|
|
237
|
+
calls.push('after');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
container.register(ScopedService);
|
|
242
|
+
|
|
243
|
+
const request = new Request('http://localhost:3000/api/test');
|
|
244
|
+
const context = new Context(request);
|
|
245
|
+
|
|
246
|
+
await contextStore.run(context, async () => {
|
|
247
|
+
const instance = container.resolve(ScopedService);
|
|
248
|
+
expect(instance).toBeInstanceOf(ScopedService);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await container.disposeScopedInstances(context);
|
|
252
|
+
|
|
253
|
+
expect(calls).toEqual(['before', 'destroy', 'after']);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('should call scoped destroy hooks once per request context', async () => {
|
|
257
|
+
const calls: string[] = [];
|
|
258
|
+
|
|
259
|
+
@Injectable({ lifecycle: Lifecycle.Scoped })
|
|
260
|
+
class ScopedService {
|
|
261
|
+
public onModuleDestroy(): void {
|
|
262
|
+
calls.push('destroy');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
container.register(ScopedService);
|
|
267
|
+
|
|
268
|
+
const context1 = new Context(new Request('http://localhost:3000/api/test-1'));
|
|
269
|
+
const context2 = new Context(new Request('http://localhost:3000/api/test-2'));
|
|
270
|
+
|
|
271
|
+
await contextStore.run(context1, async () => {
|
|
272
|
+
container.resolve(ScopedService);
|
|
273
|
+
});
|
|
274
|
+
await container.disposeScopedInstances(context1);
|
|
275
|
+
|
|
276
|
+
await contextStore.run(context2, async () => {
|
|
277
|
+
container.resolve(ScopedService);
|
|
278
|
+
});
|
|
279
|
+
await container.disposeScopedInstances(context2);
|
|
280
|
+
|
|
281
|
+
expect(calls).toEqual(['destroy', 'destroy']);
|
|
282
|
+
});
|
|
222
283
|
});
|
|
223
284
|
|