@webex/internal-media-core 2.2.1 → 2.2.2

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/dist/cjs/index.js CHANGED
@@ -2806,7 +2806,7 @@ return devices.filter(device=>deviceKinds.includes(device.kind)).every(device=>d
2806
2806
  // (Firefox) will allow the user to access device information.
2807
2807
  var callbackRes=yield callback();// Stop tracks in the stream so the browser (Safari) will know that there is not an active
2808
2808
  // stream running.
2809
- stream.getTracks().forEach(track=>track.stop());return callbackRes;}return callback();}catch(e){logger$3.error(e);throw new Error('Failed to ensure device permissions.');}});}var media=/*#__PURE__*/Object.freeze({__proto__:null,get DeviceKind(){return DeviceKind;},getUserMedia:getUserMedia,getDisplayMedia:getDisplayMedia,enumerateDevices:enumerateDevices,setOnDeviceChangeHandler:setOnDeviceChangeHandler$1,checkDevicePermissions:checkDevicePermissions,ensureDevicePermissions:ensureDevicePermissions});var WebrtcCoreErrorType;(function(WebrtcCoreErrorType){WebrtcCoreErrorType["DEVICE_PERMISSION_DENIED"]="DEVICE_PERMISSION_DENIED";WebrtcCoreErrorType["CREATE_STREAM_FAILED"]="CREATE_STREAM_FAILED";})(WebrtcCoreErrorType||(WebrtcCoreErrorType={}));/**
2809
+ stream.getTracks().forEach(track=>track.stop());return callbackRes;}return callback();}catch(e){logger$3.error(e);throw new Error('Failed to ensure device permissions.');}});}var media=/*#__PURE__*/Object.freeze({__proto__:null,get DeviceKind(){return DeviceKind;},getUserMedia:getUserMedia,getDisplayMedia:getDisplayMedia,enumerateDevices:enumerateDevices,setOnDeviceChangeHandler:setOnDeviceChangeHandler$1,checkDevicePermissions:checkDevicePermissions,ensureDevicePermissions:ensureDevicePermissions});var WebrtcCoreErrorType;(function(WebrtcCoreErrorType){WebrtcCoreErrorType["DEVICE_PERMISSION_DENIED"]="DEVICE_PERMISSION_DENIED";WebrtcCoreErrorType["CREATE_STREAM_FAILED"]="CREATE_STREAM_FAILED";WebrtcCoreErrorType["ADD_EFFECT_FAILED"]="ADD_EFFECT_FAILED";})(WebrtcCoreErrorType||(WebrtcCoreErrorType={}));/**
2810
2810
  * Represents a WebRTC core error, which contains error type and error message.
2811
2811
  */class WebrtcCoreError{/**
2812
2812
  * Creates new error.
@@ -2923,13 +2923,13 @@ this[_a$1$1]=new TypedEvent$1();this[_b$1]=new TypedEvent$1();this.outputStream=
2923
2923
  * Get the track of the output stream.
2924
2924
  *
2925
2925
  * @returns The output track.
2926
- */get outputTrack(){return this.outputStream.getTracks()[0];}}_a$1$1=exports.StreamEventNames.MuteStateChange,_b$1=exports.StreamEventNames.Ended;var Stream=AddEvents(_Stream);var _a$6$1,_b;exports.LocalStreamEventNames = void 0;(function(LocalStreamEventNames){LocalStreamEventNames["ConstraintsChange"]="constraints-change";LocalStreamEventNames["OutputTrackChange"]="output-track-change";})(exports.LocalStreamEventNames||(exports.LocalStreamEventNames={}));/**
2926
+ */get outputTrack(){return this.outputStream.getTracks()[0];}}_a$1$1=exports.StreamEventNames.MuteStateChange,_b$1=exports.StreamEventNames.Ended;var Stream=AddEvents(_Stream);var _a$6$1,_b,_c;exports.LocalStreamEventNames = void 0;(function(LocalStreamEventNames){LocalStreamEventNames["ConstraintsChange"]="constraints-change";LocalStreamEventNames["OutputTrackChange"]="output-track-change";LocalStreamEventNames["EffectAdded"]="effect-added";})(exports.LocalStreamEventNames||(exports.LocalStreamEventNames={}));/**
2927
2927
  * A stream which originates on the local device.
2928
2928
  */class _LocalStream extends Stream{/**
2929
2929
  * Create a LocalStream from the given values.
2930
2930
  *
2931
2931
  * @param stream - The initial output MediaStream for this Stream.
2932
- */constructor(stream){super(stream);this[_a$6$1]=new TypedEvent$1();this[_b]=new TypedEvent$1();this.effects=[];this.loadingEffects=new Map();this.inputStream=stream;}/**
2932
+ */constructor(stream){super(stream);this[_a$6$1]=new TypedEvent$1();this[_b]=new TypedEvent$1();this[_c]=new TypedEvent$1();this.effects=[];this.loadingEffects=new Map();this.inputStream=stream;}/**
2933
2933
  * Get the track within the MediaStream with which this LocalStream was created.
2934
2934
  *
2935
2935
  * @returns The track within the MediaStream with which this LocalStream
@@ -2969,28 +2969,52 @@ if(this.inputTrack.id===this.outputTrack.id){this.inputStream=this.outputStream;
2969
2969
  this[exports.StreamEventNames.Ended].emit();}/**
2970
2970
  * Adds an effect to a local stream.
2971
2971
  *
2972
- * @param name - The name of the effect.
2973
2972
  * @param effect - The effect to add.
2974
- */addEffect(name,effect){return __awaiter$2(this,void 0,void 0,function*(){// Load the effect
2975
- this.loadingEffects.set(name,effect);var outputTrack=yield effect.load(this.outputTrack);// Check that the loaded effect is the latest one and dispose if not
2976
- if(effect!==this.loadingEffects.get(name)){yield effect.dispose();throw new Error("Effect \"".concat(name,"\" not required after loading"));}// Use the effect
2977
- this.loadingEffects.delete(name);this.effects.push({name,effect});this.changeOutputTrack(outputTrack);// When the effect's track is updated, update the next effect or output stream.
2978
- // TODO: using EffectEvent.TrackUpdated will cause the entire web-media-effects lib to be built
2979
- // and makes the size of the webrtc-core build much larger, so we use type assertion here as a
2980
- // temporary workaround.
2981
- effect.on('track-updated',track=>{var _c;var effectIndex=this.effects.findIndex(e=>e.name===name);if(effectIndex===this.effects.length-1){this.changeOutputTrack(track);}else {(_c=this.effects[effectIndex+1])===null||_c===void 0?void 0:_c.effect.replaceInputTrack(track);}});});}/**
2982
- * Get an effect from the effects list.
2973
+ */addEffect(effect){return __awaiter$2(this,void 0,void 0,function*(){// Check if the effect has already been added.
2974
+ if(this.effects.some(e=>e.id===effect.id)){return;}// Load the effect. Because loading is asynchronous, keep track of the loading effects.
2975
+ this.loadingEffects.set(effect.kind,effect);yield effect.load(this.outputTrack);// After loading, check whether or not we still want to use this effect. If another effect of
2976
+ // the same kind was added while this effect was loading, we only want to use the latest effect,
2977
+ // so dispose this one. If the effects list was cleared while this effect was loading, also
2978
+ // dispose it.
2979
+ if(effect!==this.loadingEffects.get(effect.kind)){yield effect.dispose();throw new WebrtcCoreError(WebrtcCoreErrorType.ADD_EFFECT_FAILED,"Another effect with kind ".concat(effect.kind," was added while effect ").concat(effect.id," was loading, or the effects list was cleared."));}this.loadingEffects.delete(effect.kind);/**
2980
+ * Handle when the effect's output track has been changed. This will update the input of the
2981
+ * next effect in the effects list of the output of the stream.
2982
+ *
2983
+ * @param track - The new output track of the effect.
2984
+ */var handleEffectTrackUpdated=track=>{var _d;var effectIndex=this.effects.findIndex(e=>e.id===effect.id);if(effectIndex===this.effects.length-1){this.changeOutputTrack(track);}else if(effectIndex>=0){(_d=this.effects[effectIndex+1])===null||_d===void 0?void 0:_d.replaceInputTrack(track);}else {logger$3.error("Effect with ID ".concat(effect.id," not found in effects list."));}};/**
2985
+ * Handle when the effect has been disposed. This will remove all event listeners from the
2986
+ * effect.
2987
+ */var handleEffectDisposed=()=>{effect.off('track-updated',handleEffectTrackUpdated);effect.off('disposed',handleEffectDisposed);};// TODO: using EffectEvent.TrackUpdated or EffectEvent.Disposed will cause the entire
2988
+ // web-media-effects lib to be rebuilt and inflates the size of the webrtc-core build, so
2989
+ // we use type assertion here as a temporary workaround.
2990
+ effect.on('track-updated',handleEffectTrackUpdated);effect.on('disposed',handleEffectDisposed);// Add the effect to the effects list. If an effect of the same kind has already been added,
2991
+ // dispose the existing effect and replace it with the new effect. If the existing effect was
2992
+ // enabled, also enable the new effect.
2993
+ var existingEffectIndex=this.effects.findIndex(e=>e.kind===effect.kind);if(existingEffectIndex>=0){var[existingEffect]=this.effects.splice(existingEffectIndex,1,effect);if(existingEffect.isEnabled){// If the existing effect is not the first effect in the effects list, then the input of the
2994
+ // new effect should be the output of the previous effect in the effects list. We know the
2995
+ // output track of the previous effect must exist because it must have been loaded (and all
2996
+ // loaded effects have an output track).
2997
+ var inputTrack=existingEffectIndex===0?this.inputTrack:this.effects[existingEffectIndex-1].getOutputTrack();yield effect.replaceInputTrack(inputTrack);// Enabling the new effect will trigger the track-updated event, which will handle the new
2998
+ // effect's updated output track.
2999
+ yield effect.enable();}yield existingEffect.dispose();}else {this.effects.push(effect);}// Emit an event with the effect so others can listen to the effect events.
3000
+ this[exports.LocalStreamEventNames.EffectAdded].emit(effect);});}/**
3001
+ * Get an effect from the effects list by ID.
2983
3002
  *
2984
- * @param name - The name of the effect you want to get.
3003
+ * @param id - The id of the effect you want to get.
2985
3004
  * @returns The effect or undefined.
2986
- */getEffect(name){var _c;return (_c=this.effects.find(e=>e.name===name))===null||_c===void 0?void 0:_c.effect;}/**
3005
+ */getEffectById(id){return this.effects.find(effect=>effect.id===id);}/**
3006
+ * Get an effect from the effects list by kind.
3007
+ *
3008
+ * @param kind - The kind of the effect you want to get.
3009
+ * @returns The effect or undefined.
3010
+ */getEffectByKind(kind){return this.effects.find(effect=>effect.kind===kind);}/**
2987
3011
  * Get all the effects from the effects list.
2988
3012
  *
2989
- * @returns A list of effect items, each containing the name and the effect itself.
2990
- */getAllEffects(){return this.effects;}/**
3013
+ * @returns A list of effects.
3014
+ */getEffects(){return this.effects;}/**
2991
3015
  * Cleanup the local effects.
2992
3016
  */disposeEffects(){return __awaiter$2(this,void 0,void 0,function*(){this.loadingEffects.clear();// Dispose of any effects currently in use
2993
- if(this.effects.length>0){this.changeOutputTrack(this.inputTrack);yield Promise.all(this.effects.map(item=>item.effect.dispose()));this.effects=[];}});}}_a$6$1=exports.LocalStreamEventNames.ConstraintsChange,_b=exports.LocalStreamEventNames.OutputTrackChange;var LocalStream=AddEvents(_LocalStream);/**
3017
+ if(this.effects.length>0){this.changeOutputTrack(this.inputTrack);yield Promise.all(this.effects.map(effect=>effect.dispose()));this.effects=[];}});}}_a$6$1=exports.LocalStreamEventNames.ConstraintsChange,_b=exports.LocalStreamEventNames.OutputTrackChange,_c=exports.LocalStreamEventNames.EffectAdded;var LocalStream=AddEvents(_LocalStream);/**
2994
3018
  * An audio LocalStream.
2995
3019
  */class LocalAudioStream extends LocalStream{/**
2996
3020
  * Apply constraints to the stream.
package/dist/esm/index.js CHANGED
@@ -2795,7 +2795,7 @@ return devices.filter(device=>deviceKinds.includes(device.kind)).every(device=>d
2795
2795
  // (Firefox) will allow the user to access device information.
2796
2796
  var callbackRes=yield callback();// Stop tracks in the stream so the browser (Safari) will know that there is not an active
2797
2797
  // stream running.
2798
- stream.getTracks().forEach(track=>track.stop());return callbackRes;}return callback();}catch(e){logger$3.error(e);throw new Error('Failed to ensure device permissions.');}});}var media=/*#__PURE__*/Object.freeze({__proto__:null,get DeviceKind(){return DeviceKind;},getUserMedia:getUserMedia,getDisplayMedia:getDisplayMedia,enumerateDevices:enumerateDevices,setOnDeviceChangeHandler:setOnDeviceChangeHandler$1,checkDevicePermissions:checkDevicePermissions,ensureDevicePermissions:ensureDevicePermissions});var WebrtcCoreErrorType;(function(WebrtcCoreErrorType){WebrtcCoreErrorType["DEVICE_PERMISSION_DENIED"]="DEVICE_PERMISSION_DENIED";WebrtcCoreErrorType["CREATE_STREAM_FAILED"]="CREATE_STREAM_FAILED";})(WebrtcCoreErrorType||(WebrtcCoreErrorType={}));/**
2798
+ stream.getTracks().forEach(track=>track.stop());return callbackRes;}return callback();}catch(e){logger$3.error(e);throw new Error('Failed to ensure device permissions.');}});}var media=/*#__PURE__*/Object.freeze({__proto__:null,get DeviceKind(){return DeviceKind;},getUserMedia:getUserMedia,getDisplayMedia:getDisplayMedia,enumerateDevices:enumerateDevices,setOnDeviceChangeHandler:setOnDeviceChangeHandler$1,checkDevicePermissions:checkDevicePermissions,ensureDevicePermissions:ensureDevicePermissions});var WebrtcCoreErrorType;(function(WebrtcCoreErrorType){WebrtcCoreErrorType["DEVICE_PERMISSION_DENIED"]="DEVICE_PERMISSION_DENIED";WebrtcCoreErrorType["CREATE_STREAM_FAILED"]="CREATE_STREAM_FAILED";WebrtcCoreErrorType["ADD_EFFECT_FAILED"]="ADD_EFFECT_FAILED";})(WebrtcCoreErrorType||(WebrtcCoreErrorType={}));/**
2799
2799
  * Represents a WebRTC core error, which contains error type and error message.
2800
2800
  */class WebrtcCoreError{/**
2801
2801
  * Creates new error.
@@ -2912,13 +2912,13 @@ this[_a$1$1]=new TypedEvent$1();this[_b$1]=new TypedEvent$1();this.outputStream=
2912
2912
  * Get the track of the output stream.
2913
2913
  *
2914
2914
  * @returns The output track.
2915
- */get outputTrack(){return this.outputStream.getTracks()[0];}}_a$1$1=StreamEventNames.MuteStateChange,_b$1=StreamEventNames.Ended;var Stream=AddEvents(_Stream);var _a$6$1,_b;var LocalStreamEventNames;(function(LocalStreamEventNames){LocalStreamEventNames["ConstraintsChange"]="constraints-change";LocalStreamEventNames["OutputTrackChange"]="output-track-change";})(LocalStreamEventNames||(LocalStreamEventNames={}));/**
2915
+ */get outputTrack(){return this.outputStream.getTracks()[0];}}_a$1$1=StreamEventNames.MuteStateChange,_b$1=StreamEventNames.Ended;var Stream=AddEvents(_Stream);var _a$6$1,_b,_c;var LocalStreamEventNames;(function(LocalStreamEventNames){LocalStreamEventNames["ConstraintsChange"]="constraints-change";LocalStreamEventNames["OutputTrackChange"]="output-track-change";LocalStreamEventNames["EffectAdded"]="effect-added";})(LocalStreamEventNames||(LocalStreamEventNames={}));/**
2916
2916
  * A stream which originates on the local device.
2917
2917
  */class _LocalStream extends Stream{/**
2918
2918
  * Create a LocalStream from the given values.
2919
2919
  *
2920
2920
  * @param stream - The initial output MediaStream for this Stream.
2921
- */constructor(stream){super(stream);this[_a$6$1]=new TypedEvent$1();this[_b]=new TypedEvent$1();this.effects=[];this.loadingEffects=new Map();this.inputStream=stream;}/**
2921
+ */constructor(stream){super(stream);this[_a$6$1]=new TypedEvent$1();this[_b]=new TypedEvent$1();this[_c]=new TypedEvent$1();this.effects=[];this.loadingEffects=new Map();this.inputStream=stream;}/**
2922
2922
  * Get the track within the MediaStream with which this LocalStream was created.
2923
2923
  *
2924
2924
  * @returns The track within the MediaStream with which this LocalStream
@@ -2958,28 +2958,52 @@ if(this.inputTrack.id===this.outputTrack.id){this.inputStream=this.outputStream;
2958
2958
  this[StreamEventNames.Ended].emit();}/**
2959
2959
  * Adds an effect to a local stream.
2960
2960
  *
2961
- * @param name - The name of the effect.
2962
2961
  * @param effect - The effect to add.
2963
- */addEffect(name,effect){return __awaiter$2(this,void 0,void 0,function*(){// Load the effect
2964
- this.loadingEffects.set(name,effect);var outputTrack=yield effect.load(this.outputTrack);// Check that the loaded effect is the latest one and dispose if not
2965
- if(effect!==this.loadingEffects.get(name)){yield effect.dispose();throw new Error("Effect \"".concat(name,"\" not required after loading"));}// Use the effect
2966
- this.loadingEffects.delete(name);this.effects.push({name,effect});this.changeOutputTrack(outputTrack);// When the effect's track is updated, update the next effect or output stream.
2967
- // TODO: using EffectEvent.TrackUpdated will cause the entire web-media-effects lib to be built
2968
- // and makes the size of the webrtc-core build much larger, so we use type assertion here as a
2969
- // temporary workaround.
2970
- effect.on('track-updated',track=>{var _c;var effectIndex=this.effects.findIndex(e=>e.name===name);if(effectIndex===this.effects.length-1){this.changeOutputTrack(track);}else {(_c=this.effects[effectIndex+1])===null||_c===void 0?void 0:_c.effect.replaceInputTrack(track);}});});}/**
2971
- * Get an effect from the effects list.
2962
+ */addEffect(effect){return __awaiter$2(this,void 0,void 0,function*(){// Check if the effect has already been added.
2963
+ if(this.effects.some(e=>e.id===effect.id)){return;}// Load the effect. Because loading is asynchronous, keep track of the loading effects.
2964
+ this.loadingEffects.set(effect.kind,effect);yield effect.load(this.outputTrack);// After loading, check whether or not we still want to use this effect. If another effect of
2965
+ // the same kind was added while this effect was loading, we only want to use the latest effect,
2966
+ // so dispose this one. If the effects list was cleared while this effect was loading, also
2967
+ // dispose it.
2968
+ if(effect!==this.loadingEffects.get(effect.kind)){yield effect.dispose();throw new WebrtcCoreError(WebrtcCoreErrorType.ADD_EFFECT_FAILED,"Another effect with kind ".concat(effect.kind," was added while effect ").concat(effect.id," was loading, or the effects list was cleared."));}this.loadingEffects.delete(effect.kind);/**
2969
+ * Handle when the effect's output track has been changed. This will update the input of the
2970
+ * next effect in the effects list of the output of the stream.
2971
+ *
2972
+ * @param track - The new output track of the effect.
2973
+ */var handleEffectTrackUpdated=track=>{var _d;var effectIndex=this.effects.findIndex(e=>e.id===effect.id);if(effectIndex===this.effects.length-1){this.changeOutputTrack(track);}else if(effectIndex>=0){(_d=this.effects[effectIndex+1])===null||_d===void 0?void 0:_d.replaceInputTrack(track);}else {logger$3.error("Effect with ID ".concat(effect.id," not found in effects list."));}};/**
2974
+ * Handle when the effect has been disposed. This will remove all event listeners from the
2975
+ * effect.
2976
+ */var handleEffectDisposed=()=>{effect.off('track-updated',handleEffectTrackUpdated);effect.off('disposed',handleEffectDisposed);};// TODO: using EffectEvent.TrackUpdated or EffectEvent.Disposed will cause the entire
2977
+ // web-media-effects lib to be rebuilt and inflates the size of the webrtc-core build, so
2978
+ // we use type assertion here as a temporary workaround.
2979
+ effect.on('track-updated',handleEffectTrackUpdated);effect.on('disposed',handleEffectDisposed);// Add the effect to the effects list. If an effect of the same kind has already been added,
2980
+ // dispose the existing effect and replace it with the new effect. If the existing effect was
2981
+ // enabled, also enable the new effect.
2982
+ var existingEffectIndex=this.effects.findIndex(e=>e.kind===effect.kind);if(existingEffectIndex>=0){var[existingEffect]=this.effects.splice(existingEffectIndex,1,effect);if(existingEffect.isEnabled){// If the existing effect is not the first effect in the effects list, then the input of the
2983
+ // new effect should be the output of the previous effect in the effects list. We know the
2984
+ // output track of the previous effect must exist because it must have been loaded (and all
2985
+ // loaded effects have an output track).
2986
+ var inputTrack=existingEffectIndex===0?this.inputTrack:this.effects[existingEffectIndex-1].getOutputTrack();yield effect.replaceInputTrack(inputTrack);// Enabling the new effect will trigger the track-updated event, which will handle the new
2987
+ // effect's updated output track.
2988
+ yield effect.enable();}yield existingEffect.dispose();}else {this.effects.push(effect);}// Emit an event with the effect so others can listen to the effect events.
2989
+ this[LocalStreamEventNames.EffectAdded].emit(effect);});}/**
2990
+ * Get an effect from the effects list by ID.
2972
2991
  *
2973
- * @param name - The name of the effect you want to get.
2992
+ * @param id - The id of the effect you want to get.
2974
2993
  * @returns The effect or undefined.
2975
- */getEffect(name){var _c;return (_c=this.effects.find(e=>e.name===name))===null||_c===void 0?void 0:_c.effect;}/**
2994
+ */getEffectById(id){return this.effects.find(effect=>effect.id===id);}/**
2995
+ * Get an effect from the effects list by kind.
2996
+ *
2997
+ * @param kind - The kind of the effect you want to get.
2998
+ * @returns The effect or undefined.
2999
+ */getEffectByKind(kind){return this.effects.find(effect=>effect.kind===kind);}/**
2976
3000
  * Get all the effects from the effects list.
2977
3001
  *
2978
- * @returns A list of effect items, each containing the name and the effect itself.
2979
- */getAllEffects(){return this.effects;}/**
3002
+ * @returns A list of effects.
3003
+ */getEffects(){return this.effects;}/**
2980
3004
  * Cleanup the local effects.
2981
3005
  */disposeEffects(){return __awaiter$2(this,void 0,void 0,function*(){this.loadingEffects.clear();// Dispose of any effects currently in use
2982
- if(this.effects.length>0){this.changeOutputTrack(this.inputTrack);yield Promise.all(this.effects.map(item=>item.effect.dispose()));this.effects=[];}});}}_a$6$1=LocalStreamEventNames.ConstraintsChange,_b=LocalStreamEventNames.OutputTrackChange;var LocalStream=AddEvents(_LocalStream);/**
3006
+ if(this.effects.length>0){this.changeOutputTrack(this.inputTrack);yield Promise.all(this.effects.map(effect=>effect.dispose()));this.effects=[];}});}}_a$6$1=LocalStreamEventNames.ConstraintsChange,_b=LocalStreamEventNames.OutputTrackChange,_c=LocalStreamEventNames.EffectAdded;var LocalStream=AddEvents(_LocalStream);/**
2983
3007
  * An audio LocalStream.
2984
3008
  */class LocalAudioStream extends LocalStream{/**
2985
3009
  * Apply constraints to the stream.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/internal-media-core",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "files": [
5
5
  "dist/cjs",
6
6
  "dist/esm",
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@babel/runtime": "^7.18.9",
48
48
  "@webex/ts-sdp": "1.6.0",
49
- "@webex/web-client-media-engine": "3.11.0",
49
+ "@webex/web-client-media-engine": "3.11.1",
50
50
  "detectrtc": "^1.4.1",
51
51
  "events": "^3.3.0",
52
52
  "typed-emitter": "^2.1.0",