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 CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
3
  Copyright (c) 2021 Jason Miller
4
+ Copyright (c) 2025 ebogdum
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,202 +1,290 @@
1
- # 🌊ïļ Zephyr Events
1
+ # Zephyr Events — Tiny TypeScript Event Emitter
2
2
 
3
- Ultra-fast ES2023 event emitter with **905B** bundle size and race-condition safety.
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
  [![npm version](https://img.shields.io/npm/v/zephyr-events.svg)](https://www.npmjs.com/package/zephyr-events)
6
- [![Bundle Size](https://img.shields.io/badge/size-905B-brightgreen.svg)](https://bundlephobia.com/package/zephyr-events)
6
+ [![npm downloads](https://img.shields.io/npm/dm/zephyr-events.svg)](https://www.npmjs.com/package/zephyr-events)
7
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/zephyr-events)](https://bundlephobia.com/package/zephyr-events)
7
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
9
+ [![license](https://img.shields.io/npm/l/zephyr-events.svg)](https://github.com/ebogdum/zephyr-events/blob/main/LICENSE)
8
10
 
9
- ## 🙏 Acknowledgments
11
+ ## Why Zephyr Events?
10
12
 
11
- **Zephyr Events** is a **heavy modernization and performance upgrade** of the original [mitt](https://github.com/developit/mitt) package by [Jason Miller](https://github.com/developit). Thanks for the foundational work! 🎉
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
- ## ⚡ Features
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
- - **ðŸ”Ĩ Ultra Fast**: 33M+ operations/second with native Set/Map optimizations
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
- ## 🚀 Quick Start
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
- // Create typed emitter
34
- type Events = {
35
- user: { id: number; name: string }
36
- error: Error
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<Events>();
50
+ const emitter = zephyrEvents<AppEvents>();
40
51
 
41
- // Subscribe with auto-cleanup
42
- const unsubscribe = emitter.on('user', (user) => {
43
- console.log(`User: ${user.name}`);
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 events
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
- // Cleanup
60
+ // Clean up when done
50
61
  unsubscribe();
51
62
  ```
52
63
 
53
- ## ðŸŽĻ API
64
+ ### JavaScript Event Emitter
65
+
66
+ ```javascript
67
+ const zephyrEvents = require('zephyr-events');
54
68
 
55
- ### `zephyrEvents<Events>()`
69
+ const emitter = zephyrEvents();
56
70
 
57
- Create a new event emitter instance.
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
- Register event handler. Returns unsubscribe function.
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(`Event ${type}:`, event);
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
- Remove event handler(s).
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', handler);
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
- Emit event to all registered handlers.
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
- ## 🏗ïļ Architecture
136
+ ### `emitter.all`
103
137
 
104
- Zephyr Events uses a **dual-storage architecture** for maximum performance:
138
+ The underlying `Map<EventType, Handler[]>` that stores all registered handlers. Can be inspected, serialized, or shared between emitter instances.
105
139
 
106
- - **Set**: O(1) add/remove operations
107
- - **Array snapshots**: Fast iteration with race-condition safety
108
- - **ES2023 optimizations**: Native optional chaining and nullish coalescing
140
+ ## Use Cases
109
141
 
110
- ## ⚡ Performance
142
+ ### Event Bus for React Components
111
143
 
112
144
  ```typescript
113
- // Benchmark: Comprehensive performance testing
114
- // Result: Up to 33M+ operations/second
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
- ## 🔒 Race-Condition Safety
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
- Handlers are executed from immutable snapshots:
165
+ ### Pub/Sub in Node.js Microservices
120
166
 
121
167
  ```typescript
122
- emitter.on('test', function selfRemover() {
123
- emitter.off('test', selfRemover); // Safe during emit
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
- ## 🌟 ES2023 Features
189
+ ### Plugin System
128
190
 
129
- - **Nullish coalescing**: `all ??= new Map()`
130
- - **Optional chaining**: `handlers?.size`
131
- - **Spread operators**: `[...handlers]` for fast snapshots
132
-
133
- ## ðŸ“Ķ Bundle Formats
191
+ ```typescript
192
+ import zephyrEvents from 'zephyr-events';
134
193
 
135
- - **ESM**: `dist/zephyr-events.mjs` (905B)
136
- - **CommonJS**: `dist/zephyr-events.js` (977B)
137
- - **UMD**: `dist/zephyr-events.umd.js` (1.3KB)
194
+ type PluginEvents = {
195
+ 'init': { config: Record<string, unknown> }
196
+ 'transform': { input: string }
197
+ 'destroy': undefined
198
+ }
138
199
 
139
- ## 🆚 Comparison
200
+ function createPluginHost() {
201
+ const emitter = zephyrEvents<PluginEvents>();
140
202
 
141
- | Feature | Zephyr Events | mitt* | eventemitter3 |
142
- |---------|---------------|------|---------------|
143
- | Bundle Size | 905B | 200B | 7KB |
144
- | TypeScript | ✅ Native | ✅ | ✅ |
145
- | Race-Safe | ✅ | ❌ | ❌ |
146
- | ES2023 | ✅ | ❌ | ❌ |
147
- | Performance | 33M ops/s | 15M ops/s | 10M ops/s |
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
- *\*Based on original mitt package by Jason Miller*
212
+ ## Race-Condition Safety
150
213
 
151
- ## ðŸĪ Contributing
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
- Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md).
216
+ ```typescript
217
+ const emitter = zephyrEvents();
154
218
 
155
- ## 📄 License
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
- MIT ÂĐ [ebogdum](https://github.com/ebogdum)
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
- Original mitt: MIT ÂĐ [Jason Miller](https://github.com/developit)
233
+ ## Comparison with Other Event Emitters
160
234
 
161
- ## 🚀 Performance Benchmarks
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
- Comprehensive performance benchmarks on **Apple Silicon M-series (ARM64)** with **Node.js v23.10.0**:
246
+ ## Performance Benchmarks
164
247
 
165
- ### Core Operations Performance
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
- | **Emitter Creation** | **10.54M** | Creating new emitter instances |
170
- | **Single Handler Emit** | **33.69M** | Emitting to one event handler |
171
- | **Wildcard Emit** | **26.12M** | Emitting to wildcard listeners |
172
- | **10 Handlers Emit** | **9.32M** | Emitting to 10 concurrent handlers |
173
- | **100 Handlers Emit** | **1.57M** | Emitting to 100 concurrent handlers |
174
- | **Mixed Operations** | **7.17M** | Realistic usage: on/emit/off cycle |
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
- ### Management Operations Performance
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
- | Operation | Ops/Second | Description |
179
- |-----------|------------|-------------|
180
- | **Off Method** | **194.17M** | Removing specific handler with `.off()` |
181
- | **Unsubscribe** | **143.54M** | Removing handler with returned function |
182
- | **Event Subscription** | **9.19K** | Adding new event handlers with `.on()` |
183
- | **Memory Stress** | **130** | Complex multi-event scenario |
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
- ### Key Performance Insights
280
+ Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
186
281
 
187
- - **ðŸ”Ĩ Ultra-fast emission**: Up to **33.69M operations/second** for single handlers
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
- ### Architecture Benefits
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
- - **Dual Storage**: Set for O(1) add/remove + Array snapshots for fast iteration
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
- *Built with âĪïļ and ES2023 â€Ē Inspired by mitt*
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
- const handlers = getHandlerSet(type);
26
- handlers.add(handler);
27
- syncMap(type);
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
- handlers.delete(handler);
30
- syncMap(type);
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 = handlerSets.get(type);
29
+ const handlers = all.get(type);
35
30
  if (!handlers)
36
31
  return;
37
- handler ? handlers.delete(handler) : handlers.clear();
38
- syncMap(type);
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 = handlerSets.get(type);
42
- if (handlers?.size) {
43
- for (const handler of [...handlers]) {
44
- handler(evt);
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
- const wildcards = handlerSets.get('*');
48
- if (wildcards?.size) {
49
- for (const handler of [...wildcards]) {
50
- handler(type, evt);
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.2"}
1
+ {"root":["../src/index.ts"],"version":"5.9.3"}
@@ -1 +1,65 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value: true});exports.default=zephyrEvents;function zephyrEvents(all){all ??=new Map();const handlerSets=new Map();const getHandlerSet=(type)=>{let handlers=handlerSets.get(type);if(!handlers){handlers=new Set();handlerSets.set(type,handlers);all.set(type,[]);}return handlers;};const syncMap=(type)=>{const handlers=handlerSets.get(type);if(handlers){all.set(type,[...handlers]);}};return{all,on(type,handler){const handlers=getHandlerSet(type);handlers.add(handler);syncMap(type);return()=>{handlers.delete(handler);syncMap(type);};},off(type,handler){const handlers=handlerSets.get(type);if(!handlers)return;handler ? handlers.delete(handler): handlers.clear();syncMap(type);},emit(type,evt){const handlers=handlerSets.get(type);if(handlers?.size){for(const handler of [...handlers]){handler(evt);}}const wildcards=handlerSets.get('*');if(wildcards?.size){for(const handler of [...wildcards]){handler(type,evt);}}}};}
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;
@@ -1 +1,63 @@
1
- export default zephyrEvents;function zephyrEvents(all){all ??=new Map();const handlerSets=new Map();const getHandlerSet=(type)=>{let handlers=handlerSets.get(type);if(!handlers){handlers=new Set();handlerSets.set(type,handlers);all.set(type,[]);}return handlers;};const syncMap=(type)=>{const handlers=handlerSets.get(type);if(handlers){all.set(type,[...handlers]);}};return{all,on(type,handler){const handlers=getHandlerSet(type);handlers.add(handler);syncMap(type);return()=>{handlers.delete(handler);syncMap(type);};},off(type,handler){const handlers=handlerSets.get(type);if(!handlers)return;handler ? handlers.delete(handler): handlers.clear();syncMap(type);},emit(type,evt){const handlers=handlerSets.get(type);if(handlers?.size){for(const handler of [...handlers]){handler(evt);}}const wildcards=handlerSets.get('*');if(wildcards?.size){for(const handler of [...wildcards]){handler(type,evt);}}}};}
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
- 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 () { 'use strict';
6
- "use strict";Object.defineProperty(exports,"__esModule",{value: true});exports.default=zephyrEvents;function zephyrEvents(all){all ??=new Map();const handlerSets=new Map();const getHandlerSet=(type)=>{let handlers=handlerSets.get(type);if(!handlers){handlers=new Set();handlerSets.set(type,handlers);all.set(type,[]);}return handlers;};const syncMap=(type)=>{const handlers=handlerSets.get(type);if(handlers){all.set(type,[...handlers]);}};return{all,on(type,handler){const handlers=getHandlerSet(type);handlers.add(handler);syncMap(type);return()=>{handlers.delete(handler);syncMap(type);};},off(type,handler){const handlers=handlerSets.get(type);if(!handlers)return;handler ? handlers.delete(handler): handlers.clear();syncMap(type);},emit(type,evt){const handlers=handlerSets.get(type);if(handlers?.size){for(const handler of [...handlers]){handler(evt);}}const wildcards=handlerSets.get('*');if(wildcards?.size){for(const handler of [...wildcards]){handler(type,evt);}}}};}
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.1.0",
4
- "description": "Ultra-fast ES2023 event emitter with 889B bundle size and race-condition safety",
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
- "prepublishOnly": "npm run build"
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
- "events",
30
+ "event-emitter",
30
31
  "eventemitter",
32
+ "events",
31
33
  "emitter",
32
34
  "pubsub",
35
+ "event-bus",
33
36
  "typescript",
34
- "es2023",
35
- "fast",
37
+ "typed-events",
38
+ "mitt-alternative",
39
+ "lightweight",
36
40
  "tiny",
37
- "performance",
38
- "race-condition-safe"
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": ">=16"
53
+ "node": ">=18"
48
54
  },
49
55
  "devDependencies": {
50
56
  "typescript": "^5.9.2"