@loopback/context
Advanced tools
Comparing version 3.1.0 to 3.2.0
@@ -6,2 +6,14 @@ # Change Log | ||
# [3.2.0](https://github.com/strongloop/loopback-next/compare/@loopback/context@3.1.0...@loopback/context@3.2.0) (2020-03-24) | ||
### Features | ||
* **context:** emit bind/unbind events on ContextView ([65e3d38](https://github.com/strongloop/loopback-next/commit/65e3d38a34b351929ba422de667bc236e9619ebe)) | ||
* **context:** improve context view for bind/unbind events ([6a5f90a](https://github.com/strongloop/loopback-next/commit/6a5f90aadb5f5ba213f2da7ea7843f488a09f95d)) | ||
# [3.1.0](https://github.com/strongloop/loopback-next/compare/@loopback/context@3.0.0...@loopback/context@3.1.0) (2020-03-17) | ||
@@ -8,0 +20,0 @@ |
@@ -7,2 +7,3 @@ /// <reference types="node" /> | ||
import { Context } from './context'; | ||
import { ContextEvent } from './context-event'; | ||
import { ContextEventType, ContextObserver } from './context-observer'; | ||
@@ -14,2 +15,11 @@ import { Subscription } from './context-subscription'; | ||
/** | ||
* An event emitted by a `ContextView` | ||
*/ | ||
export interface ContextViewEvent<T> extends ContextEvent { | ||
/** | ||
* Optional cached value for an `unbind` event | ||
*/ | ||
cachedValue?: T; | ||
} | ||
/** | ||
* `ContextView` provides a view for a given context chain to maintain a live | ||
@@ -24,2 +34,4 @@ * list of matching bindings and their resolved values within the context | ||
* `ContextView` is an event emitter that emits the following events: | ||
* - 'bind': when a binding is added to the view | ||
* - 'unbind': when a binding is removed from the view | ||
* - 'close': when the view is closed (stopped observing context events) | ||
@@ -30,10 +42,31 @@ * - 'refresh': when the view is refreshed as bindings are added/removed | ||
export declare class ContextView<T = unknown> extends EventEmitter implements ContextObserver { | ||
protected readonly context: Context; | ||
readonly context: Context; | ||
readonly filter: BindingFilter; | ||
readonly comparator?: BindingComparator | undefined; | ||
/** | ||
* An array of cached bindings that matches the binding filter | ||
*/ | ||
protected _cachedBindings: Readonly<Binding<T>>[] | undefined; | ||
protected _cachedValues: T[] | undefined; | ||
/** | ||
* A map of cached values by binding | ||
*/ | ||
protected _cachedValues: Map<Readonly<Binding<T>>, T> | undefined; | ||
private _subscription; | ||
/** | ||
* Create a context view | ||
* @param context - Context object to watch | ||
* @param filter - Binding filter to match bindings of interest | ||
* @param comparator - Comparator to sort the matched bindings | ||
*/ | ||
constructor(context: Context, filter: BindingFilter, comparator?: BindingComparator | undefined); | ||
/** | ||
* Update the cached values keyed by binding | ||
* @param values - An array of resolved values | ||
*/ | ||
private updateCachedValues; | ||
/** | ||
* Get an array of cached values | ||
*/ | ||
private getCachedValues; | ||
/** | ||
* Start listening events from the context | ||
@@ -58,3 +91,3 @@ */ | ||
*/ | ||
observe(event: ContextEventType, binding: Readonly<Binding<unknown>>): void; | ||
observe(event: ContextEventType, binding: Readonly<Binding<unknown>>, context: Context): void; | ||
/** | ||
@@ -61,0 +94,0 @@ * Refresh the view by invalidating its cache |
@@ -25,2 +25,4 @@ "use strict"; | ||
* `ContextView` is an event emitter that emits the following events: | ||
* - 'bind': when a binding is added to the view | ||
* - 'unbind': when a binding is removed from the view | ||
* - 'close': when the view is closed (stopped observing context events) | ||
@@ -31,2 +33,8 @@ * - 'refresh': when the view is refreshed as bindings are added/removed | ||
class ContextView extends events_1.EventEmitter { | ||
/** | ||
* Create a context view | ||
* @param context - Context object to watch | ||
* @param filter - Binding filter to match bindings of interest | ||
* @param comparator - Comparator to sort the matched bindings | ||
*/ | ||
constructor(context, filter, comparator) { | ||
@@ -39,2 +47,23 @@ super(); | ||
/** | ||
* Update the cached values keyed by binding | ||
* @param values - An array of resolved values | ||
*/ | ||
updateCachedValues(values) { | ||
var _a; | ||
if (this._cachedBindings == null) | ||
return undefined; | ||
this._cachedValues = new Map(); | ||
for (let i = 0; i < ((_a = this._cachedBindings) === null || _a === void 0 ? void 0 : _a.length); i++) { | ||
this._cachedValues.set(this._cachedBindings[i], values[i]); | ||
} | ||
return this._cachedValues; | ||
} | ||
/** | ||
* Get an array of cached values | ||
*/ | ||
getCachedValues() { | ||
var _a, _b; | ||
return Array.from((_b = (_a = this._cachedValues) === null || _a === void 0 ? void 0 : _a.values()) !== null && _b !== void 0 ? _b : []); | ||
} | ||
/** | ||
* Start listening events from the context | ||
@@ -81,3 +110,6 @@ */ | ||
} | ||
this._cachedBindings = found; | ||
/* istanbul ignore if */ | ||
if (debug.enabled) { | ||
debug('Bindings found', found.map(b => b.key)); | ||
} | ||
return found; | ||
@@ -88,3 +120,17 @@ } | ||
*/ | ||
observe(event, binding) { | ||
observe(event, binding, context) { | ||
var _a; | ||
const ctxEvent = { | ||
context, | ||
binding, | ||
type: event, | ||
}; | ||
debug('Observed event %s %s %s', event, binding.key, context.name); | ||
if (event === 'unbind') { | ||
const cachedValue = (_a = this._cachedValues) === null || _a === void 0 ? void 0 : _a.get(binding); | ||
this.emit(event, Object.assign(Object.assign({}, ctxEvent), { cachedValue })); | ||
} | ||
else { | ||
this.emit(event, ctxEvent); | ||
} | ||
this.refresh(); | ||
@@ -107,5 +153,7 @@ } | ||
debug('Resolving values'); | ||
if (this._cachedValues != null) | ||
return this._cachedValues; | ||
let result = value_promise_1.resolveList(this.bindings, b => { | ||
if (this._cachedValues != null) { | ||
return this.getCachedValues(); | ||
} | ||
const bindings = this.bindings; | ||
let result = value_promise_1.resolveList(bindings, b => { | ||
return b.getValue(this.context, resolution_session_1.ResolutionSession.fork(session)); | ||
@@ -115,3 +163,3 @@ }); | ||
result = result.then(values => { | ||
this._cachedValues = values; | ||
this.updateCachedValues(values); | ||
this.emit('resolve', values); | ||
@@ -122,3 +170,4 @@ return values; | ||
else { | ||
this._cachedValues = result; | ||
// Clone the array so that the cached values won't be mutated | ||
this.updateCachedValues(result); | ||
this.emit('resolve', result); | ||
@@ -137,5 +186,5 @@ } | ||
if (this._cachedValues == null) { | ||
this._cachedValues = await this.resolve(session); | ||
return this.resolve(session); | ||
} | ||
return this._cachedValues; | ||
return this.getCachedValues(); | ||
} | ||
@@ -142,0 +191,0 @@ /** |
{ | ||
"name": "@loopback/context", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"description": "LoopBack's container for Inversion of Control", | ||
@@ -21,3 +21,3 @@ "engines": { | ||
"dependencies": { | ||
"@loopback/metadata": "^2.0.1", | ||
"@loopback/metadata": "^2.0.2", | ||
"debug": "^4.1.1", | ||
@@ -29,9 +29,9 @@ "p-event": "^4.1.0", | ||
"devDependencies": { | ||
"@loopback/build": "^4.0.1", | ||
"@loopback/eslint-config": "^6.0.1", | ||
"@loopback/testlab": "^2.0.1", | ||
"@loopback/build": "^5.0.0", | ||
"@loopback/eslint-config": "^6.0.2", | ||
"@loopback/testlab": "^2.0.2", | ||
"@types/bluebird": "^3.5.30", | ||
"@types/debug": "^4.1.5", | ||
"@types/node": "^10.17.17", | ||
"@types/uuid": "^7.0.0", | ||
"@types/uuid": "^7.0.2", | ||
"bluebird": "^3.7.2" | ||
@@ -61,3 +61,3 @@ }, | ||
}, | ||
"gitHead": "85a906121febac2dbd2d4adcdc21b5939a770951" | ||
"gitHead": "020ed59b8bed8b38e1e4cede06a22b234effdb57" | ||
} |
@@ -13,2 +13,3 @@ // Copyright IBM Corp. 2019,2020. All Rights Reserved. | ||
import {Context} from './context'; | ||
import {ContextEvent} from './context-event'; | ||
import {ContextEventType, ContextObserver} from './context-observer'; | ||
@@ -23,2 +24,12 @@ import {Subscription} from './context-subscription'; | ||
/** | ||
* An event emitted by a `ContextView` | ||
*/ | ||
export interface ContextViewEvent<T> extends ContextEvent { | ||
/** | ||
* Optional cached value for an `unbind` event | ||
*/ | ||
cachedValue?: T; | ||
} | ||
/** | ||
* `ContextView` provides a view for a given context chain to maintain a live | ||
@@ -33,2 +44,4 @@ * list of matching bindings and their resolved values within the context | ||
* `ContextView` is an event emitter that emits the following events: | ||
* - 'bind': when a binding is added to the view | ||
* - 'unbind': when a binding is removed from the view | ||
* - 'close': when the view is closed (stopped observing context events) | ||
@@ -40,8 +53,20 @@ * - 'refresh': when the view is refreshed as bindings are added/removed | ||
implements ContextObserver { | ||
/** | ||
* An array of cached bindings that matches the binding filter | ||
*/ | ||
protected _cachedBindings: Readonly<Binding<T>>[] | undefined; | ||
protected _cachedValues: T[] | undefined; | ||
/** | ||
* A map of cached values by binding | ||
*/ | ||
protected _cachedValues: Map<Readonly<Binding<T>>, T> | undefined; | ||
private _subscription: Subscription | undefined; | ||
/** | ||
* Create a context view | ||
* @param context - Context object to watch | ||
* @param filter - Binding filter to match bindings of interest | ||
* @param comparator - Comparator to sort the matched bindings | ||
*/ | ||
constructor( | ||
protected readonly context: Context, | ||
public readonly context: Context, | ||
public readonly filter: BindingFilter, | ||
@@ -54,2 +79,22 @@ public readonly comparator?: BindingComparator, | ||
/** | ||
* Update the cached values keyed by binding | ||
* @param values - An array of resolved values | ||
*/ | ||
private updateCachedValues(values: T[]) { | ||
if (this._cachedBindings == null) return undefined; | ||
this._cachedValues = new Map(); | ||
for (let i = 0; i < this._cachedBindings?.length; i++) { | ||
this._cachedValues.set(this._cachedBindings[i], values[i]); | ||
} | ||
return this._cachedValues; | ||
} | ||
/** | ||
* Get an array of cached values | ||
*/ | ||
private getCachedValues() { | ||
return Array.from(this._cachedValues?.values() ?? []); | ||
} | ||
/** | ||
* Start listening events from the context | ||
@@ -98,3 +143,9 @@ */ | ||
} | ||
this._cachedBindings = found; | ||
/* istanbul ignore if */ | ||
if (debug.enabled) { | ||
debug( | ||
'Bindings found', | ||
found.map(b => b.key), | ||
); | ||
} | ||
return found; | ||
@@ -106,3 +157,23 @@ } | ||
*/ | ||
observe(event: ContextEventType, binding: Readonly<Binding<unknown>>) { | ||
observe( | ||
event: ContextEventType, | ||
binding: Readonly<Binding<unknown>>, | ||
context: Context, | ||
) { | ||
const ctxEvent: ContextViewEvent<T> = { | ||
context, | ||
binding, | ||
type: event, | ||
}; | ||
debug('Observed event %s %s %s', event, binding.key, context.name); | ||
if (event === 'unbind') { | ||
const cachedValue = this._cachedValues?.get( | ||
binding as Readonly<Binding<T>>, | ||
); | ||
this.emit(event, {...ctxEvent, cachedValue}); | ||
} else { | ||
this.emit(event, ctxEvent); | ||
} | ||
this.refresh(); | ||
@@ -127,4 +198,7 @@ } | ||
debug('Resolving values'); | ||
if (this._cachedValues != null) return this._cachedValues; | ||
let result = resolveList(this.bindings, b => { | ||
if (this._cachedValues != null) { | ||
return this.getCachedValues(); | ||
} | ||
const bindings = this.bindings; | ||
let result = resolveList(bindings, b => { | ||
return b.getValue(this.context, ResolutionSession.fork(session)); | ||
@@ -134,3 +208,3 @@ }); | ||
result = result.then(values => { | ||
this._cachedValues = values; | ||
this.updateCachedValues(values); | ||
this.emit('resolve', values); | ||
@@ -140,3 +214,4 @@ return values; | ||
} else { | ||
this._cachedValues = result; | ||
// Clone the array so that the cached values won't be mutated | ||
this.updateCachedValues(result); | ||
this.emit('resolve', result); | ||
@@ -156,5 +231,5 @@ } | ||
if (this._cachedValues == null) { | ||
this._cachedValues = await this.resolve(session); | ||
return this.resolve(session); | ||
} | ||
return this._cachedValues; | ||
return this.getCachedValues(); | ||
} | ||
@@ -161,0 +236,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
613910
13546
Updated@loopback/metadata@^2.0.2