@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.
- package/CHANGELOG.md +80 -0
- package/dist/binding-filter.d.ts +20 -2
- package/dist/binding-filter.js +58 -9
- package/dist/binding-filter.js.map +1 -1
- package/dist/binding.d.ts +50 -2
- package/dist/binding.js +33 -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 +44 -68
- package/dist/context.js +69 -249
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +9 -2
- package/dist/inject.js +60 -0
- package/dist/inject.js.map +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/json-types.d.ts +28 -0
- package/dist/json-types.js +7 -0
- package/dist/json-types.js.map +1 -0
- package/dist/resolution-session.d.ts +2 -6
- package/dist/resolution-session.js +0 -4
- package/dist/resolution-session.js.map +1 -1
- package/dist/value-promise.d.ts +1 -3
- package/package.json +9 -9
- package/src/binding-filter.ts +83 -11
- package/src/binding.ts +79 -3
- 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 +104 -290
- package/src/index.ts +3 -0
- package/src/inject.ts +65 -0
- package/src/interceptor.ts +7 -6
- package/src/invocation.ts +1 -3
- package/src/json-types.ts +35 -0
- package/src/resolution-session.ts +4 -7
- package/src/value-promise.ts +1 -1
package/dist/context-view.d.ts
CHANGED
|
@@ -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
|
|
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';
|
package/dist/context-view.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-view.js","sourceRoot":"","sources":["../src/context-view.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;;;AAEhE,kDAAiC;AACjC,mCAAoC;AACpC,+BAA+B;
|
|
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 {
|
|
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
|
-
*
|
|
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
|
|
31
|
+
protected readonly tagIndexer: ContextTagIndexer;
|
|
35
32
|
/**
|
|
36
|
-
*
|
|
37
|
-
* first observer is added.
|
|
33
|
+
* Manager for observer subscriptions
|
|
38
34
|
*/
|
|
39
|
-
|
|
35
|
+
readonly subscriptionManager: ContextSubscriptionManager;
|
|
40
36
|
/**
|
|
41
|
-
*
|
|
42
|
-
* processed by observers.
|
|
37
|
+
* Parent context
|
|
43
38
|
*/
|
|
44
|
-
|
|
39
|
+
protected _parent?: Context;
|
|
45
40
|
/**
|
|
46
|
-
*
|
|
41
|
+
* Configuration resolver
|
|
47
42
|
*/
|
|
48
|
-
|
|
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
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
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
|
-
|
|
84
|
+
emitEvent<T extends ContextEvent>(type: string, event: T): void;
|
|
109
85
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
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
|
-
|
|
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():
|
|
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
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.
|
|
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 :
|
|
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
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
*
|
|
156
|
-
* @param err
|
|
110
|
+
* Emit an `error` event
|
|
111
|
+
* @param err Error
|
|
157
112
|
*/
|
|
158
|
-
|
|
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.
|
|
147
|
+
this.emitEvent('unbind', {
|
|
148
|
+
binding: existingBinding,
|
|
149
|
+
context: this,
|
|
150
|
+
type: 'unbind',
|
|
151
|
+
});
|
|
294
152
|
}
|
|
295
|
-
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
439
|
-
|
|
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
|
-
|
|
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
|
|
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
|
*/
|