-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dispose.ts
158 lines (148 loc) · 5.25 KB
/
dispose.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import {
INSTANCE,
NO_PROVIDER,
SILO_CONTEXT,
SPECIAL_PROPS,
location,
parseDependencyDeclaration,
service,
} from './util.js';
import initDebug from 'debug';
import type { Disposer, ServiceName } from './util.js';
import type { Knifecycle, SiloContext } from './index.js';
const debug = initDebug('knifecycle');
export const DISPOSE = '$dispose';
/**
* Allow to dispose the services of an
* initialized silo content.
*/
async function initDispose({
$instance,
$siloContext,
}: {
$instance: Knifecycle;
$siloContext: SiloContext;
}): Promise<Disposer> {
return async () => {
$siloContext._shutdownPromise =
$siloContext._shutdownPromise ||
_shutdownNextServices($siloContext.loadingSequences.concat());
await $siloContext._shutdownPromise;
delete $instance._silosContexts[$siloContext.index];
// Shutdown services in their instanciation order
async function _shutdownNextServices(
serviceLoadSequences: ServiceName[][],
) {
if (0 === serviceLoadSequences.length) {
return;
}
const currentServiceLoadSequence = serviceLoadSequences.pop() || [];
// First ensure to remove services that are depend on
// by another service loaded in the same batch (may
// happen depending on the load sequence)
const dependendedByAServiceInTheSameBatch =
currentServiceLoadSequence.filter((serviceName) => {
if (
currentServiceLoadSequence
.filter(
(anotherServiceName) => anotherServiceName !== serviceName,
)
.some((anotherServiceName) =>
(
$instance._initializersStates[anotherServiceName]
?.initializer?.[SPECIAL_PROPS.INJECT] || []
)
.map(
(declaration) =>
parseDependencyDeclaration(declaration).serviceName,
)
.includes(serviceName),
)
) {
debug(
`Delaying service "${serviceName}" dependencies shutdown to a dedicated batch.'`,
);
return true;
}
});
await Promise.all(
currentServiceLoadSequence
.filter(
(serviceName) =>
!dependendedByAServiceInTheSameBatch.includes(serviceName),
)
.map(async (serviceName) => {
const initializeState = $instance._initializersStates[serviceName];
if ('silosInstances' in initializeState) {
const provider = $instance._getServiceProvider(
$siloContext,
serviceName,
);
if (
serviceLoadSequences.some((servicesLoadSequence) =>
servicesLoadSequence.includes(serviceName),
)
) {
debug(
'Delaying service shutdown to another batch:',
serviceName,
);
return Promise.resolve();
}
if (
!initializeState.silosInstances[$siloContext.index]
.instanceDisposePromise
) {
debug('Shutting down a service:', serviceName);
initializeState.silosInstances[
$siloContext.index
].instanceDisposePromise =
provider &&
provider !== NO_PROVIDER &&
'dispose' in provider &&
provider.dispose
? provider.dispose()
: Promise.resolve();
} else {
debug('Reusing a service shutdown promise:', serviceName);
}
await initializeState.silosInstances[$siloContext.index]
.instanceDisposePromise;
} else if ('singletonProvider' in initializeState) {
initializeState.dependents = initializeState.dependents.filter(
({ silo }) => silo !== $siloContext.index,
);
if (initializeState.dependents.length) {
debug(
`Will not shut down the ${serviceName} singleton service (still used ${initializeState.dependents.length} times).`,
initializeState.dependents,
);
} else {
const provider = $instance._getServiceProvider(
$siloContext,
serviceName,
);
debug('Shutting down a singleton service:', serviceName);
delete initializeState.singletonProviderLoadPromise;
delete initializeState.singletonProvider;
return provider &&
provider !== NO_PROVIDER &&
'dispose' in provider &&
provider.dispose
? provider.dispose()
: Promise.resolve();
}
}
}),
);
if (dependendedByAServiceInTheSameBatch.length) {
serviceLoadSequences.unshift(dependendedByAServiceInTheSameBatch);
}
await _shutdownNextServices(serviceLoadSequences);
}
};
}
export default location(
service(initDispose, DISPOSE, [INSTANCE, SILO_CONTEXT]),
import.meta.url,
);