feat: use EvenEmitter3 for web-sdk (#847)
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->
## This PR
<!-- add the description of the PR here -->
Fixes an issue where the `events` node polyfill does not comply to the
`node:events` types.
When trying to use the web OpenFeatureEventEmitter the following error
message comes up, describing that the `events` polyfill's EventEmitter
is incompatible to `node:events` EventEmitter.
```
✘ [ERROR] TS2416: Property 'eventEmitter' in type 'OpenFeatureEventEmitter' is not assignable to the same property in base type 'GenericEventEmitter<ProviderEmittableEvents, Record<string, unknown>>'.
Type 'EventEmitter' is not assignable to type 'PlatformEventEmitter'.
Types of property 'addListener' are incompatible.
Type '(type: string | number, listener: Listener) => EventEmitter' is not assignable to type '(eventName: string | symbol, listener: (...args: any[]) => void) => PlatformEventEmitter'.
Types of parameters 'type' and 'eventName' are incompatible.
Type 'string | symbol' is not assignable to type 'string | number'.
Type 'symbol' is not assignable to type 'string | number'.
```
This PR fixes that issue by not using the `events` anymore and instead
using https://www.npmjs.com/package/eventemitter3
cc @toddbaert
### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->
Fixes #845
---------
Signed-off-by: Lukas Reining <lukas.reining@codecentric.de>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
This commit is contained in:
parent
1461074f20
commit
861cf83782
|
|
@ -17,7 +17,6 @@
|
|||
],
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@types/events": "^3.0.3",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.11.16",
|
||||
"@types/react": "^18.2.55",
|
||||
|
|
@ -31,7 +30,7 @@
|
|||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest": "^27.6.3",
|
||||
"eslint-plugin-jsdoc": "^48.0.6",
|
||||
"events": "^3.3.0",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-config": "^29.7.0",
|
||||
"jest-cucumber": "^3.0.1",
|
||||
|
|
@ -2154,12 +2153,6 @@
|
|||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/events": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
|
||||
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/glob": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
||||
|
|
@ -4787,14 +4780,11 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/exec-sh": {
|
||||
"version": "0.3.6",
|
||||
|
|
@ -13743,7 +13733,7 @@
|
|||
},
|
||||
"packages/client": {
|
||||
"name": "@openfeature/web-sdk",
|
||||
"version": "0.4.13",
|
||||
"version": "0.4.14",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@openfeature/core": "0.0.27"
|
||||
|
|
@ -13754,7 +13744,7 @@
|
|||
},
|
||||
"packages/nest": {
|
||||
"name": "@openfeature/nestjs-sdk",
|
||||
"version": "0.1.0-experimental",
|
||||
"version": "0.1.1-experimental",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "^10.2.10",
|
||||
|
|
@ -13775,7 +13765,7 @@
|
|||
},
|
||||
"packages/react": {
|
||||
"name": "@openfeature/react-sdk",
|
||||
"version": "0.2.0-experimental",
|
||||
"version": "0.2.1-experimental",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@openfeature/core": "*",
|
||||
|
|
@ -13788,7 +13778,7 @@
|
|||
},
|
||||
"packages/server": {
|
||||
"name": "@openfeature/server-sdk",
|
||||
"version": "1.12.0",
|
||||
"version": "1.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@openfeature/core": "0.0.27"
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@types/events": "^3.0.3",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.11.16",
|
||||
"@types/react": "^18.2.55",
|
||||
|
|
@ -51,7 +50,7 @@
|
|||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest": "^27.6.3",
|
||||
"eslint-plugin-jsdoc": "^48.0.6",
|
||||
"events": "^3.3.0",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-config": "^29.7.0",
|
||||
"jest-cucumber": "^3.0.1",
|
||||
|
|
@ -76,4 +75,4 @@
|
|||
"packages/react",
|
||||
"packages/nest"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { GenericEventEmitter } from '@openfeature/core';
|
||||
import { EventEmitter } from 'events';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { ProviderEmittableEvents } from './events';
|
||||
|
||||
/**
|
||||
* The OpenFeatureEventEmitter can be used by provider developers to emit
|
||||
* events at various parts of the provider lifecycle.
|
||||
|
|
@ -9,12 +10,9 @@ import { ProviderEmittableEvents } from './events';
|
|||
* the result of the initialize method.
|
||||
*/
|
||||
export class OpenFeatureEventEmitter extends GenericEventEmitter<ProviderEmittableEvents> {
|
||||
protected readonly eventEmitter = new EventEmitter({ captureRejections: true });
|
||||
protected readonly eventEmitter = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.eventEmitter.on('error', (err) => {
|
||||
this._logger?.error('Error running event handler:', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ import { AllProviderEvents, AnyProviderEvent } from './events';
|
|||
* The GenericEventEmitter should only be used within the SDK. It supports additional properties that can be included
|
||||
* in the event details.
|
||||
*/
|
||||
export abstract class GenericEventEmitter<E extends AnyProviderEvent, AdditionalContext extends Record<string, unknown> = Record<string, unknown>>
|
||||
export abstract class GenericEventEmitter<
|
||||
E extends AnyProviderEvent,
|
||||
AdditionalContext extends Record<string, unknown> = Record<string, unknown>,
|
||||
>
|
||||
implements ProviderEventEmitter<E>, ManageLogger<GenericEventEmitter<E, AdditionalContext>>
|
||||
{
|
||||
protected abstract readonly eventEmitter: PlatformEventEmitter;
|
||||
|
||||
private readonly _handlers: { [key in AnyProviderEvent]: WeakMap<EventHandler, EventHandler[]>} = {
|
||||
private readonly _handlers: { [key in AnyProviderEvent]: WeakMap<EventHandler, EventHandler[]> } = {
|
||||
[AllProviderEvents.ConfigurationChanged]: new WeakMap<EventHandler, EventHandler[]>(),
|
||||
[AllProviderEvents.ContextChanged]: new WeakMap<EventHandler, EventHandler[]>(),
|
||||
[AllProviderEvents.Ready]: new WeakMap<EventHandler, EventHandler[]>(),
|
||||
|
|
@ -33,7 +36,11 @@ export abstract class GenericEventEmitter<E extends AnyProviderEvent, Additional
|
|||
// The handlers have to be wrapped with an async function because if a synchronous functions throws an error,
|
||||
// the other handlers will not run.
|
||||
const asyncHandler = async (details?: EventDetails) => {
|
||||
await handler(details);
|
||||
try {
|
||||
await handler(details);
|
||||
} catch (err) {
|
||||
this._logger?.error('Error running event handler:', err);
|
||||
}
|
||||
};
|
||||
// The async handler has to be written to the map, because we need to get the wrapper function when deleting a listener
|
||||
const existingAsyncHandlers = this._handlers[eventType].get(handler);
|
||||
|
|
@ -84,7 +91,7 @@ export abstract class GenericEventEmitter<E extends AnyProviderEvent, Additional
|
|||
* This is an un-exported type that corresponds to NodeJS.EventEmitter.
|
||||
* We can't use that type here, because this module is used in both the browser, and the server.
|
||||
* In the server, node (or whatever server runtime) provides an implementation for this.
|
||||
* In the browser, we bundle in the popular 'events' package, which is a polyfill of NodeJS.EventEmitter.
|
||||
* In the browser, we bundle in the popular 'EventEmitter3' package, which is a polyfill of NodeJS.EventEmitter.
|
||||
*/
|
||||
/* eslint-disable */
|
||||
interface PlatformEventEmitter {
|
||||
|
|
@ -94,13 +101,8 @@ interface PlatformEventEmitter {
|
|||
removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
off(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
removeAllListeners(event?: string | symbol): this;
|
||||
setMaxListeners(n: number): this;
|
||||
getMaxListeners(): number;
|
||||
listeners(eventName: string | symbol): Function[];
|
||||
rawListeners(eventName: string | symbol): Function[];
|
||||
emit(eventName: string | symbol, ...args: any[]): boolean;
|
||||
listenerCount(eventName: string | symbol, listener?: Function): number;
|
||||
prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
eventNames(): Array<string | symbol>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue