Skip to content

Commit 0b741dc

Browse files
committed
wip
1 parent 1edd1ec commit 0b741dc

File tree

11 files changed

+168
-156
lines changed

11 files changed

+168
-156
lines changed

src/Init.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@ import { Logger } from 'tslog';
33

44
import { GetServiceResolverEvent } from './BrowserEvent/index.js';
55
import { ServiceResolver } from './Service/index.js';
6-
import {
7-
EventDispatcher,
8-
PriorityRegistry,
9-
Registry,
10-
validateServiceIdentifierFromString,
11-
} from './Type/Definition/index.js';
6+
import { EventDispatcher } from './Service/index.js';
7+
import { PriorityRegistry, Registry, validateServiceIdentifierFromString } from './Type/Definition/index.js';
128
import { EventIdentifier } from './Type/Enum/index.js';
139
import { ServiceIdentifier } from './Type/Enum/ServiceIdentifier.js';
1410

src/Service/EventDispatcher.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import {
2+
EventDispatcherInterface,
3+
EventInterface,
4+
EventListener,
5+
EventListenerIdentifier,
6+
LoggerInterface,
7+
getEventListenerIdentifiersFromEventIdentifier,
8+
} from '../Type/Definition/index.js';
9+
10+
type EventDispatcherEntry = {
11+
priority: number;
12+
eventListener: EventListener;
13+
};
14+
15+
class EventDispatcher implements EventDispatcherInterface {
16+
private readonly entries: Map<string, EventDispatcherEntry[]> = new Map();
17+
private logger: LoggerInterface;
18+
19+
public constructor(logger: LoggerInterface) {
20+
this.logger = logger;
21+
}
22+
23+
async dispatchEvent(event: EventInterface): Promise<void> {
24+
this.logger.debug(`Dispatching event of identifier ${event.getIdentifier()}.`, { event: event });
25+
if (event.isPropagationStopped()) {
26+
this.logger.debug(`Stopped event propagation because it is already stopped.`, { event: event });
27+
return undefined;
28+
}
29+
const eventListenerIdentifiersToNotify = getEventListenerIdentifiersFromEventIdentifier(event.getIdentifier());
30+
for (let i = 0; i < eventListenerIdentifiersToNotify.length; ++i) {
31+
const eventListenerIdentifier = eventListenerIdentifiersToNotify[i];
32+
const eventListeners = this.entries.get(eventListenerIdentifier);
33+
if (eventListeners === undefined) {
34+
continue;
35+
}
36+
this.logger.debug(`Iterating over resolved event listeners of identifier ${eventListenerIdentifier}`, {
37+
event: event,
38+
});
39+
for (let j = eventListeners.length - 1; j >= 0; j--) {
40+
try {
41+
await Promise.resolve(eventListeners[j].eventListener(event));
42+
} catch (e: unknown) {
43+
this.logger.error(`Event handler threw exception, dispatcher continues with next event listener.`, {
44+
event: event,
45+
error: e,
46+
});
47+
}
48+
if (event.isPropagationStopped()) {
49+
this.logger.debug(`Stopped event propagation as it got stopped.`, { event: event });
50+
return undefined;
51+
}
52+
}
53+
}
54+
this.logger.debug(`Event got handled by all event listeners.`, { event: event });
55+
return undefined;
56+
}
57+
58+
addListener(eventListenerIdentifier: EventListenerIdentifier, eventListener: EventListener, priority?: number): this {
59+
if (priority === undefined) {
60+
priority = 0;
61+
}
62+
63+
let eventListenersList: EventDispatcherEntry[];
64+
if (!this.entries.has(eventListenerIdentifier)) {
65+
eventListenersList = [];
66+
} else {
67+
eventListenersList = this.entries.get(eventListenerIdentifier)!;
68+
}
69+
70+
// find correct index in list to add entry to
71+
let left = 0;
72+
let right = eventListenersList.length - 1;
73+
let result = -1;
74+
75+
while (left <= right) {
76+
const mid = Math.floor((left + right) / 2);
77+
78+
if (eventListenersList[mid].priority >= priority) {
79+
result = mid; // Found a candidate, but keep searching in the left half
80+
right = mid - 1;
81+
} else {
82+
left = mid + 1; // Search in the right half
83+
}
84+
}
85+
86+
const insertAt = result === -1 ? left : result;
87+
88+
eventListenersList.splice(insertAt, 0, { priority: priority, eventListener: eventListener });
89+
this.entries.set(eventListenerIdentifier, eventListenersList);
90+
return this;
91+
}
92+
93+
removeListener(eventListenerIdentifier: EventListenerIdentifier, eventListener: EventListener): this {
94+
const eventListenersList = this.entries.get(eventListenerIdentifier);
95+
if (!eventListenersList) {
96+
return this;
97+
}
98+
for (let i = 0; i < eventListenersList.length; ++i) {
99+
if (eventListenersList[i].eventListener === eventListener) {
100+
eventListenersList.splice(i, 1);
101+
break;
102+
}
103+
}
104+
if (eventListenersList.length === 0) {
105+
this.entries.delete(eventListenerIdentifier);
106+
}
107+
return this;
108+
}
109+
110+
getListeners(eventListenerIdentifier: EventListenerIdentifier): EventListener[] {
111+
const entries = this.entries.get(eventListenerIdentifier);
112+
return entries ? entries.map((entry) => entry.eventListener) : [];
113+
}
114+
115+
hasListeners(eventListenerIdentifier: EventListenerIdentifier): boolean {
116+
return this.entries.has(eventListenerIdentifier);
117+
}
118+
}
119+
120+
export { EventDispatcher };

