@axijs/emitter 1.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/README.md +113 -0
- package/dist/index.d.mts +116 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.js +172 -0
- package/dist/index.mjs +143 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @axijs/emitter
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@axijs/emitter)
|
|
4
|
+
|
|
5
|
+
A minimalistic, type-safe library for single-event and state emitting.
|
|
6
|
+
|
|
7
|
+
Inspired by the Observer pattern and RxJS (like `BehaviorSubject`),
|
|
8
|
+
it provides a clean way to manage subscriptions.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @axijs/emitter
|
|
14
|
+
# or
|
|
15
|
+
pnpm add @axijs/emitter
|
|
16
|
+
# or
|
|
17
|
+
yarn add @axijs/emitter
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **Strictly Typed**: Full TypeScript support for event arguments.
|
|
23
|
+
- **No Magic Strings**: Object-based emitters instead of string keys (e.g., `on('event-name')`).
|
|
24
|
+
- **State Emitting**: `StateEmitter` remembers the last emitted value and immediately triggers new subscribers.
|
|
25
|
+
- **Composite Subscriptions**: Easily group multiple subscriptions and teardown logic into a single `Subscription` object to prevent memory leaks.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### 1. Basic Event Emitter
|
|
30
|
+
Create an isolated, strongly-typed event emitter.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Emitter } from '@axijs/emitter';
|
|
34
|
+
|
|
35
|
+
// Define an emitter that expects a string and a number
|
|
36
|
+
const onPlayerMove = new Emitter<[string, number]>();
|
|
37
|
+
|
|
38
|
+
// Subscribe to the event
|
|
39
|
+
const sub = onPlayerMove.subscribe((player, x) => {
|
|
40
|
+
console.log(`${player} moved to ${x}`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Fire the event
|
|
44
|
+
onPlayerMove.emit('Alice', 10);
|
|
45
|
+
|
|
46
|
+
// Subscribe to fire only once
|
|
47
|
+
onPlayerMove.once((player, x) => {
|
|
48
|
+
console.log('This will only run once!');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Unsubscribe when no longer needed
|
|
52
|
+
sub.unsubscribe();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. State Emitter
|
|
56
|
+
Acts like a state container (similar to `BehaviorSubject`).
|
|
57
|
+
It holds the latest value and immediately provides it to new subscribers.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { StateEmitter } from '@axijs/emitter';
|
|
61
|
+
|
|
62
|
+
// Initialize with a default state
|
|
63
|
+
const health = new StateEmitter<[number]>([100]);
|
|
64
|
+
|
|
65
|
+
// New subscribers immediately receive the current state (100)
|
|
66
|
+
health.subscribe((currentHealth) => {
|
|
67
|
+
console.log(`Health is: ${currentHealth}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Emit a new state. All listeners will be notified.
|
|
71
|
+
health.emit(80);
|
|
72
|
+
|
|
73
|
+
// Get current state synchronously without subscribing
|
|
74
|
+
console.log(health.value); // [80]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 3. Composite Subscriptions
|
|
78
|
+
Group multiple unsubscriptions together. Very useful for cleaning up UI components or game objects.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { Emitter, Subscription } from '@axijs/emitter';
|
|
82
|
+
|
|
83
|
+
const onJump = new Emitter<[]>();
|
|
84
|
+
const onShoot = new Emitter<[]>();
|
|
85
|
+
|
|
86
|
+
const masterSub = new Subscription();
|
|
87
|
+
|
|
88
|
+
// Add multiple subscriptions to the master Subscription
|
|
89
|
+
masterSub.add(onJump.subscribe(() => console.log('Jumped!')));
|
|
90
|
+
masterSub.add(onShoot.subscribe(() => console.log('Pew pew!')));
|
|
91
|
+
|
|
92
|
+
// You can also add custom teardown functions
|
|
93
|
+
masterSub.add(() => {
|
|
94
|
+
console.log('Custom cleanup logic executed');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Later, when the component/object is destroyed:
|
|
98
|
+
masterSub.unsubscribe();
|
|
99
|
+
// This automatically unsubscribes from both events and runs the custom logic
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## API Documentation
|
|
103
|
+
|
|
104
|
+
For a complete list of classes, interfaces, and methods, please visit the [API Documentation](https://github.com/axijs/tools/tree/main/packages/emitter/docs/api).
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the public, read-only contract for an event emitter.
|
|
3
|
+
* It allows subscribing to an event but not emitting it.
|
|
4
|
+
* @template T A tuple representing the types of the event arguments.
|
|
5
|
+
*/
|
|
6
|
+
type Subscribable<T extends any[]> = {
|
|
7
|
+
readonly listenerCount: number;
|
|
8
|
+
/**
|
|
9
|
+
* Subscribes a listener to this event.
|
|
10
|
+
* @returns A function to unsubscribe the listener.
|
|
11
|
+
*/
|
|
12
|
+
subscribe(listener: (...args: T) => void): Unsubscribable;
|
|
13
|
+
unsubscribe(listener: (...args: T) => void): boolean;
|
|
14
|
+
clear(): void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Describes an object that can be unsubscribed from.
|
|
18
|
+
*/
|
|
19
|
+
interface Unsubscribable {
|
|
20
|
+
unsubscribe(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Represents a disposable resource, such as the execution of an Observable or an Event Listener.
|
|
25
|
+
* Allows grouping multiple teardown logic into a single unit (Composite Subscription).
|
|
26
|
+
*/
|
|
27
|
+
declare class Subscription implements Unsubscribable {
|
|
28
|
+
private _closed;
|
|
29
|
+
private _teardowns;
|
|
30
|
+
/**
|
|
31
|
+
* Indicates whether this subscription has already been unsubscribed.
|
|
32
|
+
*/
|
|
33
|
+
get closed(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* @param teardown Optional initial teardown logic to execute when unsubscribed.
|
|
36
|
+
*/
|
|
37
|
+
constructor(teardown?: () => void);
|
|
38
|
+
/**
|
|
39
|
+
* Adds a teardown logic to this subscription.
|
|
40
|
+
* If the subscription is already closed, the teardown is executed immediately.
|
|
41
|
+
* @param teardown A function or another Unsubscribable object to be managed.
|
|
42
|
+
*/
|
|
43
|
+
add(teardown: Unsubscribable | (() => void)): void;
|
|
44
|
+
/**
|
|
45
|
+
* Disposes the resources held by the subscription.
|
|
46
|
+
* Executes all attached teardown logic and clears the list.
|
|
47
|
+
*/
|
|
48
|
+
unsubscribe(): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A minimal, type-safe event emitter for a single event.
|
|
53
|
+
* It does not manage state, it only manages subscribers and event dispatching.
|
|
54
|
+
* @template T A tuple representing the types of the event arguments.
|
|
55
|
+
*/
|
|
56
|
+
declare class Emitter<T extends any[]> implements Subscribable<T> {
|
|
57
|
+
private listeners;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the number of listeners.
|
|
60
|
+
*/
|
|
61
|
+
get listenerCount(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Subscribes a listener to this event.
|
|
64
|
+
* @returns A Subscription object to manage the unsubscription.
|
|
65
|
+
*/
|
|
66
|
+
subscribe(listener: (...args: T) => void): Subscription;
|
|
67
|
+
/**
|
|
68
|
+
* Subscribes a listener that triggers only once and then automatically unsubscribes.
|
|
69
|
+
* @param listener The callback function to execute once.
|
|
70
|
+
* @returns A Subscription object (can be used to cancel before the event fires).
|
|
71
|
+
*/
|
|
72
|
+
once(listener: (...args: T) => void): Subscription;
|
|
73
|
+
/**
|
|
74
|
+
* Manually unsubscribe by listener
|
|
75
|
+
* @returns returns true if an listener has been removed, or false if the listener does not exist.
|
|
76
|
+
*/
|
|
77
|
+
unsubscribe(listener: (...args: T) => void): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Dispatches the event to all subscribed listeners.
|
|
80
|
+
*/
|
|
81
|
+
emit(...args: T): void;
|
|
82
|
+
/**
|
|
83
|
+
* Clears all listeners.
|
|
84
|
+
*/
|
|
85
|
+
clear(): void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* An Emitter that stores the last emitted value.
|
|
90
|
+
* New subscribers immediately receive the last value upon subscription.
|
|
91
|
+
*/
|
|
92
|
+
declare class StateEmitter<T extends any[]> extends Emitter<T> {
|
|
93
|
+
private _lastValue;
|
|
94
|
+
/**
|
|
95
|
+
* @param initialValue Optional initial value to set.
|
|
96
|
+
*/
|
|
97
|
+
constructor(initialValue?: T);
|
|
98
|
+
/**
|
|
99
|
+
* Gets the current value synchronously without subscribing.
|
|
100
|
+
*/
|
|
101
|
+
get value(): T | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Updates the state and notifies all listeners.
|
|
104
|
+
* @param args The new value(s).
|
|
105
|
+
*/
|
|
106
|
+
emit(...args: T): void;
|
|
107
|
+
/**
|
|
108
|
+
* Subscribes to the event. If a value exists, the listener is called immediately.
|
|
109
|
+
* @param listener The callback function.
|
|
110
|
+
* @returns A Subscription object.
|
|
111
|
+
*/
|
|
112
|
+
subscribe(listener: (...args: T) => void): Subscription;
|
|
113
|
+
clear(): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { Emitter, StateEmitter, type Subscribable, Subscription, type Unsubscribable };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the public, read-only contract for an event emitter.
|
|
3
|
+
* It allows subscribing to an event but not emitting it.
|
|
4
|
+
* @template T A tuple representing the types of the event arguments.
|
|
5
|
+
*/
|
|
6
|
+
type Subscribable<T extends any[]> = {
|
|
7
|
+
readonly listenerCount: number;
|
|
8
|
+
/**
|
|
9
|
+
* Subscribes a listener to this event.
|
|
10
|
+
* @returns A function to unsubscribe the listener.
|
|
11
|
+
*/
|
|
12
|
+
subscribe(listener: (...args: T) => void): Unsubscribable;
|
|
13
|
+
unsubscribe(listener: (...args: T) => void): boolean;
|
|
14
|
+
clear(): void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Describes an object that can be unsubscribed from.
|
|
18
|
+
*/
|
|
19
|
+
interface Unsubscribable {
|
|
20
|
+
unsubscribe(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Represents a disposable resource, such as the execution of an Observable or an Event Listener.
|
|
25
|
+
* Allows grouping multiple teardown logic into a single unit (Composite Subscription).
|
|
26
|
+
*/
|
|
27
|
+
declare class Subscription implements Unsubscribable {
|
|
28
|
+
private _closed;
|
|
29
|
+
private _teardowns;
|
|
30
|
+
/**
|
|
31
|
+
* Indicates whether this subscription has already been unsubscribed.
|
|
32
|
+
*/
|
|
33
|
+
get closed(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* @param teardown Optional initial teardown logic to execute when unsubscribed.
|
|
36
|
+
*/
|
|
37
|
+
constructor(teardown?: () => void);
|
|
38
|
+
/**
|
|
39
|
+
* Adds a teardown logic to this subscription.
|
|
40
|
+
* If the subscription is already closed, the teardown is executed immediately.
|
|
41
|
+
* @param teardown A function or another Unsubscribable object to be managed.
|
|
42
|
+
*/
|
|
43
|
+
add(teardown: Unsubscribable | (() => void)): void;
|
|
44
|
+
/**
|
|
45
|
+
* Disposes the resources held by the subscription.
|
|
46
|
+
* Executes all attached teardown logic and clears the list.
|
|
47
|
+
*/
|
|
48
|
+
unsubscribe(): void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A minimal, type-safe event emitter for a single event.
|
|
53
|
+
* It does not manage state, it only manages subscribers and event dispatching.
|
|
54
|
+
* @template T A tuple representing the types of the event arguments.
|
|
55
|
+
*/
|
|
56
|
+
declare class Emitter<T extends any[]> implements Subscribable<T> {
|
|
57
|
+
private listeners;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the number of listeners.
|
|
60
|
+
*/
|
|
61
|
+
get listenerCount(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Subscribes a listener to this event.
|
|
64
|
+
* @returns A Subscription object to manage the unsubscription.
|
|
65
|
+
*/
|
|
66
|
+
subscribe(listener: (...args: T) => void): Subscription;
|
|
67
|
+
/**
|
|
68
|
+
* Subscribes a listener that triggers only once and then automatically unsubscribes.
|
|
69
|
+
* @param listener The callback function to execute once.
|
|
70
|
+
* @returns A Subscription object (can be used to cancel before the event fires).
|
|
71
|
+
*/
|
|
72
|
+
once(listener: (...args: T) => void): Subscription;
|
|
73
|
+
/**
|
|
74
|
+
* Manually unsubscribe by listener
|
|
75
|
+
* @returns returns true if an listener has been removed, or false if the listener does not exist.
|
|
76
|
+
*/
|
|
77
|
+
unsubscribe(listener: (...args: T) => void): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Dispatches the event to all subscribed listeners.
|
|
80
|
+
*/
|
|
81
|
+
emit(...args: T): void;
|
|
82
|
+
/**
|
|
83
|
+
* Clears all listeners.
|
|
84
|
+
*/
|
|
85
|
+
clear(): void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* An Emitter that stores the last emitted value.
|
|
90
|
+
* New subscribers immediately receive the last value upon subscription.
|
|
91
|
+
*/
|
|
92
|
+
declare class StateEmitter<T extends any[]> extends Emitter<T> {
|
|
93
|
+
private _lastValue;
|
|
94
|
+
/**
|
|
95
|
+
* @param initialValue Optional initial value to set.
|
|
96
|
+
*/
|
|
97
|
+
constructor(initialValue?: T);
|
|
98
|
+
/**
|
|
99
|
+
* Gets the current value synchronously without subscribing.
|
|
100
|
+
*/
|
|
101
|
+
get value(): T | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Updates the state and notifies all listeners.
|
|
104
|
+
* @param args The new value(s).
|
|
105
|
+
*/
|
|
106
|
+
emit(...args: T): void;
|
|
107
|
+
/**
|
|
108
|
+
* Subscribes to the event. If a value exists, the listener is called immediately.
|
|
109
|
+
* @param listener The callback function.
|
|
110
|
+
* @returns A Subscription object.
|
|
111
|
+
*/
|
|
112
|
+
subscribe(listener: (...args: T) => void): Subscription;
|
|
113
|
+
clear(): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { Emitter, StateEmitter, type Subscribable, Subscription, type Unsubscribable };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Emitter: () => Emitter,
|
|
24
|
+
StateEmitter: () => StateEmitter,
|
|
25
|
+
Subscription: () => Subscription
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// ../ensure/dist/index.mjs
|
|
30
|
+
function isFunction(value) {
|
|
31
|
+
return typeof value === "function";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/subscription.ts
|
|
35
|
+
var Subscription = class {
|
|
36
|
+
_closed = false;
|
|
37
|
+
_teardowns = [];
|
|
38
|
+
/**
|
|
39
|
+
* Indicates whether this subscription has already been unsubscribed.
|
|
40
|
+
*/
|
|
41
|
+
get closed() {
|
|
42
|
+
return this._closed;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @param teardown Optional initial teardown logic to execute when unsubscribed.
|
|
46
|
+
*/
|
|
47
|
+
constructor(teardown) {
|
|
48
|
+
if (teardown) {
|
|
49
|
+
this._teardowns.push(teardown);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Adds a teardown logic to this subscription.
|
|
54
|
+
* If the subscription is already closed, the teardown is executed immediately.
|
|
55
|
+
* @param teardown A function or another Unsubscribable object to be managed.
|
|
56
|
+
*/
|
|
57
|
+
add(teardown) {
|
|
58
|
+
if (this._closed) {
|
|
59
|
+
isFunction(teardown) ? teardown() : teardown.unsubscribe();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this._teardowns.push(isFunction(teardown) ? teardown : () => teardown.unsubscribe());
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Disposes the resources held by the subscription.
|
|
66
|
+
* Executes all attached teardown logic and clears the list.
|
|
67
|
+
*/
|
|
68
|
+
unsubscribe() {
|
|
69
|
+
if (this._closed) return;
|
|
70
|
+
this._closed = true;
|
|
71
|
+
this._teardowns.forEach((fn) => fn());
|
|
72
|
+
this._teardowns = [];
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/emitter.ts
|
|
77
|
+
var Emitter = class {
|
|
78
|
+
listeners = /* @__PURE__ */ new Set();
|
|
79
|
+
/**
|
|
80
|
+
* Returns the number of listeners.
|
|
81
|
+
*/
|
|
82
|
+
get listenerCount() {
|
|
83
|
+
return this.listeners.size;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Subscribes a listener to this event.
|
|
87
|
+
* @returns A Subscription object to manage the unsubscription.
|
|
88
|
+
*/
|
|
89
|
+
subscribe(listener) {
|
|
90
|
+
this.listeners.add(listener);
|
|
91
|
+
return new Subscription(() => this.unsubscribe(listener));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Subscribes a listener that triggers only once and then automatically unsubscribes.
|
|
95
|
+
* @param listener The callback function to execute once.
|
|
96
|
+
* @returns A Subscription object (can be used to cancel before the event fires).
|
|
97
|
+
*/
|
|
98
|
+
once(listener) {
|
|
99
|
+
const wrapper = (...args) => {
|
|
100
|
+
this.unsubscribe(wrapper);
|
|
101
|
+
listener(...args);
|
|
102
|
+
};
|
|
103
|
+
return this.subscribe(wrapper);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Manually unsubscribe by listener
|
|
107
|
+
* @returns returns true if an listener has been removed, or false if the listener does not exist.
|
|
108
|
+
*/
|
|
109
|
+
unsubscribe(listener) {
|
|
110
|
+
return this.listeners.delete(listener);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Dispatches the event to all subscribed listeners.
|
|
114
|
+
*/
|
|
115
|
+
emit(...args) {
|
|
116
|
+
this.listeners.forEach((listener) => listener(...args));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Clears all listeners.
|
|
120
|
+
*/
|
|
121
|
+
clear() {
|
|
122
|
+
this.listeners.clear();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/state-emitter.ts
|
|
127
|
+
var StateEmitter = class extends Emitter {
|
|
128
|
+
_lastValue;
|
|
129
|
+
/**
|
|
130
|
+
* @param initialValue Optional initial value to set.
|
|
131
|
+
*/
|
|
132
|
+
constructor(initialValue) {
|
|
133
|
+
super();
|
|
134
|
+
this._lastValue = initialValue ?? void 0;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Gets the current value synchronously without subscribing.
|
|
138
|
+
*/
|
|
139
|
+
get value() {
|
|
140
|
+
return this._lastValue;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Updates the state and notifies all listeners.
|
|
144
|
+
* @param args The new value(s).
|
|
145
|
+
*/
|
|
146
|
+
emit(...args) {
|
|
147
|
+
this._lastValue = args;
|
|
148
|
+
super.emit(...args);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Subscribes to the event. If a value exists, the listener is called immediately.
|
|
152
|
+
* @param listener The callback function.
|
|
153
|
+
* @returns A Subscription object.
|
|
154
|
+
*/
|
|
155
|
+
subscribe(listener) {
|
|
156
|
+
const unsubscribe = super.subscribe(listener);
|
|
157
|
+
if (this._lastValue !== void 0) {
|
|
158
|
+
listener(...this._lastValue);
|
|
159
|
+
}
|
|
160
|
+
return unsubscribe;
|
|
161
|
+
}
|
|
162
|
+
clear() {
|
|
163
|
+
super.clear();
|
|
164
|
+
this._lastValue = void 0;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
168
|
+
0 && (module.exports = {
|
|
169
|
+
Emitter,
|
|
170
|
+
StateEmitter,
|
|
171
|
+
Subscription
|
|
172
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// ../ensure/dist/index.mjs
|
|
2
|
+
function isFunction(value) {
|
|
3
|
+
return typeof value === "function";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/subscription.ts
|
|
7
|
+
var Subscription = class {
|
|
8
|
+
_closed = false;
|
|
9
|
+
_teardowns = [];
|
|
10
|
+
/**
|
|
11
|
+
* Indicates whether this subscription has already been unsubscribed.
|
|
12
|
+
*/
|
|
13
|
+
get closed() {
|
|
14
|
+
return this._closed;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* @param teardown Optional initial teardown logic to execute when unsubscribed.
|
|
18
|
+
*/
|
|
19
|
+
constructor(teardown) {
|
|
20
|
+
if (teardown) {
|
|
21
|
+
this._teardowns.push(teardown);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Adds a teardown logic to this subscription.
|
|
26
|
+
* If the subscription is already closed, the teardown is executed immediately.
|
|
27
|
+
* @param teardown A function or another Unsubscribable object to be managed.
|
|
28
|
+
*/
|
|
29
|
+
add(teardown) {
|
|
30
|
+
if (this._closed) {
|
|
31
|
+
isFunction(teardown) ? teardown() : teardown.unsubscribe();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._teardowns.push(isFunction(teardown) ? teardown : () => teardown.unsubscribe());
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Disposes the resources held by the subscription.
|
|
38
|
+
* Executes all attached teardown logic and clears the list.
|
|
39
|
+
*/
|
|
40
|
+
unsubscribe() {
|
|
41
|
+
if (this._closed) return;
|
|
42
|
+
this._closed = true;
|
|
43
|
+
this._teardowns.forEach((fn) => fn());
|
|
44
|
+
this._teardowns = [];
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/emitter.ts
|
|
49
|
+
var Emitter = class {
|
|
50
|
+
listeners = /* @__PURE__ */ new Set();
|
|
51
|
+
/**
|
|
52
|
+
* Returns the number of listeners.
|
|
53
|
+
*/
|
|
54
|
+
get listenerCount() {
|
|
55
|
+
return this.listeners.size;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Subscribes a listener to this event.
|
|
59
|
+
* @returns A Subscription object to manage the unsubscription.
|
|
60
|
+
*/
|
|
61
|
+
subscribe(listener) {
|
|
62
|
+
this.listeners.add(listener);
|
|
63
|
+
return new Subscription(() => this.unsubscribe(listener));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Subscribes a listener that triggers only once and then automatically unsubscribes.
|
|
67
|
+
* @param listener The callback function to execute once.
|
|
68
|
+
* @returns A Subscription object (can be used to cancel before the event fires).
|
|
69
|
+
*/
|
|
70
|
+
once(listener) {
|
|
71
|
+
const wrapper = (...args) => {
|
|
72
|
+
this.unsubscribe(wrapper);
|
|
73
|
+
listener(...args);
|
|
74
|
+
};
|
|
75
|
+
return this.subscribe(wrapper);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Manually unsubscribe by listener
|
|
79
|
+
* @returns returns true if an listener has been removed, or false if the listener does not exist.
|
|
80
|
+
*/
|
|
81
|
+
unsubscribe(listener) {
|
|
82
|
+
return this.listeners.delete(listener);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Dispatches the event to all subscribed listeners.
|
|
86
|
+
*/
|
|
87
|
+
emit(...args) {
|
|
88
|
+
this.listeners.forEach((listener) => listener(...args));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Clears all listeners.
|
|
92
|
+
*/
|
|
93
|
+
clear() {
|
|
94
|
+
this.listeners.clear();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/state-emitter.ts
|
|
99
|
+
var StateEmitter = class extends Emitter {
|
|
100
|
+
_lastValue;
|
|
101
|
+
/**
|
|
102
|
+
* @param initialValue Optional initial value to set.
|
|
103
|
+
*/
|
|
104
|
+
constructor(initialValue) {
|
|
105
|
+
super();
|
|
106
|
+
this._lastValue = initialValue ?? void 0;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Gets the current value synchronously without subscribing.
|
|
110
|
+
*/
|
|
111
|
+
get value() {
|
|
112
|
+
return this._lastValue;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Updates the state and notifies all listeners.
|
|
116
|
+
* @param args The new value(s).
|
|
117
|
+
*/
|
|
118
|
+
emit(...args) {
|
|
119
|
+
this._lastValue = args;
|
|
120
|
+
super.emit(...args);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Subscribes to the event. If a value exists, the listener is called immediately.
|
|
124
|
+
* @param listener The callback function.
|
|
125
|
+
* @returns A Subscription object.
|
|
126
|
+
*/
|
|
127
|
+
subscribe(listener) {
|
|
128
|
+
const unsubscribe = super.subscribe(listener);
|
|
129
|
+
if (this._lastValue !== void 0) {
|
|
130
|
+
listener(...this._lastValue);
|
|
131
|
+
}
|
|
132
|
+
return unsubscribe;
|
|
133
|
+
}
|
|
134
|
+
clear() {
|
|
135
|
+
super.clear();
|
|
136
|
+
this._lastValue = void 0;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
export {
|
|
140
|
+
Emitter,
|
|
141
|
+
StateEmitter,
|
|
142
|
+
Subscription
|
|
143
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axijs/emitter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A minimalistic, type-safe library for single-event and state emitting",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/axijs/tools/tree/main/packages/emitter",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/axijs/tools.git",
|
|
10
|
+
"directory": "packages/emitter"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
],
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.mjs",
|
|
21
|
+
"require": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"prebuild": "npm test",
|
|
27
|
+
"docs": "typedoc src/index.ts --out docs/api --options ../../typedoc.json",
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
]
|
|
33
|
+
}
|