@loopback/context 1.25.0 → 2.1.1

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/binding-filter.d.ts +20 -2
  3. package/dist/binding-filter.js +58 -9
  4. package/dist/binding-filter.js.map +1 -1
  5. package/dist/binding.d.ts +50 -2
  6. package/dist/binding.js +33 -1
  7. package/dist/binding.js.map +1 -1
  8. package/dist/context-event.d.ts +23 -0
  9. package/dist/context-event.js +7 -0
  10. package/dist/context-event.js.map +1 -0
  11. package/dist/context-observer.d.ts +1 -36
  12. package/dist/context-subscription.d.ts +147 -0
  13. package/dist/context-subscription.js +336 -0
  14. package/dist/context-subscription.js.map +1 -0
  15. package/dist/context-tag-indexer.d.ts +42 -0
  16. package/dist/context-tag-indexer.js +134 -0
  17. package/dist/context-tag-indexer.js.map +1 -0
  18. package/dist/context-view.d.ts +2 -1
  19. package/dist/context-view.js.map +1 -1
  20. package/dist/context.d.ts +44 -68
  21. package/dist/context.js +69 -249
  22. package/dist/context.js.map +1 -1
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.js +1 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/inject.d.ts +9 -2
  27. package/dist/inject.js +60 -0
  28. package/dist/inject.js.map +1 -1
  29. package/dist/interceptor.js +4 -4
  30. package/dist/interceptor.js.map +1 -1
  31. package/dist/invocation.d.ts +0 -1
  32. package/dist/invocation.js +1 -5
  33. package/dist/invocation.js.map +1 -1
  34. package/dist/json-types.d.ts +28 -0
  35. package/dist/json-types.js +7 -0
  36. package/dist/json-types.js.map +1 -0
  37. package/dist/resolution-session.d.ts +2 -6
  38. package/dist/resolution-session.js +0 -4
  39. package/dist/resolution-session.js.map +1 -1
  40. package/dist/value-promise.d.ts +1 -3
  41. package/package.json +9 -9
  42. package/src/binding-filter.ts +83 -11
  43. package/src/binding.ts +79 -3
  44. package/src/context-event.ts +30 -0
  45. package/src/context-observer.ts +1 -38
  46. package/src/context-subscription.ts +403 -0
  47. package/src/context-tag-indexer.ts +149 -0
  48. package/src/context-view.ts +2 -5
  49. package/src/context.ts +104 -290
  50. package/src/index.ts +3 -0
  51. package/src/inject.ts +65 -0
  52. package/src/interceptor.ts +7 -6
  53. package/src/invocation.ts +1 -3
  54. package/src/json-types.ts +35 -0
  55. package/src/resolution-session.ts +4 -7
  56. package/src/value-promise.ts +1 -1
@@ -4,7 +4,8 @@ import { Binding } from './binding';
4
4
  import { BindingFilter } from './binding-filter';
5
5
  import { BindingComparator } from './binding-sorter';
6
6
  import { Context } from './context';
7
- import { ContextEventType, ContextObserver, Subscription } from './context-observer';
7
+ import { ContextEventType, ContextObserver } from './context-observer';
8
+ import { Subscription } from './context-subscription';
8
9
  import { Getter } from './inject';
9
10
  import { ResolutionSession } from './resolution-session';
10
11
  import { ValueOrPromise } from './value-promise';
