From 861cf8378271daf6205c5fc199ffc1bde8dfcc64 Mon Sep 17 00:00:00 2001 From: Lukas Reining Date: Tue, 5 Mar 2024 23:45:49 +0100 Subject: [PATCH] feat: use EvenEmitter3 for web-sdk (#847) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## This PR 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>'. 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 Fixes #845 --------- Signed-off-by: Lukas Reining Signed-off-by: Todd Baert Co-authored-by: Todd Baert --- package-lock.json | 30 +++++++------------ package.json | 5 ++-- .../src/events/open-feature-event-emitter.ts | 8 ++--- .../src/events/generic-event-emitter.ts | 22 +++++++------- 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5071ea93..ca025f86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" diff --git a/package.json b/package.json index 97e13c2f..aa03322e 100644 --- a/package.json +++ b/package.json @@ -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" ] -} \ No newline at end of file +} diff --git a/packages/client/src/events/open-feature-event-emitter.ts b/packages/client/src/events/open-feature-event-emitter.ts index 1a2d5016..79c86863 100644 --- a/packages/client/src/events/open-feature-event-emitter.ts +++ b/packages/client/src/events/open-feature-event-emitter.ts @@ -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 { - 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); - }); } } diff --git a/packages/shared/src/events/generic-event-emitter.ts b/packages/shared/src/events/generic-event-emitter.ts index c3c0c07f..43b287d9 100644 --- a/packages/shared/src/events/generic-event-emitter.ts +++ b/packages/shared/src/events/generic-event-emitter.ts @@ -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 = Record> +export abstract class GenericEventEmitter< + E extends AnyProviderEvent, + AdditionalContext extends Record = Record, + > implements ProviderEventEmitter, ManageLogger> { protected abstract readonly eventEmitter: PlatformEventEmitter; - private readonly _handlers: { [key in AnyProviderEvent]: WeakMap} = { + private readonly _handlers: { [key in AnyProviderEvent]: WeakMap } = { [AllProviderEvents.ConfigurationChanged]: new WeakMap(), [AllProviderEvents.ContextChanged]: new WeakMap(), [AllProviderEvents.Ready]: new WeakMap(), @@ -33,7 +36,11 @@ export abstract class GenericEventEmitter { - 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 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; -} \ No newline at end of file +}