fastevent 1.1.0 → 1.1.2

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 (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/devTools.d.mts +543 -0
  3. package/dist/devTools.d.ts +543 -0
  4. package/dist/devTools.js +3 -0
  5. package/dist/devTools.js.map +1 -0
  6. package/dist/devTools.mjs +3 -0
  7. package/dist/devTools.mjs.map +1 -0
  8. package/dist/index.d.mts +282 -37
  9. package/dist/index.d.ts +282 -37
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.mjs.map +1 -1
  14. package/example/README.md +54 -0
  15. package/example/eslint.config.js +28 -0
  16. package/example/index.html +13 -0
  17. package/example/package.json +29 -0
  18. package/example/pnpm-lock.yaml +2047 -0
  19. package/example/public/vite.svg +1 -0
  20. package/example/src/App.css +42 -0
  21. package/example/src/App.tsx +60 -0
  22. package/example/src/assets/react.svg +1 -0
  23. package/example/src/index.css +68 -0
  24. package/example/src/main.tsx +10 -0
  25. package/example/src/myEvent.ts +32 -0
  26. package/example/src/vite-env.d.ts +1 -0
  27. package/example/tsconfig.app.json +26 -0
  28. package/example/tsconfig.json +7 -0
  29. package/example/tsconfig.node.json +24 -0
  30. package/example/vite.config.ts +7 -0
  31. package/package.json +15 -2
  32. package/packages/native/index.ts +1 -0
  33. package/packages/turbo/.zig-cache/h/271c82d991949fd7788fd5451f0ca834.txt +0 -0
  34. package/packages/turbo/.zig-cache/h/timestamp +0 -0
  35. package/packages/turbo/.zig-cache/o/ebd7ddab8ffe003267120d598aecce68/dependencies.zig +2 -0
  36. package/packages/turbo/.zig-cache/z/c8114b040daa461a9e2eabd0357554a4 +0 -0
  37. package/packages/turbo/build.zig +60 -0
  38. package/packages/turbo/examples/basic.zig +107 -0
  39. package/packages/turbo/src/event.zig +251 -0
  40. package/packages/turbo/src/index.zig +70 -0
  41. package/packages/turbo/src/scope.zig +104 -0
  42. package/packages/turbo/src/types.zig +88 -0
  43. package/packages/turbo/src/utils.zig +171 -0
  44. package/readme.md +86 -2
  45. package/readme_cn.md +86 -2
  46. package/src/__tests__/scope.test.ts +2 -2
  47. package/src/__tests__/types.test.ts +23 -12
  48. package/src/__tests__/waitFor.test.ts +1 -0
  49. package/src/devTools.ts +166 -0
  50. package/src/event.ts +272 -38
  51. package/src/scope.ts +52 -11
  52. package/src/types.ts +11 -4
  53. package/src/utils/WeakObjectMap.ts +64 -0
  54. package/tsconfig.json +103 -111
  55. package/tsup.config.ts +17 -6
package/readme.md CHANGED
@@ -346,9 +346,23 @@ events.on('process', () => {
346
346
  events.emit('process');
347
347
  ```
348
348
 
349
- ## TypeScript Support
349
+ ## Generic Parameters
350
350
 
351
- FastEvent is written in TypeScript and provides full type support:
351
+ FastEvent supports three generic type parameters for precise type control:
352
+
353
+ ```typescript
354
+ class FastEvent<
355
+ Events extends Record<string, any> = Record<string, any>,
356
+ Meta extends Record<string, any> = Record<string, any>,
357
+ Types extends keyof Events = keyof Events
358
+ >
359
+ ```
360
+
361
+ 1. `Events`: Defines the mapping between event types and their payload types
362
+ 2. `Meta`: Defines the type of metadata that can be attached to events
363
+ 3. `Types`: The union type of all event types (usually inferred from Events)
364
+
365
+ ### Basic Type Safety
352
366
 
353
367
  ```typescript
354
368
  // Define event types
@@ -373,6 +387,59 @@ events.emit('wrong/event', {});
373
387
  events.emit('user/login', { wrong: 'type' });
374
388
  ```
375
389
 
390
+ ### Custom Metadata Types
391
+
392
+ ```typescript
393
+ // Define metadata structure
394
+ interface MyMeta {
395
+ timestamp: number;
396
+ source: string;
397
+ }
398
+
399
+ // Define events with custom metadata
400
+ const events = new FastEvent<MyEvents, MyMeta>();
401
+
402
+ events.on('user/login', (message) => {
403
+ // message.meta is typed as MyMeta
404
+ const { timestamp, source } = message.meta;
405
+ console.log(`Login from ${source} at ${timestamp}`);
406
+ });
407
+
408
+ // Emit with typed metadata
409
+ events.emit('user/login', { id: 1, name: 'Alice' }, false, { timestamp: Date.now(), source: 'web' });
410
+ ```
411
+
412
+ ### Advanced Type Usage
413
+
414
+ ```typescript
415
+ // Define events with different payload types
416
+ interface ComplexEvents {
417
+ 'data/number': number;
418
+ 'data/string': string;
419
+ 'data/object': { value: any };
420
+ }
421
+
422
+ const events = new FastEvent<ComplexEvents>();
423
+
424
+ // TypeScript ensures type safety for each event
425
+ events.on('data/number', (message) => {
426
+ const sum = message.payload + 1; // payload is typed as number
427
+ });
428
+
429
+ events.on('data/string', (message) => {
430
+ const upper = message.payload.toUpperCase(); // payload is typed as string
431
+ });
432
+
433
+ events.on('data/object', (message) => {
434
+ const value = message.payload.value; // payload is typed as { value: any }
435
+ });
436
+
437
+ // All emissions are type checked
438
+ events.emit('data/number', 42);
439
+ events.emit('data/string', 'hello');
440
+ events.emit('data/object', { value: true });
441
+ ```
442
+
376
443
  ## Custom Options
377
444
 
378
445
  The FastEvent constructor supports multiple options:
@@ -399,6 +466,23 @@ const events = new FastEvent({
399
466
  onRemoveListener: (path, listener) => {
400
467
  console.log('Listener removed:', path);
401
468
  }
469
+ onClearListeners: () => {
470
+ console.log('清空监听器:', path);
471
+ },
472
+ onExecuteListener: (message: FastEventMessage, returns: any[], listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[]) => {
473
+ console.log('监听器执行后的回调:');
474
+ }
475
+ });
476
+ ```
477
+
478
+ ### debug
479
+
480
+ Use `debug` option to enable debug mode and import `fastevent/devtools`, so that you can see the events in `Redux Dev Tools`.
481
+
482
+ ```ts
483
+ import 'fastevent/devtools';
484
+ const emitter = new FastEvent({
485
+ debug: true,
402
486
  });
403
487
  ```
404
488
 
package/readme_cn.md CHANGED
@@ -346,9 +346,23 @@ events.on('process', () => {
346
346
  events.emit('process');
347
347
  ```
348
348
 
349
- ## TypeScript 类型支持
349
+ ## 泛型参数
350
350
 
351
- FastEvent 使用 TypeScript 编写,提供完整的类型支持:
351
+ FastEvent 支持三个泛型类型参数来实现精确的类型控制:
352
+
353
+ ```typescript
354
+ class FastEvent<
355
+ Events extends Record<string, any> = Record<string, any>,
356
+ Meta extends Record<string, any> = Record<string, any>,
357
+ Types extends keyof Events = keyof Events
358
+ >
359
+ ```
360
+
361
+ 1. `Events`:定义事件类型和对应的载荷(payload)类型的映射
362
+ 2. `Meta`:定义可以附加到事件的元数据类型
363
+ 3. `Types`:所有事件类型的联合类型(通常从 Events 中推导)
364
+
365
+ ### 基本类型安全
352
366
 
353
367
  ```typescript
354
368
  // 定义事件类型
@@ -373,6 +387,59 @@ events.emit('wrong/event', {});
373
387
  events.emit('user/login', { wrong: 'type' });
374
388
  ```
375
389
 
390
+ ### 自定义元数据类型
391
+
392
+ ```typescript
393
+ // 定义元数据结构
394
+ interface MyMeta {
395
+ timestamp: number;
396
+ source: string;
397
+ }
398
+
399
+ // 创建带自定义元数据的事件发射器
400
+ const events = new FastEvent<MyEvents, MyMeta>();
401
+
402
+ events.on('user/login', (message) => {
403
+ // message.meta 的类型为 MyMeta
404
+ const { timestamp, source } = message.meta;
405
+ console.log(`来自 ${source} 的登录,时间:${timestamp}`);
406
+ });
407
+
408
+ // 发送带类型化元数据的事件
409
+ events.emit('user/login', { id: 1, name: 'Alice' }, false, { timestamp: Date.now(), source: 'web' });
410
+ ```
411
+
412
+ ### 高级类型用法
413
+
414
+ ```typescript
415
+ // 定义具有不同载荷类型的事件
416
+ interface ComplexEvents {
417
+ 'data/number': number;
418
+ 'data/string': string;
419
+ 'data/object': { value: any };
420
+ }
421
+
422
+ const events = new FastEvent<ComplexEvents>();
423
+
424
+ // TypeScript 确保每个事件的类型安全
425
+ events.on('data/number', (message) => {
426
+ const sum = message.payload + 1; // payload 的类型为 number
427
+ });
428
+
429
+ events.on('data/string', (message) => {
430
+ const upper = message.payload.toUpperCase(); // payload 的类型为 string
431
+ });
432
+
433
+ events.on('data/object', (message) => {
434
+ const value = message.payload.value; // payload 的类型为 { value: any }
435
+ });
436
+
437
+ // 所有的事件发送都会进行类型检查
438
+ events.emit('data/number', 42);
439
+ events.emit('data/string', 'hello');
440
+ events.emit('data/object', { value: true });
441
+ ```
442
+
376
443
  ## 自定义选项
377
444
 
378
445
  FastEvent 构造函数支持多个选项:
@@ -398,10 +465,27 @@ const events = new FastEvent({
398
465
  },
399
466
  onRemoveListener: (path, listener) => {
400
467
  console.log('移除监听器:', path);
468
+ },
469
+ onClearListeners: () => {
470
+ console.log('清空监听器:', path);
471
+ },
472
+ onExecuteListener: (message: FastEventMessage, returns: any[], listeners: (FastEventListener<any, any, any> | [FastEventListener<any, any>, number])[]) => {
473
+ console.log('监听器执行后的回调:');
401
474
  }
402
475
  });
403
476
  ```
404
477
 
478
+ ### 调试
479
+
480
+ 启用`debug=true`并导入`fastevent/devtools`,可以在`Redux Dev Tools`中查看事件.
481
+
482
+ ```ts
483
+ import 'fastevent/devtools';
484
+ const emitter = new FastEvent<MyEvents>({
485
+ debug: true,
486
+ });
487
+ ```
488
+
405
489
  # 性能
406
490
 
407
491
  ![](./bench.png)
@@ -95,14 +95,14 @@ describe("scope", () => {
95
95
  ]);
96
96
 
97
97
  expect(results[0].status).toBe('fulfilled');
98
- expect((results[0] as any).value).toEqual({ type: 'a/b/c/x', payload: 'payload1', meta: undefined });
98
+ expect((results[0] as any).value).toEqual({ type: 'x', payload: 'payload1', meta: undefined });
99
99
 
100
100
  expect(results[1].status).toBe('rejected');
101
101
  //@ts-ignore
102
102
  expect(results[1].reason).toBeInstanceOf(Error);
103
103
 
104
104
  expect(results[2].status).toBe('fulfilled');
105
- expect((results[2] as any).value).toEqual({ type: 'a/b/c/z', payload: 'payload3', meta: undefined });
105
+ expect((results[2] as any).value).toEqual({ type: 'z', payload: 'payload3', meta: undefined });
106
106
  });
107
107
 
108
108
 
@@ -85,6 +85,28 @@ describe("Types", () => {
85
85
  })
86
86
  emitter.emit("x/y/z", 1)
87
87
 
88
+ emitter.on("x/y/z", (message) => {
89
+ type cases = [
90
+ Expect<Equal<typeof message.type, "x/y/z">>,
91
+ Expect<Equal<typeof message.payload, unknown>>
92
+ ]
93
+ })
94
+
95
+ emitter.on("x/y/z/a", (message) => {
96
+ type cases = [
97
+ Expect<Equal<typeof message.type, "x/y/z/a">>,
98
+ Expect<Equal<typeof message.payload, 1>>
99
+ ]
100
+ })
101
+ emitter.waitFor("x/y/z/a").then((message) => {
102
+ type cases = [
103
+ Expect<Equal<typeof message.type, "x/y/z/a">>,
104
+ Expect<Equal<typeof message.payload, 1>>
105
+ ]
106
+ })
107
+
108
+
109
+
88
110
  // ----- scope -----
89
111
 
90
112
  const scope = emitter.scope("x/y/z")
@@ -116,19 +138,8 @@ describe("Types", () => {
116
138
  Expect<Equal<typeof message.payload, 3>>
117
139
  ]
118
140
  })
119
- emitter.on("x/y/z", (message) => {
120
- type cases = [
121
- Expect<Equal<typeof message.type, "x/y/z">>,
122
- Expect<Equal<typeof message.payload, unknown>>
123
- ]
124
- })
125
141
 
126
- emitter.on("x/y/z/a", (message) => {
127
- type cases = [
128
- Expect<Equal<typeof message.type, "x/y/z/a">>,
129
- Expect<Equal<typeof message.payload, 1>>
130
- ]
131
- })
142
+
132
143
 
133
144
  })