src/Service/Logger.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ILogObj, Logger as TsLogger } from 'tslog';
2+
3+
import { LoggerInterface } from '../Type/Definition/index.js';
4+
5+
class Logger implements LoggerInterface {
6+
private logger: TsLogger<ILogObj>;
7+
constructor(logger: TsLogger<ILogObj>) {
8+
this.logger = logger;
9+
}
10+
11+
debug(...args: unknown[]): unknown | undefined {
12+
return this.logger.debug(args);
13+
}
14+
15+
error(...args: unknown[]): unknown | undefined {
16+
return this.logger.error(args);
17+
}
18+
19+
info(...args: unknown[]): unknown | undefined {
20+
return this.logger.info(args);
21+
}
22+
23+
warn(...args: unknown[]): unknown | undefined {
24+
return this.logger.warn(args);
25+
}
26+
}
27+
28+
export { Logger };

src/Service/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
export * from './EventDispatcher.js';
2+
export * from './Logger.js';
13
export * from './ServiceResolver.js';
Lines changed: 2 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { EventInterface } from './Event.js';
22
import { EventListener } from './EventListener.js';
3-
import { EventListenerIdentifier, getEventListenerIdentifiersFromEventIdentifier } from './EventListenerIdentifier.js';
4-
import { LoggerInterface } from './Logger.js';
3+
import { EventListenerIdentifier } from './EventListenerIdentifier.js';
54

