asljs-eventful 0.2.2 → 0.3.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.md +1 -1
- package/README.md +143 -13
- package/dist/eventful.js +8 -8
- package/dist/guards.d.ts +1 -1
- package/dist/guards.js +1 -1
- package/dist/types.d.ts +6 -6
- package/eventful.js +6 -1
- package/package.json +2 -1
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 Alexandrite Software Ltd
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -11,16 +11,140 @@ Lightweight event helper adding on/off/emit to any object.
|
|
|
11
11
|
npm install asljs-eventful
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
NPM Package: [asljs-eventful](https://www.npmjs.com/package/asljs-eventful)
|
|
15
|
+
|
|
14
16
|
## Usage
|
|
15
17
|
|
|
16
|
-
### Basic
|
|
18
|
+
### Basic (JavaScript)
|
|
19
|
+
|
|
20
|
+
Adding events to an object, add listeners, and emit events:
|
|
17
21
|
|
|
18
22
|
```js
|
|
19
23
|
import { eventful } from 'asljs-eventful';
|
|
20
24
|
|
|
21
25
|
const obj = eventful({ name: 'Alice' });
|
|
22
|
-
|
|
23
|
-
obj.
|
|
26
|
+
|
|
27
|
+
obj.on('greet',
|
|
28
|
+
msg => console.log(`${msg}, ${obj.name}!`));
|
|
29
|
+
|
|
30
|
+
// writes "Hello, Alice!" to console
|
|
31
|
+
obj.emit('greet', 'Hello');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Basic (TypeScript)
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { eventful, type Eventful } from 'asljs-eventful';
|
|
38
|
+
|
|
39
|
+
type Events =
|
|
40
|
+
{ greet: [msg: string] };
|
|
41
|
+
|
|
42
|
+
const obj: { name: string } & Eventful<Events> =
|
|
43
|
+
eventful({ name: 'Alice' });
|
|
44
|
+
|
|
45
|
+
obj.on('greet',
|
|
46
|
+
msg => console.log(`${msg}, ${obj.name}!`));
|
|
47
|
+
|
|
48
|
+
// writes "Hello, Alice!" to console
|
|
49
|
+
obj.emit('greet', 'Hello');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Inheritance (JavaScript)
|
|
53
|
+
|
|
54
|
+
Adding events to a class via inheritance:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import { EventfulBase } from 'asljs-eventful';
|
|
58
|
+
|
|
59
|
+
class MyClass extends EventfulBase {
|
|
60
|
+
constructor(name) {
|
|
61
|
+
super();
|
|
62
|
+
|
|
63
|
+
this.name = name;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
greet() {
|
|
67
|
+
this.emit(
|
|
68
|
+
'greet',
|
|
69
|
+
`Hello, ${this.name}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Inheritance (TypeScript)
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { EventfulBase } from 'asljs-eventful';
|
|
78
|
+
|
|
79
|
+
class MyClass extends EventfulBase {
|
|
80
|
+
name: string;
|
|
81
|
+
|
|
82
|
+
constructor(name: string) {
|
|
83
|
+
super();
|
|
84
|
+
|
|
85
|
+
this.name = name;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
greet() {
|
|
89
|
+
this.emit(
|
|
90
|
+
'greet',
|
|
91
|
+
`Hello, ${this.name}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Construction (JavaScript)
|
|
97
|
+
|
|
98
|
+
Adding events to an existing class during construction:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
import { eventful } from 'asljs-eventful';
|
|
102
|
+
|
|
103
|
+
export class MyClass {
|
|
104
|
+
constructor(name) {
|
|
105
|
+
eventful(this);
|
|
106
|
+
|
|
107
|
+
this.name = name;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
greet() {
|
|
111
|
+
this.emit(
|
|
112
|
+
'greet',
|
|
113
|
+
`Hello, ${this.name}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Construction (TypeScript)
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { eventful, type Eventful } from 'asljs-eventful';
|
|
122
|
+
|
|
123
|
+
type MyClassEvents =
|
|
124
|
+
{ greet: [message: string]; };
|
|
125
|
+
|
|
126
|
+
export class MyClass implements Eventful<MyClassEvents> {
|
|
127
|
+
name: string;
|
|
128
|
+
|
|
129
|
+
declare on: Eventful<MyClassEvents>['on'];
|
|
130
|
+
declare once: Eventful<MyClassEvents>['once'];
|
|
131
|
+
declare off: Eventful<MyClassEvents>['off'];
|
|
132
|
+
declare emit: Eventful<MyClassEvents>['emit'];
|
|
133
|
+
declare emitAsync: Eventful<MyClassEvents>['emitAsync'];
|
|
134
|
+
declare has: Eventful<MyClassEvents>['has'];
|
|
135
|
+
|
|
136
|
+
constructor(name: string) {
|
|
137
|
+
eventful(this);
|
|
138
|
+
|
|
139
|
+
this.name = name;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
greet() {
|
|
143
|
+
this.emit(
|
|
144
|
+
'greet',
|
|
145
|
+
`Hello, ${this.name}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
24
148
|
```
|
|
25
149
|
|
|
26
150
|
### Advanced Options
|
|
@@ -38,12 +162,12 @@ const obj =
|
|
|
38
162
|
payload);
|
|
39
163
|
} });
|
|
40
164
|
|
|
41
|
-
// Tracing
|
|
42
|
-
// - 'new' on creation
|
|
43
|
-
// - 'on' when subscribing
|
|
44
|
-
// - 'off' when unsubscribing
|
|
45
|
-
// - 'emit' for sync emit
|
|
46
|
-
// - 'emitAsync' for async emit
|
|
165
|
+
// Tracing (event, payload):
|
|
166
|
+
// - 'new' on creation, { object }
|
|
167
|
+
// - 'on' when subscribing, { object, event, listener }
|
|
168
|
+
// - 'off' when unsubscribing, { object, event, listener }
|
|
169
|
+
// - 'emit' for sync emit, { object, event, args, listeners }
|
|
170
|
+
// - 'emitAsync' for async emit, { object, event, args, listeners }
|
|
47
171
|
```
|
|
48
172
|
|
|
49
173
|
Custom error handler for listener errors:
|
|
@@ -71,7 +195,9 @@ const obj =
|
|
|
71
195
|
|
|
72
196
|
### Global Events
|
|
73
197
|
|
|
74
|
-
`eventful` is also a global emitter. When you create an enhanced object via
|
|
198
|
+
`eventful` is also a global emitter. When you create an enhanced object via
|
|
199
|
+
`eventful(target, options)`, its lifecycle and actions are traced via the
|
|
200
|
+
per-instance `trace` hook and also emitted as global events on `eventful`.
|
|
75
201
|
|
|
76
202
|
```js
|
|
77
203
|
const offNew =
|
|
@@ -93,7 +219,9 @@ offNew();
|
|
|
93
219
|
offError();
|
|
94
220
|
```
|
|
95
221
|
|
|
96
|
-
Note: if a **global** `eventful.on('error', ...)` listener throws, `eventful`
|
|
222
|
+
Note: if a **global** `eventful.on('error', ...)` listener throws, `eventful`
|
|
223
|
+
throws a `ListenerError` (an `Error` subclass with fields
|
|
224
|
+
`{ error, object, event, listener }`) to avoid an infinite error loop.
|
|
97
225
|
|
|
98
226
|
## API
|
|
99
227
|
|
|
@@ -104,8 +232,10 @@ a new empty object is created.
|
|
|
104
232
|
|
|
105
233
|
- `target` (Object): The object to be enhanced with event capabilities.
|
|
106
234
|
- `options` (Object): Configuration options.
|
|
107
|
-
- `error` (Function | null): Optional error hook called with
|
|
108
|
-
|
|
235
|
+
- `error` (Function | null): Optional error hook called with
|
|
236
|
+
`{ error, object, event, listener }`.
|
|
237
|
+
- `trace` (Function | null): Optional trace hook called with
|
|
238
|
+
`(action, payload)`.
|
|
109
239
|
- `strict` (Boolean): If true, propagates listener errors; otherwise they
|
|
110
240
|
are isolated. Defaults to false.
|
|
111
241
|
|
package/dist/eventful.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ListenerError } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { eventNameTypeGuard, functionTypeGuard, isFunction, isObject, } from './guards.js';
|
|
3
3
|
const eventfulImpl = (object = Object.create(null), options = {}) => {
|
|
4
4
|
if (!isObject(object)
|
|
5
5
|
&& !isFunction(object)) {
|
|
6
6
|
throw new TypeError('Expect an object or a function.');
|
|
7
7
|
}
|
|
8
8
|
for (const method of ['on', 'once', 'off', 'emit', 'emitAsync', 'has']) {
|
|
9
|
-
if (
|
|
9
|
+
if (method in object) {
|
|
10
10
|
throw new Error(`Method "${method}" already exists.`);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -66,7 +66,7 @@ const eventfulImpl = (object = Object.create(null), options = {}) => {
|
|
|
66
66
|
eventful.emit('error', errorArgs);
|
|
67
67
|
}
|
|
68
68
|
function on(event, listener) {
|
|
69
|
-
|
|
69
|
+
eventNameTypeGuard(event);
|
|
70
70
|
functionTypeGuard(listener);
|
|
71
71
|
traceFn('on', { object,
|
|
72
72
|
event,
|
|
@@ -78,7 +78,7 @@ const eventfulImpl = (object = Object.create(null), options = {}) => {
|
|
|
78
78
|
: false;
|
|
79
79
|
}
|
|
80
80
|
function once(event, listener) {
|
|
81
|
-
|
|
81
|
+
eventNameTypeGuard(event);
|
|
82
82
|
functionTypeGuard(listener);
|
|
83
83
|
const off = on(event, (...args) => {
|
|
84
84
|
off();
|
|
@@ -87,7 +87,7 @@ const eventfulImpl = (object = Object.create(null), options = {}) => {
|
|
|
87
87
|
return off;
|
|
88
88
|
}
|
|
89
89
|
function off(event, listener) {
|
|
90
|
-
|
|
90
|
+
eventNameTypeGuard(event);
|
|
91
91
|
functionTypeGuard(listener);
|
|
92
92
|
traceFn('off', { object,
|
|
93
93
|
event,
|
|
@@ -95,11 +95,11 @@ const eventfulImpl = (object = Object.create(null), options = {}) => {
|
|
|
95
95
|
return remove(event, listener);
|
|
96
96
|
}
|
|
97
97
|
function has(event) {
|
|
98
|
-
|
|
98
|
+
eventNameTypeGuard(event);
|
|
99
99
|
return (map.get(event)?.size ?? 0) > 0;
|
|
100
100
|
}
|
|
101
101
|
function emit(event, ...args) {
|
|
102
|
-
|
|
102
|
+
eventNameTypeGuard(event);
|
|
103
103
|
const listeners = map.get(event)
|
|
104
104
|
|| emptySet;
|
|
105
105
|
traceFn('emit', { object,
|
|
@@ -120,7 +120,7 @@ const eventfulImpl = (object = Object.create(null), options = {}) => {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
async function emitAsync(event, ...args) {
|
|
123
|
-
|
|
123
|
+
eventNameTypeGuard(event);
|
|
124
124
|
const listeners = map.get(event)
|
|
125
125
|
|| emptySet;
|
|
126
126
|
traceFn('emitAsync', { object,
|
package/dist/guards.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EventName } from './types.js';
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function eventNameTypeGuard(value: any): asserts value is EventName;
|
|
3
3
|
export declare function isFunction(value: any): value is Function;
|
|
4
4
|
export declare function isObject(value: any): value is object;
|
|
5
5
|
export declare function functionTypeGuard(value: any): asserts value is Function;
|
package/dist/guards.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -69,29 +69,29 @@ export interface Eventful<E extends EventMap = EventMap> {
|
|
|
69
69
|
/**
|
|
70
70
|
* Subscribe to an event. Returns an unsubscribe function.
|
|
71
71
|
*/
|
|
72
|
-
on
|
|
72
|
+
on<K extends keyof E & EventName>(event: K, listener: Listener<E[K]>): () => boolean;
|
|
73
73
|
/**
|
|
74
74
|
* Subscribe once to an event. Returns an unsubscribe function
|
|
75
75
|
* (called automatically).
|
|
76
76
|
*/
|
|
77
|
-
once
|
|
77
|
+
once<K extends keyof E & EventName>(event: K, listener: Listener<E[K]>): () => boolean;
|
|
78
78
|
/**
|
|
79
79
|
* Unsubscribe a previously registered listener. Returns true if removed.
|
|
80
80
|
*/
|
|
81
|
-
off
|
|
81
|
+
off<K extends keyof E & EventName>(event: K, listener: Listener<E[K]>): boolean;
|
|
82
82
|
/**
|
|
83
83
|
* Emit an event synchronously. All listeners run in order.
|
|
84
84
|
* Errors are isolated (ignored) unless `strict` is true.
|
|
85
85
|
*/
|
|
86
|
-
emit
|
|
86
|
+
emit<K extends keyof E & EventName>(event: K, ...args: E[K]): void;
|
|
87
87
|
/**
|
|
88
88
|
* Emit an event and wait for all listeners (run in parallel).
|
|
89
89
|
* Errors are isolated (ignored) unless `strict` is true.
|
|
90
90
|
*/
|
|
91
|
-
emitAsync
|
|
91
|
+
emitAsync<K extends keyof E & EventName>(event: K, ...args: E[K]): Promise<void>;
|
|
92
92
|
/**
|
|
93
93
|
* Returns true if there is at least one listener for the event.
|
|
94
94
|
*/
|
|
95
|
-
has
|
|
95
|
+
has<K extends keyof E & EventName>(event: K): boolean;
|
|
96
96
|
}
|
|
97
97
|
export {};
|
package/eventful.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asljs-eventful",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Lightweight event helper adding on/off/emit to any object.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"doc": "docs"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
|
+
"clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
40
41
|
"build": "tsc -p .",
|
|
41
42
|
"build:tests": "tsc -p tsconfig.tests.json",
|
|
42
43
|
"lint": "eslint .",
|