mutts 1.0.0 → 1.0.1

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.
Files changed (53) hide show
  1. package/dist/chunks/{decorator-BXsign4Z.js → decorator-8qjFb7dw.js} +2 -2
  2. package/dist/chunks/decorator-8qjFb7dw.js.map +1 -0
  3. package/dist/chunks/{decorator-CPbZNnsX.esm.js → decorator-AbRkXM5O.esm.js} +2 -2
  4. package/dist/chunks/decorator-AbRkXM5O.esm.js.map +1 -0
  5. package/dist/decorator.d.ts +1 -1
  6. package/dist/decorator.esm.js +1 -1
  7. package/dist/decorator.js +1 -1
  8. package/dist/destroyable.esm.js +1 -1
  9. package/dist/destroyable.js +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.esm.js +2 -2
  12. package/dist/index.js +2 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/mutts.umd.js +1 -1
  15. package/dist/mutts.umd.js.map +1 -1
  16. package/dist/mutts.umd.min.js +1 -1
  17. package/dist/mutts.umd.min.js.map +1 -1
  18. package/dist/reactive.d.ts +4 -3
  19. package/dist/reactive.esm.js +61 -57
  20. package/dist/reactive.esm.js.map +1 -1
  21. package/dist/reactive.js +61 -56
  22. package/dist/reactive.js.map +1 -1
  23. package/dist/std-decorators.esm.js +1 -1
  24. package/dist/std-decorators.js +1 -1
  25. package/docs/reactive.md +616 -0
  26. package/package.json +1 -2
  27. package/dist/chunks/decorator-BXsign4Z.js.map +0 -1
  28. package/dist/chunks/decorator-CPbZNnsX.esm.js.map +0 -1
  29. package/src/decorator.test.ts +0 -495
  30. package/src/decorator.ts +0 -205
  31. package/src/destroyable.test.ts +0 -155
  32. package/src/destroyable.ts +0 -158
  33. package/src/eventful.test.ts +0 -380
  34. package/src/eventful.ts +0 -69
  35. package/src/index.ts +0 -7
  36. package/src/indexable.test.ts +0 -388
  37. package/src/indexable.ts +0 -124
  38. package/src/promiseChain.test.ts +0 -201
  39. package/src/promiseChain.ts +0 -99
  40. package/src/reactive/array.test.ts +0 -923
  41. package/src/reactive/array.ts +0 -352
  42. package/src/reactive/core.test.ts +0 -1663
  43. package/src/reactive/core.ts +0 -866
  44. package/src/reactive/index.ts +0 -28
  45. package/src/reactive/interface.test.ts +0 -1477
  46. package/src/reactive/interface.ts +0 -231
  47. package/src/reactive/map.test.ts +0 -866
  48. package/src/reactive/map.ts +0 -162
  49. package/src/reactive/set.test.ts +0 -289
  50. package/src/reactive/set.ts +0 -142
  51. package/src/std-decorators.test.ts +0 -679
  52. package/src/std-decorators.ts +0 -182
  53. package/src/utils.ts +0 -52
