@hkdigital/lib-sveltekit 0.1.70 → 0.1.72
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/dist/classes/cache/IndexedDbCache.d.ts +212 -0
- package/dist/classes/cache/IndexedDbCache.js +673 -0
- package/dist/classes/cache/MemoryResponseCache.d.ts +101 -14
- package/dist/classes/cache/MemoryResponseCache.js +97 -12
- package/dist/classes/cache/index.d.ts +1 -1
- package/dist/classes/cache/index.js +2 -1
- package/dist/classes/events/EventEmitter.d.ts +142 -0
- package/dist/classes/events/EventEmitter.js +275 -0
- package/dist/classes/events/index.d.ts +1 -0
- package/dist/classes/events/index.js +2 -0
- package/dist/classes/logging/Logger.d.ts +74 -0
- package/dist/classes/logging/Logger.js +158 -0
- package/dist/classes/logging/constants.d.ts +14 -0
- package/dist/classes/logging/constants.js +18 -0
- package/dist/classes/logging/index.d.ts +2 -0
- package/dist/classes/logging/index.js +4 -0
- package/dist/classes/services/ServiceBase.d.ts +153 -0
- package/dist/classes/services/ServiceBase.js +409 -0
- package/dist/classes/services/ServiceManager.d.ts +350 -0
- package/dist/classes/services/ServiceManager.js +1114 -0
- package/dist/classes/services/constants.d.ts +11 -0
- package/dist/classes/services/constants.js +12 -0
- package/dist/classes/services/index.d.ts +3 -0
- package/dist/classes/services/index.js +5 -0
- package/dist/util/env/index.d.ts +1 -0
- package/dist/util/env/index.js +9 -0
- package/dist/util/http/caching.js +24 -12
- package/dist/util/http/http-request.js +12 -7
- package/package.json +2 -1
- package/dist/classes/cache/PersistentResponseCache.d.ts +0 -46
- /package/dist/classes/cache/{PersistentResponseCache.js → PersistentResponseCache.js__} +0 -0
@@ -1,19 +1,106 @@
|
|
1
1
|
/**
|
2
|
-
*
|
3
|
-
*
|
2
|
+
* @fileoverview In-memory implementation of response cache.
|
3
|
+
*
|
4
|
+
* This implementation provides a simple in-memory cache for HTTP responses
|
5
|
+
* with the same API as IndexedDbCache. It's useful for tests and environments
|
6
|
+
* where persistent storage isn't needed or available.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* // Create a memory cache
|
10
|
+
* const cache = new MemoryResponseCache();
|
11
|
+
*
|
12
|
+
* // Store a response
|
13
|
+
* const response = await fetch('https://example.com/api/data');
|
14
|
+
* await cache.set('api-data', response, { expiresIn: 3600000 });
|
15
|
+
*
|
16
|
+
* // Retrieve a cached response
|
17
|
+
* const cached = await cache.get('api-data');
|
18
|
+
* if (cached) {
|
19
|
+
* console.log(cached.response);
|
20
|
+
* }
|
21
|
+
*/
|
22
|
+
/**
|
23
|
+
* @typedef {Object} CacheEntry
|
24
|
+
* @property {Response} response - Cached Response object
|
25
|
+
* @property {Object} metadata - Cache entry metadata
|
26
|
+
* @property {string} url - Original URL
|
27
|
+
* @property {number} timestamp - When the entry was cached
|
28
|
+
* @property {number|null} expires - Expiration timestamp (null if no expiration)
|
29
|
+
* @property {string|null} etag - ETag header if present
|
30
|
+
* @property {string|null} lastModified - Last-Modified header if present
|
31
|
+
*/
|
32
|
+
/**
|
33
|
+
* In-memory response cache implementation
|
4
34
|
*/
|
5
35
|
export default class MemoryResponseCache {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
36
|
+
/**
|
37
|
+
* Internal cache storage using Map
|
38
|
+
* @type {Map<string, Object>}
|
39
|
+
*/
|
40
|
+
cache: Map<string, any>;
|
41
|
+
/**
|
42
|
+
* Get a cached response
|
43
|
+
*
|
44
|
+
* @param {string} key - Cache key
|
45
|
+
* @returns {Promise<CacheEntry|null>} Cache entry or null if not found/expired
|
46
|
+
*/
|
47
|
+
get(key: string): Promise<CacheEntry | null>;
|
48
|
+
/**
|
49
|
+
* Store a response in the cache
|
50
|
+
*
|
51
|
+
* @param {string} key - Cache key
|
52
|
+
* @param {Response} response - Response to cache
|
53
|
+
* @param {Object} [metadata={}] - Cache metadata
|
54
|
+
* @returns {Promise<void>}
|
55
|
+
*/
|
56
|
+
set(key: string, response: Response, metadata?: any): Promise<void>;
|
57
|
+
/**
|
58
|
+
* Delete a cached entry
|
59
|
+
*
|
60
|
+
* @param {string} key - Cache key
|
61
|
+
* @returns {Promise<boolean>} True if entry was deleted
|
62
|
+
*/
|
63
|
+
delete(key: string): Promise<boolean>;
|
64
|
+
/**
|
65
|
+
* Clear all cached responses
|
66
|
+
*
|
67
|
+
* @returns {Promise<void>}
|
68
|
+
*/
|
18
69
|
clear(): Promise<void>;
|
70
|
+
/**
|
71
|
+
* Close the cache (no-op for memory cache, for API compatibility)
|
72
|
+
*
|
73
|
+
* @returns {Promise<void>}
|
74
|
+
*/
|
75
|
+
close(): Promise<void>;
|
19
76
|
}
|
77
|
+
export type CacheEntry = {
|
78
|
+
/**
|
79
|
+
* - Cached Response object
|
80
|
+
*/
|
81
|
+
response: Response;
|
82
|
+
/**
|
83
|
+
* - Cache entry metadata
|
84
|
+
*/
|
85
|
+
metadata: any;
|
86
|
+
/**
|
87
|
+
* - Original URL
|
88
|
+
*/
|
89
|
+
url: string;
|
90
|
+
/**
|
91
|
+
* - When the entry was cached
|
92
|
+
*/
|
93
|
+
timestamp: number;
|
94
|
+
/**
|
95
|
+
* - Expiration timestamp (null if no expiration)
|
96
|
+
*/
|
97
|
+
expires: number | null;
|
98
|
+
/**
|
99
|
+
* - ETag header if present
|
100
|
+
*/
|
101
|
+
etag: string | null;
|
102
|
+
/**
|
103
|
+
* - Last-Modified header if present
|
104
|
+
*/
|
105
|
+
lastModified: string | null;
|
106
|
+
};
|
@@ -1,25 +1,73 @@
|
|
1
1
|
/**
|
2
|
-
*
|
3
|
-
*
|
2
|
+
* @fileoverview In-memory implementation of response cache.
|
3
|
+
*
|
4
|
+
* This implementation provides a simple in-memory cache for HTTP responses
|
5
|
+
* with the same API as IndexedDbCache. It's useful for tests and environments
|
6
|
+
* where persistent storage isn't needed or available.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* // Create a memory cache
|
10
|
+
* const cache = new MemoryResponseCache();
|
11
|
+
*
|
12
|
+
* // Store a response
|
13
|
+
* const response = await fetch('https://example.com/api/data');
|
14
|
+
* await cache.set('api-data', response, { expiresIn: 3600000 });
|
15
|
+
*
|
16
|
+
* // Retrieve a cached response
|
17
|
+
* const cached = await cache.get('api-data');
|
18
|
+
* if (cached) {
|
19
|
+
* console.log(cached.response);
|
20
|
+
* }
|
21
|
+
*/
|
22
|
+
|
23
|
+
/**
|
24
|
+
* @typedef {Object} CacheEntry
|
25
|
+
* @property {Response} response - Cached Response object
|
26
|
+
* @property {Object} metadata - Cache entry metadata
|
27
|
+
* @property {string} url - Original URL
|
28
|
+
* @property {number} timestamp - When the entry was cached
|
29
|
+
* @property {number|null} expires - Expiration timestamp (null if no expiration)
|
30
|
+
* @property {string|null} etag - ETag header if present
|
31
|
+
* @property {string|null} lastModified - Last-Modified header if present
|
32
|
+
*/
|
33
|
+
|
34
|
+
/**
|
35
|
+
* In-memory response cache implementation
|
4
36
|
*/
|
5
37
|
export default class MemoryResponseCache {
|
38
|
+
/**
|
39
|
+
* Create a new in-memory cache
|
40
|
+
*/
|
6
41
|
constructor() {
|
42
|
+
/**
|
43
|
+
* Internal cache storage using Map
|
44
|
+
* @type {Map<string, Object>}
|
45
|
+
*/
|
7
46
|
this.cache = new Map();
|
8
47
|
}
|
9
|
-
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Get a cached response
|
51
|
+
*
|
52
|
+
* @param {string} key - Cache key
|
53
|
+
* @returns {Promise<CacheEntry|null>} Cache entry or null if not found/expired
|
54
|
+
*/
|
10
55
|
async get(key) {
|
11
56
|
const entry = this.cache.get(key);
|
12
|
-
|
57
|
+
|
13
58
|
if (!entry) {
|
14
59
|
return null;
|
15
60
|
}
|
16
|
-
|
61
|
+
|
17
62
|
// Check if expired
|
18
63
|
if (entry.expires && Date.now() > entry.expires) {
|
19
64
|
this.delete(key);
|
20
65
|
return null;
|
21
66
|
}
|
22
|
-
|
67
|
+
|
68
|
+
// Update last accessed time
|
69
|
+
entry.lastAccessed = Date.now();
|
70
|
+
|
23
71
|
return {
|
24
72
|
response: entry.response.clone(),
|
25
73
|
metadata: entry.metadata,
|
@@ -30,24 +78,61 @@ export default class MemoryResponseCache {
|
|
30
78
|
lastModified: entry.lastModified
|
31
79
|
};
|
32
80
|
}
|
33
|
-
|
34
|
-
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Store a response in the cache
|
84
|
+
*
|
85
|
+
* @param {string} key - Cache key
|
86
|
+
* @param {Response} response - Response to cache
|
87
|
+
* @param {Object} [metadata={}] - Cache metadata
|
88
|
+
* @returns {Promise<void>}
|
89
|
+
*/
|
90
|
+
async set(key, response, metadata = {}) {
|
91
|
+
const now = Date.now();
|
92
|
+
|
93
|
+
// Calculate expiration time if expiresIn is provided
|
94
|
+
let expires = metadata.expires || null;
|
95
|
+
if (!expires && metadata.expiresIn) {
|
96
|
+
expires = now + metadata.expiresIn;
|
97
|
+
}
|
98
|
+
|
35
99
|
this.cache.set(key, {
|
36
100
|
response: response.clone(),
|
37
101
|
metadata,
|
38
102
|
url: response.url,
|
39
|
-
timestamp:
|
40
|
-
|
103
|
+
timestamp: now,
|
104
|
+
lastAccessed: now,
|
105
|
+
expires,
|
41
106
|
etag: response.headers.get('ETag'),
|
42
107
|
lastModified: response.headers.get('Last-Modified')
|
43
108
|
});
|
44
109
|
}
|
45
|
-
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Delete a cached entry
|
113
|
+
*
|
114
|
+
* @param {string} key - Cache key
|
115
|
+
* @returns {Promise<boolean>} True if entry was deleted
|
116
|
+
*/
|
46
117
|
async delete(key) {
|
47
118
|
return this.cache.delete(key);
|
48
119
|
}
|
49
|
-
|
120
|
+
|
121
|
+
/**
|
122
|
+
* Clear all cached responses
|
123
|
+
*
|
124
|
+
* @returns {Promise<void>}
|
125
|
+
*/
|
50
126
|
async clear() {
|
51
127
|
this.cache.clear();
|
52
128
|
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Close the cache (no-op for memory cache, for API compatibility)
|
132
|
+
*
|
133
|
+
* @returns {Promise<void>}
|
134
|
+
*/
|
135
|
+
async close() {
|
136
|
+
// No-op for memory cache
|
137
|
+
}
|
53
138
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
export { default as MemoryResponseCache } from "./MemoryResponseCache.js";
|
3
|
-
export { default as PersistentResponseCache } from "./PersistentResponseCache.js";
|
3
|
+
// export { default as PersistentResponseCache } from "./PersistentResponseCache.js";
|
4
|
+
export { default as IndexedDbCache } from "./IndexedDbCache.js";
|
4
5
|
|
5
6
|
export * from './typedef.js';
|
@@ -0,0 +1,142 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Simple event emitter implementation to support event-based architecture
|
3
|
+
* in service management and other parts of the application.
|
4
|
+
*
|
5
|
+
* This implementation provides standard event publishing and subscription methods
|
6
|
+
* with support for namespaced events, wildcard listeners, and callback removal.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* // Basic usage
|
10
|
+
* import { EventEmitter } from './EventEmitter.js';
|
11
|
+
*
|
12
|
+
* // Create an emitter
|
13
|
+
* const events = new EventEmitter();
|
14
|
+
*
|
15
|
+
* // Subscribe to events
|
16
|
+
* const unsubscribe = events.on('data-loaded', (data) => {
|
17
|
+
* console.log('Data loaded:', data);
|
18
|
+
* });
|
19
|
+
*
|
20
|
+
* // Subscribe to all events with a specific prefix
|
21
|
+
* events.on('database:*', ({ event, data }) => {
|
22
|
+
* console.log(`Database event ${event}:`, data);
|
23
|
+
* });
|
24
|
+
*
|
25
|
+
* // Emit events
|
26
|
+
* events.emit('data-loaded', { items: [1, 2, 3] });
|
27
|
+
* events.emit('database:connected', { connectionId: 'abc123' });
|
28
|
+
*
|
29
|
+
* // Clean up when done
|
30
|
+
* unsubscribe();
|
31
|
+
*
|
32
|
+
* // Or remove all listeners
|
33
|
+
* events.removeAllListeners();
|
34
|
+
*/
|
35
|
+
/**
|
36
|
+
* EventEmitter class for event-based programming
|
37
|
+
*/
|
38
|
+
export default class EventEmitter {
|
39
|
+
/**
|
40
|
+
* Map to store event handlers
|
41
|
+
* @type {Map<string, Set<Function>>}
|
42
|
+
* @private
|
43
|
+
*/
|
44
|
+
private eventHandlers;
|
45
|
+
/**
|
46
|
+
* Map to store wildcard event handlers (events ending with *)
|
47
|
+
* @type {Map<string, Set<Function>>}
|
48
|
+
* @private
|
49
|
+
*/
|
50
|
+
private wildcardHandlers;
|
51
|
+
/**
|
52
|
+
* Register an event handler
|
53
|
+
*
|
54
|
+
* @param {string} eventName - Event name to listen for. Can use wildcard (*)
|
55
|
+
* at the end to listen to all events with a specific prefix.
|
56
|
+
* @param {Function} handler - Handler function to call when event is emitted
|
57
|
+
* @returns {Function} Function to remove this specific handler
|
58
|
+
*
|
59
|
+
* @example
|
60
|
+
* // Listen for a specific event
|
61
|
+
* emitter.on('userLoggedIn', (user) => {
|
62
|
+
* console.log(`User logged in: ${user.name}`);
|
63
|
+
* });
|
64
|
+
*
|
65
|
+
* // Listen for all events with a prefix
|
66
|
+
* emitter.on('api:*', ({ event, data }) => {
|
67
|
+
* console.log(`API event ${event}:`, data);
|
68
|
+
* });
|
69
|
+
*/
|
70
|
+
on(eventName: string, handler: Function): Function;
|
71
|
+
/**
|
72
|
+
* Register a one-time event handler that will be removed after first execution
|
73
|
+
*
|
74
|
+
* @param {string} eventName - Event name to listen for
|
75
|
+
* @param {Function} handler - Handler function to call when event is emitted
|
76
|
+
* @returns {Function} Function to remove this specific handler
|
77
|
+
*
|
78
|
+
* @example
|
79
|
+
* emitter.once('initialization', () => {
|
80
|
+
* console.log('Initialization happened');
|
81
|
+
* });
|
82
|
+
*/
|
83
|
+
once(eventName: string, handler: Function): Function;
|
84
|
+
/**
|
85
|
+
* Remove an event handler
|
86
|
+
*
|
87
|
+
* @param {string} eventName - Event name the handler was registered for
|
88
|
+
* @param {Function} handler - Handler function to remove
|
89
|
+
* @returns {boolean} True if the handler was removed, false otherwise
|
90
|
+
*
|
91
|
+
* @example
|
92
|
+
* const handler = (data) => console.log(data);
|
93
|
+
* emitter.on('data', handler);
|
94
|
+
* emitter.off('data', handler);
|
95
|
+
*/
|
96
|
+
off(eventName: string, handler: Function): boolean;
|
97
|
+
/**
|
98
|
+
* Remove all event handlers for a specific event
|
99
|
+
*
|
100
|
+
* @param {string} [eventName] - Event name to remove handlers for.
|
101
|
+
* If not provided, removes all handlers for all events.
|
102
|
+
*
|
103
|
+
* @example
|
104
|
+
* // Remove all 'data' event handlers
|
105
|
+
* emitter.removeAllListeners('data');
|
106
|
+
*
|
107
|
+
* // Remove all event handlers
|
108
|
+
* emitter.removeAllListeners();
|
109
|
+
*/
|
110
|
+
removeAllListeners(eventName?: string): void;
|
111
|
+
/**
|
112
|
+
* Emit an event
|
113
|
+
*
|
114
|
+
* @param {string} eventName - Name of the event to emit
|
115
|
+
* @param {*} data - Data to pass to event handlers
|
116
|
+
* @returns {boolean} True if there were handlers for this event, false otherwise
|
117
|
+
*
|
118
|
+
* @example
|
119
|
+
* emitter.emit('dataLoaded', { users: [...] });
|
120
|
+
*/
|
121
|
+
emit(eventName: string, data: any): boolean;
|
122
|
+
/**
|
123
|
+
* Get the number of listeners for a specific event
|
124
|
+
*
|
125
|
+
* @param {string} eventName - Event name to count listeners for
|
126
|
+
* @returns {number} Number of listeners for this event
|
127
|
+
*
|
128
|
+
* @example
|
129
|
+
* const count = emitter.listenerCount('data');
|
130
|
+
* console.log(`There are ${count} data event listeners`);
|
131
|
+
*/
|
132
|
+
listenerCount(eventName: string): number;
|
133
|
+
/**
|
134
|
+
* Get all registered event names
|
135
|
+
*
|
136
|
+
* @returns {string[]} Array of event names that have listeners
|
137
|
+
*
|
138
|
+
* @example
|
139
|
+
* console.log('Events with listeners:', emitter.eventNames());
|
140
|
+
*/
|
141
|
+
eventNames(): string[];
|
142
|
+
}
|
@@ -0,0 +1,275 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Simple event emitter implementation to support event-based architecture
|
3
|
+
* in service management and other parts of the application.
|
4
|
+
*
|
5
|
+
* This implementation provides standard event publishing and subscription methods
|
6
|
+
* with support for namespaced events, wildcard listeners, and callback removal.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* // Basic usage
|
10
|
+
* import { EventEmitter } from './EventEmitter.js';
|
11
|
+
*
|
12
|
+
* // Create an emitter
|
13
|
+
* const events = new EventEmitter();
|
14
|
+
*
|
15
|
+
* // Subscribe to events
|
16
|
+
* const unsubscribe = events.on('data-loaded', (data) => {
|
17
|
+
* console.log('Data loaded:', data);
|
18
|
+
* });
|
19
|
+
*
|
20
|
+
* // Subscribe to all events with a specific prefix
|
21
|
+
* events.on('database:*', ({ event, data }) => {
|
22
|
+
* console.log(`Database event ${event}:`, data);
|
23
|
+
* });
|
24
|
+
*
|
25
|
+
* // Emit events
|
26
|
+
* events.emit('data-loaded', { items: [1, 2, 3] });
|
27
|
+
* events.emit('database:connected', { connectionId: 'abc123' });
|
28
|
+
*
|
29
|
+
* // Clean up when done
|
30
|
+
* unsubscribe();
|
31
|
+
*
|
32
|
+
* // Or remove all listeners
|
33
|
+
* events.removeAllListeners();
|
34
|
+
*/
|
35
|
+
|
36
|
+
/**
|
37
|
+
* EventEmitter class for event-based programming
|
38
|
+
*/
|
39
|
+
export default class EventEmitter {
|
40
|
+
/**
|
41
|
+
* Create a new EventEmitter instance
|
42
|
+
*/
|
43
|
+
constructor() {
|
44
|
+
/**
|
45
|
+
* Map to store event handlers
|
46
|
+
* @type {Map<string, Set<Function>>}
|
47
|
+
* @private
|
48
|
+
*/
|
49
|
+
this.eventHandlers = new Map();
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Map to store wildcard event handlers (events ending with *)
|
53
|
+
* @type {Map<string, Set<Function>>}
|
54
|
+
* @private
|
55
|
+
*/
|
56
|
+
this.wildcardHandlers = new Map();
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Register an event handler
|
61
|
+
*
|
62
|
+
* @param {string} eventName - Event name to listen for. Can use wildcard (*)
|
63
|
+
* at the end to listen to all events with a specific prefix.
|
64
|
+
* @param {Function} handler - Handler function to call when event is emitted
|
65
|
+
* @returns {Function} Function to remove this specific handler
|
66
|
+
*
|
67
|
+
* @example
|
68
|
+
* // Listen for a specific event
|
69
|
+
* emitter.on('userLoggedIn', (user) => {
|
70
|
+
* console.log(`User logged in: ${user.name}`);
|
71
|
+
* });
|
72
|
+
*
|
73
|
+
* // Listen for all events with a prefix
|
74
|
+
* emitter.on('api:*', ({ event, data }) => {
|
75
|
+
* console.log(`API event ${event}:`, data);
|
76
|
+
* });
|
77
|
+
*/
|
78
|
+
on(eventName, handler) {
|
79
|
+
if (typeof handler !== 'function') {
|
80
|
+
throw new TypeError('Event handler must be a function');
|
81
|
+
}
|
82
|
+
|
83
|
+
// Handle wildcard listeners
|
84
|
+
if (eventName.endsWith('*')) {
|
85
|
+
const prefix = eventName.slice(0, -1);
|
86
|
+
|
87
|
+
if (!this.wildcardHandlers.has(prefix)) {
|
88
|
+
this.wildcardHandlers.set(prefix, new Set());
|
89
|
+
}
|
90
|
+
|
91
|
+
this.wildcardHandlers.get(prefix).add(handler);
|
92
|
+
|
93
|
+
return () => this.off(eventName, handler);
|
94
|
+
}
|
95
|
+
|
96
|
+
// Handle normal listeners
|
97
|
+
if (!this.eventHandlers.has(eventName)) {
|
98
|
+
this.eventHandlers.set(eventName, new Set());
|
99
|
+
}
|
100
|
+
|
101
|
+
this.eventHandlers.get(eventName).add(handler);
|
102
|
+
|
103
|
+
return () => this.off(eventName, handler);
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Register a one-time event handler that will be removed after first execution
|
108
|
+
*
|
109
|
+
* @param {string} eventName - Event name to listen for
|
110
|
+
* @param {Function} handler - Handler function to call when event is emitted
|
111
|
+
* @returns {Function} Function to remove this specific handler
|
112
|
+
*
|
113
|
+
* @example
|
114
|
+
* emitter.once('initialization', () => {
|
115
|
+
* console.log('Initialization happened');
|
116
|
+
* });
|
117
|
+
*/
|
118
|
+
once(eventName, handler) {
|
119
|
+
if (typeof handler !== 'function') {
|
120
|
+
throw new TypeError('Event handler must be a function');
|
121
|
+
}
|
122
|
+
|
123
|
+
const wrapper = (...args) => {
|
124
|
+
this.off(eventName, wrapper);
|
125
|
+
handler(...args);
|
126
|
+
};
|
127
|
+
|
128
|
+
return this.on(eventName, wrapper);
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* Remove an event handler
|
133
|
+
*
|
134
|
+
* @param {string} eventName - Event name the handler was registered for
|
135
|
+
* @param {Function} handler - Handler function to remove
|
136
|
+
* @returns {boolean} True if the handler was removed, false otherwise
|
137
|
+
*
|
138
|
+
* @example
|
139
|
+
* const handler = (data) => console.log(data);
|
140
|
+
* emitter.on('data', handler);
|
141
|
+
* emitter.off('data', handler);
|
142
|
+
*/
|
143
|
+
off(eventName, handler) {
|
144
|
+
// Handle wildcard listeners
|
145
|
+
if (eventName.endsWith('*')) {
|
146
|
+
const prefix = eventName.slice(0, -1);
|
147
|
+
const handlers = this.wildcardHandlers.get(prefix);
|
148
|
+
|
149
|
+
if (handlers) {
|
150
|
+
return handlers.delete(handler);
|
151
|
+
}
|
152
|
+
|
153
|
+
return false;
|
154
|
+
}
|
155
|
+
|
156
|
+
// Handle normal listeners
|
157
|
+
const handlers = this.eventHandlers.get(eventName);
|
158
|
+
|
159
|
+
if (handlers) {
|
160
|
+
return handlers.delete(handler);
|
161
|
+
}
|
162
|
+
|
163
|
+
return false;
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Remove all event handlers for a specific event
|
168
|
+
*
|
169
|
+
* @param {string} [eventName] - Event name to remove handlers for.
|
170
|
+
* If not provided, removes all handlers for all events.
|
171
|
+
*
|
172
|
+
* @example
|
173
|
+
* // Remove all 'data' event handlers
|
174
|
+
* emitter.removeAllListeners('data');
|
175
|
+
*
|
176
|
+
* // Remove all event handlers
|
177
|
+
* emitter.removeAllListeners();
|
178
|
+
*/
|
179
|
+
removeAllListeners(eventName) {
|
180
|
+
if (eventName) {
|
181
|
+
// Handle wildcard listeners
|
182
|
+
if (eventName.endsWith('*')) {
|
183
|
+
const prefix = eventName.slice(0, -1);
|
184
|
+
this.wildcardHandlers.delete(prefix);
|
185
|
+
} else {
|
186
|
+
this.eventHandlers.delete(eventName);
|
187
|
+
}
|
188
|
+
} else {
|
189
|
+
// Clear all handlers
|
190
|
+
this.eventHandlers.clear();
|
191
|
+
this.wildcardHandlers.clear();
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
/**
|
196
|
+
* Emit an event
|
197
|
+
*
|
198
|
+
* @param {string} eventName - Name of the event to emit
|
199
|
+
* @param {*} data - Data to pass to event handlers
|
200
|
+
* @returns {boolean} True if there were handlers for this event, false otherwise
|
201
|
+
*
|
202
|
+
* @example
|
203
|
+
* emitter.emit('dataLoaded', { users: [...] });
|
204
|
+
*/
|
205
|
+
emit(eventName, data) {
|
206
|
+
let handled = false;
|
207
|
+
|
208
|
+
// Call specific event handlers
|
209
|
+
const handlers = this.eventHandlers.get(eventName);
|
210
|
+
if (handlers && handlers.size > 0) {
|
211
|
+
handlers.forEach(handler => handler(data));
|
212
|
+
handled = true;
|
213
|
+
}
|
214
|
+
|
215
|
+
// Call matching wildcard handlers
|
216
|
+
this.wildcardHandlers.forEach((handlers, prefix) => {
|
217
|
+
if (eventName.startsWith(prefix)) {
|
218
|
+
handlers.forEach(handler =>
|
219
|
+
handler({ event: eventName, data })
|
220
|
+
);
|
221
|
+
handled = true;
|
222
|
+
}
|
223
|
+
});
|
224
|
+
|
225
|
+
return handled;
|
226
|
+
}
|
227
|
+
|
228
|
+
/**
|
229
|
+
* Get the number of listeners for a specific event
|
230
|
+
*
|
231
|
+
* @param {string} eventName - Event name to count listeners for
|
232
|
+
* @returns {number} Number of listeners for this event
|
233
|
+
*
|
234
|
+
* @example
|
235
|
+
* const count = emitter.listenerCount('data');
|
236
|
+
* console.log(`There are ${count} data event listeners`);
|
237
|
+
*/
|
238
|
+
listenerCount(eventName) {
|
239
|
+
let count = 0;
|
240
|
+
|
241
|
+
// Count specific event handlers
|
242
|
+
const handlers = this.eventHandlers.get(eventName);
|
243
|
+
if (handlers) {
|
244
|
+
count += handlers.size;
|
245
|
+
}
|
246
|
+
|
247
|
+
// Count matching wildcard handlers
|
248
|
+
this.wildcardHandlers.forEach((handlers, prefix) => {
|
249
|
+
if (eventName.startsWith(prefix)) {
|
250
|
+
count += handlers.size;
|
251
|
+
}
|
252
|
+
});
|
253
|
+
|
254
|
+
return count;
|
255
|
+
}
|
256
|
+
|
257
|
+
/**
|
258
|
+
* Get all registered event names
|
259
|
+
*
|
260
|
+
* @returns {string[]} Array of event names that have listeners
|
261
|
+
*
|
262
|
+
* @example
|
263
|
+
* console.log('Events with listeners:', emitter.eventNames());
|
264
|
+
*/
|
265
|
+
eventNames() {
|
266
|
+
const events = [...this.eventHandlers.keys()];
|
267
|
+
|
268
|
+
// Add wildcard events
|
269
|
+
this.wildcardHandlers.forEach((_, prefix) => {
|
270
|
+
events.push(`${prefix}*`);
|
271
|
+
});
|
272
|
+
|
273
|
+
return events;
|
274
|
+
}
|
275
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as EventEmitter } from "./EventEmitter.js";
|