eventify 2.0.1 → 3.0.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/BENCHMARKS.md ADDED
@@ -0,0 +1,196 @@
1
+ # Benchmarks
2
+
3
+ Microbenchmarks for internal data-structure and hot-path changes. Not a promise of real-world throughput.
4
+
5
+ ## Commands
6
+
7
+ - `bun run bench`
8
+ - `bun run bench:structures`
9
+ - `bun run bench:patterns`
10
+ - `bun run bench:node`
11
+ - `bun run bench:node:structures`
12
+ - `bun run bench:node:patterns`
13
+ - `bun run bench:browser` (Playwright)
14
+ - `bun run bench:all`
15
+
16
+ ## Environment
17
+
18
+ - Date: 2026-02-06
19
+ - Bun: 1.3.1
20
+ - Node: 20.10.0
21
+ - Browser: Playwright Chromium (Chrome for Testing 145.0.7632.6)
22
+ - samples=5 warmup=2 iters=50,000
23
+
24
+ ## Iteration 1 - Baseline
25
+
26
+ Change
27
+ Baseline (post-coverage cleanup).
28
+
29
+ Results
30
+ | Case | Median (ms) | Ops/s |
31
+ | --- | --- | --- |
32
+ | trigger (no listeners) | 1.824 | 27,412,281 |
33
+ | trigger (1 listener) | 6.055 | 8,258,264 |
34
+ | trigger (10 listeners) | 28.527 | 1,752,710 |
35
+ | trigger (all listener) | 7.249 | 6,897,741 |
36
+ | trigger (pattern match) | 69.722 | 717,129 |
37
+ | on/off (100 listeners) | 0.396 | 505,635 |
38
+ | listenTo/stopListening | 0.338 | 295,675 |
39
+
40
+ ## Iteration 2 - Precompute pattern event segments
41
+
42
+ Change
43
+ Precompute `eventSegments` once per trigger and reuse in pattern matching.
44
+
45
+ Results
46
+ | Case | Median (ms) | Ops/s |
47
+ | --- | --- | --- |
48
+ | trigger (no listeners) | 1.558 | 32,101,884 |
49
+ | trigger (1 listener) | 6.377 | 7,840,574 |
50
+ | trigger (10 listeners) | 20.741 | 2,410,694 |
51
+ | trigger (all listener) | 7.575 | 6,600,370 |
52
+ | trigger (pattern match) | 24.975 | 2,001,992 |
53
+ | on/off (100 listeners) | 0.192 | 1,042,796 |
54
+ | listenTo/stopListening | 0.471 | 212,089 |
55
+
56
+ ## Iteration 3 - isPatternName fast path
57
+
58
+ Change
59
+ Skip `splitName` when the event name does not contain the wildcard segment.
60
+
61
+ Results
62
+ | Case | Median (ms) | Ops/s |
63
+ | --- | --- | --- |
64
+ | trigger (no listeners) | 1.629 | 30,696,824 |
65
+ | trigger (1 listener) | 7.531 | 6,638,930 |
66
+ | trigger (10 listeners) | 26.175 | 1,910,229 |
67
+ | trigger (all listener) | 7.933 | 6,303,183 |
68
+ | trigger (pattern match) | 28.955 | 1,726,795 |
69
+ | on/off (100 listeners) | 0.427 | 468,567 |
70
+ | listenTo/stopListening | 0.184 | 544,218 |
71
+
72
+ ## Iteration 4 - listeningTo Set
73
+
74
+ Change
75
+ Replace `listeningTo` Map + id tracking with a `Set` of emitters.
76
+
77
+ Results
78
+ | Case | Median (ms) | Ops/s |
79
+ | --- | --- | --- |
80
+ | trigger (no listeners) | 1.654 | 30,228,211 |
81
+ | trigger (1 listener) | 5.888 | 8,491,727 |
82
+ | trigger (10 listeners) | 21.555 | 2,319,598 |
83
+ | trigger (all listener) | 6.788 | 7,365,669 |
84
+ | trigger (pattern match) | 22.498 | 2,222,387 |
85
+ | on/off (100 listeners) | 0.118 | 1,696,713 |
86
+ | listenTo/stopListening | 0.138 | 726,612 |
87
+
88
+ ## Iteration 5 - events Map vs object check
89
+
90
+ Change
91
+ Dedicated microbench comparing Map vs object for set/get/delete workload.
92
+
93
+ Results
94
+ | Structure | Median (ms) | Ops/s |
95
+ | --- | --- | --- |
96
+ | Map | 5.649 | 53,107,130 |
97
+ | Object | 10.263 | 29,230,151 |
98
+
99
+ Decision
100
+ Keep `Map` for `events` storage (faster on this workload).
101
+
102
+ ## Iteration 6 - Pattern matching algorithms
103
+
104
+ Change
105
+ Compare pattern matching strategies for wildcard-heavy workloads.
106
+
107
+ Results (patterns=300, events=900)
108
+ Suite: mixed wildcards
109
+ | Strategy | Median (ms) | Ops/s |
110
+ | --- | --- | --- |
111
+ | segments (precomputed) | 4.155 | 64,982,606 |
112
+ | split per match | 94.772 | 2,848,954 |
113
+ | regex (compiled) | 9.698 | 27,839,833 |
114
+
115
+ Suite: trailing wildcard only
116
+ | Strategy | Median (ms) | Ops/s |
117
+ | --- | --- | --- |
118
+ | segments (precomputed) | 5.181 | 52,117,686 |
119
+ | split per match | 80.489 | 3,354,478 |
120
+ | regex (compiled) | 15.189 | 17,775,827 |
121
+ | prefix (trailing `*`) | 3.518 | 76,737,246 |
122
+
123
+ Decision
124
+ Keep precomputed segment matching for correctness and overall speed across mixed patterns. Prefix-only checks are faster for trailing-only patterns but do not cover middle wildcards.
125
+
126
+ ## Iteration 7 - Hybrid prefix matcher for trailing wildcards
127
+
128
+ Change
129
+ Precompile trailing-wildcard-only patterns to prefix matchers while keeping segment matching for mixed wildcards.
130
+
131
+ Results (eventify microbench)
132
+ | Case | Median (ms) | Ops/s |
133
+ | --- | --- | --- |
134
+ | trigger (no listeners) | 1.723 | 29,017,755 |
135
+ | trigger (1 listener) | 6.185 | 8,084,019 |
136
+ | trigger (10 listeners) | 22.503 | 2,221,918 |
137
+ | trigger (all listener) | 6.519 | 7,669,348 |
138
+ | trigger (pattern match) | 21.779 | 2,295,768 |
139
+ | on/off (100 listeners) | 0.109 | 1,841,197 |
140
+ | listenTo/stopListening | 0.141 | 711,111 |
141
+
142
+ Results (patterns=300, events=900)
143
+ Suite: mixed wildcards
144
+ | Strategy | Median (ms) | Ops/s |
145
+ | --- | --- | --- |
146
+ | segments (precomputed) | 4.429 | 60,968,147 |
147
+ | split per match | 94.170 | 2,867,156 |
148
+ | regex (compiled) | 9.993 | 27,018,575 |
149
+
150
+ Suite: trailing wildcard only
151
+ | Strategy | Median (ms) | Ops/s |
152
+ | --- | --- | --- |
153
+ | segments (precomputed) | 5.353 | 50,437,828 |
154
+ | split per match | 87.348 | 3,091,072 |
155
+ | regex (compiled) | 15.264 | 17,689,162 |
156
+ | prefix (trailing `*`) | 3.616 | 74,660,399 |
157
+
158
+ Decision
159
+ Use prefix matching for trailing-only wildcard patterns and keep segment matching for mixed wildcards.
160
+
161
+ ## Iteration 8 - Skip EventTarget dispatch without native listeners
162
+
163
+ Change
164
+ Call Eventify listeners directly when there are no native `addEventListener` listeners, and dispatch `CustomEvent`s only when native listeners exist.
165
+
166
+ Results (eventify microbench)
167
+ | Case | Median (ms) | Ops/s |
168
+ | --- | --- | --- |
169
+ | trigger (no listeners) | 1.517 | 32,960,702 |
170
+ | trigger (1 listener) | 5.710 | 8,756,247 |
171
+ | trigger (10 listeners) | 20.007 | 2,499,131 |
172
+ | trigger (all listener) | 6.570 | 7,610,833 |
173
+ | trigger (pattern match) | 21.654 | 2,309,033 |
174
+ | on/off (100 listeners) | 0.102 | 1,955,187 |
175
+ | listenTo/stopListening | 0.266 | 376,176 |
176
+
177
+ Decision
178
+ Keep the hybrid path to preserve EventTarget interop without regressing baseline emit throughput.
179
+
180
+ ## Summary
181
+
182
+ Kept precomputed pattern segments and the `listeningTo` Set. The `isPatternName` fast path keeps wildcard-free paths cheap. Map remains the best fit for event storage based on the structure microbench, and the runtime now uses a hybrid pattern matcher: prefix checks for trailing-only wildcards and segment matching for mixed wildcards. EventTarget dispatch is skipped unless native listeners exist to avoid overhead on the hot path.
183
+
184
+ ## Strategy Summary (Quick Matrix)
185
+
186
+ | Area | Strategy | Most Efficient | Implemented |
187
+ | ------------------------------------ | ------------------------------------------------ | ------------------------------------------- | ------------------------------- |
188
+ | Event storage | `Map` vs object | `Map` | `Map` |
189
+ | Pattern matching (mixed wildcards) | precomputed segments vs split-per-match vs regex | precomputed segments | precomputed segments |
190
+ | Pattern matching (trailing `*` only) | prefix vs segments vs regex | prefix | prefix (only for trailing-only) |
191
+ | Dispatch path | always `EventTarget` vs hybrid direct call | hybrid direct call when no native listeners | hybrid direct call |
192
+
193
+ Notes
194
+
195
+ - Mixed-wildcard patterns fall back to segment matching for correctness across arbitrary wildcard positions.
196
+ - Prefix-only matching is used only when the pattern ends with a trailing wildcard and has no internal wildcards.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## [3.0.0] - 2026-02-06
4
+
5
+ ### Breaking
6
+
7
+ - ESM-only distribution. CommonJS `require()` and global/UMD builds are no longer provided.
8
+ - Browser build output moved to `dist/index.js` (the legacy `dist/eventify.min.js` is no longer produced).
9
+ - Listener errors no longer crash by default; they are swallowed unless you provide `onError`.
10
+ - Node requirement is now >= 20.
11
+
12
+ ### Added
13
+
14
+ - Strict TypeScript typings for all APIs.
15
+ - Optional schema validation via DI (`schemas` + `validate`) with `defaultSchemaValidator` / `setDefaultSchemaValidator` for Zod-compatible `parse`/`safeParse`.
16
+ - Namespaced event wildcards with configurable `namespaceDelimiter` and `wildcard`.
17
+ - `emit` and `produce` aliases for `trigger`.
18
+ - Async iterator API via `iterate()`.
19
+ - EventTarget interop (`addEventListener`, `removeEventListener`, `dispatchEvent`) with payloads in `CustomEvent.detail`.
20
+ - Named exports: `createEmitter`, `decorateWithEvents`, `setDefaultSchemaValidator`.
21
+
22
+ ### Migration Notes (2.x -> 3.x)
23
+
24
+ - Replace script-tag usage with ESM imports, or bundle `dist/index.js` into your app.
25
+ - If you previously relied on thrown listener errors, provide `onError` and re-throw in your handler if desired.
26
+ - Update your tooling to Node 20+.
27
+ - If you want schema validation, pass `schemas` and either use `defaultSchemaValidator` or your own validator.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bermi Ferrer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.