1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | Object.defineProperty(exports, "__esModule", { value: true });
|
7 | exports.ResolutionError = exports.asResolutionOptions = exports.ResolutionSession = void 0;
|
8 | const tslib_1 = require("tslib");
|
9 | const metadata_1 = require("@loopback/metadata");
|
10 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
11 | const value_promise_1 = require("./value-promise");
|
12 | const debugSession = (0, debug_1.default)('loopback:context:resolver:session');
|
13 | const getTargetName = metadata_1.DecoratorFactory.getTargetName;
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | function isBinding(element) {
|
19 | return element != null && element.type === 'binding';
|
20 | }
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | function isInjection(element) {
|
26 | return element != null && element.type === 'injection';
|
27 | }
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | class ResolutionSession {
|
33 | constructor() {
|
34 | |
35 |
|
36 |
|
37 |
|
38 | this.stack = [];
|
39 | }
|
40 | |
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | static fork(session) {
|
47 | if (session === undefined)
|
48 | return undefined;
|
49 | const copy = new ResolutionSession();
|
50 | copy.stack.push(...session.stack);
|
51 | return copy;
|
52 | }
|
53 | |
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | static runWithBinding(action, binding, session = new ResolutionSession()) {
|
60 |
|
61 | session.pushBinding(binding);
|
62 | return (0, value_promise_1.tryWithFinally)(() => action(session), () => session.popBinding());
|
63 | }
|
64 | |
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | static runWithInjection(action, injection, session = new ResolutionSession()) {
|
71 | session.pushInjection(injection);
|
72 | return (0, value_promise_1.tryWithFinally)(() => action(session), () => session.popInjection());
|
73 | }
|
74 | |
75 |
|
76 |
|
77 |
|
78 | static describeInjection(injection) {
|
79 | const name = getTargetName(injection.target, injection.member, injection.methodDescriptorOrParameterIndex);
|
80 | return {
|
81 | targetName: name,
|
82 | bindingSelector: injection.bindingSelector,
|
83 | metadata: injection.metadata,
|
84 | };
|
85 | }
|
86 | |
87 |
|
88 |
|
89 |
|
90 | pushInjection(injection) {
|
91 |
|
92 | if (debugSession.enabled) {
|
93 | debugSession('Enter injection:', ResolutionSession.describeInjection(injection));
|
94 | }
|
95 | this.stack.push({ type: 'injection', value: injection });
|
96 |
|
97 | if (debugSession.enabled) {
|
98 | debugSession('Resolution path:', this.getResolutionPath());
|
99 | }
|
100 | }
|
101 | |
102 |
|
103 |
|
104 | popInjection() {
|
105 | const top = this.stack.pop();
|
106 | if (!isInjection(top)) {
|
107 | throw new Error('The top element must be an injection');
|
108 | }
|
109 | const injection = top.value;
|
110 |
|
111 | if (debugSession.enabled) {
|
112 | debugSession('Exit injection:', ResolutionSession.describeInjection(injection));
|
113 | debugSession('Resolution path:', this.getResolutionPath() || '<empty>');
|
114 | }
|
115 | return injection;
|
116 | }
|
117 | |
118 |
|
119 |
|
120 | get currentInjection() {
|
121 | for (let i = this.stack.length - 1; i >= 0; i--) {
|
122 | const element = this.stack[i];
|
123 | if (isInjection(element))
|
124 | return element.value;
|
125 | }
|
126 | return undefined;
|
127 | }
|
128 | |
129 |
|
130 |
|
131 | get currentBinding() {
|
132 | for (let i = this.stack.length - 1; i >= 0; i--) {
|
133 | const element = this.stack[i];
|
134 | if (isBinding(element))
|
135 | return element.value;
|
136 | }
|
137 | return undefined;
|
138 | }
|
139 | |
140 |
|
141 |
|
142 |
|
143 | pushBinding(binding) {
|
144 |
|
145 | if (debugSession.enabled) {
|
146 | debugSession('Enter binding:', binding.toJSON());
|
147 | }
|
148 | if (this.stack.find(i => isBinding(i) && i.value === binding)) {
|
149 | const msg = `Circular dependency detected: ` +
|
150 | `${this.getResolutionPath()} --> ${binding.key}`;
|
151 | debugSession(msg);
|
152 | throw new Error(msg);
|
153 | }
|
154 | this.stack.push({ type: 'binding', value: binding });
|
155 |
|
156 | if (debugSession.enabled) {
|
157 | debugSession('Resolution path:', this.getResolutionPath());
|
158 | }
|
159 | }
|
160 | |
161 |
|
162 |
|
163 | popBinding() {
|
164 | const top = this.stack.pop();
|
165 | if (!isBinding(top)) {
|
166 | throw new Error('The top element must be a binding');
|
167 | }
|
168 | const binding = top.value;
|
169 |
|
170 | if (debugSession.enabled) {
|
171 | debugSession('Exit binding:', binding === null || binding === void 0 ? void 0 : binding.toJSON());
|
172 | debugSession('Resolution path:', this.getResolutionPath() || '<empty>');
|
173 | }
|
174 | return binding;
|
175 | }
|
176 | |
177 |
|
178 |
|
179 | get bindingStack() {
|
180 | return this.stack.filter(isBinding).map(e => e.value);
|
181 | }
|
182 | |
183 |
|
184 |
|
185 | get injectionStack() {
|
186 | return this.stack.filter(isInjection).map(e => e.value);
|
187 | }
|
188 | |
189 |
|
190 |
|
191 | getBindingPath() {
|
192 | return this.stack.filter(isBinding).map(describe).join(' --> ');
|
193 | }
|
194 | |
195 |
|
196 |
|
197 | getInjectionPath() {
|
198 | return this.injectionStack
|
199 | .map(i => ResolutionSession.describeInjection(i).targetName)
|
200 | .join(' --> ');
|
201 | }
|
202 | |
203 |
|
204 |
|
205 |
|
206 |
|
207 | getResolutionPath() {
|
208 | return this.stack.map(describe).join(' --> ');
|
209 | }
|
210 | toString() {
|
211 | return this.getResolutionPath();
|
212 | }
|
213 | }
|
214 | exports.ResolutionSession = ResolutionSession;
|
215 | function describe(e) {
|
216 | switch (e.type) {
|
217 | case 'injection':
|
218 | return '@' + ResolutionSession.describeInjection(e.value).targetName;
|
219 | case 'binding':
|
220 | return e.value.key;
|
221 | }
|
222 | }
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | function asResolutionOptions(optionsOrSession) {
|
228 |
|
229 | if (optionsOrSession instanceof ResolutionSession) {
|
230 | return { session: optionsOrSession };
|
231 | }
|
232 | return optionsOrSession !== null && optionsOrSession !== void 0 ? optionsOrSession : {};
|
233 | }
|
234 | exports.asResolutionOptions = asResolutionOptions;
|
235 |
|
236 |
|
237 |
|
238 | class ResolutionError extends Error {
|
239 | constructor(message, resolutionCtx) {
|
240 | super(ResolutionError.buildMessage(message, resolutionCtx));
|
241 | this.resolutionCtx = resolutionCtx;
|
242 | this.name = ResolutionError.name;
|
243 | }
|
244 | static buildDetails(resolutionCtx) {
|
245 | var _a, _b, _c, _d, _e, _f, _g;
|
246 | return {
|
247 | context: (_b = (_a = resolutionCtx.context) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : '',
|
248 | binding: (_d = (_c = resolutionCtx.binding) === null || _c === void 0 ? void 0 : _c.key) !== null && _d !== void 0 ? _d : '',
|
249 | resolutionPath: (_g = (_f = (_e = resolutionCtx.options) === null || _e === void 0 ? void 0 : _e.session) === null || _f === void 0 ? void 0 : _f.getResolutionPath()) !== null && _g !== void 0 ? _g : '',
|
250 | };
|
251 | }
|
252 | |
253 |
|
254 |
|
255 |
|
256 |
|
257 | static buildMessage(reason, resolutionCtx) {
|
258 | const info = this.describeResolutionContext(resolutionCtx);
|
259 | const message = `${reason} (${info})`;
|
260 | return message;
|
261 | }
|
262 | static describeResolutionContext(resolutionCtx) {
|
263 | const details = ResolutionError.buildDetails(resolutionCtx);
|
264 | const items = [];
|
265 | for (const [name, val] of Object.entries(details)) {
|
266 | if (val !== '') {
|
267 | items.push(`${name}: ${val}`);
|
268 | }
|
269 | }
|
270 | return items.join(', ');
|
271 | }
|
272 | }
|
273 | exports.ResolutionError = ResolutionError;
|
274 |
|
\ | No newline at end of file |