134
145
  })
@@ -53,6 +53,7 @@ describe("waitfor", () => {
53
53
  emitter.emit('event3', 'payload3');
54
54
  }, 300);
55
55
 
56
+
56
57
  // Assert
57
58
  Promise.all([
58
59
  event1Promise,
@@ -0,0 +1,166 @@
1
+ /**
2
+ *
3
+ * 基于开发工具
4
+ *
5
+ * Redux DevTools 是一个实用工具,用于开发和调试 Redux 应用程序。
6
+ * FLEXEVENT是基于Redux DevTools 的简单包装,可以让开发者使用Redux DevTools
7
+ * 来查看FLEXEVENT的状态变化
8
+ *
9
+ * import { createStore } from "@FLEXEVENTjs/react"
10
+ * import { install } from "@FLEXEVENTjs/devtools"
11
+ *
12
+ *
13
+ * const store = createStore({})
14
+ *
15
+ * install()
16
+ *
17
+ */
18
+
19
+ //@ts-ignore
20
+ import { legacy_createStore as createStore } from "redux"
21
+ import { WeakObjectMap } from "./utils/WeakObjectMap"
22
+ import { FastEvent } from "./event"
23
+
24
+ const initialState = {
25
+
26
+ }
27
+
28
+
29
+ function getDefaultFastEventState(instance: FastEvent) {
30
+ return {
31
+ messageCount: 0,
32
+ listenerCount: instance.listenerCount,
33
+ retainMessageCount: instance.retainedMessages.size,
34
+ }
35
+ }
36
+
37
+ export class FlexEventDevTools {
38
+ private reduxStore: any
39
+ private _installed: boolean = false
40
+ fastEvents = new WeakObjectMap()
41
+ constructor() {
42
+ this.install()
43
+ }
44
+ add(instance: FastEvent) {
45
+ this.fastEvents.set(instance.id, instance)
46
+
47
+ instance.options.onAddListener = (type: string[], listener: any) => {
48
+ this.reduxStore.dispatch({
49
+ type: "__ADD_LISTENER__",
50
+ event: type.join("/"),
51
+ listener,
52
+ fastEventId: instance.id
53
+ })
54
+ }
55
+ instance.options.onRemoveListener = (type: string[], listener: any) => {
56
+ this.reduxStore.dispatch({
57
+ type: "__REMOVE_LISTENER__",
58
+ event: type.join("/"),
59
+ listener,
60
+ fastEventId: instance.id
61
+ })
62
+ }
63
+ instance.options.onClearListeners = () => {
64
+ this.reduxStore.dispatch({
65
+ type: "__CLEAR_LISTENERS__",
66
+ fastEventId: instance.id
67
+ })
68
+ }
69
+ instance.options.onExecuteListener = (message, returns, listeners) => {
70
+ const results = returns.map(r => r instanceof Error ? `Error(${r.message})` : r)
71
+ const sresults = listeners.map(listener => (listener as any).name || 'anonymous')
72
+ .reduce((pre, cur, index) => {
73
+ pre[cur] = results[index]
74
+ return pre
75
+ }, {})
76
+ console.log(`FastEvent<\x1B[31m${message.type}<\x1B[30m> is emit, listeners:`, listeners)
77
+ this.reduxStore.dispatch({
78
+ type: message.type,
79
+ payload: message.payload,
80
+ meta: message.meta,
81
+ returns: sresults,
82
+ fastEventId: instance.id
83
+ })
84
+ }
85
+ this.reduxStore.dispatch({
86
+ type: "__ADD_FASTEVENT__",
87
+ fastEventId: instance.id
88
+ })
89
+ }
90
+ remove(instance: FastEvent) {
91
+ if (this.fastEvents.has(instance.id)) {
92
+ this.fastEvents.delete(instance.id)
93
+ }
94
+ }
95
+ private reducer(state: any = initialState, action: any) {
96
+ if (action.type.startsWith("@@")) return state
97
+ const instance = this.fastEvents.get(action.fastEventId) as FastEvent
98
+ if (action.type === '__ADD_FASTEVENT__') {
99
+ return {
100
+ ...state,
101
+ [action.fastEventId]: getDefaultFastEventState(instance)
102
+ }
103
+ } else if (action.type == '__ADD_LISTENER__') {
104
+ const eventState = state[action.fastEventId] || getDefaultFastEventState(instance)
105
+ eventState.listenerCount++
106
+ return {
107
+ ...state,
108
+ [action.fastEventId]: {
109
+ ...eventState
110
+ }
111
+ }
112
+ } else if (action.type == '__REMOVE_LISTENER__') {
113
+ const eventState = state[action.fastEventId]
114
+ if (!eventState) return state
115
+ eventState.listenerCount++
116
+ return {
117
+ ...state,
118
+ [action.fastEventId]: {
119
+ ...eventState
120
+ }
121
+ }
122
+ } else if (action.type === '__CLEAR_LISTENERS__') {
123
+ const eventState = state[action.fastEventId]
124
+ if (!eventState) return state
125
+ eventState.listenerCount++
126
+ return {
127
+ ...state,
128
+ [action.fastEventId]: getDefaultFastEventState(instance)
129
+ }
130
+ } else {
131
+ const eventState = state[action.fastEventId]
132
+ if (!eventState) return state
133
+ eventState.messageCount++
134
+ eventState.listenerCount = instance.listenerCount
135
+ eventState.retainMessageCount = instance.retainedMessages.size
136
+ return {
137
+ ...state,
138
+ [action.fastEventId]: { ...eventState }
139
+ }
140
+ }
141
+ }
142
+ private install() {
143
+ if (this._installed) return
144
+ this.reduxStore = createStore(
145
+ this.reducer.bind(this),
146
+ // @ts-ignore
147
+ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
148
+ );
149
+ this._installed = true
150
+ console.info('%c FlexEventDevTools installed. Please open <Redux devtools> to view. %c', "color:red;", '')
151
+ }
152
+ }
153
+
154
+ export function install() {
155
+ // @ts-ignore
156
+ if (!globalThis.__FLEXEVENT_DEVTOOLS__) globalThis.__FLEXEVENT_DEVTOOLS__ = new FlexEventDevTools()
157
+ }
158
+
159
+ declare global {
160
+ // @ts-ignore
161
+ var __FLEXEVENT_DEVTOOLS__: FlexEventDevTools
162
+ }
163
+
164
+
165
+ install()
166
+