@@ -1,380 +0,0 @@
1
- import { Eventful } from './eventful'
2
-
3
- describe('Eventful', () => {
4
- // Define test event types
5
- interface TestEvents extends Record<string, (...args: any[]) => void> {
6
- userLogin: (userId: string, timestamp: Date) => void
7
- dataUpdate: (data: any[]) => void
8
- error: (error: Error) => void
9
- simple: () => void
10
- withNumber: (value: number) => void
11
- }
12
-
13
- class TestEventful extends Eventful<TestEvents> {
14
- // Test class that extends Eventful
15
- // Just make `emit` public for testing
16
- public emit<EventType extends keyof TestEvents>(
17
- event: EventType,
18
- ...args: Parameters<TestEvents[EventType]>
19
- ) {
20
- super.emit(event, ...args)
21
- }
22
- }
23
-
24
- describe('basic functionality', () => {
25
- it('should register and emit single events', () => {
26
- const eventful = new TestEventful()
27
- let callCount = 0
28
- let receivedUserId = ''
29
- let receivedTimestamp: Date | null = null
30
-
31
- eventful.on('userLogin', (userId, timestamp) => {
32
- callCount++
33
- receivedUserId = userId
34
- receivedTimestamp = timestamp
35
- })
36
-
37
- const testDate = new Date()
38
- eventful.emit('userLogin', 'user123', testDate)
39
-
40
- expect(callCount).toBe(1)
41
- expect(receivedUserId).toBe('user123')
42
- expect(receivedTimestamp).toBe(testDate)
43
- })
44
-
45
- it('should register and emit events with no parameters', () => {
46
- const eventful = new TestEventful()
47
- let callCount = 0
48
-
49
- eventful.on('simple', () => {
50
- callCount++
51
- })
52
-
53
- eventful.emit('simple')
54
-
55
- expect(callCount).toBe(1)
56
- })
57
-
58
- it('should register and emit events with primitive parameters', () => {
59
- const eventful = new TestEventful()
60
- let receivedValue = 0
61
-
62
- eventful.on('withNumber', (value) => {
63
- receivedValue = value
64
- })
65
-
66
- eventful.emit('withNumber', 42)
67
-
68
- expect(receivedValue).toBe(42)
69
- })
70
- })
71
-
72
- describe('multiple listeners', () => {
73
- it('should support multiple listeners for the same event', () => {
74
- const eventful = new TestEventful()
75
- let callCount1 = 0
76
- let callCount2 = 0
77
-
78
- eventful.on('simple', () => callCount1++)
79
- eventful.on('simple', () => callCount2++)
80
-
81
- eventful.emit('simple')
82
-
83
- expect(callCount1).toBe(1)
84
- expect(callCount2).toBe(1)
85
- })
86
-
87
- it('should call all listeners in registration order', () => {
88
- const eventful = new TestEventful()
89
- const callOrder: number[] = []
90
-
91
- eventful.on('simple', () => callOrder.push(1))
92
- eventful.on('simple', () => callOrder.push(2))
93
- eventful.on('simple', () => callOrder.push(3))
94
-
95
- eventful.emit('simple')
96
-
97
- expect(callOrder).toEqual([1, 2, 3])
98
- })
99
- })
100
-
101
- describe('unsubscribe functionality', () => {
102
- it('should return unsubscribe function for single event', () => {
103
- const eventful = new TestEventful()
104
- let callCount = 0
105
-
106
- const unsubscribe = eventful.on('simple', () => callCount++)
107
-
108
- eventful.emit('simple')
109
- expect(callCount).toBe(1)
110
-
111
- unsubscribe()
112
- eventful.emit('simple')
113
- expect(callCount).toBe(1) // Should not increment
114
- })
115
-
116
- it('should unsubscribe specific callback when provided', () => {
117
- const eventful = new TestEventful()
118
- let callCount1 = 0
119
- let callCount2 = 0
120
-
121
- const callback1 = () => callCount1++
122
- const callback2 = () => callCount2++
123
-
124
- eventful.on('simple', callback1)
125
- eventful.on('simple', callback2)
126
-
127
- eventful.emit('simple')
128
- expect(callCount1).toBe(1)
129
- expect(callCount2).toBe(1)
130
-
131
- eventful.off('simple', callback1)
132
- eventful.emit('simple')
133
- expect(callCount1).toBe(1) // Should not increment
134
- expect(callCount2).toBe(2) // Should still increment
135
- })
136
-
137
- it('should remove all listeners when no callback provided to off', () => {
138
- const eventful = new TestEventful()
139
- let callCount1 = 0
140
- let callCount2 = 0
141
-
142
- eventful.on('simple', () => callCount1++)
143
- eventful.on('simple', () => callCount2++)
144
-
145
- eventful.emit('simple')
146
- expect(callCount1).toBe(1)
147
- expect(callCount2).toBe(1)
148
-
149
- eventful.off('simple')
150
- eventful.emit('simple')
151
- expect(callCount1).toBe(1) // Should not increment
152
- expect(callCount2).toBe(1) // Should not increment
153
- })
154
- })
155
-
156
- describe('bulk event registration', () => {
157
- it('should register multiple events at once', () => {
158
- const eventful = new TestEventful()
159
- let loginCount = 0
160
- let dataCount = 0
161
- let errorCount = 0
162
-
163
- eventful.on({
164
- userLogin: () => loginCount++,
165
- dataUpdate: () => dataCount++,
166
- error: () => errorCount++,
167
- })
168
-
169
- eventful.emit('userLogin', 'user123', new Date())
170
- eventful.emit('dataUpdate', [1, 2, 3])
171
- eventful.emit('error', new Error('test'))
172
-
173
- expect(loginCount).toBe(1)
174
- expect(dataCount).toBe(1)
175
- expect(errorCount).toBe(1)
176
- })
177
-
178
- it('should unsubscribe multiple events at once', () => {
179
- const eventful = new TestEventful()
180
- let loginCount = 0
181
- let dataCount = 0
182
-
183
- const loginCallback = () => loginCount++
184
- const dataCallback = () => dataCount++
185
-
186
- eventful.on('userLogin', loginCallback)
187
- eventful.on('dataUpdate', dataCallback)
188
-
189
- eventful.emit('userLogin', 'user123', new Date())
190
- eventful.emit('dataUpdate', [1, 2, 3])
191
- expect(loginCount).toBe(1)
192
- expect(dataCount).toBe(1)
193
-
194
- eventful.off({
195
- userLogin: loginCallback,
196
- dataUpdate: dataCallback,
197
- })
198
-
199
- eventful.emit('userLogin', 'user123', new Date())
200
- eventful.emit('dataUpdate', [1, 2, 3])
201
- expect(loginCount).toBe(1) // Should not increment
202
- expect(dataCount).toBe(1) // Should not increment
203
- })
204
- })
205
-
206
- describe('global hooks', () => {
207
- it('should call global hooks for all events', () => {
208
- const eventful = new TestEventful()
209
- const hookCalls: Array<{ event: string; args: any[] }> = []
210
-
211
- eventful.hook((event, ...args) => {
212
- hookCalls.push({ event: String(event), args })
213
- })
214
-
215
- eventful.emit('userLogin', 'user123', new Date())
216
- eventful.emit('simple')
217
- eventful.emit('withNumber', 42)
218
-
219
- expect(hookCalls).toHaveLength(3)
220
- expect(hookCalls[0].event).toBe('userLogin')
221
- expect(hookCalls[0].args).toEqual(['user123', expect.any(Date)])
222
- expect(hookCalls[1].event).toBe('simple')
223
- expect(hookCalls[1].args).toEqual([])
224
- expect(hookCalls[2].event).toBe('withNumber')
225
- expect(hookCalls[2].args).toEqual([42])
226
- })
227
-
228
- it('should support multiple global hooks', () => {
229
- const eventful = new TestEventful()
230
- let hook1Count = 0
231
- let hook2Count = 0
232
-
233
- eventful.hook(() => hook1Count++)
234
- eventful.hook(() => hook2Count++)
235
-
236
- eventful.emit('simple')
237
-
238
- expect(hook1Count).toBe(1)
239
- expect(hook2Count).toBe(1)
240
- })
241
-
242
- it('should return unsubscribe function for hooks', () => {
243
- const eventful = new TestEventful()
244
- let hookCount = 0
245
-
246
- const unsubscribe = eventful.hook(() => hookCount++)
247
-
248
- eventful.emit('simple')
249
- expect(hookCount).toBe(1)
250
-
251
- unsubscribe()
252
- eventful.emit('simple')
253
- expect(hookCount).toBe(1) // Should not increment
254
- })
255
-
256
- it('should prevent duplicate hook registration', () => {
257
- const eventful = new TestEventful()
258
- let hookCount = 0
259
-
260
- const hookCallback = () => hookCount++
261
-
262
- eventful.hook(hookCallback)
263
- eventful.hook(hookCallback) // Should not add duplicate
264
-
265
- eventful.emit('simple')
266
- expect(hookCount).toBe(1) // Should only be called once
267
- })
268
- })
269
-
270
- describe('combined functionality', () => {
271
- it('should call both specific listeners and global hooks', () => {
272
- const eventful = new TestEventful()
273
- let specificCount = 0
274
- let hookCount = 0
275
-
276
- eventful.on('simple', () => specificCount++)
277
- eventful.hook(() => hookCount++)
278
-
279
- eventful.emit('simple')
280
-
281
- expect(specificCount).toBe(1)
282
- expect(hookCount).toBe(1)
283
- })
284
-
285
- it('should handle complex event scenarios', () => {
286
- const eventful = new TestEventful()
287
- const events: string[] = []
288
-
289
- // Register multiple listeners
290
- eventful.on('userLogin', () => events.push('login1'))
291
- eventful.on('userLogin', () => events.push('login2'))
292
- eventful.on('dataUpdate', () => events.push('data1'))
293
-
294
- // Register global hook
295
- eventful.hook((event, ..._args) => {
296
- events.push(`hook:${String(event)}`)
297
- })
298
-
299
- // Emit events
300
- eventful.emit('userLogin', 'user123', new Date())
301
- eventful.emit('dataUpdate', [1, 2, 3])
302
-
303
- expect(events).toEqual(['login1', 'login2', 'hook:userLogin', 'data1', 'hook:dataUpdate'])
304
- })
305
- })
306
-
307
- describe('edge cases', () => {
308
- it('should handle emitting events with no listeners', () => {
309
- const eventful = new TestEventful()
310
-
311
- // Should not throw
312
- expect(() => {
313
- eventful.emit('userLogin', 'user123', new Date())
314
- eventful.emit('simple')
315
- }).not.toThrow()
316
- })
317
-
318
- it('should handle unsubscribing non-existent callbacks', () => {
319
- const eventful = new TestEventful()
320
-
321
- // Should not throw
322
- expect(() => {
323
- eventful.off('simple', () => {})
324
- eventful.off('simple')
325
- }).not.toThrow()
326
- })
327
-
328
- it('should handle unsubscribing from non-existent events', () => {
329
- const eventful = new TestEventful()
330
-
331
- // Should not throw
332
- expect(() => {
333
- eventful.off('nonExistentEvent' as any)
334
- }).not.toThrow()
335
- })
336
-
337
- it('should handle multiple unsubscribes of the same callback', () => {
338
- const eventful = new TestEventful()
339
- let callCount = 0
340
-
341
- const callback = () => callCount++
342
- const unsubscribe = eventful.on('simple', callback)
343
-
344
- eventful.emit('simple')
345
- expect(callCount).toBe(1)
346
-
347
- unsubscribe()
348
- unsubscribe() // Should not throw
349
- unsubscribe() // Should not throw
350
-
351
- eventful.emit('simple')
352
- expect(callCount).toBe(1) // Should not increment
353
- })
354
- })
355
-
356
- describe('type safety', () => {
357
- it('should enforce correct parameter types', () => {
358
- const eventful = new TestEventful()
359
-
360
- // These should compile without errors
361
- eventful.on('userLogin', (userId: string, timestamp: Date) => {
362
- expect(typeof userId).toBe('string')
363
- expect(timestamp).toBeInstanceOf(Date)
364
- })
365
-
366
- eventful.on('dataUpdate', (data: any[]) => {
367
- expect(Array.isArray(data)).toBe(true)
368
- })
369
-
370
- eventful.on('error', (error: Error) => {
371
- expect(error).toBeInstanceOf(Error)
372
- })
373
-
374
- // Emit with correct types
375
- eventful.emit('userLogin', 'user123', new Date())
376
- eventful.emit('dataUpdate', [1, 2, 3])
377
- eventful.emit('error', new Error('test'))
378
- })
379
- })
380
- })
package/src/eventful.ts DELETED
@@ -1,69 +0,0 @@
1
- export class Eventful<Events extends Record<string, (...args: any[]) => void>> {
2
- readonly #events = new Map<keyof Events, ((...args: any[]) => void)[]>()
3
- readonly #hooks = [] as ((...args: any[]) => void)[]
4
-
5
- public hook(
6
- cb: <EventType extends keyof Events>(
7
- event: EventType,
8
- ...args: Parameters<Events[EventType]>
9
- ) => void
10
- ): () => void {
11
- if (!this.#hooks.includes(cb)) this.#hooks.push(cb)
12
- return () => {
13
- this.#hooks.splice(this.#hooks.indexOf(cb), 1)
14
- }
15
- }
16
-
17
- public on(events: Partial<Events>): void
18
- public on<EventType extends keyof Events>(event: EventType, cb: Events[EventType]): () => void
19
- public on<EventType extends keyof Events>(
20
- eventOrEvents: EventType | Partial<Events>,
21
- cb?: Events[EventType]
22
- ): () => void {
23
- if (typeof eventOrEvents === 'object') {
24
- for (const e of Object.keys(eventOrEvents) as (keyof Events)[]) {
25
- this.on(e, eventOrEvents[e]!)
26
- }
27
- } else if (cb !== undefined) {
28
- let callbacks = this.#events.get(eventOrEvents)
29
- if (!callbacks) {
30
- callbacks = []
31
- this.#events.set(eventOrEvents, callbacks)
32
- }
33
- callbacks.push(cb)
34
- }
35
- // @ts-expect-error Generic case leads to generic case
36
- return () => this.off(eventOrEvents, cb)
37
- }
38
- public off(events: Partial<Events>): void
39
- public off<EventType extends keyof Events>(event: EventType, cb?: Events[EventType]): void
40
- public off<EventType extends keyof Events>(
41
- eventOrEvents: EventType | Partial<Events>,
42
- cb?: Events[EventType]
43
- ): void {
44
- if (typeof eventOrEvents === 'object') {
45
- for (const e of Object.keys(eventOrEvents) as (keyof Events)[]) {
46
- this.off(e, eventOrEvents[e])
47
- }
48
- } else if (cb !== null && cb !== undefined) {
49
- const callbacks = this.#events.get(eventOrEvents)
50
- if (callbacks) {
51
- this.#events.set(
52
- eventOrEvents,
53
- callbacks.filter((c) => c !== cb)
54
- )
55
- }
56
- } else {
57
- // Remove all listeners for this event
58
- this.#events.delete(eventOrEvents)
59
- }
60
- }
61
- public emit<EventType extends keyof Events>(
62
- event: EventType,
63
- ...args: Parameters<Events[EventType]>
64
- ) {
65
- const callbacks = this.#events.get(event)
66
- if (callbacks) for (const cb of callbacks) cb.apply(this, args)
67
- for (const cb of this.#hooks) cb.call(this, event, ...args)
68
- }
69
- }
package/src/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export * from './decorator'
2
- export * from './destroyable'
3
- export * from './eventful'
4
- export * from './indexable'
5
- export * from './reactive'
6
- export * from './std-decorators'
7
- export * from './utils'