@@ -1 +1 @@
1
- {"version":3,"file":"context-view.js","sourceRoot":"","sources":["../src/context-view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context-view.js","sourceRoot":"","sources":["../src/context-view.ts"],"names":[],"mappings":"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"}
package/dist/context.d.ts CHANGED
@@ -1,12 +1,16 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from 'events';
3
- import { Binding, BindingTag } from './binding';
3
+ import { Binding, BindingInspectOptions, BindingTag } from './binding';
4
4
  import { ConfigurationResolver } from './binding-config';
5
5
  import { BindingFilter } from './binding-filter';
6
6
  import { BindingAddress } from './binding-key';
7
7
  import { BindingComparator } from './binding-sorter';
8
- import { ContextEventObserver, ContextEventType, ContextObserver, Subscription } from './context-observer';
8
+ import { ContextEvent } from './context-event';
9
+ import { ContextEventObserver, ContextObserver } from './context-observer';
10
+ import { ContextSubscriptionManager, Subscription } from './context-subscription';
11
+ import { ContextTagIndexer } from './context-tag-indexer';
9
12
  import { ContextView } from './context-view';
13
+ import { JSONObject } from './json-types';
10
14
  import { ResolutionOptions, ResolutionOptionsOrSession, ResolutionSession } from './resolution-session';
11
15
  import { BoundValue, ValueOrPromise } from './value-promise';
12
16
  /**
@@ -22,30 +26,21 @@ export declare class Context extends EventEmitter {
22
26
  */
23
27
  protected readonly registry: Map<string, Binding>;
24
28
  /**
25
- * Parent context
26
- */
27
- protected _parent?: Context;
28
- protected configResolver: ConfigurationResolver;
29
- /**
30
- * Event listeners for parent context keyed by event names. It keeps track
31
- * of listeners from this context against its parent so that we can remove
32
- * these listeners when this context is closed.
29
+ * Indexer for bindings by tag
33
30
  */
34
- protected _parentEventListeners: Map<string, (...args: any[]) => void> | undefined;
31
+ protected readonly tagIndexer: ContextTagIndexer;
35
32
  /**
36
- * A list of registered context observers. The Set will be created when the
37
- * first observer is added.
33
+ * Manager for observer subscriptions
38
34
  */
39
- protected observers: Set<ContextEventObserver> | undefined;
35
+ readonly subscriptionManager: ContextSubscriptionManager;
40
36
  /**
41
- * Internal counter for pending notification events which are yet to be
42
- * processed by observers.
37
+ * Parent context
43
38
  */
44
- private pendingNotifications;
39
+ protected _parent?: Context;
45
40
  /**
46
- * Queue for background notifications for observers
41
+ * Configuration resolver
47
42
  */
48
- private notificationQueue;
43
+ protected configResolver: ConfigurationResolver;
49
44
  /**
50
45
  * Create a new context.
51
46
  *
@@ -69,6 +64,12 @@ export declare class Context extends EventEmitter {
69
64
  * generated as the name
70
65
  */
71
66
  constructor(_parent?: Context | string, name?: string);
67
+ private generateName;
68
+ /**
69
+ * @internal
70
+ * Getter for ContextSubscriptionManager
71
+ */
72
+ get parent(): Context | undefined;
72
73
  /**
73
74
  * Wrap the debug statement so that it always print out the context name
74
75
  * as the prefix
@@ -76,44 +77,16 @@ export declare class Context extends EventEmitter {
76
77
  */
77
78
  private _debug;
78
79
  /**
79
- * Set up an internal listener to notify registered observers asynchronously
80
- * upon `bind` and `unbind` events. This method will be called lazily when
81
- * the first observer is added.
82
- */
83
- private setupEventHandlersIfNeeded;
84
- /**
85
- * Add an event listener to its parent context so that this context will
86
- * be notified of parent events, such as `bind` or `unbind`.
87
- * @param event - Event name
88
- */
89
- private addParentEventListener;
90
- /**
91
- * Handle errors caught during the notification of observers
92
- * @param err - Error
93
- */
94
- private handleNotificationError;
95
- /**
96
- * Start a background task to listen on context events and notify observers
97
- */
98
- private startNotificationTask;
99
- /**
100
- * Process notification events as they arrive on the queue
101
- */
102
- private processNotifications;
103
- /**
104
- * Listen on given event types and emit `notification` event. This method
105
- * merge multiple event types into one for notification.
106
- * @param eventTypes - Context event types
80
+ * A strongly-typed method to emit context events
81
+ * @param type Event type
82
+ * @param event Context event
107
83
  */
108
- private setupNotification;
84
+ emitEvent<T extends ContextEvent>(type: string, event: T): void;
109
85
  /**
110
- * Wait until observers are notified for all of currently pending notification
111
- * events.
112
- *
113
- * This method is for test only to perform assertions after observers are
114
- * notified for relevant events.
86
+ * Emit an `error` event
87
+ * @param err Error
115
88
  */
116
- protected waitUntilPendingNotificationsDone(timeout?: number): Promise<void>;
89
+ emitError(err: unknown): void;
117
90
  /**
118
91
  * Create a binding with the given key in the context. If a locked binding
119
92
  * already exists with the same key, an error will be thrown.
@@ -220,18 +193,6 @@ export declare class Context extends EventEmitter {
220
193
  * @param comparator - A function to sort matched bindings
221
194
  */
222
195
  createView<T = unknown>(filter: BindingFilter, comparator?: BindingComparator): ContextView<T>;
223
- /**
224
- * Publish an event to the registered observers. Please note the
225
- * notification is queued and performed asynchronously so that we allow fluent
226
- * APIs such as `ctx.bind('key').to(...).tag(...);` and give observers the
227
- * fully populated binding.
228
- *
229
- * @param eventType - Event names: `bind` or `unbind`
230
- * @param binding - Binding bound or unbound
231
- * @param context - Owner context
232
- * @param observers - Current set of context observers
233
- */
234
- protected notifyObservers(eventType: ContextEventType, binding: Readonly<Binding<unknown>>, context: Context, observers?: Set<ContextEventObserver> | undefined): Promise<void>;
235
196
  /**
236
197
  * Check if a binding exists with the given key in the local context without
237
198
  * delegating to the parent context
@@ -278,6 +239,11 @@ export declare class Context extends EventEmitter {
278
239
  * `{name: 'my-controller'}`
279
240
  */
280
241
  findByTag<ValueType = BoundValue>(tagFilter: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
242
+ /**
243
+ * Find bindings by tag leveraging indexes
244
+ * @param tag - Tag name pattern or name/value pairs
245
+ */
246
+ protected _findByTagIndex<ValueType = BoundValue>(tag: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
281
247
  protected _mergeWithParent<ValueType>(childList: Readonly<Binding<ValueType>>[], parentList?: Readonly<Binding<ValueType>>[]): Readonly<Binding<ValueType>>[];
282
248
  /**
283
249
  * Get the value bound to the given key, throw an error when no value is
@@ -430,12 +396,22 @@ export declare class Context extends EventEmitter {
430
396
  /**
431
397
  * Create a plain JSON object for the context
432
398
  */
433
- toJSON(): object;
399
+ toJSON(): JSONObject;
434
400
  /**
435
401
  * Inspect the context and dump out a JSON object representing the context
436
402
  * hierarchy
403
+ * @param options - Options for inspect
404
+ */
405
+ inspect(options?: ContextInspectOptions): JSONObject;
406
+ }
407
+ /**
408
+ * Options for context.inspect()
409
+ */
410
+ export interface ContextInspectOptions extends BindingInspectOptions {
411
+ /**
412
+ * The flag to control if parent context should be inspected
437
413
  */
438
- inspect(): object;
414
+ includeParent?: boolean;
439
415
  }
440
416
  /**
441
417
  * Policy to control if a binding should be created for the context
package/dist/context.js CHANGED
@@ -3,13 +3,6 @@
3
3
  // Node module: @loopback/context
4
4
  // This file is licensed under the MIT License.
5
5
  // License text available at https://opensource.org/licenses/MIT
6
- var __asyncValues = (this && this.__asyncValues) || function (o) {
7
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
8
- var m = o[Symbol.asyncIterator], i;
9
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
10
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
11
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
12
- };
13
6
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
8
  };
@@ -21,24 +14,12 @@ const binding_1 = require("./binding");
21
14
  const binding_config_1 = require("./binding-config");
22
15
  const binding_filter_1 = require("./binding-filter");
23
16
  const binding_key_1 = require("./binding-key");
17
+ const context_subscription_1 = require("./context-subscription");
18
+ const context_tag_indexer_1 = require("./context-tag-indexer");
24
19
  const context_view_1 = require("./context-view");
25
20
  const keys_1 = require("./keys");
26
21
  const resolution_session_1 = require("./resolution-session");
27
22
  const value_promise_1 = require("./value-promise");
28
- /**
29
- * Polyfill Symbol.asyncIterator as required by TypeScript for Node 8.x.
30
- * See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html
31
- */
32
- if (!Symbol.asyncIterator) {
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
35
- }
36
- /**
37
- * WARNING: This following import must happen after the polyfill. The
38
- * `auto-import` by an IDE such as VSCode may move the import before the
39
- * polyfill. It must be then fixed manually.
40
- */
41
- const p_event_1 = require("p-event");
42
23
  const debug = debug_1.default('loopback:context');
43
24
  /**
44
25
  * Context provides an implementation of Inversion of Control (IoC) container
@@ -72,24 +53,39 @@ class Context extends events_1.EventEmitter {
72
53
  * Key to binding map as the internal registry
73
54
  */
74
55
  this.registry = new Map();
75
- /**
76
- * Internal counter for pending notification events which are yet to be
77
- * processed by observers.
78
- */
79
- this.pendingNotifications = 0;
56
+ // The number of listeners can grow with the number of child contexts
57
+ // For example, each request can add a listener to the RestServer and the
58
+ // listener is removed when the request processing is finished.
59
+ // See https://github.com/strongloop/loopback-next/issues/4363
60
+ this.setMaxListeners(Infinity);
80
61
  if (typeof _parent === 'string') {
81
62
  name = _parent;
82
63
  _parent = undefined;
83
64
  }
84
65
  this._parent = _parent;
85
- this.name = (name !== null && name !== void 0 ? name : uuid_1.v1());
66
+ this.name = (name !== null && name !== void 0 ? name : this.generateName());
67
+ this.tagIndexer = new context_tag_indexer_1.ContextTagIndexer(this);
68
+ this.subscriptionManager = new context_subscription_1.ContextSubscriptionManager(this);
69
+ }
70
+ generateName() {
71
+ const id = uuid_1.v1();
72
+ let prefix = `${this.constructor.name}-`;
73
+ if (prefix === 'Context-')
74
+ prefix = '';
75
+ return `${prefix}${id}`;
76
+ }
77
+ /**
78
+ * @internal
79
+ * Getter for ContextSubscriptionManager
80
+ */
81
+ get parent() {
82
+ return this._parent;
86
83
  }
87
84
  /**
88
85
  * Wrap the debug statement so that it always print out the context name
89
86
  * as the prefix
90
87
  * @param args - Arguments for the debug
91
88
  */
92
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
89
  _debug(...args) {
94
90
  /* istanbul ignore if */
95
91
  if (!debug.enabled)
@@ -103,162 +99,20 @@ class Context extends events_1.EventEmitter {
103
99
  }
104
100
  }
105
101
  /**
106
- * Set up an internal listener to notify registered observers asynchronously
107
- * upon `bind` and `unbind` events. This method will be called lazily when
108
- * the first observer is added.
109
- */
110
- setupEventHandlersIfNeeded() {
111
- if (this.notificationQueue != null)
112
- return;
113
- this.addParentEventListener('bind');
114
- this.addParentEventListener('unbind');
115
- // The following are two async functions. Returned promises are ignored as
116
- // they are long-running background tasks.
117
- this.startNotificationTask().catch(err => {
118
- this.handleNotificationError(err);
119
- });
120
- let ctx = this._parent;
121
- while (ctx) {
122
- ctx.setupEventHandlersIfNeeded();
123
- ctx = ctx._parent;
124
- }
125
- }
126
- /**
127
- * Add an event listener to its parent context so that this context will
128
- * be notified of parent events, such as `bind` or `unbind`.
129
- * @param event - Event name
102
+ * A strongly-typed method to emit context events
103
+ * @param type Event type
104
+ * @param event Context event
130
105
  */
131
- addParentEventListener(event) {
132
- var _a;
133
- if (this._parent == null)
134
- return;
135
- // Keep track of parent event listeners so that we can remove them
136
- this._parentEventListeners = (_a = this._parentEventListeners, (_a !== null && _a !== void 0 ? _a : new Map()));
137
- if (this._parentEventListeners.has(event))
138
- return;
139
- const parentEventListener = (binding, context) => {
140
- // Propagate the event to this context only if the binding key does not
141
- // exist in this context. The parent binding is shadowed if there is a
142
- // binding with the same key in this one.
143
- if (this.contains(binding.key)) {
144
- this._debug('Event %s %s is not re-emitted from %s to %s', event, binding.key, context.name, this.name);
145
- return;
146
- }
147
- this._debug('Re-emitting %s %s from %s to %s', event, binding.key, context.name, this.name);
148
- this.emit(event, binding, context);
149
- };
150
- this._parentEventListeners.set(event, parentEventListener);
151
- // Listen on the parent context events
152
- this._parent.on(event, parentEventListener);
106
+ emitEvent(type, event) {
107
+ this.emit(type, event);
153
108
  }
154
109
  /**
155
- * Handle errors caught during the notification of observers
156
- * @param err - Error
110
+ * Emit an `error` event
111
+ * @param err Error
157
112
  */
158
- handleNotificationError(err) {
159
- // Bubbling up the error event over the context chain
160
- // until we find an error listener
161
- // eslint-disable-next-line @typescript-eslint/no-this-alias
162
- let ctx = this;
163
- while (ctx) {
164
- if (ctx.listenerCount('error') === 0) {
165
- // No error listener found, try its parent
166
- ctx = ctx._parent;
167
- continue;
168
- }
169
- this._debug('Emitting error to context %s', ctx.name, err);
170
- ctx.emit('error', err);
171
- return;
172
- }
173
- // No context with error listeners found
174
- this._debug('No error handler is configured for the context chain', err);
175
- // Let it crash now by emitting an error event
113
+ emitError(err) {
176
114
  this.emit('error', err);
177
115
  }
178
- /**
179
- * Start a background task to listen on context events and notify observers
180
- */
181
- startNotificationTask() {
182
- // Set up listeners on `bind` and `unbind` for notifications
183
- this.setupNotification('bind', 'unbind');
184
- // Create an async iterator for the `notification` event as a queue
185
- this.notificationQueue = p_event_1.iterator(this, 'notification');
186
- return this.processNotifications();
187
- }
188
- /**
189
- * Process notification events as they arrive on the queue
190
- */
191
- async processNotifications() {
192
- var e_1, _a;
193
- const events = this.notificationQueue;
194
- if (events == null)
195
- return;
196
- try {
197
- for (var events_2 = __asyncValues(events), events_2_1; events_2_1 = await events_2.next(), !events_2_1.done;) {
198
- const { eventType, binding, context, observers } = events_2_1.value;
199
- // The loop will happen asynchronously upon events
200
- try {
201
- // The execution of observers happen in the Promise micro-task queue
202
- await this.notifyObservers(eventType, binding, context, observers);
203
- this.pendingNotifications--;
204
- this._debug('Observers notified for %s of binding %s', eventType, binding.key);
205
- this.emit('observersNotified', { eventType, binding });
206
- }
207
- catch (err) {
208
- this.pendingNotifications--;
209
- this._debug('Error caught from observers', err);
210
- // Errors caught from observers. Emit it to the current context.
211
- // If no error listeners are registered, crash the process.
212
- this.emit('error', err);
213
- }
214
- }
215
- }
216
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
217
- finally {
218
- try {
219
- if (events_2_1 && !events_2_1.done && (_a = events_2.return)) await _a.call(events_2);
220
- }
221
- finally { if (e_1) throw e_1.error; }
222
- }
223
- }
224
- /**
225
- * Listen on given event types and emit `notification` event. This method
226
- * merge multiple event types into one for notification.
227
- * @param eventTypes - Context event types
228
- */
229
- setupNotification(...eventTypes) {
230
- for (const eventType of eventTypes) {
231
- this.on(eventType, (binding, context) => {
232
- // No need to schedule notifications if no observers are present
233
- if (!this.observers || this.observers.size === 0)
234
- return;
235
- // Track pending events
236
- this.pendingNotifications++;
237
- // Take a snapshot of current observers to ensure notifications of this
238
- // event will only be sent to current ones. Emit a new event to notify
239
- // current context observers.
240
- this.emit('notification', {
241
- eventType,
242
- binding,
243
- context,
244
- observers: new Set(this.observers),
245
- });
246
- });
247
- }
248
- }
249
- /**
250
- * Wait until observers are notified for all of currently pending notification
251
- * events.
252
- *
253
- * This method is for test only to perform assertions after observers are
254
- * notified for relevant events.
255
- */
256
- async waitUntilPendingNotificationsDone(timeout) {
257
- const count = this.pendingNotifications;
258
- if (count === 0)
259
- return;
260
- await p_event_1.multiple(this, 'observersNotified', { count, timeout });
261
- }
262
116
  /**
263
117
  * Create a binding with the given key in the context. If a locked binding
264
118
  * already exists with the same key, an error will be thrown.
@@ -290,9 +144,13 @@ class Context extends events_1.EventEmitter {
290
144
  this.registry.set(key, binding);
291
145
  if (existingBinding !== binding) {
292
146
  if (existingBinding != null) {
293
- this.emit('unbind', existingBinding, this);
147
+ this.emitEvent('unbind', {
148
+ binding: existingBinding,
149
+ context: this,
150
+ type: 'unbind',
151
+ });
294
152
  }
295
- this.emit('bind', binding, this);
153
+ this.emitEvent('bind', { binding, context: this, type: 'bind' });
296
154
  }
297
155
  return this;
298
156
  }
@@ -401,7 +259,7 @@ class Context extends events_1.EventEmitter {
401
259
  if ((_a = binding) === null || _a === void 0 ? void 0 : _a.isLocked)
402
260
  throw new Error(`Cannot unbind key "${key}" of a locked binding`);
403
261
  this.registry.delete(key);
404
- this.emit('unbind', binding, this);
262
+ this.emitEvent('unbind', { binding, context: this, type: 'unbind' });
405
263
  return true;
406
264
  }
407
265
  /**
@@ -409,20 +267,14 @@ class Context extends events_1.EventEmitter {
409
267
  * @param observer - Context observer instance or function
410
268
  */
411
269
  subscribe(observer) {
412
- var _a;
413
- this.observers = (_a = this.observers, (_a !== null && _a !== void 0 ? _a : new Set()));
414
- this.setupEventHandlersIfNeeded();
415
- this.observers.add(observer);
416
- return new ContextSubscription(this, observer);
270
+ return this.subscriptionManager.subscribe(observer);
417
271
  }
418
272
  /**
419
273
  * Remove the context event observer from the context
420
274
  * @param observer - Context event observer
421
275
  */
422
276
  unsubscribe(observer) {
423
- if (!this.observers)
424
- return false;
425
- return this.observers.delete(observer);
277
+ return this.subscriptionManager.unsubscribe(observer);
426
278
  }
427
279
  /**
428
280
  * Close the context: clear observers, stop notifications, and remove event
@@ -435,29 +287,15 @@ class Context extends events_1.EventEmitter {
435
287
  */
436
288
  close() {
437
289
  this._debug('Closing context...');
438
- this.observers = undefined;
439
- if (this.notificationQueue != null) {
440
- // Cancel the notification iterator
441
- this.notificationQueue.return(undefined).catch(err => {
442
- this.handleNotificationError(err);
443
- });
444
- this.notificationQueue = undefined;
445
- }
446
- if (this._parent && this._parentEventListeners) {
447
- for (const [event, listener] of this._parentEventListeners) {
448
- this._parent.removeListener(event, listener);
449
- }
450
- this._parentEventListeners = undefined;
451
- }
290
+ this.subscriptionManager.close();
291
+ this.tagIndexer.close();
452
292
  }
453
293
  /**
454
294
  * Check if an observer is subscribed to this context
455
295
  * @param observer - Context observer
456
296
  */
457
297
  isSubscribed(observer) {
458
- if (!this.observers)
459
- return false;
460
- return this.observers.has(observer);
298
+ return this.subscriptionManager.isSubscribed(observer);
461
299
  }
462
300
  /**
463
301
  * Create a view of the context chain with the given binding filter
@@ -469,29 +307,6 @@ class Context extends events_1.EventEmitter {
469
307
  view.open();
470
308
  return view;
471
309
  }
472
- /**
473
- * Publish an event to the registered observers. Please note the
474
- * notification is queued and performed asynchronously so that we allow fluent
475
- * APIs such as `ctx.bind('key').to(...).tag(...);` and give observers the
476
- * fully populated binding.
477
- *
478
- * @param eventType - Event names: `bind` or `unbind`
479
- * @param binding - Binding bound or unbound
480
- * @param context - Owner context
481
- * @param observers - Current set of context observers
482
- */
483
- async notifyObservers(eventType, binding, context, observers = this.observers) {
484
- if (!observers || observers.size === 0)
485
- return;
486
- for (const observer of observers) {
487
- if (typeof observer === 'function') {
488
- await observer(eventType, binding, context);
489
- }
490
- else if (!observer.filter || observer.filter(binding)) {
491
- await observer.observe(eventType, binding, context);
492
- }
493
- }
494
- }
495
310
  /**
496
311
  * Check if a binding exists with the given key in the local context without
497
312
  * delegating to the parent context
@@ -540,6 +355,10 @@ class Context extends events_1.EventEmitter {
540
355
  * - return `false` to exclude it.
541
356
  */
542
357
  find(pattern) {
358
+ // Optimize if the binding filter is for tags
359
+ if (typeof pattern === 'function' && binding_filter_1.isBindingTagFilter(pattern)) {
360
+ return this._findByTagIndex(pattern.bindingTagPattern);
361
+ }
543
362
  const bindings = [];
544
363
  const filter = binding_filter_1.filterByKey(pattern);
545
364
  for (const b of this.registry.values()) {
@@ -566,6 +385,16 @@ class Context extends events_1.EventEmitter {
566
385
  findByTag(tagFilter) {
567
386
  return this.find(binding_filter_1.filterByTag(tagFilter));
568
387
  }
388
+ /**
389
+ * Find bindings by tag leveraging indexes
390
+ * @param tag - Tag name pattern or name/value pairs
391
+ */
392
+ _findByTagIndex(tag) {
393
+ var _a;
394
+ const currentBindings = this.tagIndexer.findByTagIndex(tag);
395
+ const parentBindings = this._parent && ((_a = this._parent) === null || _a === void 0 ? void 0 : _a._findByTagIndex(tag));
396
+ return this._mergeWithParent(currentBindings, parentBindings);
397
+ }
569
398
  _mergeWithParent(childList, parentList) {
570
399
  if (!parentList)
571
400
  return childList;
@@ -681,37 +510,28 @@ class Context extends events_1.EventEmitter {
681
510
  /**
682
511
  * Inspect the context and dump out a JSON object representing the context
683
512
  * hierarchy
513
+ * @param options - Options for inspect
684
514
  */
685
515
  // TODO(rfeng): Evaluate https://nodejs.org/api/util.html#util_custom_inspection_functions_on_objects
686
- inspect() {
516
+ inspect(options = {}) {
517
+ options = Object.assign({ includeParent: true, includeInjections: false }, options);
518
+ const bindings = {};
519
+ for (const [k, v] of this.registry) {
520
+ bindings[k] = v.inspect(options);
521
+ }
687
522
  const json = {
688
523
  name: this.name,
689
- bindings: this.toJSON(),
524
+ bindings,
690
525
  };
526
+ if (!options.includeParent)
527
+ return json;
691
528
  if (this._parent) {
692
- json.parent = this._parent.inspect();
529
+ json.parent = this._parent.inspect(options);
693
530
  }
694
531
  return json;
695
532
  }
696
533
  }
697
534
  exports.Context = Context;
698
- /**
699
- * An implementation of `Subscription` interface for context events
700
- */
701
- class ContextSubscription {
702
- constructor(context, observer) {
703
- this.context = context;
704
- this.observer = observer;
705
- this._closed = false;
706
- }
707
- unsubscribe() {
708
- this.context.unsubscribe(this.observer);
709
- this._closed = true;
710
- }
711
- get closed() {
712
- return this._closed;
713
- }
714
- }
715
535
  /**
716
536
  * Policy to control if a binding should be created for the context
717
537
  */