@loopback/context 1.25.1 → 2.0.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/CHANGELOG.md +48 -0
- package/dist/binding-filter.d.ts +19 -1
- package/dist/binding-filter.js +40 -7
- package/dist/binding-filter.js.map +1 -1
- package/dist/binding.d.ts +33 -1
- package/dist/binding.js +14 -1
- package/dist/binding.js.map +1 -1
- package/dist/context-event.d.ts +23 -0
- package/dist/context-event.js +7 -0
- package/dist/context-event.js.map +1 -0
- package/dist/context-observer.d.ts +1 -36
- package/dist/context-subscription.d.ts +147 -0
- package/dist/context-subscription.js +336 -0
- package/dist/context-subscription.js.map +1 -0
- package/dist/context-tag-indexer.d.ts +42 -0
- package/dist/context-tag-indexer.js +134 -0
- package/dist/context-tag-indexer.js.map +1 -0
- package/dist/context-view.d.ts +2 -1
- package/dist/context-view.js.map +1 -1
- package/dist/context.d.ts +29 -65
- package/dist/context.js +50 -245
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +1 -1
- package/dist/interceptor.js +4 -4
- package/dist/interceptor.js.map +1 -1
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +1 -5
- package/dist/invocation.js.map +1 -1
- package/dist/value-promise.d.ts +1 -3
- package/package.json +7 -7
- package/src/binding-filter.ts +61 -9
- package/src/binding.ts +43 -1
- package/src/context-event.ts +30 -0
- package/src/context-observer.ts +1 -38
- package/src/context-subscription.ts +403 -0
- package/src/context-tag-indexer.ts +149 -0
- package/src/context-view.ts +2 -5
- package/src/context.ts +71 -286
- package/src/index.ts +2 -0
- package/src/interceptor.ts +7 -6
- package/src/invocation.ts +1 -3
- package/src/value-promise.ts +1 -1
package/src/context.ts
CHANGED
|
@@ -11,16 +11,18 @@ import {
|
|
|
11
11
|
ConfigurationResolver,
|
|
12
12
|
DefaultConfigurationResolver,
|
|
13
13
|
} from './binding-config';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
BindingFilter,
|
|
16
|
+
filterByKey,
|
|
17
|
+
filterByTag,
|
|
18
|
+
isBindingTagFilter,
|
|
19
|
+
} from './binding-filter';
|
|
15
20
|
import {BindingAddress, BindingKey} from './binding-key';
|
|
16
21
|
import {BindingComparator} from './binding-sorter';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Notification,
|
|
22
|
-
Subscription,
|
|
23
|
-
} from './context-observer';
|
|
22
|
+
import {ContextEvent} from './context-event';
|
|
23
|
+
import {ContextEventObserver, ContextObserver} from './context-observer';
|
|
24
|
+
import {ContextSubscriptionManager, Subscription} from './context-subscription';
|
|
25
|
+
import {ContextTagIndexer} from './context-tag-indexer';
|
|
24
26
|
import {ContextView} from './context-view';
|
|
25
27
|
import {ContextBindings} from './keys';
|
|
26
28
|
import {
|
|
@@ -36,21 +38,6 @@ import {
|
|
|
36
38
|
ValueOrPromise,
|
|
37
39
|
} from './value-promise';
|
|
38
40
|
|
|
39
|
-
/**
|
|
40
|
-
* Polyfill Symbol.asyncIterator as required by TypeScript for Node 8.x.
|
|
41
|
-
* See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html
|
|
42
|
-
*/
|
|
43
|
-
if (!Symbol.asyncIterator) {
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
(Symbol as any).asyncIterator = Symbol.for('Symbol.asyncIterator');
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* WARNING: This following import must happen after the polyfill. The
|
|
49
|
-
* `auto-import` by an IDE such as VSCode may move the import before the
|
|
50
|
-
* polyfill. It must be then fixed manually.
|
|
51
|
-
*/
|
|
52
|
-
import {iterator, multiple} from 'p-event';
|
|
53
|
-
|
|
54
41
|
const debug = debugFactory('loopback:context');
|
|
55
42
|
|
|
56
43
|
/**
|
|
@@ -68,41 +55,24 @@ export class Context extends EventEmitter {
|
|
|
68
55
|
protected readonly registry: Map<string, Binding> = new Map();
|
|
69
56
|
|
|
70
57
|
/**
|
|
71
|
-
*
|
|
72
|
-
*/
|
|
73
|
-
protected _parent?: Context;
|
|
74
|
-
|
|
75
|
-
protected configResolver: ConfigurationResolver;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Event listeners for parent context keyed by event names. It keeps track
|
|
79
|
-
* of listeners from this context against its parent so that we can remove
|
|
80
|
-
* these listeners when this context is closed.
|
|
58
|
+
* Indexer for bindings by tag
|
|
81
59
|
*/
|
|
82
|
-
protected
|
|
83
|
-
| Map<
|
|
84
|
-
string,
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
-
(...args: any[]) => void
|
|
87
|
-
>
|
|
88
|
-
| undefined;
|
|
60
|
+
protected readonly tagIndexer: ContextTagIndexer;
|
|
89
61
|
|
|
90
62
|
/**
|
|
91
|
-
*
|
|
92
|
-
* first observer is added.
|
|
63
|
+
* Manager for observer subscriptions
|
|
93
64
|
*/
|
|
94
|
-
|
|
65
|
+
readonly subscriptionManager: ContextSubscriptionManager;
|
|
95
66
|
|
|
96
67
|
/**
|
|
97
|
-
*
|
|
98
|
-
* processed by observers.
|
|
68
|
+
* Parent context
|
|
99
69
|
*/
|
|
100
|
-
|
|
70
|
+
protected _parent?: Context;
|
|
101
71
|
|
|
102
72
|
/**
|
|
103
|
-
*
|
|
73
|
+
* Configuration resolver
|
|
104
74
|
*/
|
|
105
|
-
|
|
75
|
+
protected configResolver: ConfigurationResolver;
|
|
106
76
|
|
|
107
77
|
/**
|
|
108
78
|
* Create a new context.
|
|
@@ -128,12 +98,27 @@ export class Context extends EventEmitter {
|
|
|
128
98
|
*/
|
|
129
99
|
constructor(_parent?: Context | string, name?: string) {
|
|
130
100
|
super();
|
|
101
|
+
// The number of listeners can grow with the number of child contexts
|
|
102
|
+
// For example, each request can add a listener to the RestServer and the
|
|
103
|
+
// listener is removed when the request processing is finished.
|
|
104
|
+
// See https://github.com/strongloop/loopback-next/issues/4363
|
|
105
|
+
this.setMaxListeners(Infinity);
|
|
131
106
|
if (typeof _parent === 'string') {
|
|
132
107
|
name = _parent;
|
|
133
108
|
_parent = undefined;
|
|
134
109
|
}
|
|
135
110
|
this._parent = _parent;
|
|
136
111
|
this.name = name ?? uuidv1();
|
|
112
|
+
this.tagIndexer = new ContextTagIndexer(this);
|
|
113
|
+
this.subscriptionManager = new ContextSubscriptionManager(this);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @internal
|
|
118
|
+
* Getter for ContextSubscriptionManager
|
|
119
|
+
*/
|
|
120
|
+
get parent() {
|
|
121
|
+
return this._parent;
|
|
137
122
|
}
|
|
138
123
|
|
|
139
124
|
/**
|
|
@@ -141,8 +126,7 @@ export class Context extends EventEmitter {
|
|
|
141
126
|
* as the prefix
|
|
142
127
|
* @param args - Arguments for the debug
|
|
143
128
|
*/
|
|
144
|
-
|
|
145
|
-
private _debug(...args: any[]) {
|
|
129
|
+
private _debug(...args: unknown[]) {
|
|
146
130
|
/* istanbul ignore if */
|
|
147
131
|
if (!debug.enabled) return;
|
|
148
132
|
const formatter = args.shift();
|
|
@@ -154,176 +138,22 @@ export class Context extends EventEmitter {
|
|
|
154
138
|
}
|
|
155
139
|
|
|
156
140
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
141
|
+
* A strongly-typed method to emit context events
|
|
142
|
+
* @param type Event type
|
|
143
|
+
* @param event Context event
|
|
160
144
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
this.addParentEventListener('bind');
|
|
165
|
-
this.addParentEventListener('unbind');
|
|
166
|
-
|
|
167
|
-
// The following are two async functions. Returned promises are ignored as
|
|
168
|
-
// they are long-running background tasks.
|
|
169
|
-
this.startNotificationTask().catch(err => {
|
|
170
|
-
this.handleNotificationError(err);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
let ctx = this._parent;
|
|
174
|
-
while (ctx) {
|
|
175
|
-
ctx.setupEventHandlersIfNeeded();
|
|
176
|
-
ctx = ctx._parent;
|
|
177
|
-
}
|
|
145
|
+
emitEvent<T extends ContextEvent>(type: string, event: T) {
|
|
146
|
+
this.emit(type, event);
|
|
178
147
|
}
|
|
179
148
|
|
|
180
149
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* @param event - Event name
|
|
150
|
+
* Emit an `error` event
|
|
151
|
+
* @param err Error
|
|
184
152
|
*/
|
|
185
|
-
|
|
186
|
-
if (this._parent == null) return;
|
|
187
|
-
|
|
188
|
-
// Keep track of parent event listeners so that we can remove them
|
|
189
|
-
this._parentEventListeners = this._parentEventListeners ?? new Map();
|
|
190
|
-
if (this._parentEventListeners.has(event)) return;
|
|
191
|
-
|
|
192
|
-
const parentEventListener = (
|
|
193
|
-
binding: Readonly<Binding<unknown>>,
|
|
194
|
-
context: Context,
|
|
195
|
-
) => {
|
|
196
|
-
// Propagate the event to this context only if the binding key does not
|
|
197
|
-
// exist in this context. The parent binding is shadowed if there is a
|
|
198
|
-
// binding with the same key in this one.
|
|
199
|
-
if (this.contains(binding.key)) {
|
|
200
|
-
this._debug(
|
|
201
|
-
'Event %s %s is not re-emitted from %s to %s',
|
|
202
|
-
event,
|
|
203
|
-
binding.key,
|
|
204
|
-
context.name,
|
|
205
|
-
this.name,
|
|
206
|
-
);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
this._debug(
|
|
210
|
-
'Re-emitting %s %s from %s to %s',
|
|
211
|
-
event,
|
|
212
|
-
binding.key,
|
|
213
|
-
context.name,
|
|
214
|
-
this.name,
|
|
215
|
-
);
|
|
216
|
-
this.emit(event, binding, context);
|
|
217
|
-
};
|
|
218
|
-
this._parentEventListeners.set(event, parentEventListener);
|
|
219
|
-
// Listen on the parent context events
|
|
220
|
-
this._parent.on(event, parentEventListener);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Handle errors caught during the notification of observers
|
|
225
|
-
* @param err - Error
|
|
226
|
-
*/
|
|
227
|
-
private handleNotificationError(err: unknown) {
|
|
228
|
-
// Bubbling up the error event over the context chain
|
|
229
|
-
// until we find an error listener
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
231
|
-
let ctx: Context | undefined = this;
|
|
232
|
-
while (ctx) {
|
|
233
|
-
if (ctx.listenerCount('error') === 0) {
|
|
234
|
-
// No error listener found, try its parent
|
|
235
|
-
ctx = ctx._parent;
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
this._debug('Emitting error to context %s', ctx.name, err);
|
|
239
|
-
ctx.emit('error', err);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
// No context with error listeners found
|
|
243
|
-
this._debug('No error handler is configured for the context chain', err);
|
|
244
|
-
// Let it crash now by emitting an error event
|
|
153
|
+
emitError(err: unknown) {
|
|
245
154
|
this.emit('error', err);
|
|
246
155
|
}
|
|
247
156
|
|
|
248
|
-
/**
|
|
249
|
-
* Start a background task to listen on context events and notify observers
|
|
250
|
-
*/
|
|
251
|
-
private startNotificationTask() {
|
|
252
|
-
// Set up listeners on `bind` and `unbind` for notifications
|
|
253
|
-
this.setupNotification('bind', 'unbind');
|
|
254
|
-
|
|
255
|
-
// Create an async iterator for the `notification` event as a queue
|
|
256
|
-
this.notificationQueue = iterator(this, 'notification');
|
|
257
|
-
|
|
258
|
-
return this.processNotifications();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Process notification events as they arrive on the queue
|
|
263
|
-
*/
|
|
264
|
-
private async processNotifications() {
|
|
265
|
-
const events = this.notificationQueue;
|
|
266
|
-
if (events == null) return;
|
|
267
|
-
for await (const {eventType, binding, context, observers} of events) {
|
|
268
|
-
// The loop will happen asynchronously upon events
|
|
269
|
-
try {
|
|
270
|
-
// The execution of observers happen in the Promise micro-task queue
|
|
271
|
-
await this.notifyObservers(eventType, binding, context, observers);
|
|
272
|
-
this.pendingNotifications--;
|
|
273
|
-
this._debug(
|
|
274
|
-
'Observers notified for %s of binding %s',
|
|
275
|
-
eventType,
|
|
276
|
-
binding.key,
|
|
277
|
-
);
|
|
278
|
-
this.emit('observersNotified', {eventType, binding});
|
|
279
|
-
} catch (err) {
|
|
280
|
-
this.pendingNotifications--;
|
|
281
|
-
this._debug('Error caught from observers', err);
|
|
282
|
-
// Errors caught from observers. Emit it to the current context.
|
|
283
|
-
// If no error listeners are registered, crash the process.
|
|
284
|
-
this.emit('error', err);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Listen on given event types and emit `notification` event. This method
|
|
291
|
-
* merge multiple event types into one for notification.
|
|
292
|
-
* @param eventTypes - Context event types
|
|
293
|
-
*/
|
|
294
|
-
private setupNotification(...eventTypes: ContextEventType[]) {
|
|
295
|
-
for (const eventType of eventTypes) {
|
|
296
|
-
this.on(eventType, (binding, context) => {
|
|
297
|
-
// No need to schedule notifications if no observers are present
|
|
298
|
-
if (!this.observers || this.observers.size === 0) return;
|
|
299
|
-
// Track pending events
|
|
300
|
-
this.pendingNotifications++;
|
|
301
|
-
// Take a snapshot of current observers to ensure notifications of this
|
|
302
|
-
// event will only be sent to current ones. Emit a new event to notify
|
|
303
|
-
// current context observers.
|
|
304
|
-
this.emit('notification', {
|
|
305
|
-
eventType,
|
|
306
|
-
binding,
|
|
307
|
-
context,
|
|
308
|
-
observers: new Set(this.observers),
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Wait until observers are notified for all of currently pending notification
|
|
316
|
-
* events.
|
|
317
|
-
*
|
|
318
|
-
* This method is for test only to perform assertions after observers are
|
|
319
|
-
* notified for relevant events.
|
|
320
|
-
*/
|
|
321
|
-
protected async waitUntilPendingNotificationsDone(timeout?: number) {
|
|
322
|
-
const count = this.pendingNotifications;
|
|
323
|
-
if (count === 0) return;
|
|
324
|
-
await multiple(this, 'observersNotified', {count, timeout});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
157
|
/**
|
|
328
158
|
* Create a binding with the given key in the context. If a locked binding
|
|
329
159
|
* already exists with the same key, an error will be thrown.
|
|
@@ -357,9 +187,13 @@ export class Context extends EventEmitter {
|
|
|
357
187
|
this.registry.set(key, binding);
|
|
358
188
|
if (existingBinding !== binding) {
|
|
359
189
|
if (existingBinding != null) {
|
|
360
|
-
this.
|
|
190
|
+
this.emitEvent('unbind', {
|
|
191
|
+
binding: existingBinding,
|
|
192
|
+
context: this,
|
|
193
|
+
type: 'unbind',
|
|
194
|
+
});
|
|
361
195
|
}
|
|
362
|
-
this.
|
|
196
|
+
this.emitEvent('bind', {binding, context: this, type: 'bind'});
|
|
363
197
|
}
|
|
364
198
|
return this;
|
|
365
199
|
}
|
|
@@ -505,7 +339,7 @@ export class Context extends EventEmitter {
|
|
|
505
339
|
if (binding?.isLocked)
|
|
506
340
|
throw new Error(`Cannot unbind key "${key}" of a locked binding`);
|
|
507
341
|
this.registry.delete(key);
|
|
508
|
-
this.
|
|
342
|
+
this.emitEvent('unbind', {binding, context: this, type: 'unbind'});
|
|
509
343
|
return true;
|
|
510
344
|
}
|
|
511
345
|
|
|
@@ -514,10 +348,7 @@ export class Context extends EventEmitter {
|
|
|
514
348
|
* @param observer - Context observer instance or function
|
|
515
349
|
*/
|
|
516
350
|
subscribe(observer: ContextEventObserver): Subscription {
|
|
517
|
-
|
|
518
|
-
this.setupEventHandlersIfNeeded();
|
|
519
|
-
this.observers.add(observer);
|
|
520
|
-
return new ContextSubscription(this, observer);
|
|
351
|
+
return this.subscriptionManager.subscribe(observer);
|
|
521
352
|
}
|
|
522
353
|
|
|
523
354
|
/**
|
|
@@ -525,8 +356,7 @@ export class Context extends EventEmitter {
|
|
|
525
356
|
* @param observer - Context event observer
|
|
526
357
|
*/
|
|
527
358
|
unsubscribe(observer: ContextEventObserver): boolean {
|
|
528
|
-
|
|
529
|
-
return this.observers.delete(observer);
|
|
359
|
+
return this.subscriptionManager.unsubscribe(observer);
|
|
530
360
|
}
|
|
531
361
|
|
|
532
362
|
/**
|
|
@@ -540,20 +370,8 @@ export class Context extends EventEmitter {
|
|
|
540
370
|
*/
|
|
541
371
|
close() {
|
|
542
372
|
this._debug('Closing context...');
|
|
543
|
-
this.
|
|
544
|
-
|
|
545
|
-
// Cancel the notification iterator
|
|
546
|
-
this.notificationQueue.return!(undefined).catch(err => {
|
|
547
|
-
this.handleNotificationError(err);
|
|
548
|
-
});
|
|
549
|
-
this.notificationQueue = undefined;
|
|
550
|
-
}
|
|
551
|
-
if (this._parent && this._parentEventListeners) {
|
|
552
|
-
for (const [event, listener] of this._parentEventListeners) {
|
|
553
|
-
this._parent.removeListener(event, listener);
|
|
554
|
-
}
|
|
555
|
-
this._parentEventListeners = undefined;
|
|
556
|
-
}
|
|
373
|
+
this.subscriptionManager.close();
|
|
374
|
+
this.tagIndexer.close();
|
|
557
375
|
}
|
|
558
376
|
|
|
559
377
|
/**
|
|
@@ -561,8 +379,7 @@ export class Context extends EventEmitter {
|
|
|
561
379
|
* @param observer - Context observer
|
|
562
380
|
*/
|
|
563
381
|
isSubscribed(observer: ContextObserver) {
|
|
564
|
-
|
|
565
|
-
return this.observers.has(observer);
|
|
382
|
+
return this.subscriptionManager.isSubscribed(observer);
|
|
566
383
|
}
|
|
567
384
|
|
|
568
385
|
/**
|
|
@@ -579,34 +396,6 @@ export class Context extends EventEmitter {
|
|
|
579
396
|
return view;
|
|
580
397
|
}
|
|
581
398
|
|
|
582
|
-
/**
|
|
583
|
-
* Publish an event to the registered observers. Please note the
|
|
584
|
-
* notification is queued and performed asynchronously so that we allow fluent
|
|
585
|
-
* APIs such as `ctx.bind('key').to(...).tag(...);` and give observers the
|
|
586
|
-
* fully populated binding.
|
|
587
|
-
*
|
|
588
|
-
* @param eventType - Event names: `bind` or `unbind`
|
|
589
|
-
* @param binding - Binding bound or unbound
|
|
590
|
-
* @param context - Owner context
|
|
591
|
-
* @param observers - Current set of context observers
|
|
592
|
-
*/
|
|
593
|
-
protected async notifyObservers(
|
|
594
|
-
eventType: ContextEventType,
|
|
595
|
-
binding: Readonly<Binding<unknown>>,
|
|
596
|
-
context: Context,
|
|
597
|
-
observers = this.observers,
|
|
598
|
-
) {
|
|
599
|
-
if (!observers || observers.size === 0) return;
|
|
600
|
-
|
|
601
|
-
for (const observer of observers) {
|
|
602
|
-
if (typeof observer === 'function') {
|
|
603
|
-
await observer(eventType, binding, context);
|
|
604
|
-
} else if (!observer.filter || observer.filter(binding)) {
|
|
605
|
-
await observer.observe(eventType, binding, context);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
399
|
/**
|
|
611
400
|
* Check if a binding exists with the given key in the local context without
|
|
612
401
|
* delegating to the parent context
|
|
@@ -658,6 +447,11 @@ export class Context extends EventEmitter {
|
|
|
658
447
|
find<ValueType = BoundValue>(
|
|
659
448
|
pattern?: string | RegExp | BindingFilter,
|
|
660
449
|
): Readonly<Binding<ValueType>>[] {
|
|
450
|
+
// Optimize if the binding filter is for tags
|
|
451
|
+
if (typeof pattern === 'function' && isBindingTagFilter(pattern)) {
|
|
452
|
+
return this._findByTagIndex(pattern.bindingTagPattern);
|
|
453
|
+
}
|
|
454
|
+
|
|
661
455
|
const bindings: Readonly<Binding<ValueType>>[] = [];
|
|
662
456
|
const filter = filterByKey(pattern);
|
|
663
457
|
|
|
@@ -689,6 +483,18 @@ export class Context extends EventEmitter {
|
|
|
689
483
|
return this.find(filterByTag(tagFilter));
|
|
690
484
|
}
|
|
691
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Find bindings by tag leveraging indexes
|
|
488
|
+
* @param tag - Tag name pattern or name/value pairs
|
|
489
|
+
*/
|
|
490
|
+
protected _findByTagIndex<ValueType = BoundValue>(
|
|
491
|
+
tag: BindingTag | RegExp,
|
|
492
|
+
): Readonly<Binding<ValueType>>[] {
|
|
493
|
+
const currentBindings = this.tagIndexer.findByTagIndex(tag);
|
|
494
|
+
const parentBindings = this._parent && this._parent?._findByTagIndex(tag);
|
|
495
|
+
return this._mergeWithParent(currentBindings, parentBindings);
|
|
496
|
+
}
|
|
497
|
+
|
|
692
498
|
protected _mergeWithParent<ValueType>(
|
|
693
499
|
childList: Readonly<Binding<ValueType>>[],
|
|
694
500
|
parentList?: Readonly<Binding<ValueType>>[],
|
|
@@ -993,27 +799,6 @@ export class Context extends EventEmitter {
|
|
|
993
799
|
}
|
|
994
800
|
}
|
|
995
801
|
|
|
996
|
-
/**
|
|
997
|
-
* An implementation of `Subscription` interface for context events
|
|
998
|
-
*/
|
|
999
|
-
class ContextSubscription implements Subscription {
|
|
1000
|
-
constructor(
|
|
1001
|
-
protected context: Context,
|
|
1002
|
-
protected observer: ContextEventObserver,
|
|
1003
|
-
) {}
|
|
1004
|
-
|
|
1005
|
-
private _closed = false;
|
|
1006
|
-
|
|
1007
|
-
unsubscribe() {
|
|
1008
|
-
this.context.unsubscribe(this.observer);
|
|
1009
|
-
this._closed = true;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
get closed() {
|
|
1013
|
-
return this._closed;
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
802
|
/**
|
|
1018
803
|
* Policy to control if a binding should be created for the context
|
|
1019
804
|
*/
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,9 @@ export * from './binding-inspector';
|
|
|
12
12
|
export * from './binding-key';
|
|
13
13
|
export * from './binding-sorter';
|
|
14
14
|
export * from './context';
|
|
15
|
+
export * from './context-event';
|
|
15
16
|
export * from './context-observer';
|
|
17
|
+
export * from './context-subscription';
|
|
16
18
|
export * from './context-view';
|
|
17
19
|
export * from './inject';
|
|
18
20
|
export * from './inject-config';
|
package/src/interceptor.ts
CHANGED
|
@@ -15,7 +15,6 @@ import assert from 'assert';
|
|
|
15
15
|
import debugFactory from 'debug';
|
|
16
16
|
import {Binding, BindingTemplate} from './binding';
|
|
17
17
|
import {bind} from './binding-decorator';
|
|
18
|
-
import {filterByTag} from './binding-filter';
|
|
19
18
|
import {BindingSpec} from './binding-inspector';
|
|
20
19
|
import {sortBindingsByPhase} from './binding-sorter';
|
|
21
20
|
import {Context} from './context';
|
|
@@ -47,12 +46,14 @@ export class InterceptedInvocationContext extends InvocationContext {
|
|
|
47
46
|
* ContextTags.GLOBAL_INTERCEPTOR)
|
|
48
47
|
*/
|
|
49
48
|
getGlobalInterceptorBindingKeys(): string[] {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
filterByTag(ContextTags.GLOBAL_INTERCEPTOR)(binding) &&
|
|
53
|
-
// Only include interceptors that match the source type of the invocation
|
|
54
|
-
this.applicableTo(binding),
|
|
49
|
+
let bindings: Readonly<Binding<Interceptor>>[] = this.findByTag(
|
|
50
|
+
ContextTags.GLOBAL_INTERCEPTOR,
|
|
55
51
|
);
|
|
52
|
+
bindings = bindings.filter(binding =>
|
|
53
|
+
// Only include interceptors that match the source type of the invocation
|
|
54
|
+
this.applicableTo(binding),
|
|
55
|
+
);
|
|
56
|
+
|
|
56
57
|
this.sortGlobalInterceptorBindings(bindings);
|
|
57
58
|
const keys = bindings.map(b => b.key);
|
|
58
59
|
debug('Global interceptor binding keys:', keys);
|
package/src/invocation.ts
CHANGED
|
@@ -55,9 +55,7 @@ export class InvocationContext extends Context {
|
|
|
55
55
|
* @param args - An array of arguments
|
|
56
56
|
*/
|
|
57
57
|
constructor(
|
|
58
|
-
|
|
59
|
-
// the request context, for example, tracing id
|
|
60
|
-
public readonly parent: Context,
|
|
58
|
+
parent: Context,
|
|
61
59
|
public readonly target: object,
|
|
62
60
|
public readonly methodName: string,
|
|
63
61
|
public readonly args: InvocationArgs,
|
package/src/value-promise.ts
CHANGED