detached 5367de1

refactor: Replace EventEmitter with custom SSE event emitter implementation

- Create a type-safe CustomEventEmitter class for SSE events
- Implement custom event handling with type-specific data
- Add listener management with max listener tracking
- Preserve existing logging and initialization behavior
- Improve type safety and control over event emissions
commit
5367de1ec0b6fff42fcab70d5e0299bb4c600f55
author
Kit · · 2025-02-14 08:05 UTC
parent
1f4430f
signature
SSH UnverifiedSHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6Tk The key is unknown
1 file · +48 -4
M src/lib/sseEmitter.ts +48 -4
@@ -1,5 +1,49 @@
1-import { EventEmitter } from 'events';
1+type SSEEventData = {
2+ type: string;
3+ data: unknown;
4+};
25
3-console.debug('Initializing shared SSE emitter');
4-export const sseEmitter = new EventEmitter();
5-sseEmitter.setMaxListeners(100); ⏎̸
6+class CustomEventEmitter {
7+ private listeners: Map<string, Array<(data: SSEEventData) => void>> = new Map();
8+ private maxListeners: number = 100;
9+
10+ constructor() {
11+ console.debug('Initializing custom SSE emitter');
12+ }
13+
14+ emit(event: string, data: SSEEventData) {
15+ const eventListeners = this.listeners.get(event);
16+ if (eventListeners) {
17+ eventListeners.forEach(listener => listener(data));
18+ }
19+ }
20+
21+ addListener(event: string, listener: (data: SSEEventData) => void) {
22+ if (!this.listeners.has(event)) {
23+ this.listeners.set(event, []);
24+ }
25+ const eventListeners = this.listeners.get(event)!;
26+
27+ if (eventListeners.length >= this.maxListeners) {
28+ console.warn(`Warning: Event '${event}' has exceeded the maximum number of listeners (${this.maxListeners})`);
29+ }
30+
31+ eventListeners.push(listener);
32+ }
33+
34+ removeListener(event: string, listenerToRemove: (data: SSEEventData) => void) {
35+ const eventListeners = this.listeners.get(event);
36+ if (eventListeners) {
37+ this.listeners.set(
38+ event,
39+ eventListeners.filter(listener => listener !== listenerToRemove)
40+ );
41+ }
42+ }
43+
44+ setMaxListeners(n: number) {
45+ this.maxListeners = n;
46+ }
47+}
48+
49+export const sseEmitter = new CustomEventEmitter(); ⏎̸
@@ -1,5 +1,49 @@
1-import { EventEmitter } from 'events';1+type SSEEventData = {
2+ type: string;
3+ data: unknown;
4+};
2 5
3-console.debug('Initializing shared SSE emitter');6+class CustomEventEmitter {
4-export const sseEmitter = new EventEmitter();7+ private listeners: Map<string, Array<(data: SSEEventData) => void>> = new Map();
5-sseEmitter.setMaxListeners(100); ⏎̸8+ private maxListeners: number = 100;
9+
10+ constructor() {
11+ console.debug('Initializing custom SSE emitter');
12+ }
13+
14+ emit(event: string, data: SSEEventData) {
15+ const eventListeners = this.listeners.get(event);
16+ if (eventListeners) {
17+ eventListeners.forEach(listener => listener(data));
18+ }
19+ }
20+
21+ addListener(event: string, listener: (data: SSEEventData) => void) {
22+ if (!this.listeners.has(event)) {
23+ this.listeners.set(event, []);
24+ }
25+ const eventListeners = this.listeners.get(event)!;
26+
27+ if (eventListeners.length >= this.maxListeners) {
28+ console.warn(`Warning: Event '${event}' has exceeded the maximum number of listeners (${this.maxListeners})`);
29+ }
30+
31+ eventListeners.push(listener);
32+ }
33+
34+ removeListener(event: string, listenerToRemove: (data: SSEEventData) => void) {
35+ const eventListeners = this.listeners.get(event);
36+ if (eventListeners) {
37+ this.listeners.set(
38+ event,
39+ eventListeners.filter(listener => listener !== listenerToRemove)
40+ );
41+ }
42+ }
43+
44+ setMaxListeners(n: number) {
45+ this.maxListeners = n;
46+ }
47+}
48+
49+export const sseEmitter = new CustomEventEmitter(); ⏎̸

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close