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 +196 -0
- package/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +319 -160
- package/dist/index.d.ts +117 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +624 -0
- package/dist/index.js.map +10 -0
- package/package.json +54 -64
- package/.jshintrc +0 -35
- package/.npmignore +0 -8
- package/.travis.yml +0 -9
- package/Gruntfile.js +0 -53
- package/Makefile +0 -49
- package/dist/eventify.min.js +0 -1
- package/index.js +0 -1
- package/lib/ender.js +0 -8
- package/lib/eventify.js +0 -377
- package/test/browser-dist.html +0 -22
- package/test/browser.html +0 -23
- package/test/eventify.test.js +0 -730
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.
|