65
interface EventDispatcherInterface {
76
dispatchEvent(event: EventInterface): Promise<void>;
@@ -11,114 +10,4 @@ interface EventDispatcherInterface {
1110
hasListeners(eventListenerIdentifier: EventListenerIdentifier): boolean;
1211
}
1312

14-
type EventDispatcherEntry = {
15-
priority: number;
16-
eventListener: EventListener;
17-
};
18-
19-
class EventDispatcher implements EventDispatcherInterface {
20-
private readonly entries: Map<string, EventDispatcherEntry[]> = new Map();
21-
private logger: LoggerInterface;
22-
23-
public constructor(logger: LoggerInterface) {
24-
this.logger = logger;
25-
}
26-
27-
async dispatchEvent(event: EventInterface): Promise<void> {
28-
this.logger.debug(`Dispatching event of identifier ${event.getIdentifier()}.`, { event: event });
29-
if (event.isPropagationStopped()) {
30-
this.logger.debug(`Stopped event propagation because it is already stopped.`, { event: event });
31-
return undefined;
32-
}
33-
const eventListenerIdentifiersToNotify = getEventListenerIdentifiersFromEventIdentifier(event.getIdentifier());
34-
for (let i = 0; i < eventListenerIdentifiersToNotify.length; ++i) {
35-
const eventListenerIdentifier = eventListenerIdentifiersToNotify[i];
36-
const eventListeners = this.entries.get(eventListenerIdentifier);
37-
if (eventListeners === undefined) {
38-
continue;
39-
}
40-
this.logger.debug(`Iterating over resolved event listeners of identifier ${eventListenerIdentifier}`, {
41-
event: event,
42-
});
43-
for (let j = eventListeners.length - 1; j >= 0; j--) {
44-
try {
45-
await Promise.resolve(eventListeners[j].eventListener(event));
46-
} catch (e: unknown) {
47-
this.logger.error(`Event handler threw exception, dispatcher continues with next event listener.`, {
48-
event: event,
49-
error: e,
50-
});
51-
}
52-
if (event.isPropagationStopped()) {
53-
this.logger.debug(`Stopped event propagation as it got stopped.`, { event: event });
54-
return undefined;
55-
}
56-
}
57-
}
58-
this.logger.debug(`Event got handled by all event listeners.`, { event: event });
59-
return undefined;
60-
}
61-
62-
addListener(eventListenerIdentifier: EventListenerIdentifier, eventListener: EventListener, priority?: number): this {
63-
if (priority === undefined) {
64-
priority = 0;
65-
}
66-
67-
let eventListenersList: EventDispatcherEntry[];
68-
if (!this.entries.has(eventListenerIdentifier)) {
69-
eventListenersList = [];
70-
} else {
71-
eventListenersList = this.entries.get(eventListenerIdentifier)!;
72-
}
73-
74-
// find correct index in list to add entry to
75-
let left = 0;
76-
let right = eventListenersList.length - 1;
77-
let result = -1;
78-
79-
while (left <= right) {
80-
const mid = Math.floor((left + right) / 2);
81-
82-
if (eventListenersList[mid].priority >= priority) {
83-
result = mid; // Found a candidate, but keep searching in the left half
84-
right = mid - 1;
85-
} else {
86-
left = mid + 1; // Search in the right half
87-
}
88-
}
89-
90-
const insertAt = result === -1 ? left : result;
91-
92-
eventListenersList.splice(insertAt, 0, { priority: priority, eventListener: eventListener });
93-
this.entries.set(eventListenerIdentifier, eventListenersList);
94-
return this;
95-
}
96-
97-
removeListener(eventListenerIdentifier: EventListenerIdentifier, eventListener: EventListener): this {
98-
const eventListenersList = this.entries.get(eventListenerIdentifier);
99-
if (!eventListenersList) {
100-
return this;
101-
}
102-
for (let i = 0; i < eventListenersList.length; ++i) {
103-
if (eventListenersList[i].eventListener === eventListener) {
104-
eventListenersList.splice(i, 1);
105-
break;
106-
}
107-
}
108-
if (eventListenersList.length === 0) {
109-
this.entries.delete(eventListenerIdentifier);
110-
}
111-
return this;
112-
}
113-
114-
getListeners(eventListenerIdentifier: EventListenerIdentifier): EventListener[] {
115-
const entries = this.entries.get(eventListenerIdentifier);
116-
return entries ? entries.map((entry) => entry.eventListener) : [];
117-
}
118-
119-
hasListeners(eventListenerIdentifier: EventListenerIdentifier): boolean {
120-
return this.entries.has(eventListenerIdentifier);
121-
}
122-
}
123-
124-
export { EventDispatcherInterface, EventDispatcher };
13+
export { EventDispatcherInterface };

src/Type/Definition/Logger.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,8 @@
1-
import { ILogObj, Logger as TsLogger } from 'tslog';
2-
31
interface LoggerInterface {
42
debug(...args: unknown[]): unknown | undefined;
53
info(...args: unknown[]): unknown | undefined;
64
warn(...args: unknown[]): unknown | undefined;
75
error(...args: unknown[]): unknown | undefined;
86
}
97

10-
class Logger implements LoggerInterface {
11-
private logger: TsLogger<ILogObj>;
12-
constructor(logger: TsLogger<ILogObj>) {
13-
this.logger = logger;
14-
}
15-
16-
debug(...args: unknown[]): unknown | undefined {
17-
return this.logger.debug(args);
18-
}
19-
20-
error(...args: unknown[]): unknown | undefined {
21-
return this.logger.error(args);
22-
}
23-
24-
info(...args: unknown[]): unknown | undefined {
25-
return this.logger.info(args);
26-
}
27-
28-
warn(...args: unknown[]): unknown | undefined {
29-
return this.logger.warn(args);
30-
}
31-
}
32-
33-
export { LoggerInterface, Logger };
8+
export { LoggerInterface };

src/Type/Enum/ServiceIdentifier.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
enum ServiceIdentifier {
22
emberNexusWebSDK = 'ember-nexus.web-sdk',
3-
eventDispatcher = 'event-dispatcher',
4-
logger = 'logger',
5-
action = 'action',
6-
setting = 'setting',
7-
icon = 'icon',
3+
eventDispatcher = 'global.event-dispatcher',
4+
logger = 'global.logger',
5+
action = 'global.action-registry',
6+
setting = 'global.setting-registry',
7+
icon = 'global.icon-registry',
88
}
99

1010
export { ServiceIdentifier };

src/Type/Enum/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './EventIdentifier.js';
2+
export * from './ServiceIdentifier.js';

test/Unit/Init.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test('should have action registry registered', () => {
3535
const rootNode = document.createElement('div');
3636
const serviceResolver = init(rootNode);
3737

38-
const serviceIdentifier = validateServiceIdentifierFromString('action');
38+
const serviceIdentifier = validateServiceIdentifierFromString('global.action-registry');
3939
expect(serviceResolver.hasService(serviceIdentifier)).toBeTruthy();
4040
const actionRegistry = serviceResolver.getService(serviceIdentifier) as PriorityRegistry;
4141
expect(actionRegistry).toBeInstanceOf(PriorityRegistry);
@@ -48,7 +48,7 @@ test('should have setting registry registered', () => {
4848
const rootNode = document.createElement('div');
4949
const serviceResolver = init(rootNode);
5050

51-
const serviceIdentifier = validateServiceIdentifierFromString('setting');
51+
const serviceIdentifier = validateServiceIdentifierFromString('global.setting-registry');
5252
expect(serviceResolver.hasService(serviceIdentifier)).toBeTruthy();
5353
const settingRegistry = serviceResolver.getService(serviceIdentifier) as Registry;
5454
expect(settingRegistry).toBeInstanceOf(Registry);
@@ -61,7 +61,7 @@ test('should have icon registry registered', () => {
6161
const rootNode = document.createElement('div');
6262
const serviceResolver = init(rootNode);
6363

64-
const serviceIdentifier = validateServiceIdentifierFromString('icon');
64+
const serviceIdentifier = validateServiceIdentifierFromString('global.icon-registry');
6565
expect(serviceResolver.hasService(serviceIdentifier)).toBeTruthy();
6666
const iconRegistry = serviceResolver.getService(serviceIdentifier) as Registry;
6767
expect(iconRegistry).toBeInstanceOf(Registry);

test/Unit/Type/Definition/EventDispatcher.test.ts renamed to test/Unit/Service/EventDispatcher.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { expect, test } from 'vitest';
22

3+
import { EventDispatcher } from '../../../src/Service';
34
import {
45
Event,
5-
EventDispatcher,
66
EventInterface,
77
EventListener,
88
OptionalPromise,
99
validateEventIdentifierFromString,
1010
validateEventListenerIdentifierFromString,
11-
} from '../../../../src/Type/Definition';
12-
import { testLogger } from '../../TestLogger';
11+
} from '../../../src/Type/Definition';
12+
import { testLogger } from '../TestLogger';
1313

1414
function createAnonymousEventListener(calledValue: string, stopsEvent: boolean = false): EventListener {
1515
return (event: EventInterface): OptionalPromise<void> => {

0 commit comments

Comments
 (0)