@unchainedshop/events 1.1.5 → 1.2.0
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/jest.config.js +5 -0
- package/package.json +13 -17
- package/src/EventDirector.ts +89 -0
- package/src/events-index.ts +26 -0
- package/tests/events-index.test.ts +78 -0
- package/{tsconfig.build.json → tsconfig.json} +10 -7
- package/babel.config.js +0 -3
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unchainedshop/events",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "lib/events-index.js",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./lib/events-index.js",
|
|
7
|
+
"./*": "./lib/*"
|
|
8
|
+
},
|
|
5
9
|
"types": "lib/events-index.d.ts",
|
|
6
10
|
"type": "module",
|
|
7
11
|
"scripts": {
|
|
12
|
+
"prepublishOnly": "npm install && npm run build || :",
|
|
8
13
|
"clean": "rm -rf lib",
|
|
9
|
-
"build": "npm run clean && tsc
|
|
10
|
-
"watch": "tsc
|
|
11
|
-
"link:core": "npm link @unchainedshop/types",
|
|
14
|
+
"build": "npm run clean && tsc",
|
|
15
|
+
"watch": "tsc -w",
|
|
12
16
|
"test": "jest --watch"
|
|
13
17
|
},
|
|
14
18
|
"repository": {
|
|
@@ -27,23 +31,15 @@
|
|
|
27
31
|
},
|
|
28
32
|
"homepage": "https://github.com/unchainedshop/unchained#readme",
|
|
29
33
|
"dependencies": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
34
|
+
"@unchainedshop/logger": "^1.1.9",
|
|
35
|
+
"redis": "^4.1.0"
|
|
32
36
|
},
|
|
33
37
|
"devDependencies": {
|
|
34
|
-
"@babel/core": "^7.18.6",
|
|
35
|
-
"@babel/preset-env": "^7.18.6",
|
|
36
|
-
"@babel/preset-typescript": "^7.18.6",
|
|
37
|
-
"@types/chai": "^4.3.1",
|
|
38
38
|
"@types/jest": "^28.1.3",
|
|
39
|
-
"@types/
|
|
40
|
-
"@unchainedshop/types": "^1.1.
|
|
41
|
-
"
|
|
42
|
-
"chai": "^4.3.6",
|
|
43
|
-
"cross-env": "^7.0.3",
|
|
44
|
-
"jest": "^28.1.1",
|
|
39
|
+
"@types/node": "^16.11.44",
|
|
40
|
+
"@unchainedshop/types": "^1.1.9",
|
|
41
|
+
"jest": "^28.1.2",
|
|
45
42
|
"ts-jest": "^28.0.5",
|
|
46
|
-
"ts-node": "^10.8.1",
|
|
47
43
|
"typescript": "^4.7.4"
|
|
48
44
|
}
|
|
49
45
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContextNormalizerFunction,
|
|
3
|
+
EmitAdapter,
|
|
4
|
+
EventDirector as IEventDirector,
|
|
5
|
+
} from '@unchainedshop/types/events';
|
|
6
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
7
|
+
|
|
8
|
+
const logger = createLogger('unchained:events');
|
|
9
|
+
|
|
10
|
+
export const defaultNormalizer: ContextNormalizerFunction = (context) => {
|
|
11
|
+
return {
|
|
12
|
+
userAgent: context?.userAgent,
|
|
13
|
+
language: context?.localeContext?.code,
|
|
14
|
+
country: context?.localeContext?.country,
|
|
15
|
+
remoteAddress: context?.remoteAddress,
|
|
16
|
+
referer: context?.req?.headers?.referer,
|
|
17
|
+
origin: context?.req?.headers?.origin,
|
|
18
|
+
userId: context?.userId,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const RegisteredEventsSet = new Set();
|
|
23
|
+
const RegisteredCallbacksSet = new Set();
|
|
24
|
+
|
|
25
|
+
let Adapter: EmitAdapter; // Public (customizable)
|
|
26
|
+
let HistoryAdapter: EmitAdapter; // (Per default: Core-events adapter to write into DB)
|
|
27
|
+
let ContextNormalizer = defaultNormalizer;
|
|
28
|
+
|
|
29
|
+
export const EventDirector: IEventDirector = {
|
|
30
|
+
registerEvents: (events: string[]): void => {
|
|
31
|
+
if (events.length) {
|
|
32
|
+
events.forEach((e) => RegisteredEventsSet.add(e));
|
|
33
|
+
logger.verbose(`EventDirector -> Registered ${JSON.stringify(events)}`);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
getRegisteredEvents: (): string[] => {
|
|
38
|
+
return Array.from(RegisteredEventsSet) as string[];
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
setContextNormalizer: (fn: ContextNormalizerFunction): void => {
|
|
42
|
+
ContextNormalizer = fn;
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
setEmitAdapter: (adapter: EmitAdapter) => {
|
|
46
|
+
Adapter = adapter;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
getEmitAdapter: (): EmitAdapter => Adapter,
|
|
50
|
+
|
|
51
|
+
setEmitHistoryAdapter: (adapter: EmitAdapter) => {
|
|
52
|
+
HistoryAdapter = adapter;
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
getEmitHistoryAdapter: (): EmitAdapter => HistoryAdapter,
|
|
56
|
+
|
|
57
|
+
emit: async (eventName: string, data: any): Promise<void> => {
|
|
58
|
+
const extractedContext = ContextNormalizer(null);
|
|
59
|
+
|
|
60
|
+
if (!RegisteredEventsSet.has(eventName))
|
|
61
|
+
throw new Error(`Event with ${eventName} is not registered`);
|
|
62
|
+
|
|
63
|
+
Adapter?.publish(eventName, {
|
|
64
|
+
payload: { ...data },
|
|
65
|
+
context: extractedContext,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
HistoryAdapter?.publish(eventName, {
|
|
69
|
+
payload: { ...data },
|
|
70
|
+
context: extractedContext,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
logger.verbose(`EventDirector -> Emitted ${eventName} with ${JSON.stringify(data)}`);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
subscribe: (eventName: string, callback: () => void): void => {
|
|
77
|
+
const currentSubscription = `${eventName}${callback?.toString()}`; // used to avaoid registering the same event handler callback
|
|
78
|
+
|
|
79
|
+
if (!RegisteredEventsSet.has(eventName))
|
|
80
|
+
throw new Error(`Event with ${eventName} is not registered`);
|
|
81
|
+
|
|
82
|
+
if (!RegisteredCallbacksSet.has(currentSubscription)) {
|
|
83
|
+
Adapter?.subscribe(eventName, callback);
|
|
84
|
+
HistoryAdapter?.subscribe(eventName, callback);
|
|
85
|
+
RegisteredCallbacksSet.add(currentSubscription);
|
|
86
|
+
logger.verbose(`EventDirector -> Subscribed to ${eventName}`);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { EventDirector } from './EventDirector';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
emit,
|
|
5
|
+
getEmitAdapter,
|
|
6
|
+
getEmitHistoryAdapter,
|
|
7
|
+
getRegisteredEvents,
|
|
8
|
+
registerEvents,
|
|
9
|
+
setEmitAdapter,
|
|
10
|
+
setEmitHistoryAdapter,
|
|
11
|
+
subscribe,
|
|
12
|
+
} = EventDirector;
|
|
13
|
+
|
|
14
|
+
const GLOBAL_EVENTS = ['PAGE_VIEW'];
|
|
15
|
+
registerEvents(GLOBAL_EVENTS);
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
emit,
|
|
19
|
+
getEmitAdapter,
|
|
20
|
+
getEmitHistoryAdapter,
|
|
21
|
+
getRegisteredEvents,
|
|
22
|
+
registerEvents,
|
|
23
|
+
setEmitAdapter,
|
|
24
|
+
setEmitHistoryAdapter,
|
|
25
|
+
subscribe,
|
|
26
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { assert } from 'chai';
|
|
2
|
+
import {
|
|
3
|
+
emit,
|
|
4
|
+
getEmitAdapter,
|
|
5
|
+
getEmitHistoryAdapter,
|
|
6
|
+
getRegisteredEvents,
|
|
7
|
+
registerEvents,
|
|
8
|
+
setEmitAdapter,
|
|
9
|
+
setEmitHistoryAdapter,
|
|
10
|
+
} from '../src/events-index';
|
|
11
|
+
import { log } from '@unchainedshop/logger';
|
|
12
|
+
import { EmitAdapter } from '@unchainedshop/types/events';
|
|
13
|
+
|
|
14
|
+
const subscribedEvents = new Set();
|
|
15
|
+
|
|
16
|
+
const TestEventEmitter: EmitAdapter = {
|
|
17
|
+
publish: (eventName, payload) => log(eventName),
|
|
18
|
+
subscribe: (eventName, callback) => {
|
|
19
|
+
if (!subscribedEvents.has(eventName)) {
|
|
20
|
+
log(`Subscribe event: ${eventName}`);
|
|
21
|
+
subscribedEvents.add(eventName);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('Test exports', () => {
|
|
27
|
+
it('Check registered events', () => {
|
|
28
|
+
let registeredEvents = getRegisteredEvents();
|
|
29
|
+
const NEW_TEST_EVENT_1 = 'new test event 1';
|
|
30
|
+
const NEW_TEST_EVENT_2 = 'new test event 2';
|
|
31
|
+
|
|
32
|
+
assert.isTrue(registeredEvents.length === 1);
|
|
33
|
+
assert.isFalse(registeredEvents.includes(NEW_TEST_EVENT_1));
|
|
34
|
+
assert.isFalse(registeredEvents.includes(NEW_TEST_EVENT_2));
|
|
35
|
+
|
|
36
|
+
registerEvents([NEW_TEST_EVENT_1, NEW_TEST_EVENT_2]);
|
|
37
|
+
|
|
38
|
+
registeredEvents = getRegisteredEvents();
|
|
39
|
+
|
|
40
|
+
assert.isTrue(registeredEvents.length === 3);
|
|
41
|
+
assert.isTrue(registeredEvents.includes(NEW_TEST_EVENT_1));
|
|
42
|
+
assert.isTrue(registeredEvents.includes(NEW_TEST_EVENT_2));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('Check set Adapter', () => {
|
|
46
|
+
assert.isUndefined(getEmitAdapter());
|
|
47
|
+
assert.isFunction(setEmitAdapter);
|
|
48
|
+
assert.isFunction(getEmitHistoryAdapter);
|
|
49
|
+
assert.isFunction(setEmitHistoryAdapter);
|
|
50
|
+
|
|
51
|
+
setEmitAdapter(TestEventEmitter);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('Check emit global page event', () => {
|
|
55
|
+
assert.isFunction(emit);
|
|
56
|
+
|
|
57
|
+
emit('PAGE_VIEW');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('Check emit event', () => {
|
|
61
|
+
assert.isFunction(registerEvents);
|
|
62
|
+
|
|
63
|
+
registerEvents(['TEST_EVENT', 'TEST_EVENT_2']);
|
|
64
|
+
emit('TEST_EVENT');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('Check emit non-existing event', (done) => {
|
|
68
|
+
emit('I_AM_NOT_REGISTERED_EVENT').then(() => {
|
|
69
|
+
assert(false, 'Non-existing event should not be emittable')
|
|
70
|
+
done()
|
|
71
|
+
}).catch(error => {
|
|
72
|
+
assert.equal(error.message, 'Event with I_AM_NOT_REGISTERED_EVENT is not registered')
|
|
73
|
+
done()
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
});
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
"esModuleInterop": true,
|
|
7
7
|
"experimentalDecorators": true,
|
|
8
8
|
"forceConsistentCasingInFileNames": true,
|
|
9
|
-
"lib": [
|
|
9
|
+
"lib": [
|
|
10
|
+
"esnext"
|
|
11
|
+
],
|
|
10
12
|
"module": "esnext",
|
|
11
13
|
"moduleResolution": "node",
|
|
12
14
|
"noImplicitReturns": true,
|
|
@@ -16,11 +18,12 @@
|
|
|
16
18
|
"skipLibCheck": true,
|
|
17
19
|
"sourceMap": true,
|
|
18
20
|
"target": "esnext",
|
|
19
|
-
"types": [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
21
|
+
"types": [
|
|
22
|
+
"node",
|
|
23
|
+
"jest"
|
|
24
|
+
]
|
|
24
25
|
},
|
|
25
|
-
"include": [
|
|
26
|
+
"include": [
|
|
27
|
+
"src"
|
|
28
|
+
]
|
|
26
29
|
}
|
package/babel.config.js
DELETED