zephyr-events 1.1.0 â 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/LICENSE +1 -0
- package/README.md +191 -103
- package/dist/index.js +43 -32
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/zephyr-events.js +65 -1
- package/dist/zephyr-events.mjs +63 -1
- package/dist/zephyr-events.umd.js +70 -6
- package/package.json +16 -10
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,202 +1,290 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Zephyr Events â Tiny TypeScript Event Emitter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A lightweight, type-safe event emitter for TypeScript and JavaScript. Zero dependencies, under 2KB, with built-in race-condition safety that larger alternatives like EventEmitter3 and Node.js EventEmitter lack.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/zephyr-events)
|
|
6
|
-
[](https://www.npmjs.com/package/zephyr-events)
|
|
7
|
+
[](https://bundlephobia.com/package/zephyr-events)
|
|
7
8
|
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://github.com/ebogdum/zephyr-events/blob/main/LICENSE)
|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## Why Zephyr Events?
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Most event emitter libraries break when handlers modify the listener list during emission â a handler that calls `off()` on itself can skip the next handler, or adding a new listener mid-emit can cause infinite loops. Zephyr Events solves this with snapshot-based iteration, making it safe for real-world use in UI frameworks, state machines, and plugin systems.
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
- **Under 2KB** â 1.9KB ESM, zero dependencies, tree-shakeable
|
|
16
|
+
- **Race-condition safe** â handlers can subscribe, unsubscribe, or clear during emit without side effects
|
|
17
|
+
- **Full TypeScript support** â generic event maps, strict handler signatures, IDE autocompletion
|
|
18
|
+
- **Universal builds** â ESM, CommonJS, and UMD for browsers, Node.js, and bundlers
|
|
19
|
+
- **Wildcard listeners** â subscribe to all events with `*`
|
|
20
|
+
- **Shared state** â pass a handler map between emitters for cross-module communication
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
- **ðŠķ Tiny Bundle**: Only 905B minified, 0 dependencies
|
|
17
|
-
- **ðĄïļ Race-Condition Safe**: Immutable snapshots prevent handler modification issues
|
|
18
|
-
- **ðŊ ES2023 Native**: Optional chaining, nullish coalescing, spread operators
|
|
19
|
-
- **ðĶ Tree Shakeable**: ES modules with proper exports
|
|
20
|
-
- **ð§ TypeScript**: Full type safety with generics and strict types
|
|
21
|
-
|
|
22
|
-
## ðĨ Installation
|
|
22
|
+
## Installation
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npm install zephyr-events
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
```bash
|
|
29
|
+
yarn add zephyr-events
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm add zephyr-events
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### TypeScript Event Emitter
|
|
29
39
|
|
|
30
40
|
```typescript
|
|
31
41
|
import zephyrEvents from 'zephyr-events';
|
|
32
42
|
|
|
33
|
-
//
|
|
34
|
-
type
|
|
35
|
-
user: { id: number; name: string }
|
|
36
|
-
|
|
43
|
+
// Define your event types
|
|
44
|
+
type AppEvents = {
|
|
45
|
+
'user:login': { id: number; name: string }
|
|
46
|
+
'user:logout': { id: number }
|
|
47
|
+
'error': Error
|
|
37
48
|
}
|
|
38
49
|
|
|
39
|
-
const emitter = zephyrEvents<
|
|
50
|
+
const emitter = zephyrEvents<AppEvents>();
|
|
40
51
|
|
|
41
|
-
// Subscribe
|
|
42
|
-
const unsubscribe = emitter.on('user', (user) => {
|
|
43
|
-
console.log(`
|
|
52
|
+
// Subscribe â returns an unsubscribe function for easy cleanup
|
|
53
|
+
const unsubscribe = emitter.on('user:login', (user) => {
|
|
54
|
+
console.log(`Welcome, ${user.name}`);
|
|
44
55
|
});
|
|
45
56
|
|
|
46
|
-
// Emit
|
|
47
|
-
emitter.emit('user', { id: 1, name: 'Alice' });
|
|
57
|
+
// Emit with full type checking
|
|
58
|
+
emitter.emit('user:login', { id: 1, name: 'Alice' });
|
|
48
59
|
|
|
49
|
-
//
|
|
60
|
+
// Clean up when done
|
|
50
61
|
unsubscribe();
|
|
51
62
|
```
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
### JavaScript Event Emitter
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
const zephyrEvents = require('zephyr-events');
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
const emitter = zephyrEvents();
|
|
56
70
|
|
|
57
|
-
|
|
71
|
+
emitter.on('message', (data) => {
|
|
72
|
+
console.log('Received:', data);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
emitter.emit('message', { text: 'Hello' });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API Reference
|
|
79
|
+
|
|
80
|
+
### `zephyrEvents<Events>(all?)`
|
|
81
|
+
|
|
82
|
+
Creates a new typed event emitter instance. Optionally accepts an existing handler map to share event state between emitters.
|
|
58
83
|
|
|
59
84
|
```typescript
|
|
60
85
|
const emitter = zephyrEvents<{
|
|
61
86
|
message: string
|
|
62
87
|
data: { value: number }
|
|
63
88
|
}>();
|
|
89
|
+
|
|
90
|
+
// Share handlers between emitters
|
|
91
|
+
const shared = new Map();
|
|
92
|
+
const emitterA = zephyrEvents(shared);
|
|
93
|
+
const emitterB = zephyrEvents(shared);
|
|
64
94
|
```
|
|
65
95
|
|
|
66
|
-
### `emitter.on(type, handler)`
|
|
96
|
+
### `emitter.on(type, handler): Unsubscribe`
|
|
67
97
|
|
|
68
|
-
|
|
98
|
+
Registers an event handler. Returns an unsubscribe function for automatic cleanup â no need to keep a reference to the handler.
|
|
69
99
|
|
|
70
100
|
```typescript
|
|
101
|
+
// Type-safe subscription
|
|
71
102
|
const unsub = emitter.on('message', (msg) => {
|
|
72
103
|
console.log(msg);
|
|
73
104
|
});
|
|
74
105
|
|
|
75
|
-
// Wildcard listener
|
|
106
|
+
// Wildcard listener â receives every event
|
|
76
107
|
emitter.on('*', (type, event) => {
|
|
77
|
-
console.log(`
|
|
108
|
+
console.log(`[${String(type)}]`, event);
|
|
78
109
|
});
|
|
110
|
+
|
|
111
|
+
// Unsubscribe when no longer needed
|
|
112
|
+
unsub();
|
|
79
113
|
```
|
|
80
114
|
|
|
81
115
|
### `emitter.off(type, handler?)`
|
|
82
116
|
|
|
83
|
-
|
|
117
|
+
Removes a specific handler, or all handlers for an event type.
|
|
84
118
|
|
|
85
119
|
```typescript
|
|
86
|
-
// Remove specific handler
|
|
87
|
-
emitter.off('message',
|
|
120
|
+
// Remove a specific handler
|
|
121
|
+
emitter.off('message', myHandler);
|
|
88
122
|
|
|
89
|
-
// Remove all handlers for type
|
|
123
|
+
// Remove all handlers for an event type
|
|
90
124
|
emitter.off('message');
|
|
91
125
|
```
|
|
92
126
|
|
|
93
127
|
### `emitter.emit(type, event)`
|
|
94
128
|
|
|
95
|
-
|
|
129
|
+
Emits an event to all registered handlers. Handlers run synchronously from a snapshot, so the listener list can be safely modified during emission.
|
|
96
130
|
|
|
97
131
|
```typescript
|
|
98
132
|
emitter.emit('message', 'Hello World!');
|
|
99
133
|
emitter.emit('data', { value: 42 });
|
|
100
134
|
```
|
|
101
135
|
|
|
102
|
-
|
|
136
|
+
### `emitter.all`
|
|
103
137
|
|
|
104
|
-
|
|
138
|
+
The underlying `Map<EventType, Handler[]>` that stores all registered handlers. Can be inspected, serialized, or shared between emitter instances.
|
|
105
139
|
|
|
106
|
-
|
|
107
|
-
- **Array snapshots**: Fast iteration with race-condition safety
|
|
108
|
-
- **ES2023 optimizations**: Native optional chaining and nullish coalescing
|
|
140
|
+
## Use Cases
|
|
109
141
|
|
|
110
|
-
|
|
142
|
+
### Event Bus for React Components
|
|
111
143
|
|
|
112
144
|
```typescript
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
145
|
+
import zephyrEvents from 'zephyr-events';
|
|
146
|
+
|
|
147
|
+
type UIEvents = {
|
|
148
|
+
'modal:open': { id: string }
|
|
149
|
+
'modal:close': { id: string }
|
|
150
|
+
'toast': { message: string; severity: 'info' | 'error' }
|
|
151
|
+
}
|
|
116
152
|
|
|
117
|
-
|
|
153
|
+
// Create a shared event bus
|
|
154
|
+
export const uiBus = zephyrEvents<UIEvents>();
|
|
155
|
+
|
|
156
|
+
// In a React component â clean up on unmount
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
const unsub = uiBus.on('toast', (toast) => {
|
|
159
|
+
showToast(toast.message, toast.severity);
|
|
160
|
+
});
|
|
161
|
+
return unsub;
|
|
162
|
+
}, []);
|
|
163
|
+
```
|
|
118
164
|
|
|
119
|
-
|
|
165
|
+
### Pub/Sub in Node.js Microservices
|
|
120
166
|
|
|
121
167
|
```typescript
|
|
122
|
-
|
|
123
|
-
|
|
168
|
+
import zephyrEvents from 'zephyr-events';
|
|
169
|
+
|
|
170
|
+
type ServiceEvents = {
|
|
171
|
+
'order:created': { orderId: string; total: number }
|
|
172
|
+
'order:shipped': { orderId: string; trackingId: string }
|
|
173
|
+
'inventory:low': { sku: string; remaining: number }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const events = zephyrEvents<ServiceEvents>();
|
|
177
|
+
|
|
178
|
+
// Multiple subscribers
|
|
179
|
+
events.on('order:created', sendConfirmationEmail);
|
|
180
|
+
events.on('order:created', updateAnalytics);
|
|
181
|
+
events.on('inventory:low', notifyWarehouse);
|
|
182
|
+
|
|
183
|
+
// Audit logging with wildcard
|
|
184
|
+
events.on('*', (type, data) => {
|
|
185
|
+
logger.info({ event: type, payload: data });
|
|
124
186
|
});
|
|
125
187
|
```
|
|
126
188
|
|
|
127
|
-
|
|
189
|
+
### Plugin System
|
|
128
190
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
- **Spread operators**: `[...handlers]` for fast snapshots
|
|
132
|
-
|
|
133
|
-
## ðĶ Bundle Formats
|
|
191
|
+
```typescript
|
|
192
|
+
import zephyrEvents from 'zephyr-events';
|
|
134
193
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
194
|
+
type PluginEvents = {
|
|
195
|
+
'init': { config: Record<string, unknown> }
|
|
196
|
+
'transform': { input: string }
|
|
197
|
+
'destroy': undefined
|
|
198
|
+
}
|
|
138
199
|
|
|
139
|
-
|
|
200
|
+
function createPluginHost() {
|
|
201
|
+
const emitter = zephyrEvents<PluginEvents>();
|
|
140
202
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
203
|
+
return {
|
|
204
|
+
register(plugin: (on: typeof emitter.on) => void) {
|
|
205
|
+
plugin(emitter.on.bind(emitter));
|
|
206
|
+
},
|
|
207
|
+
emit: emitter.emit.bind(emitter),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
```
|
|
148
211
|
|
|
149
|
-
|
|
212
|
+
## Race-Condition Safety
|
|
150
213
|
|
|
151
|
-
|
|
214
|
+
Unlike most event emitters, Zephyr Events handles listener modification during emission safely. Each `emit()` call iterates over a snapshot of the handler list, so adding or removing handlers mid-emit never causes skipped handlers or infinite loops:
|
|
152
215
|
|
|
153
|
-
|
|
216
|
+
```typescript
|
|
217
|
+
const emitter = zephyrEvents();
|
|
154
218
|
|
|
155
|
-
|
|
219
|
+
// Safe: handler removes itself during emit
|
|
220
|
+
emitter.on('data', function once(data) {
|
|
221
|
+
emitter.off('data', once);
|
|
222
|
+
process(data); // next handler still fires
|
|
223
|
+
});
|
|
156
224
|
|
|
157
|
-
|
|
225
|
+
// Safe: handler adds new listeners during emit
|
|
226
|
+
emitter.on('init', () => {
|
|
227
|
+
emitter.on('init', () => {
|
|
228
|
+
// this will NOT fire during the current emit cycle
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
```
|
|
158
232
|
|
|
159
|
-
|
|
233
|
+
## Comparison with Other Event Emitters
|
|
160
234
|
|
|
161
|
-
|
|
235
|
+
| Feature | Zephyr Events | mitt | EventEmitter3 | Node.js EventEmitter |
|
|
236
|
+
|---------|:---:|:---:|:---:|:---:|
|
|
237
|
+
| Bundle size | ~2KB | ~200B | ~7KB | Built-in |
|
|
238
|
+
| TypeScript types | Native | Native | Bundled | `@types/node` |
|
|
239
|
+
| Race-condition safe | Yes | No | No | No |
|
|
240
|
+
| Wildcard listeners | Yes | Yes | No | No |
|
|
241
|
+
| Unsubscribe function | Yes | No | No | No |
|
|
242
|
+
| Shared handler maps | Yes | Yes | No | No |
|
|
243
|
+
| Zero dependencies | Yes | Yes | Yes | N/A |
|
|
244
|
+
| Tree-shakeable ESM | Yes | Yes | Yes | No |
|
|
162
245
|
|
|
163
|
-
|
|
246
|
+
## Performance Benchmarks
|
|
164
247
|
|
|
165
|
-
|
|
248
|
+
Tested on Apple Silicon M-series (ARM64), Node.js v25.2.1. Run `node benchmark.js` to reproduce.
|
|
166
249
|
|
|
167
250
|
| Operation | Ops/Second | Description |
|
|
168
251
|
|-----------|------------|-------------|
|
|
169
|
-
| **
|
|
170
|
-
| **
|
|
171
|
-
| **
|
|
172
|
-
| **
|
|
173
|
-
| **
|
|
174
|
-
| **
|
|
252
|
+
| **Emit (1 handler)** | **27.5M** | Emitting to a single event handler |
|
|
253
|
+
| **Emit (10 handlers)** | **12.0M** | Emitting to 10 concurrent handlers |
|
|
254
|
+
| **Emit (100 handlers)** | **1.9M** | Emitting to 100 concurrent handlers |
|
|
255
|
+
| **Wildcard emit** | **27.5M** | Emitting with a wildcard listener |
|
|
256
|
+
| **On + unsub cycle** | **9.4M** | Subscribe and immediately unsubscribe |
|
|
257
|
+
| **On + off(handler)** | **9.3M** | Subscribe and remove with `.off()` |
|
|
258
|
+
| **Mixed (on/emit/unsub)** | **7.0M** | Realistic usage: subscribe, emit, cleanup |
|
|
175
259
|
|
|
176
|
-
|
|
260
|
+
All benchmarks use proper setup/run separation with warmup passes. No-op handlers are used to measure emitter overhead only â real-world throughput depends on handler complexity.
|
|
177
261
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
|
183
|
-
|
|
262
|
+
## Bundle Formats
|
|
263
|
+
|
|
264
|
+
Zephyr Events ships three bundle formats for maximum compatibility:
|
|
265
|
+
|
|
266
|
+
| Format | File | Use case |
|
|
267
|
+
|--------|------|----------|
|
|
268
|
+
| ESM | `dist/zephyr-events.mjs` | Modern bundlers (Vite, Rollup, webpack 5+) |
|
|
269
|
+
| CommonJS | `dist/zephyr-events.js` | Node.js `require()`, older bundlers |
|
|
270
|
+
| UMD | `dist/zephyr-events.umd.js` | Script tags, AMD loaders, legacy environments |
|
|
271
|
+
|
|
272
|
+
## Requirements
|
|
273
|
+
|
|
274
|
+
- **Node.js** >= 18
|
|
275
|
+
- **TypeScript** >= 4.7 (for type features; runtime has no TS dependency)
|
|
276
|
+
- Works in all modern browsers (ES2020+ support)
|
|
277
|
+
|
|
278
|
+
## Contributing
|
|
184
279
|
|
|
185
|
-
|
|
280
|
+
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
186
281
|
|
|
187
|
-
|
|
188
|
-
- **⥠Instant cleanup**: Handler removal at **194.17M operations/second**
|
|
189
|
-
- **ð Scales efficiently**: Maintains high performance with multiple handlers
|
|
190
|
-
- **ðĄïļ Race-condition safe**: Minimal overhead for safety guarantees
|
|
191
|
-
- **ðŊ Real-world optimized**: **7.17M ops/sec** for typical usage patterns
|
|
282
|
+
## Acknowledgments
|
|
192
283
|
|
|
193
|
-
|
|
284
|
+
Zephyr Events is a modernization of [mitt](https://github.com/developit/mitt) by [Jason Miller](https://github.com/developit). Built on the same simple API with added type safety, race-condition handling, and universal module support.
|
|
194
285
|
|
|
195
|
-
|
|
196
|
-
- **ES2023 Native**: Optional chaining (`?.`) and nullish coalescing (`??`) optimizations
|
|
197
|
-
- **Memory Efficient**: Stable performance under stress conditions
|
|
198
|
-
- **Zero Dependencies**: Pure JavaScript with no external overhead
|
|
286
|
+
## License
|
|
199
287
|
|
|
200
|
-
|
|
288
|
+
[MIT](LICENSE)
|
|
201
289
|
|
|
202
|
-
|
|
290
|
+
Original mitt: MIT (c) [Jason Miller](https://github.com/developit)
|
package/dist/index.js
CHANGED
|
@@ -3,51 +3,62 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.default = zephyrEvents;
|
|
4
4
|
function zephyrEvents(all) {
|
|
5
5
|
all ??= new Map();
|
|
6
|
-
const handlerSets = new Map();
|
|
7
|
-
const getHandlerSet = (type) => {
|
|
8
|
-
let handlers = handlerSets.get(type);
|
|
9
|
-
if (!handlers) {
|
|
10
|
-
handlers = new Set();
|
|
11
|
-
handlerSets.set(type, handlers);
|
|
12
|
-
all.set(type, []);
|
|
13
|
-
}
|
|
14
|
-
return handlers;
|
|
15
|
-
};
|
|
16
|
-
const syncMap = (type) => {
|
|
17
|
-
const handlers = handlerSets.get(type);
|
|
18
|
-
if (handlers) {
|
|
19
|
-
all.set(type, [...handlers]);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
6
|
return {
|
|
23
7
|
all,
|
|
24
8
|
on(type, handler) {
|
|
25
|
-
|
|
26
|
-
handlers
|
|
27
|
-
|
|
9
|
+
let handlers = all.get(type);
|
|
10
|
+
if (!handlers) {
|
|
11
|
+
handlers = [];
|
|
12
|
+
all.set(type, handlers);
|
|
13
|
+
}
|
|
14
|
+
handlers.push(handler);
|
|
28
15
|
return () => {
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
const current = all.get(type);
|
|
17
|
+
if (current) {
|
|
18
|
+
const idx = current.indexOf(handler);
|
|
19
|
+
if (-1 < idx) {
|
|
20
|
+
current.splice(idx, 1);
|
|
21
|
+
}
|
|
22
|
+
if (0 === current.length) {
|
|
23
|
+
all.delete(type);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
31
26
|
};
|
|
32
27
|
},
|
|
33
28
|
off(type, handler) {
|
|
34
|
-
const handlers =
|
|
29
|
+
const handlers = all.get(type);
|
|
35
30
|
if (!handlers)
|
|
36
31
|
return;
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
if (handler) {
|
|
33
|
+
const idx = handlers.indexOf(handler);
|
|
34
|
+
if (-1 < idx) {
|
|
35
|
+
handlers.splice(idx, 1);
|
|
36
|
+
}
|
|
37
|
+
if (0 === handlers.length) {
|
|
38
|
+
all.delete(type);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
all.delete(type);
|
|
43
|
+
}
|
|
39
44
|
},
|
|
40
45
|
emit(type, evt) {
|
|
41
|
-
const handlers =
|
|
42
|
-
if (handlers
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const handlers = all.get(type);
|
|
47
|
+
if (handlers) {
|
|
48
|
+
const snapshot = handlers.slice();
|
|
49
|
+
const len = snapshot.length;
|
|
50
|
+
for (let i = 0; i < len; i++) {
|
|
51
|
+
snapshot[i](evt);
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
if ('*' !== type) {
|
|
55
|
+
const wildcards = all.get('*');
|
|
56
|
+
if (wildcards) {
|
|
57
|
+
const snapshot = wildcards.slice();
|
|
58
|
+
const len = snapshot.length;
|
|
59
|
+
for (let i = 0; i < len; i++) {
|
|
60
|
+
snapshot[i](type, evt);
|
|
61
|
+
}
|
|
51
62
|
}
|
|
52
63
|
}
|
|
53
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts"],"version":"5.9.
|
|
1
|
+
{"root":["../src/index.ts"],"version":"5.9.3"}
|
package/dist/zephyr-events.js
CHANGED
|
@@ -1 +1,65 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = function zephyrEvents(all) {
|
|
3
|
+
all ??= new Map();
|
|
4
|
+
return {
|
|
5
|
+
all,
|
|
6
|
+
on(type, handler) {
|
|
7
|
+
let handlers = all.get(type);
|
|
8
|
+
if (!handlers) {
|
|
9
|
+
handlers = [];
|
|
10
|
+
all.set(type, handlers);
|
|
11
|
+
}
|
|
12
|
+
handlers.push(handler);
|
|
13
|
+
return () => {
|
|
14
|
+
const current = all.get(type);
|
|
15
|
+
if (current) {
|
|
16
|
+
const idx = current.indexOf(handler);
|
|
17
|
+
if (-1 < idx) {
|
|
18
|
+
current.splice(idx, 1);
|
|
19
|
+
}
|
|
20
|
+
if (0 === current.length) {
|
|
21
|
+
all.delete(type);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
off(type, handler) {
|
|
27
|
+
const handlers = all.get(type);
|
|
28
|
+
if (!handlers)
|
|
29
|
+
return;
|
|
30
|
+
if (handler) {
|
|
31
|
+
const idx = handlers.indexOf(handler);
|
|
32
|
+
if (-1 < idx) {
|
|
33
|
+
handlers.splice(idx, 1);
|
|
34
|
+
}
|
|
35
|
+
if (0 === handlers.length) {
|
|
36
|
+
all.delete(type);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
all.delete(type);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
emit(type, evt) {
|
|
44
|
+
const handlers = all.get(type);
|
|
45
|
+
if (handlers) {
|
|
46
|
+
const snapshot = handlers.slice();
|
|
47
|
+
const len = snapshot.length;
|
|
48
|
+
for (let i = 0; i < len; i++) {
|
|
49
|
+
snapshot[i](evt);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if ('*' !== type) {
|
|
53
|
+
const wildcards = all.get('*');
|
|
54
|
+
if (wildcards) {
|
|
55
|
+
const snapshot = wildcards.slice();
|
|
56
|
+
const len = snapshot.length;
|
|
57
|
+
for (let i = 0; i < len; i++) {
|
|
58
|
+
snapshot[i](type, evt);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
module.exports.default = module.exports;
|
package/dist/zephyr-events.mjs
CHANGED
|
@@ -1 +1,63 @@
|
|
|
1
|
-
export default
|
|
1
|
+
export default function zephyrEvents(all) {
|
|
2
|
+
all ??= new Map();
|
|
3
|
+
return {
|
|
4
|
+
all,
|
|
5
|
+
on(type, handler) {
|
|
6
|
+
let handlers = all.get(type);
|
|
7
|
+
if (!handlers) {
|
|
8
|
+
handlers = [];
|
|
9
|
+
all.set(type, handlers);
|
|
10
|
+
}
|
|
11
|
+
handlers.push(handler);
|
|
12
|
+
return () => {
|
|
13
|
+
const current = all.get(type);
|
|
14
|
+
if (current) {
|
|
15
|
+
const idx = current.indexOf(handler);
|
|
16
|
+
if (-1 < idx) {
|
|
17
|
+
current.splice(idx, 1);
|
|
18
|
+
}
|
|
19
|
+
if (0 === current.length) {
|
|
20
|
+
all.delete(type);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
off(type, handler) {
|
|
26
|
+
const handlers = all.get(type);
|
|
27
|
+
if (!handlers)
|
|
28
|
+
return;
|
|
29
|
+
if (handler) {
|
|
30
|
+
const idx = handlers.indexOf(handler);
|
|
31
|
+
if (-1 < idx) {
|
|
32
|
+
handlers.splice(idx, 1);
|
|
33
|
+
}
|
|
34
|
+
if (0 === handlers.length) {
|
|
35
|
+
all.delete(type);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
all.delete(type);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
emit(type, evt) {
|
|
43
|
+
const handlers = all.get(type);
|
|
44
|
+
if (handlers) {
|
|
45
|
+
const snapshot = handlers.slice();
|
|
46
|
+
const len = snapshot.length;
|
|
47
|
+
for (let i = 0; i < len; i++) {
|
|
48
|
+
snapshot[i](evt);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if ('*' !== type) {
|
|
52
|
+
const wildcards = all.get('*');
|
|
53
|
+
if (wildcards) {
|
|
54
|
+
const snapshot = wildcards.slice();
|
|
55
|
+
const len = snapshot.length;
|
|
56
|
+
for (let i = 0; i < len; i++) {
|
|
57
|
+
snapshot[i](type, evt);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -1,7 +1,71 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}(this, (function () {
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = global || self, global.zephyrEvents = factory());
|
|
5
|
+
}(this, (function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
function zephyrEvents(all) {
|
|
8
|
+
all ??= new Map();
|
|
9
|
+
return {
|
|
10
|
+
all,
|
|
11
|
+
on(type, handler) {
|
|
12
|
+
let handlers = all.get(type);
|
|
13
|
+
if (!handlers) {
|
|
14
|
+
handlers = [];
|
|
15
|
+
all.set(type, handlers);
|
|
16
|
+
}
|
|
17
|
+
handlers.push(handler);
|
|
18
|
+
return () => {
|
|
19
|
+
const current = all.get(type);
|
|
20
|
+
if (current) {
|
|
21
|
+
const idx = current.indexOf(handler);
|
|
22
|
+
if (-1 < idx) {
|
|
23
|
+
current.splice(idx, 1);
|
|
24
|
+
}
|
|
25
|
+
if (0 === current.length) {
|
|
26
|
+
all.delete(type);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
off(type, handler) {
|
|
32
|
+
const handlers = all.get(type);
|
|
33
|
+
if (!handlers)
|
|
34
|
+
return;
|
|
35
|
+
if (handler) {
|
|
36
|
+
const idx = handlers.indexOf(handler);
|
|
37
|
+
if (-1 < idx) {
|
|
38
|
+
handlers.splice(idx, 1);
|
|
39
|
+
}
|
|
40
|
+
if (0 === handlers.length) {
|
|
41
|
+
all.delete(type);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
all.delete(type);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
emit(type, evt) {
|
|
49
|
+
const handlers = all.get(type);
|
|
50
|
+
if (handlers) {
|
|
51
|
+
const snapshot = handlers.slice();
|
|
52
|
+
const len = snapshot.length;
|
|
53
|
+
for (let i = 0; i < len; i++) {
|
|
54
|
+
snapshot[i](evt);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if ('*' !== type) {
|
|
58
|
+
const wildcards = all.get('*');
|
|
59
|
+
if (wildcards) {
|
|
60
|
+
const snapshot = wildcards.slice();
|
|
61
|
+
const len = snapshot.length;
|
|
62
|
+
for (let i = 0; i < len; i++) {
|
|
63
|
+
snapshot[i](type, evt);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return zephyrEvents;
|
|
71
|
+
})));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zephyr-events",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Tiny typed event emitter with race-condition safety",
|
|
5
5
|
"main": "dist/zephyr-events.js",
|
|
6
6
|
"module": "dist/zephyr-events.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -19,23 +19,29 @@
|
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc --build && node build.js",
|
|
22
|
-
"
|
|
22
|
+
"test": "node test/index.js",
|
|
23
|
+
"prepublishOnly": "npm run build && npm test"
|
|
23
24
|
},
|
|
24
25
|
"repository": {
|
|
25
26
|
"type": "git",
|
|
26
|
-
"url": "https://github.com/ebogdum/zephyr-events.git"
|
|
27
|
+
"url": "git+https://github.com/ebogdum/zephyr-events.git"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
|
29
|
-
"
|
|
30
|
+
"event-emitter",
|
|
30
31
|
"eventemitter",
|
|
32
|
+
"events",
|
|
31
33
|
"emitter",
|
|
32
34
|
"pubsub",
|
|
35
|
+
"event-bus",
|
|
33
36
|
"typescript",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
37
|
+
"typed-events",
|
|
38
|
+
"mitt-alternative",
|
|
39
|
+
"lightweight",
|
|
36
40
|
"tiny",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
41
|
+
"subscribe",
|
|
42
|
+
"observer",
|
|
43
|
+
"race-condition-safe",
|
|
44
|
+
"wildcard"
|
|
39
45
|
],
|
|
40
46
|
"homepage": "https://github.com/ebogdum/zephyr-events",
|
|
41
47
|
"bugs": {
|
|
@@ -44,7 +50,7 @@
|
|
|
44
50
|
"author": "ebogdum <https://github.com/ebogdum>",
|
|
45
51
|
"license": "MIT",
|
|
46
52
|
"engines": {
|
|
47
|
-
"node": ">=
|
|
53
|
+
"node": ">=18"
|
|
48
54
|
},
|
|
49
55
|
"devDependencies": {
|
|
50
56
|
"typescript": "^5.9.2"
|