@dafengzhen/event-bus 0.1.5 → 0.1.7

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.
@@ -0,0 +1,178 @@
1
+ import type { AnyListener, EventMap, Listener, Middleware, PatternMiddleware, PatternOptions } from './types.ts';
2
+ /**
3
+ * A strongly-typed EventBus supporting:
4
+ *
5
+ * - Exact event listeners (`on`, `once`)
6
+ * - Global listeners (`onAny`)
7
+ * - Pattern listeners (`onPattern`, `oncePattern`)
8
+ * - Global middleware & pattern middleware
9
+ *
10
+ * Pattern syntax (separator defaults to `:`):
11
+ * - `*` → single-segment wildcard
12
+ * - `**` → deep wildcard (matches 0..n segments)
13
+ * - `{name}` → named parameter segment
14
+ * - `?` → single-character wildcard within a segment (regex-like)
15
+ *
16
+ * Error handling:
17
+ * - Listener errors are rethrown asynchronously via `queueMicrotask`,
18
+ * so they don't break the current emit loop.
19
+ *
20
+ * Emit modes:
21
+ * - `emit()` : fire-and-forget (async errors rethrown)
22
+ * - `emitAsync()`: await completion of middleware + listeners
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * type Events = {
27
+ * 'user:login': { id: string };
28
+ * 'user:logout': void;
29
+ * };
30
+ *
31
+ * const bus = new EventBus<Events>();
32
+ *
33
+ * bus.on('user:login', (p) => console.log(p.id));
34
+ * bus.onPattern('user:{action}', (evt, payload, params) => {
35
+ * console.log(evt, params?.action);
36
+ * });
37
+ *
38
+ * bus.emit('user:login', { id: '42' });
39
+ * ```
40
+ *
41
+ * @author dafengzhen
42
+ */
43
+ export declare class EventBus<E extends EventMap> {
44
+ /** Listeners that receive all events */
45
+ private anyListeners;
46
+ /** Exact listeners mapped by event name */
47
+ private listenersByEvent;
48
+ /** Global middleware chain (runs on every emit) */
49
+ private middlewares;
50
+ /**
51
+ * Pattern-specific middleware chain.
52
+ * Only runs when there is at least one matched pattern listener for the emitted event.
53
+ */
54
+ private patternMiddlewares;
55
+ /** Compiled pattern listeners, sorted by priority (descending) */
56
+ private patternListeners;
57
+ /** Cache for compiled patterns (pattern + separator) */
58
+ private patternCache;
59
+ /**
60
+ * Clear listeners and middleware.
61
+ * - If `event` is omitted, clears everything (exact/any/pattern listeners + middleware + cache).
62
+ * - If `event` is provided, only clears exact listeners of that event.
63
+ */
64
+ clear(event?: keyof E): void;
65
+ /**
66
+ * Register an exact listener for a specific event.
67
+ * @returns Unsubscribe function
68
+ */
69
+ on<K extends keyof E>(event: K, listener: Listener<E[K]>): () => void;
70
+ /**
71
+ * Remove an exact event listener (no-op if missing).
72
+ */
73
+ off<K extends keyof E>(event: K, listener: Listener<E[K]>): void;
74
+ /**
75
+ * Register an exact listener that runs only once.
76
+ * @returns Unsubscribe function (still works before the first run)
77
+ */
78
+ once<K extends keyof E>(event: K, listener: Listener<E[K]>): () => void;
79
+ /**
80
+ * Register a listener that receives all events.
81
+ * @returns Unsubscribe function
82
+ */
83
+ onAny(listener: AnyListener<E>): () => void;
84
+ /**
85
+ * Remove a global (any) listener (no-op if missing).
86
+ */
87
+ offAny(listener: AnyListener<E>): void;
88
+ /**
89
+ * Register a global (any) listener that runs only once.
90
+ * @returns Unsubscribe function (still works before the first run)
91
+ */
92
+ onceAny(listener: AnyListener<E>): () => void;
93
+ /**
94
+ * Register a global middleware (runs on every emit).
95
+ *
96
+ * Middleware must call `next()` exactly once to continue.
97
+ * If it never calls `next()`, dispatch stops.
98
+ *
99
+ * @returns Unsubscribe function
100
+ */
101
+ use(mw: Middleware<E>): () => void;
102
+ /**
103
+ * Register a pattern-specific middleware.
104
+ *
105
+ * Only invoked when there is at least one matched pattern listener.
106
+ *
107
+ * Gate semantics:
108
+ * - If a pattern middleware does not call `next()`, dispatch stops (acts like a guard).
109
+ * - `next()` must be called exactly once.
110
+ *
111
+ * @returns Unsubscribe function
112
+ */
113
+ usePattern(mw: PatternMiddleware<E>): () => void;
114
+ /**
115
+ * Register a pattern listener.
116
+ *
117
+ * Supported syntax (separator defaults to `:`):
118
+ * - `*` → single-segment wildcard
119
+ * - `**` → deep wildcard (matches 0..n segments)
120
+ * - `{param}`→ named parameter segment
121
+ * - `?` → single-character wildcard inside a segment
122
+ *
123
+ * Priority:
124
+ * - If `options.priority` is provided, it overrides auto-derived priority.
125
+ * - Higher priority runs earlier.
126
+ *
127
+ * @param pattern Pattern string
128
+ * @param handler Callback invoked when pattern matches
129
+ * @param options Pattern options
130
+ * @returns Unsubscribe function
131
+ */
132
+ onPattern(pattern: string, handler: (event: keyof E, payload: E[keyof E], params?: Record<string, string>) => void, options?: PatternOptions): () => void;
133
+ /**
134
+ * Register a one-time pattern listener.
135
+ * @returns Unsubscribe function
136
+ */
137
+ oncePattern(pattern: string, handler: (event: keyof E, payload: E[keyof E], params?: Record<string, string>) => void, options?: Omit<PatternOptions, 'once'>): () => void;
138
+ /**
139
+ * Count listeners matching the event.
140
+ *
141
+ * Includes:
142
+ * - Exact listeners for `event`
143
+ * - Global `onAny` listeners
144
+ * - Pattern listeners that match (only when `event` is a string)
145
+ */
146
+ listenerCount(event: keyof E): number;
147
+ /**
148
+ * Emit an event synchronously (fire-and-forget).
149
+ *
150
+ * - Execution is async internally, but this method does not await.
151
+ * - Listener errors are rethrown asynchronously.
152
+ */
153
+ emit<K extends keyof E>(event: K, ...args: E[K] extends void ? [] : [payload: E[K]]): void;
154
+ /**
155
+ * Emit an event asynchronously.
156
+ * Resolves when middleware + listeners have finished.
157
+ */
158
+ emitAsync<K extends keyof E>(event: K, ...args: E[K] extends void ? [] : [payload: E[K]]): Promise<void>;
159
+ private _emit;
160
+ private runMiddlewares;
161
+ private invokeUnifiedDispatch;
162
+ private matchPatternListeners;
163
+ private invokeExactAndAnyListeners;
164
+ private getListenerSet;
165
+ private insertPatternByPriority;
166
+ private removeFromArray;
167
+ private safeCall;
168
+ private rethrowAsync;
169
+ /**
170
+ * Compile a pattern into a matcher.
171
+ *
172
+ * Notes:
173
+ * - `**` as a segment means "match 0..n segments" (deep wildcard).
174
+ * - `*` as a segment means "match exactly 1 segment".
175
+ * - If the whole pattern is exactly `"**"`, it matches any event (including empty params).
176
+ */
177
+ private compilePattern;
178
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,440 +1,2 @@
1
- /**
2
- * Event map definition.
3
- *
4
- * A mapping from **event name** to **payload type**.
5
- *
6
- * - **Key**: event name (string literal recommended)
7
- * - **Value**: payload type for that event
8
- *
9
- * Notes:
10
- * - Event names are typically namespaced strings, e.g. `user:login`, `order:created`.
11
- * - Payload can be `void` for events without payload.
12
- *
13
- * @example
14
- * ```ts
15
- * type MyEvents = {
16
- * 'user:login': { id: string };
17
- * 'user:logout': void;
18
- * };
19
- * ```
20
- */
21
- export type EventMap = Record<string, unknown>;
22
- /**
23
- * Listener type for a specific payload.
24
- *
25
- * If the payload is `void`, the listener receives **no arguments**.
26
- * Otherwise, it receives **exactly one argument**: the payload.
27
- *
28
- * This enables strict typing for both forms:
29
- *
30
- * @example
31
- * ```ts
32
- * on('ready', () => {});
33
- * on('data', (payload) => {
34
- * console.log(payload);
35
- * });
36
- * ```
37
- */
38
- export type Listener<Payload> = Payload extends void ? () => void : (payload: Payload) => void;
39
- /**
40
- * Listener that receives **all emitted events**.
41
- *
42
- * It receives:
43
- * - `event`: the event name
44
- * - `payload`: the payload associated with that event
45
- *
46
- * Typical use cases:
47
- * - logging / debugging
48
- * - analytics / tracing
49
- * - building devtools
50
- *
51
- * @example
52
- * ```ts
53
- * const off = bus.onAny((event, payload) => {
54
- * console.log('event=', event, 'payload=', payload);
55
- * });
56
- * off();
57
- * ```
58
- */
59
- export type AnyListener<E extends EventMap> = <K extends keyof E>(event: K, payload: E[K]) => void;
60
- /**
61
- * Pattern matching strategy used by a pattern listener.
62
- *
63
- * - `exact` → exact segment match (no wildcards / params)
64
- * - `wildcard` → contains wildcards (e.g. `*`, `**`, `?`)
65
- * - `param` → contains named params (e.g. `{id}`)
66
- *
67
- * Notes:
68
- * - These are informational categories; the actual matching is implemented by the EventBus.
69
- */
70
- export type PatternKind = 'exact' | 'wildcard' | 'param';
71
- /**
72
- * Context object passed through the emit lifecycle.
73
- *
74
- * This context is shared across:
75
- * - global middleware
76
- * - pattern middleware
77
- * - dispatch logic
78
- *
79
- * It allows middleware to:
80
- * - observe the event and payload
81
- * - attach metadata (`meta`)
82
- * - stop propagation (`block()`)
83
- */
84
- export type EmitContext<E extends EventMap, K extends keyof E> = {
85
- /**
86
- * Whether propagation has been blocked.
87
- *
88
- * Once `true`, dispatch stops and no further middleware/listeners should run.
89
- */
90
- readonly blocked: boolean;
91
- /**
92
- * Current emitted event name.
93
- */
94
- readonly event: K;
95
- /**
96
- * Payload associated with the event.
97
- *
98
- * If the event's payload type is `void`, this value is typically `undefined`
99
- * (depending on the emitter implementation).
100
- */
101
- readonly payload: E[K];
102
- /**
103
- * Information about matched pattern listeners for this emit cycle.
104
- *
105
- * - Ordered by priority (highest first).
106
- * - Empty if no pattern listeners matched (or pattern matching not applicable).
107
- *
108
- * Useful for middleware that wants to inspect or audit matches.
109
- */
110
- readonly matched: readonly PatternListenerInfo<E>[];
111
- /**
112
- * Free-form metadata container shared across middleware.
113
- *
114
- * Use this to pass state between middleware, e.g.:
115
- * - correlation IDs
116
- * - timing info
117
- * - auth decisions
118
- *
119
- * Note: Consumers should avoid putting large objects here in hot paths.
120
- */
121
- readonly meta: Record<string, unknown>;
122
- /**
123
- * Stop further propagation of this event.
124
- *
125
- * Once called:
126
- * - remaining middleware is skipped
127
- * - remaining listeners are not executed
128
- */
129
- block(): void;
130
- };
131
- /**
132
- * Middleware executed for **every emit call**.
133
- *
134
- * Middleware can:
135
- * - inspect event/payload/matches
136
- * - read/write `ctx.meta`
137
- * - block propagation via `ctx.block()`
138
- * - perform async work
139
- *
140
- * Chain semantics:
141
- * - Middlewares are executed sequentially in registration order.
142
- * - `next()` must be called exactly once to continue the chain (depending on implementation).
143
- */
144
- export type Middleware<E extends EventMap> = <K extends keyof E>(ctx: EmitContext<E, K>, next: () => Promise<void>) => Promise<void> | void;
145
- /**
146
- * Middleware executed **only when pattern listeners are involved**.
147
- *
148
- * Typical uses:
149
- * - logging pattern matches & params
150
- * - permission checks / gating
151
- * - validating extracted params
152
- *
153
- * Chain semantics depend on the EventBus implementation.
154
- * In your EventBus, pattern middleware acts as a guard:
155
- * - if it does not call `next()`, dispatch stops
156
- * - `next()` must not be called more than once
157
- */
158
- export type PatternMiddleware<E extends EventMap> = (ctx: EmitContext<E, keyof E>, next: () => Promise<void>) => Promise<void> | void;
159
- /**
160
- * Public information of a matched pattern listener.
161
- *
162
- * Exposed to middleware and user-land code through `ctx.matched`.
163
- */
164
- export interface PatternListenerInfo<E extends EventMap> {
165
- /**
166
- * Original pattern string.
167
- *
168
- * @example
169
- * ```ts
170
- * 'user:{id}'
171
- * 'order:*'
172
- * '**'
173
- * ```
174
- */
175
- readonly pattern: string;
176
- /**
177
- * Matching strategy classification.
178
- */
179
- readonly kind: PatternKind;
180
- /**
181
- * Listener priority. Higher values run earlier.
182
- */
183
- readonly priority: number;
184
- /**
185
- * Whether this listener was registered with `once`.
186
- */
187
- readonly once?: boolean;
188
- /**
189
- * Extracted params from the event name.
190
- *
191
- * Example (separator `:`):
192
- * - pattern: `user:{id}`
193
- * - event: `user:42`
194
- * - params: `{ id: '42' }`
195
- */
196
- readonly params: Readonly<Record<string, string>>;
197
- /**
198
- * The actual handler function.
199
- *
200
- * Note:
201
- * - Kept here mainly for introspection/debugging.
202
- * - Middleware should generally not call handlers directly.
203
- */
204
- readonly handler: (event: keyof E, payload: E[keyof E], params?: Record<string, string>) => void;
205
- }
206
- /**
207
- * Internal compiled representation of a pattern listener.
208
- *
209
- * Not exposed publicly.
210
- * Contains precompiled matching logic for performance.
211
- */
212
- export interface CompiledPatternListener<E extends EventMap> {
213
- /**
214
- * Original pattern string (uncompiled).
215
- */
216
- pattern: string;
217
- /**
218
- * Matching strategy classification.
219
- */
220
- kind: PatternKind;
221
- /**
222
- * Listener priority. Higher values run earlier.
223
- */
224
- priority: number;
225
- /**
226
- * Whether the listener should auto-remove after first run.
227
- */
228
- once?: boolean;
229
- /**
230
- * Match function produced by the compiler.
231
- *
232
- * @param event - Emitted event name
233
- * @returns
234
- * - `null` if no match
235
- * - params record if matched (possibly empty)
236
- */
237
- match(event: string): null | Record<string, string>;
238
- /**
239
- * Listener handler.
240
- *
241
- * @param event - Emitted event name
242
- * @param payload - Payload associated with event
243
- * @param params - Extracted params from pattern (if any)
244
- */
245
- handler(event: keyof E, payload: E[keyof E], params?: Record<string, string>): void;
246
- }
247
- /**
248
- * Options for registering a pattern listener.
249
- */
250
- export type PatternOptions = {
251
- /**
252
- * Invoke the listener only once.
253
- *
254
- * After the first successful match, it is automatically removed.
255
- */
256
- once?: boolean;
257
- /**
258
- * Listener priority.
259
- *
260
- * Higher values run earlier.
261
- * If omitted, EventBus will derive a priority from pattern specificity.
262
- */
263
- priority?: number;
264
- /**
265
- * Segment separator used when splitting patterns and events.
266
- *
267
- * IMPORTANT:
268
- * - This default should match your EventBus implementation.
269
- * - In the current EventBus code you posted, the default is `':'`.
270
- */
271
- separator?: string;
272
- };
273
- /**
274
- * Internal compiled segment representation produced by the pattern compiler.
275
- *
276
- * - `exact` → exact segment match
277
- * - `wildcard` → `*` matches exactly one segment
278
- * - `deepWildcard` → `**` matches 0..n segments (backtracking)
279
- * - `param` → `{key}` captures a segment into params
280
- * - `regex` → per-segment regex (used for `?` patterns)
281
- */
282
- export type CompiledSeg = {
283
- type: 'exact';
284
- value: string;
285
- } | {
286
- type: 'wildcard';
287
- } | {
288
- type: 'deepWildcard';
289
- } | {
290
- type: 'param';
291
- key: string;
292
- } | {
293
- type: 'regex';
294
- re: RegExp;
295
- };
296
- /**
297
- * A strongly-typed EventBus supporting:
298
- *
299
- * - Exact event listeners (`on`, `once`)
300
- * - Global listeners (`onAny`)
301
- * - Pattern listeners (`onPattern`, `oncePattern`)
302
- * - Global middleware & pattern middleware
303
- *
304
- * Pattern syntax (separator defaults to `:`):
305
- * - `*` → single-segment wildcard
306
- * - `**` → deep wildcard (matches 0..n segments)
307
- * - `{name}` → named parameter segment
308
- * - `?` → single-character wildcard within a segment (regex-like)
309
- *
310
- * Error handling:
311
- * - Listener errors are rethrown asynchronously via `queueMicrotask`,
312
- * so they don't break the current emit loop.
313
- *
314
- * Emit modes:
315
- * - `emit()` : fire-and-forget (async errors rethrown)
316
- * - `emitAsync()`: await completion of middleware + listeners
317
- *
318
- * @example
319
- * ```ts
320
- * type Events = {
321
- * 'user:login': { id: string };
322
- * 'user:logout': void;
323
- * };
324
- *
325
- * const bus = new EventBus<Events>();
326
- *
327
- * bus.on('user:login', (p) => console.log(p.id));
328
- * bus.onPattern('user:{action}', (evt, payload, params) => {
329
- * console.log(evt, params?.action);
330
- * });
331
- *
332
- * bus.emit('user:login', { id: '42' });
333
- * ```
334
- *
335
- * @author dafengzhen
336
- */
337
- export class EventBus<E extends EventMap> {
338
- /**
339
- * Clear listeners and middleware.
340
- * - If `event` is omitted, clears everything (exact/any/pattern listeners + middleware + cache).
341
- * - If `event` is provided, only clears exact listeners of that event.
342
- */
343
- clear(event?: keyof E): void;
344
- /**
345
- * Register an exact listener for a specific event.
346
- * @returns Unsubscribe function
347
- */
348
- on<K extends keyof E>(event: K, listener: Listener<E[K]>): () => void;
349
- /**
350
- * Remove an exact event listener (no-op if missing).
351
- */
352
- off<K extends keyof E>(event: K, listener: Listener<E[K]>): void;
353
- /**
354
- * Register an exact listener that runs only once.
355
- * @returns Unsubscribe function (still works before the first run)
356
- */
357
- once<K extends keyof E>(event: K, listener: Listener<E[K]>): () => void;
358
- /**
359
- * Register a listener that receives all events.
360
- * @returns Unsubscribe function
361
- */
362
- onAny(listener: AnyListener<E>): () => void;
363
- /**
364
- * Remove a global (any) listener (no-op if missing).
365
- */
366
- offAny(listener: AnyListener<E>): void;
367
- /**
368
- * Register a global (any) listener that runs only once.
369
- * @returns Unsubscribe function (still works before the first run)
370
- */
371
- onceAny(listener: AnyListener<E>): () => void;
372
- /**
373
- * Register a global middleware (runs on every emit).
374
- *
375
- * Middleware must call `next()` exactly once to continue.
376
- * If it never calls `next()`, dispatch stops.
377
- *
378
- * @returns Unsubscribe function
379
- */
380
- use(mw: Middleware<E>): () => void;
381
- /**
382
- * Register a pattern-specific middleware.
383
- *
384
- * Only invoked when there is at least one matched pattern listener.
385
- *
386
- * Gate semantics:
387
- * - If a pattern middleware does not call `next()`, dispatch stops (acts like a guard).
388
- * - `next()` must be called exactly once.
389
- *
390
- * @returns Unsubscribe function
391
- */
392
- usePattern(mw: PatternMiddleware<E>): () => void;
393
- /**
394
- * Register a pattern listener.
395
- *
396
- * Supported syntax (separator defaults to `:`):
397
- * - `*` → single-segment wildcard
398
- * - `**` → deep wildcard (matches 0..n segments)
399
- * - `{param}`→ named parameter segment
400
- * - `?` → single-character wildcard inside a segment
401
- *
402
- * Priority:
403
- * - If `options.priority` is provided, it overrides auto-derived priority.
404
- * - Higher priority runs earlier.
405
- *
406
- * @param pattern Pattern string
407
- * @param handler Callback invoked when pattern matches
408
- * @param options Pattern options
409
- * @returns Unsubscribe function
410
- */
411
- onPattern(pattern: string, handler: (event: keyof E, payload: E[keyof E], params?: Record<string, string>) => void, options?: PatternOptions): () => void;
412
- /**
413
- * Register a one-time pattern listener.
414
- * @returns Unsubscribe function
415
- */
416
- oncePattern(pattern: string, handler: (event: keyof E, payload: E[keyof E], params?: Record<string, string>) => void, options?: Omit<PatternOptions, 'once'>): () => void;
417
- /**
418
- * Count listeners matching the event.
419
- *
420
- * Includes:
421
- * - Exact listeners for `event`
422
- * - Global `onAny` listeners
423
- * - Pattern listeners that match (only when `event` is a string)
424
- */
425
- listenerCount(event: keyof E): number;
426
- /**
427
- * Emit an event synchronously (fire-and-forget).
428
- *
429
- * - Execution is async internally, but this method does not await.
430
- * - Listener errors are rethrown asynchronously.
431
- */
432
- emit<K extends keyof E>(event: K, ...args: E[K] extends void ? [] : [payload: E[K]]): void;
433
- /**
434
- * Emit an event asynchronously.
435
- * Resolves when middleware + listeners have finished.
436
- */
437
- emitAsync<K extends keyof E>(event: K, ...args: E[K] extends void ? [] : [payload: E[K]]): Promise<void>;
438
- }
439
-
440
- //# sourceMappingURL=index.d.ts.map
1
+ export type * from './types.ts';
2
+ export * from './event-bus.ts';
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Event map definition.
3
+ *
4
+ * A mapping from **event name** to **payload type**.
5
+ *
6
+ * - **Key**: event name (string literal recommended)
7
+ * - **Value**: payload type for that event
8
+ *
9
+ * Notes:
10
+ * - Event names are typically namespaced strings, e.g. `user:login`, `order:created`.
11
+ * - Payload can be `void` for events without payload.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * type MyEvents = {
16
+ * 'user:login': { id: string };
17
+ * 'user:logout': void;
18
+ * };
19
+ * ```
20
+ */
21
+ export type EventMap = Record<string, any>;
22
+ /**
23
+ * Listener type for a specific payload.
24
+ *
25
+ * If the payload is `void`, the listener receives **no arguments**.
26
+ * Otherwise, it receives **exactly one argument**: the payload.
27
+ *
28
+ * This enables strict typing for both forms:
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * on('ready', () => {});
33
+ * on('data', (payload) => {
34
+ * console.log(payload);
35
+ * });
36
+ * ```
37
+ */
38
+ export type Listener<Payload> = Payload extends void ? () => void : (payload: Payload) => void;
39
+ /**
40
+ * Listener that receives **all emitted events**.
41
+ *
42
+ * It receives:
43
+ * - `event`: the event name
44
+ * - `payload`: the payload associated with that event
45
+ *
46
+ * Typical use cases:
47
+ * - logging / debugging
48
+ * - analytics / tracing
49
+ * - building devtools
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const off = bus.onAny((event, payload) => {
54
+ * console.log('event=', event, 'payload=', payload);
55
+ * });
56
+ * off();
57
+ * ```
58
+ */
59
+ export type AnyListener<E extends EventMap> = <K extends keyof E>(event: K, payload: E[K]) => void;
60
+ /**
61
+ * Pattern matching strategy used by a pattern listener.
62
+ *
63
+ * - `exact` → exact segment match (no wildcards / params)
64
+ * - `wildcard` → contains wildcards (e.g. `*`, `**`, `?`)
65
+ * - `param` → contains named params (e.g. `{id}`)
66
+ *
67
+ * Notes:
68
+ * - These are informational categories; the actual matching is implemented by the EventBus.
69
+ */
70
+ export type PatternKind = 'exact' | 'wildcard' | 'param';
71
+ /**
72
+ * Context object passed through the emit lifecycle.
73
+ *
74
+ * This context is shared across:
75
+ * - global middleware
76
+ * - pattern middleware
77
+ * - dispatch logic
78
+ *
79
+ * It allows middleware to:
80
+ * - observe the event and payload
81
+ * - attach metadata (`meta`)
82
+ * - stop propagation (`block()`)
83
+ */
84
+ export type EmitContext<E extends EventMap, K extends keyof E> = {
85
+ /**
86
+ * Whether propagation has been blocked.
87
+ *
88
+ * Once `true`, dispatch stops and no further middleware/listeners should run.
89
+ */
90
+ readonly blocked: boolean;
91
+ /**
92
+ * Current emitted event name.
93
+ */
94
+ readonly event: K;
95
+ /**
96
+ * Payload associated with the event.
97
+ *
98
+ * If the event's payload type is `void`, this value is typically `undefined`
99
+ * (depending on the emitter implementation).
100
+ */
101
+ readonly payload: E[K];
102
+ /**
103
+ * Information about matched pattern listeners for this emit cycle.
104
+ *
105
+ * - Ordered by priority (highest first).
106
+ * - Empty if no pattern listeners matched (or pattern matching not applicable).
107
+ *
108
+ * Useful for middleware that wants to inspect or audit matches.
109
+ */
110
+ readonly matched: readonly PatternListenerInfo<E>[];
111
+ /**
112
+ * Free-form metadata container shared across middleware.
113
+ *
114
+ * Use this to pass state between middleware, e.g.:
115
+ * - correlation IDs
116
+ * - timing info
117
+ * - auth decisions
118
+ *
119
+ * Note: Consumers should avoid putting large objects here in hot paths.
120
+ */
121
+ readonly meta: Record<string, unknown>;
122
+ /**
123
+ * Stop further propagation of this event.
124
+ *
125
+ * Once called:
126
+ * - remaining middleware is skipped
127
+ * - remaining listeners are not executed
128
+ */
129
+ block(): void;
130
+ };
131
+ /**
132
+ * Middleware executed for **every emit call**.
133
+ *
134
+ * Middleware can:
135
+ * - inspect event/payload/matches
136
+ * - read/write `ctx.meta`
137
+ * - block propagation via `ctx.block()`
138
+ * - perform async work
139
+ *
140
+ * Chain semantics:
141
+ * - Middlewares are executed sequentially in registration order.
142
+ * - `next()` must be called exactly once to continue the chain (depending on implementation).
143
+ */
144
+ export type Middleware<E extends EventMap> = <K extends keyof E>(ctx: EmitContext<E, K>, next: () => Promise<void>) => Promise<void> | void;
145
+ /**
146
+ * Middleware executed **only when pattern listeners are involved**.
147
+ *
148
+ * Typical uses:
149
+ * - logging pattern matches & params
150
+ * - permission checks / gating
151
+ * - validating extracted params
152
+ *
153
+ * Chain semantics depend on the EventBus implementation.
154
+ * In your EventBus, pattern middleware acts as a guard:
155
+ * - if it does not call `next()`, dispatch stops
156
+ * - `next()` must not be called more than once
157
+ */
158
+ export type PatternMiddleware<E extends EventMap> = (ctx: EmitContext<E, keyof E>, next: () => Promise<void>) => Promise<void> | void;
159
+ /**
160
+ * Public information of a matched pattern listener.
161
+ *
162
+ * Exposed to middleware and user-land code through `ctx.matched`.
163
+ */
164
+ export interface PatternListenerInfo<E extends EventMap> {
165
+ /**
166
+ * Original pattern string.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * 'user:{id}'
171
+ * 'order:*'
172
+ * '**'
173
+ * ```
174
+ */
175
+ readonly pattern: string;
176
+ /**
177
+ * Matching strategy classification.
178
+ */
179
+ readonly kind: PatternKind;
180
+ /**
181
+ * Listener priority. Higher values run earlier.
182
+ */
183
+ readonly priority: number;
184
+ /**
185
+ * Whether this listener was registered with `once`.
186
+ */
187
+ readonly once?: boolean;
188
+ /**
189
+ * Extracted params from the event name.
190
+ *
191
+ * Example (separator `:`):
192
+ * - pattern: `user:{id}`
193
+ * - event: `user:42`
194
+ * - params: `{ id: '42' }`
195
+ */
196
+ readonly params: Readonly<Record<string, string>>;
197
+ /**
198
+ * The actual handler function.
199
+ *
200
+ * Note:
201
+ * - Kept here mainly for introspection/debugging.
202
+ * - Middleware should generally not call handlers directly.
203
+ */
204
+ readonly handler: (event: keyof E, payload: E[keyof E], params?: Record<string, string>) => void;
205
+ }
206
+ /**
207
+ * Internal compiled representation of a pattern listener.
208
+ *
209
+ * Not exposed publicly.
210
+ * Contains precompiled matching logic for performance.
211
+ */
212
+ export interface CompiledPatternListener<E extends EventMap> {
213
+ /**
214
+ * Original pattern string (uncompiled).
215
+ */
216
+ pattern: string;
217
+ /**
218
+ * Matching strategy classification.
219
+ */
220
+ kind: PatternKind;
221
+ /**
222
+ * Listener priority. Higher values run earlier.
223
+ */
224
+ priority: number;
225
+ /**
226
+ * Whether the listener should auto-remove after first run.
227
+ */
228
+ once?: boolean;
229
+ /**
230
+ * Match function produced by the compiler.
231
+ *
232
+ * @param event - Emitted event name
233
+ * @returns
234
+ * - `null` if no match
235
+ * - params record if matched (possibly empty)
236
+ */
237
+ match(event: string): null | Record<string, string>;
238
+ /**
239
+ * Listener handler.
240
+ *
241
+ * @param event - Emitted event name
242
+ * @param payload - Payload associated with event
243
+ * @param params - Extracted params from pattern (if any)
244
+ */
245
+ handler(event: keyof E, payload: E[keyof E], params?: Record<string, string>): void;
246
+ }
247
+ /**
248
+ * Options for registering a pattern listener.
249
+ */
250
+ export type PatternOptions = {
251
+ /**
252
+ * Invoke the listener only once.
253
+ *
254
+ * After the first successful match, it is automatically removed.
255
+ */
256
+ once?: boolean;
257
+ /**
258
+ * Listener priority.
259
+ *
260
+ * Higher values run earlier.
261
+ * If omitted, EventBus will derive a priority from pattern specificity.
262
+ */
263
+ priority?: number;
264
+ /**
265
+ * Segment separator used when splitting patterns and events.
266
+ *
267
+ * IMPORTANT:
268
+ * - This default should match your EventBus implementation.
269
+ * - In the current EventBus code you posted, the default is `':'`.
270
+ */
271
+ separator?: string;
272
+ };
273
+ /**
274
+ * Internal compiled segment representation produced by the pattern compiler.
275
+ *
276
+ * - `exact` → exact segment match
277
+ * - `wildcard` → `*` matches exactly one segment
278
+ * - `deepWildcard` → `**` matches 0..n segments (backtracking)
279
+ * - `param` → `{key}` captures a segment into params
280
+ * - `regex` → per-segment regex (used for `?` patterns)
281
+ */
282
+ export type CompiledSeg = {
283
+ type: 'exact';
284
+ value: string;
285
+ } | {
286
+ type: 'wildcard';
287
+ } | {
288
+ type: 'deepWildcard';
289
+ } | {
290
+ type: 'param';
291
+ key: string;
292
+ } | {
293
+ type: 'regex';
294
+ re: RegExp;
295
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dafengzhen/event-bus",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A lightweight TypeScript event bus with strong typing, middleware pipeline, and pattern-based event listeners.",
5
5
  "keywords": [
6
6
  "event-bus",
@@ -23,21 +23,27 @@
23
23
  "type": "git",
24
24
  "url": "git+https://github.com/dafengzhen/event-bus.git"
25
25
  },
26
- "source": "./src/index.ts",
27
26
  "files": [
28
27
  "dist"
29
28
  ],
30
29
  "type": "module",
30
+ "main": "./dist/cjs/modern/index.js",
31
+ "module": "./dist/esm/modern/index.js",
31
32
  "types": "./dist/types/index.d.ts",
32
33
  "exports": {
33
34
  ".": {
34
35
  "types": "./dist/types/index.d.ts",
35
36
  "import": "./dist/esm/modern/index.js",
36
- "require": "./dist/cjs/modern/index.js"
37
+ "require": "./dist/cjs/modern/index.js",
38
+ "default": "./dist/esm/modern/index.js"
37
39
  }
38
40
  },
39
41
  "scripts": {
40
- "build": "parcel build",
42
+ "build:types": "tsc -p tsconfig.types.json",
43
+ "build:modern": "parcel build src/index.ts --target modern-esm --target modern-cjs",
44
+ "build:legacy": "parcel build src/index.ts --target legacy-esm --target legacy-cjs",
45
+ "build:dev": "parcel build src/index.ts --target modern-esm-dev --target modern-cjs-dev --target legacy-esm-dev --target legacy-cjs-dev",
46
+ "build": "npm run build:types && npm run build:modern && npm run build:legacy && npm run build:dev",
41
47
  "prepare": "husky install",
42
48
  "test": "jest",
43
49
  "watch": "parcel watch",
@@ -1 +0,0 @@
1
- {"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,uBAAuB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,qBAAqB,OAAO,IAAI,OAAO,SAAS,IAAI,GAAG,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAE/F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAwB,CAAC,SAAS,QAAQ,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAEnG;;;;;;;;;GASG;AACH,0BAA0B,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,wBAAwB,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAElB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvB;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,oBAAoB,CAAC,CAAC,EAAE,CAAC;IAEpD;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEvC;;;;;;OAMG;IACH,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,uBAAuB,CAAC,SAAS,QAAQ,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAC7D,GAAG,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,EACtB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KACtB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE1B;;;;;;;;;;;;GAYG;AACH,8BAA8B,CAAC,SAAS,QAAQ,IAAI,CAClD,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KACtB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE1B;;;;GAIG;AACH,qCAAqC,CAAC,SAAS,QAAQ;IACrD;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAE3B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;OAOG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CAClG;AAED;;;;;GAKG;AACH,yCAAyC,CAAC,SAAS,QAAQ;IACzD;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpD;;;;;;OAMG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACrF;AAED;;GAEG;AACH,6BAA6B;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;GAQG;AACH,0BACI;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;ACjTlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,sBAAsB,CAAC,SAAS,QAAQ;IAsBtC;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI;IAa5B;;;OAGG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAKrE;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAYhE;;;OAGG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IASvE;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,GAAG,MAAM,IAAI;IAK3C;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,GAAG,IAAI;IAItC;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,GAAG,MAAM,IAAI;IAS7C;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,GAAG,MAAM,IAAI;IAKlC;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,EAAE,kBAAkB,CAAC,CAAC,GAAG,MAAM,IAAI;IAKhD;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CACP,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,EACvF,OAAO,CAAC,EAAE,cAAc,GACvB,MAAM,IAAI;IAuBb;;;OAGG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,EACvF,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GACrC,MAAM,IAAI;IAIb;;;;;;;OAOG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM;IAgBrC;;;;;OAKG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAQ1F;;;OAGG;IACG,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,EAC/B,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC;CA8TjB","sources":["src/src/types.ts","src/src/event-bus.ts","src/src/index.ts","src/index.ts"],"sourcesContent":[null,null,null,"export type * from './types.ts';\nexport * from './event-bus.ts';\n"],"names":[],"version":3,"file":"index.d.ts.map"}