vbstyle-event-models 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/BasicEventModel.ts +41 -0
- package/LICENSE +29 -0
- package/MessageRouter.ts +136 -0
- package/QueueEventScheduler.ts +54 -0
- package/README.md +125 -0
- package/Spinlock.ts +57 -0
- package/dist/BasicEventModel.d.ts +30 -0
- package/dist/BasicEventModel.d.ts.map +1 -0
- package/dist/BasicEventModel.js +34 -0
- package/dist/BasicEventModel.js.map +1 -0
- package/dist/MessageRouter.d.ts +46 -0
- package/dist/MessageRouter.d.ts.map +1 -0
- package/dist/MessageRouter.js +150 -0
- package/dist/MessageRouter.js.map +1 -0
- package/dist/QueueEventScheduler.d.ts +25 -0
- package/dist/QueueEventScheduler.d.ts.map +1 -0
- package/dist/QueueEventScheduler.js +70 -0
- package/dist/QueueEventScheduler.js.map +1 -0
- package/dist/Spinlock.d.ts +23 -0
- package/dist/Spinlock.d.ts.map +1 -0
- package/dist/Spinlock.js +69 -0
- package/dist/Spinlock.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/index.ts +4 -0
- package/package.json +28 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A type for event arguments that can be any array of values.
|
|
3
|
+
*/
|
|
4
|
+
export type EventArgs = any[];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A delegate type for event handlers that take any sender and event arguments.
|
|
8
|
+
*/
|
|
9
|
+
export type EventHandler = (sender: any, e: EventArgs) => void;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A basic event model that allows registration and invocation of event handlers.
|
|
13
|
+
*/
|
|
14
|
+
export class BasicEventModel {
|
|
15
|
+
private eventHandlers: EventHandler[] = [];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Add an event handler to the event model.
|
|
19
|
+
* @param {EventHandler} handler - The event handler to add.
|
|
20
|
+
*/
|
|
21
|
+
public addHandler(handler: EventHandler): void {
|
|
22
|
+
this.eventHandlers.push(handler);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Remove an event handler from the event model.
|
|
27
|
+
* @param {EventHandler} handler - The event handler to remove.
|
|
28
|
+
*/
|
|
29
|
+
public removeHandler(handler: EventHandler): void {
|
|
30
|
+
this.eventHandlers = this.eventHandlers.filter(h => h !== handler);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Raise the event from the event model, invoking all registered event handlers.
|
|
35
|
+
* @param {any} sender - The event sender.
|
|
36
|
+
* @param {EventArgs} e - The event arguments.
|
|
37
|
+
*/
|
|
38
|
+
public raiseEvent(sender: any, e: EventArgs): void {
|
|
39
|
+
this.eventHandlers.forEach(handler => handler(sender, e));
|
|
40
|
+
}
|
|
41
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, pac-dessert1436
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/MessageRouter.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Spinlock } from "./Spinlock";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A singleton class for message routing, using a spinlock for thread safety.
|
|
5
|
+
*/
|
|
6
|
+
export class MessageRouter {
|
|
7
|
+
/**
|
|
8
|
+
* Get the singleton instance of the MessageRouter.
|
|
9
|
+
* @returns {MessageRouter} The singleton instance.
|
|
10
|
+
*/
|
|
11
|
+
private static _instance: MessageRouter;
|
|
12
|
+
|
|
13
|
+
private _subscribers: Map<string, ((content: any) => void)[]> = new Map();
|
|
14
|
+
private _lock: Spinlock = new Spinlock();
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
if (MessageRouter._instance) {
|
|
18
|
+
throw new Error("Use MessageRouter.instance to get the singleton instance.");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the singleton instance of the MessageRouter.
|
|
24
|
+
* @returns {MessageRouter} The singleton instance.
|
|
25
|
+
*/
|
|
26
|
+
public static get instance(): MessageRouter {
|
|
27
|
+
if (!MessageRouter._instance) {
|
|
28
|
+
MessageRouter._instance = new MessageRouter();
|
|
29
|
+
}
|
|
30
|
+
return MessageRouter._instance;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to a message type.
|
|
35
|
+
* @param {string} messageType - The type of message to subscribe to.
|
|
36
|
+
* @param {function} handler - The handler function to call when a message of the specified type is sent.
|
|
37
|
+
*/
|
|
38
|
+
public async subscribe(messageType: string, handler: (content: any) => void): Promise<void> {
|
|
39
|
+
if (!messageType) throw new Error("messageType is required");
|
|
40
|
+
if (typeof handler !== "function") throw new Error("handler must be a function");
|
|
41
|
+
|
|
42
|
+
await this._lock.acquireLock();
|
|
43
|
+
try {
|
|
44
|
+
if (!this._subscribers.has(messageType)) {
|
|
45
|
+
this._subscribers.set(messageType, []);
|
|
46
|
+
}
|
|
47
|
+
this._subscribers.get(messageType)!.push(handler);
|
|
48
|
+
} finally {
|
|
49
|
+
this._lock.releaseLock();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Send a message of a specific type.
|
|
55
|
+
* @param {string} messageType - The type of message to send.
|
|
56
|
+
* @param {*} content - The content of the message to send.
|
|
57
|
+
*/
|
|
58
|
+
public async send(messageType: string, content: any): Promise<void> {
|
|
59
|
+
if (!messageType) throw new Error("messageType is required");
|
|
60
|
+
|
|
61
|
+
let handlers = null;
|
|
62
|
+
|
|
63
|
+
await this._lock.acquireLock();
|
|
64
|
+
try {
|
|
65
|
+
if (this._subscribers.has(messageType)) {
|
|
66
|
+
// Copy handlers to avoid issues if subscribers change during iteration
|
|
67
|
+
handlers = [...this._subscribers.get(messageType)!];
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
70
|
+
this._lock.releaseLock();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (handlers) {
|
|
74
|
+
for (const handler of handlers) {
|
|
75
|
+
try {
|
|
76
|
+
handler(content);
|
|
77
|
+
} catch (ex) {
|
|
78
|
+
// Log but don't crash the message pump
|
|
79
|
+
console.debug(`Handler error: ${(ex as Error).message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Unsubscribe from a message type.
|
|
87
|
+
* @param {string} messageType - The type of message to unsubscribe from.
|
|
88
|
+
* @param {function} handler - The handler function to remove from the subscribers list.
|
|
89
|
+
*/
|
|
90
|
+
public async unsubscribe(messageType: string, handler: (content: any) => void): Promise<void> {
|
|
91
|
+
await this._lock.acquireLock();
|
|
92
|
+
try {
|
|
93
|
+
if (this._subscribers.has(messageType)) {
|
|
94
|
+
const handlers = this._subscribers.get(messageType);
|
|
95
|
+
const index = handlers!.indexOf(handler);
|
|
96
|
+
if (index !== -1) {
|
|
97
|
+
handlers!.splice(index, 1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} finally {
|
|
101
|
+
this._lock.releaseLock();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Clear all subscribers for a specific message type.
|
|
107
|
+
* @param {string} messageType - The type of message to clear subscribers for.
|
|
108
|
+
*/
|
|
109
|
+
public async clearSubscribers(messageType: string): Promise<void> {
|
|
110
|
+
await this._lock.acquireLock();
|
|
111
|
+
try {
|
|
112
|
+
if (this._subscribers.has(messageType)) {
|
|
113
|
+
this._subscribers.get(messageType)!.length = 0;
|
|
114
|
+
}
|
|
115
|
+
} finally {
|
|
116
|
+
this._lock.releaseLock();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get the total number of subscribers across all message types.
|
|
122
|
+
* @returns {number} The total number of subscribers.
|
|
123
|
+
*/
|
|
124
|
+
public async getSubscriberCount(): Promise<number> {
|
|
125
|
+
await this._lock.acquireLock();
|
|
126
|
+
try {
|
|
127
|
+
let count = 0;
|
|
128
|
+
for (const handlers of this._subscribers.values()) {
|
|
129
|
+
count += handlers.length;
|
|
130
|
+
}
|
|
131
|
+
return count;
|
|
132
|
+
} finally {
|
|
133
|
+
this._lock.releaseLock();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Spinlock } from "./Spinlock";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A simple event scheduler that uses a queue and a spinlock.
|
|
5
|
+
*/
|
|
6
|
+
export class QueueEventScheduler {
|
|
7
|
+
private _eventQueue: (() => void)[] = [];
|
|
8
|
+
private _spinlock: Spinlock = new Spinlock();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Schedule an event action to be processed.
|
|
12
|
+
* @param {() => void} eventAction - The event action to be processed.
|
|
13
|
+
*/
|
|
14
|
+
public async scheduleEventAction(eventAction: () => void): Promise<void> {
|
|
15
|
+
await this._spinlock.acquireLock();
|
|
16
|
+
this._eventQueue.push(eventAction);
|
|
17
|
+
this._spinlock.releaseLock();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Process all events in the queue.
|
|
22
|
+
*/
|
|
23
|
+
public async processEvents(): Promise<void> {
|
|
24
|
+
if (this._eventQueue.length === 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await this._spinlock.acquireLock();
|
|
28
|
+
while (this._eventQueue.length > 0) {
|
|
29
|
+
const event = this._eventQueue.shift()!;
|
|
30
|
+
event();
|
|
31
|
+
}
|
|
32
|
+
this._spinlock.releaseLock();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Clear all pending events.
|
|
37
|
+
*/
|
|
38
|
+
public async clearEvents(): Promise<void> {
|
|
39
|
+
await this._spinlock.acquireLock();
|
|
40
|
+
this._eventQueue = [];
|
|
41
|
+
this._spinlock.releaseLock();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the number of pending events.
|
|
46
|
+
* @returns {number} The number of pending events.
|
|
47
|
+
*/
|
|
48
|
+
public get pendingEventCount(): number {
|
|
49
|
+
this._spinlock.acquireLock();
|
|
50
|
+
const count = this._eventQueue.length;
|
|
51
|
+
this._spinlock.releaseLock();
|
|
52
|
+
return count;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# VB.NET-Style Event Models
|
|
2
|
+
|
|
3
|
+
A TypeScript library providing event models, message routing, and event scheduling utilities with thread safety.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **BasicEventModel**: A simple event model for registering and invoking event handlers
|
|
8
|
+
- **MessageRouter**: A singleton message router for publish-subscribe pattern
|
|
9
|
+
- **QueueEventScheduler**: A queue-based event scheduler with thread safety
|
|
10
|
+
- **Spinlock**: A simple mutex/lock implementation for thread safety
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install vbstyle-event-models
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### BasicEventModel
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { BasicEventModel, EventHandler, EventArgs } from 'vbstyle-event-models';
|
|
24
|
+
|
|
25
|
+
// Create an event model
|
|
26
|
+
const eventModel = new BasicEventModel();
|
|
27
|
+
|
|
28
|
+
// Define an event handler
|
|
29
|
+
const handler: EventHandler = (sender, e) => {
|
|
30
|
+
console.log('Event triggered:', { sender, args: e });
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Add the handler
|
|
34
|
+
eventModel.addHandler(handler);
|
|
35
|
+
|
|
36
|
+
// Raise the event
|
|
37
|
+
eventModel.raiseEvent('sender', ['arg1', 'arg2']);
|
|
38
|
+
|
|
39
|
+
// Remove the handler
|
|
40
|
+
eventModel.removeHandler(handler);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### MessageRouter
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { MessageRouter } from 'vbstyle-event-models';
|
|
47
|
+
|
|
48
|
+
// Get the singleton instance
|
|
49
|
+
const router = MessageRouter.instance;
|
|
50
|
+
|
|
51
|
+
// Subscribe to a message type
|
|
52
|
+
await router.subscribe('userCreated', (user) => {
|
|
53
|
+
console.log('User created:', user);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Send a message
|
|
57
|
+
await router.send('userCreated', { id: 1, name: 'John' });
|
|
58
|
+
|
|
59
|
+
// Unsubscribe from a message type
|
|
60
|
+
await router.unsubscribe('userCreated', handler);
|
|
61
|
+
|
|
62
|
+
// Clear all subscribers for a message type
|
|
63
|
+
await router.clearSubscribers('userCreated');
|
|
64
|
+
|
|
65
|
+
// Get total subscriber count
|
|
66
|
+
const count = await router.getSubscriberCount();
|
|
67
|
+
console.log('Total subscribers:', count);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### QueueEventScheduler
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { QueueEventScheduler } from 'vbstyle-event-models';
|
|
74
|
+
|
|
75
|
+
// Create an event scheduler
|
|
76
|
+
const scheduler = new QueueEventScheduler();
|
|
77
|
+
|
|
78
|
+
// Schedule events
|
|
79
|
+
await scheduler.scheduleEvent(() => {
|
|
80
|
+
console.log('Event 1 executed');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await scheduler.scheduleEvent(() => {
|
|
84
|
+
console.log('Event 2 executed');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Process all events
|
|
88
|
+
await scheduler.processEvents();
|
|
89
|
+
|
|
90
|
+
// Clear pending events
|
|
91
|
+
await scheduler.clearEvents();
|
|
92
|
+
|
|
93
|
+
// Get pending event count
|
|
94
|
+
console.log('Pending events:', scheduler.pendingEventCount);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Spinlock
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { Spinlock } from 'vbstyle-event-models';
|
|
101
|
+
|
|
102
|
+
// Create a spinlock
|
|
103
|
+
const lock = new Spinlock();
|
|
104
|
+
|
|
105
|
+
// Acquire the lock
|
|
106
|
+
await lock.acquireLock();
|
|
107
|
+
try {
|
|
108
|
+
// Critical section
|
|
109
|
+
console.log('Lock acquired');
|
|
110
|
+
} finally {
|
|
111
|
+
// Release the lock
|
|
112
|
+
lock.releaseLock();
|
|
113
|
+
console.log('Lock released');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check if locked
|
|
117
|
+
console.log('Is locked:', lock.isLocked);
|
|
118
|
+
|
|
119
|
+
// Get waiter count
|
|
120
|
+
console.log('Waiters:', lock.waiterCount);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
BSD 3-Clause
|
package/Spinlock.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple mutex/lock implementation using a queue.
|
|
3
|
+
*/
|
|
4
|
+
export class Spinlock {
|
|
5
|
+
private locked: boolean = false;
|
|
6
|
+
private waitingQueue: Array<() => void> = [];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Acquire the lock. If the lock is already held, queue the request.
|
|
10
|
+
*/
|
|
11
|
+
public async acquireLock(): Promise<void> {
|
|
12
|
+
// If lock is free, acquire it immediately
|
|
13
|
+
if (!this.locked) {
|
|
14
|
+
this.locked = true;
|
|
15
|
+
return Promise.resolve();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Otherwise, queue this request
|
|
19
|
+
return new Promise(resolve => {
|
|
20
|
+
this.waitingQueue.push(resolve);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Release the lock. If there are waiting acquisitions, pass the lock to the next in line.
|
|
26
|
+
*/
|
|
27
|
+
public releaseLock(): void {
|
|
28
|
+
if (!this.locked && this.waitingQueue.length === 0) {
|
|
29
|
+
throw new Error("Lock is not held.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// If there are waiting acquisitions, pass the lock to the next one
|
|
33
|
+
if (this.waitingQueue.length > 0) {
|
|
34
|
+
const nextResolve = this.waitingQueue.shift()!;
|
|
35
|
+
// The lock remains locked, but now the next waiter has it
|
|
36
|
+
// We don't set locked = false because it's immediately re-acquired
|
|
37
|
+
nextResolve();
|
|
38
|
+
} else {
|
|
39
|
+
// No waiters, release the lock
|
|
40
|
+
this.locked = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if the lock is currently held.
|
|
46
|
+
*/
|
|
47
|
+
public get isLocked(): boolean {
|
|
48
|
+
return this.locked;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the number of waiters.
|
|
53
|
+
*/
|
|
54
|
+
public get waiterCount(): number {
|
|
55
|
+
return this.waitingQueue.length;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A type for event arguments that can be any array of values.
|
|
3
|
+
*/
|
|
4
|
+
export type EventArgs = any[];
|
|
5
|
+
/**
|
|
6
|
+
* A delegate type for event handlers that take any sender and event arguments.
|
|
7
|
+
*/
|
|
8
|
+
export type EventHandler = (sender: any, e: EventArgs) => void;
|
|
9
|
+
/**
|
|
10
|
+
* A basic event model that allows registration and invocation of event handlers.
|
|
11
|
+
*/
|
|
12
|
+
export declare class BasicEventModel {
|
|
13
|
+
private eventHandlers;
|
|
14
|
+
/**
|
|
15
|
+
* Add an event handler to the event model.
|
|
16
|
+
* @param {EventHandler} handler - The event handler to add.
|
|
17
|
+
*/
|
|
18
|
+
addHandler(handler: EventHandler): void;
|
|
19
|
+
/**
|
|
20
|
+
* Remove an event handler from the event model.
|
|
21
|
+
* @param {EventHandler} handler - The event handler to remove.
|
|
22
|
+
*/
|
|
23
|
+
removeHandler(handler: EventHandler): void;
|
|
24
|
+
/**
|
|
25
|
+
* Raise the event from the event model, invoking all registered event handlers.
|
|
26
|
+
* @param {any} sender - The event sender.
|
|
27
|
+
* @param {EventArgs} e - The event arguments.
|
|
28
|
+
*/
|
|
29
|
+
raiseEvent(sender: any, e: EventArgs): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BasicEventModel.d.ts","sourceRoot":"","sources":["../BasicEventModel.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;AAE9B;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;AAE/D;;GAEG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,aAAa,CAAsB;IAE3C;;;OAGG;IACI,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAI9C;;;OAGG;IACI,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAIjD;;;;OAIG;IACI,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,GAAG,IAAI;CAGrD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BasicEventModel = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A basic event model that allows registration and invocation of event handlers.
|
|
6
|
+
*/
|
|
7
|
+
class BasicEventModel {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.eventHandlers = [];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Add an event handler to the event model.
|
|
13
|
+
* @param {EventHandler} handler - The event handler to add.
|
|
14
|
+
*/
|
|
15
|
+
addHandler(handler) {
|
|
16
|
+
this.eventHandlers.push(handler);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Remove an event handler from the event model.
|
|
20
|
+
* @param {EventHandler} handler - The event handler to remove.
|
|
21
|
+
*/
|
|
22
|
+
removeHandler(handler) {
|
|
23
|
+
this.eventHandlers = this.eventHandlers.filter(h => h !== handler);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Raise the event from the event model, invoking all registered event handlers.
|
|
27
|
+
* @param {any} sender - The event sender.
|
|
28
|
+
* @param {EventArgs} e - The event arguments.
|
|
29
|
+
*/
|
|
30
|
+
raiseEvent(sender, e) {
|
|
31
|
+
this.eventHandlers.forEach(handler => handler(sender, e));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.BasicEventModel = BasicEventModel;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BasicEventModel.js","sourceRoot":"","sources":["../BasicEventModel.ts"],"names":[],"mappings":";;;AAUA;;GAEG;AACH,MAAa,eAAe;IAA5B;QACY,kBAAa,GAAmB,EAAE,CAAC;IA0B/C,CAAC;IAxBG;;;OAGG;IACI,UAAU,CAAC,OAAqB;QACnC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED;;;OAGG;IACI,aAAa,CAAC,OAAqB;QACtC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;IACvE,CAAC;IAED;;;;OAIG;IACI,UAAU,CAAC,MAAW,EAAE,CAAY;QACvC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;CACJ;AA3BD,0CA2BC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A singleton class for message routing, using a spinlock for thread safety.
|
|
3
|
+
*/
|
|
4
|
+
export declare class MessageRouter {
|
|
5
|
+
/**
|
|
6
|
+
* Get the singleton instance of the MessageRouter.
|
|
7
|
+
* @returns {MessageRouter} The singleton instance.
|
|
8
|
+
*/
|
|
9
|
+
private static _instance;
|
|
10
|
+
private _subscribers;
|
|
11
|
+
private _lock;
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Get the singleton instance of the MessageRouter.
|
|
15
|
+
* @returns {MessageRouter} The singleton instance.
|
|
16
|
+
*/
|
|
17
|
+
static get instance(): MessageRouter;
|
|
18
|
+
/**
|
|
19
|
+
* Subscribe to a message type.
|
|
20
|
+
* @param {string} messageType - The type of message to subscribe to.
|
|
21
|
+
* @param {function} handler - The handler function to call when a message of the specified type is sent.
|
|
22
|
+
*/
|
|
23
|
+
subscribe(messageType: string, handler: (content: any) => void): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Send a message of a specific type.
|
|
26
|
+
* @param {string} messageType - The type of message to send.
|
|
27
|
+
* @param {*} content - The content of the message to send.
|
|
28
|
+
*/
|
|
29
|
+
send(messageType: string, content: any): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Unsubscribe from a message type.
|
|
32
|
+
* @param {string} messageType - The type of message to unsubscribe from.
|
|
33
|
+
* @param {function} handler - The handler function to remove from the subscribers list.
|
|
34
|
+
*/
|
|
35
|
+
unsubscribe(messageType: string, handler: (content: any) => void): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all subscribers for a specific message type.
|
|
38
|
+
* @param {string} messageType - The type of message to clear subscribers for.
|
|
39
|
+
*/
|
|
40
|
+
clearSubscribers(messageType: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Get the total number of subscribers across all message types.
|
|
43
|
+
* @returns {number} The total number of subscribers.
|
|
44
|
+
*/
|
|
45
|
+
getSubscriberCount(): Promise<number>;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageRouter.d.ts","sourceRoot":"","sources":["../MessageRouter.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,aAAa;IACtB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS,CAAgB;IAExC,OAAO,CAAC,YAAY,CAAsD;IAC1E,OAAO,CAAC,KAAK,CAA4B;;IAQzC;;;OAGG;IACH,WAAkB,QAAQ,IAAI,aAAa,CAK1C;IAED;;;;OAIG;IACU,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3F;;;;OAIG;IACU,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BnE;;;;OAIG;IACU,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAe7F;;;OAGG;IACU,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjE;;;OAGG;IACU,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;CAYrD"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MessageRouter = void 0;
|
|
13
|
+
const Spinlock_1 = require("./Spinlock");
|
|
14
|
+
/**
|
|
15
|
+
* A singleton class for message routing, using a spinlock for thread safety.
|
|
16
|
+
*/
|
|
17
|
+
class MessageRouter {
|
|
18
|
+
constructor() {
|
|
19
|
+
this._subscribers = new Map();
|
|
20
|
+
this._lock = new Spinlock_1.Spinlock();
|
|
21
|
+
if (MessageRouter._instance) {
|
|
22
|
+
throw new Error("Use MessageRouter.instance to get the singleton instance.");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the singleton instance of the MessageRouter.
|
|
27
|
+
* @returns {MessageRouter} The singleton instance.
|
|
28
|
+
*/
|
|
29
|
+
static get instance() {
|
|
30
|
+
if (!MessageRouter._instance) {
|
|
31
|
+
MessageRouter._instance = new MessageRouter();
|
|
32
|
+
}
|
|
33
|
+
return MessageRouter._instance;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Subscribe to a message type.
|
|
37
|
+
* @param {string} messageType - The type of message to subscribe to.
|
|
38
|
+
* @param {function} handler - The handler function to call when a message of the specified type is sent.
|
|
39
|
+
*/
|
|
40
|
+
subscribe(messageType, handler) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
if (!messageType)
|
|
43
|
+
throw new Error("messageType is required");
|
|
44
|
+
if (typeof handler !== "function")
|
|
45
|
+
throw new Error("handler must be a function");
|
|
46
|
+
yield this._lock.acquireLock();
|
|
47
|
+
try {
|
|
48
|
+
if (!this._subscribers.has(messageType)) {
|
|
49
|
+
this._subscribers.set(messageType, []);
|
|
50
|
+
}
|
|
51
|
+
this._subscribers.get(messageType).push(handler);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
this._lock.releaseLock();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Send a message of a specific type.
|
|
60
|
+
* @param {string} messageType - The type of message to send.
|
|
61
|
+
* @param {*} content - The content of the message to send.
|
|
62
|
+
*/
|
|
63
|
+
send(messageType, content) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
if (!messageType)
|
|
66
|
+
throw new Error("messageType is required");
|
|
67
|
+
let handlers = null;
|
|
68
|
+
yield this._lock.acquireLock();
|
|
69
|
+
try {
|
|
70
|
+
if (this._subscribers.has(messageType)) {
|
|
71
|
+
// Copy handlers to avoid issues if subscribers change during iteration
|
|
72
|
+
handlers = [...this._subscribers.get(messageType)];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
this._lock.releaseLock();
|
|
77
|
+
}
|
|
78
|
+
if (handlers) {
|
|
79
|
+
for (const handler of handlers) {
|
|
80
|
+
try {
|
|
81
|
+
handler(content);
|
|
82
|
+
}
|
|
83
|
+
catch (ex) {
|
|
84
|
+
// Log but don't crash the message pump
|
|
85
|
+
console.debug(`Handler error: ${ex.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Unsubscribe from a message type.
|
|
93
|
+
* @param {string} messageType - The type of message to unsubscribe from.
|
|
94
|
+
* @param {function} handler - The handler function to remove from the subscribers list.
|
|
95
|
+
*/
|
|
96
|
+
unsubscribe(messageType, handler) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
yield this._lock.acquireLock();
|
|
99
|
+
try {
|
|
100
|
+
if (this._subscribers.has(messageType)) {
|
|
101
|
+
const handlers = this._subscribers.get(messageType);
|
|
102
|
+
const index = handlers.indexOf(handler);
|
|
103
|
+
if (index !== -1) {
|
|
104
|
+
handlers.splice(index, 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
this._lock.releaseLock();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Clear all subscribers for a specific message type.
|
|
115
|
+
* @param {string} messageType - The type of message to clear subscribers for.
|
|
116
|
+
*/
|
|
117
|
+
clearSubscribers(messageType) {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
yield this._lock.acquireLock();
|
|
120
|
+
try {
|
|
121
|
+
if (this._subscribers.has(messageType)) {
|
|
122
|
+
this._subscribers.get(messageType).length = 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
this._lock.releaseLock();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the total number of subscribers across all message types.
|
|
132
|
+
* @returns {number} The total number of subscribers.
|
|
133
|
+
*/
|
|
134
|
+
getSubscriberCount() {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
yield this._lock.acquireLock();
|
|
137
|
+
try {
|
|
138
|
+
let count = 0;
|
|
139
|
+
for (const handlers of this._subscribers.values()) {
|
|
140
|
+
count += handlers.length;
|
|
141
|
+
}
|
|
142
|
+
return count;
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
this._lock.releaseLock();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.MessageRouter = MessageRouter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageRouter.js","sourceRoot":"","sources":["../MessageRouter.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAsC;AAEtC;;GAEG;AACH,MAAa,aAAa;IAUtB;QAHQ,iBAAY,GAA4C,IAAI,GAAG,EAAE,CAAC;QAClE,UAAK,GAAa,IAAI,mBAAQ,EAAE,CAAC;QAGrC,IAAI,aAAa,CAAC,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QACjF,CAAC;IACL,CAAC;IAED;;;OAGG;IACI,MAAM,KAAK,QAAQ;QACtB,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;YAC3B,aAAa,CAAC,SAAS,GAAG,IAAI,aAAa,EAAE,CAAC;QAClD,CAAC;QACD,OAAO,aAAa,CAAC,SAAS,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACU,SAAS,CAAC,WAAmB,EAAE,OAA+B;;YACvE,IAAI,CAAC,WAAW;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC7D,IAAI,OAAO,OAAO,KAAK,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAEjF,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;oBACtC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;gBAC3C,CAAC;gBACD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtD,CAAC;oBAAS,CAAC;gBACP,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC;KAAA;IAED;;;;OAIG;IACU,IAAI,CAAC,WAAmB,EAAE,OAAY;;YAC/C,IAAI,CAAC,WAAW;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAE7D,IAAI,QAAQ,GAAG,IAAI,CAAC;YAEpB,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;oBACrC,uEAAuE;oBACvE,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC,CAAC;gBACxD,CAAC;YACL,CAAC;oBAAS,CAAC;gBACP,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC7B,CAAC;YAED,IAAI,QAAQ,EAAE,CAAC;gBACX,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;oBAC7B,IAAI,CAAC;wBACD,OAAO,CAAC,OAAO,CAAC,CAAC;oBACrB,CAAC;oBAAC,OAAO,EAAE,EAAE,CAAC;wBACV,uCAAuC;wBACvC,OAAO,CAAC,KAAK,CAAC,kBAAmB,EAAY,CAAC,OAAO,EAAE,CAAC,CAAC;oBAC7D,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;KAAA;IAED;;;;OAIG;IACU,WAAW,CAAC,WAAmB,EAAE,OAA+B;;YACzE,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;oBACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBACpD,MAAM,KAAK,GAAG,QAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;oBACzC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;wBACf,QAAS,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAC/B,CAAC;gBACL,CAAC;YACL,CAAC;oBAAS,CAAC;gBACP,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC;KAAA;IAED;;;OAGG;IACU,gBAAgB,CAAC,WAAmB;;YAC7C,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;oBACrC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC,MAAM,GAAG,CAAC,CAAC;gBACnD,CAAC;YACL,CAAC;oBAAS,CAAC;gBACP,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC;KAAA;IAED;;;OAGG;IACU,kBAAkB;;YAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,IAAI,KAAK,GAAG,CAAC,CAAC;gBACd,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;oBAChD,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC;gBAC7B,CAAC;gBACD,OAAO,KAAK,CAAC;YACjB,CAAC;oBAAS,CAAC;gBACP,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC;KAAA;CACJ;AAlID,sCAkIC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple event scheduler that uses a queue and a spinlock.
|
|
3
|
+
*/
|
|
4
|
+
export declare class QueueEventScheduler {
|
|
5
|
+
private _eventQueue;
|
|
6
|
+
private _spinlock;
|
|
7
|
+
/**
|
|
8
|
+
* Schedule an event action to be processed.
|
|
9
|
+
* @param {() => void} eventAction - The event action to be processed.
|
|
10
|
+
*/
|
|
11
|
+
scheduleEventAction(eventAction: () => void): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Process all events in the queue.
|
|
14
|
+
*/
|
|
15
|
+
processEvents(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Clear all pending events.
|
|
18
|
+
*/
|
|
19
|
+
clearEvents(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Get the number of pending events.
|
|
22
|
+
* @returns {number} The number of pending events.
|
|
23
|
+
*/
|
|
24
|
+
get pendingEventCount(): number;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueueEventScheduler.d.ts","sourceRoot":"","sources":["../QueueEventScheduler.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,SAAS,CAA4B;IAE7C;;;OAGG;IACU,mBAAmB,CAAC,WAAW,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxE;;OAEG;IACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3C;;OAEG;IACU,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAMzC;;;OAGG;IACH,IAAW,iBAAiB,IAAI,MAAM,CAKrC;CACJ"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.QueueEventScheduler = void 0;
|
|
13
|
+
const Spinlock_1 = require("./Spinlock");
|
|
14
|
+
/**
|
|
15
|
+
* A simple event scheduler that uses a queue and a spinlock.
|
|
16
|
+
*/
|
|
17
|
+
class QueueEventScheduler {
|
|
18
|
+
constructor() {
|
|
19
|
+
this._eventQueue = [];
|
|
20
|
+
this._spinlock = new Spinlock_1.Spinlock();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Schedule an event action to be processed.
|
|
24
|
+
* @param {() => void} eventAction - The event action to be processed.
|
|
25
|
+
*/
|
|
26
|
+
scheduleEventAction(eventAction) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
yield this._spinlock.acquireLock();
|
|
29
|
+
this._eventQueue.push(eventAction);
|
|
30
|
+
this._spinlock.releaseLock();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Process all events in the queue.
|
|
35
|
+
*/
|
|
36
|
+
processEvents() {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
if (this._eventQueue.length === 0) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
yield this._spinlock.acquireLock();
|
|
42
|
+
while (this._eventQueue.length > 0) {
|
|
43
|
+
const event = this._eventQueue.shift();
|
|
44
|
+
event();
|
|
45
|
+
}
|
|
46
|
+
this._spinlock.releaseLock();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Clear all pending events.
|
|
51
|
+
*/
|
|
52
|
+
clearEvents() {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
yield this._spinlock.acquireLock();
|
|
55
|
+
this._eventQueue = [];
|
|
56
|
+
this._spinlock.releaseLock();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the number of pending events.
|
|
61
|
+
* @returns {number} The number of pending events.
|
|
62
|
+
*/
|
|
63
|
+
get pendingEventCount() {
|
|
64
|
+
this._spinlock.acquireLock();
|
|
65
|
+
const count = this._eventQueue.length;
|
|
66
|
+
this._spinlock.releaseLock();
|
|
67
|
+
return count;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.QueueEventScheduler = QueueEventScheduler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueueEventScheduler.js","sourceRoot":"","sources":["../QueueEventScheduler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAsC;AAEtC;;GAEG;AACH,MAAa,mBAAmB;IAAhC;QACY,gBAAW,GAAmB,EAAE,CAAC;QACjC,cAAS,GAAa,IAAI,mBAAQ,EAAE,CAAC;IA8CjD,CAAC;IA5CG;;;OAGG;IACU,mBAAmB,CAAC,WAAuB;;YACpD,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YACnC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QACjC,CAAC;KAAA;IAED;;OAEG;IACU,aAAa;;YACtB,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,OAAO;YACX,CAAC;YACD,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,EAAG,CAAC;gBACxC,KAAK,EAAE,CAAC;YACZ,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QACjC,CAAC;KAAA;IAED;;OAEG;IACU,WAAW;;YACpB,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YACnC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QACjC,CAAC;KAAA;IAED;;;OAGG;IACH,IAAW,iBAAiB;QACxB,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACtC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AAhDD,kDAgDC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple mutex/lock implementation using a queue.
|
|
3
|
+
*/
|
|
4
|
+
export declare class Spinlock {
|
|
5
|
+
private locked;
|
|
6
|
+
private waitingQueue;
|
|
7
|
+
/**
|
|
8
|
+
* Acquire the lock. If the lock is already held, queue the request.
|
|
9
|
+
*/
|
|
10
|
+
acquireLock(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Release the lock. If there are waiting acquisitions, pass the lock to the next in line.
|
|
13
|
+
*/
|
|
14
|
+
releaseLock(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Check if the lock is currently held.
|
|
17
|
+
*/
|
|
18
|
+
get isLocked(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get the number of waiters.
|
|
21
|
+
*/
|
|
22
|
+
get waiterCount(): number;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Spinlock.d.ts","sourceRoot":"","sources":["../Spinlock.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,QAAQ;IACjB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAyB;IAE7C;;OAEG;IACU,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAazC;;OAEG;IACI,WAAW,IAAI,IAAI;IAiB1B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;OAEG;IACH,IAAW,WAAW,IAAI,MAAM,CAE/B;CACJ"}
|
package/dist/Spinlock.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Spinlock = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* A simple mutex/lock implementation using a queue.
|
|
15
|
+
*/
|
|
16
|
+
class Spinlock {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.locked = false;
|
|
19
|
+
this.waitingQueue = [];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Acquire the lock. If the lock is already held, queue the request.
|
|
23
|
+
*/
|
|
24
|
+
acquireLock() {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
// If lock is free, acquire it immediately
|
|
27
|
+
if (!this.locked) {
|
|
28
|
+
this.locked = true;
|
|
29
|
+
return Promise.resolve();
|
|
30
|
+
}
|
|
31
|
+
// Otherwise, queue this request
|
|
32
|
+
return new Promise(resolve => {
|
|
33
|
+
this.waitingQueue.push(resolve);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Release the lock. If there are waiting acquisitions, pass the lock to the next in line.
|
|
39
|
+
*/
|
|
40
|
+
releaseLock() {
|
|
41
|
+
if (!this.locked && this.waitingQueue.length === 0) {
|
|
42
|
+
throw new Error("Lock is not held.");
|
|
43
|
+
}
|
|
44
|
+
// If there are waiting acquisitions, pass the lock to the next one
|
|
45
|
+
if (this.waitingQueue.length > 0) {
|
|
46
|
+
const nextResolve = this.waitingQueue.shift();
|
|
47
|
+
// The lock remains locked, but now the next waiter has it
|
|
48
|
+
// We don't set locked = false because it's immediately re-acquired
|
|
49
|
+
nextResolve();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// No waiters, release the lock
|
|
53
|
+
this.locked = false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if the lock is currently held.
|
|
58
|
+
*/
|
|
59
|
+
get isLocked() {
|
|
60
|
+
return this.locked;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the number of waiters.
|
|
64
|
+
*/
|
|
65
|
+
get waiterCount() {
|
|
66
|
+
return this.waitingQueue.length;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.Spinlock = Spinlock;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Spinlock.js","sourceRoot":"","sources":["../Spinlock.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA;;GAEG;AACH,MAAa,QAAQ;IAArB;QACY,WAAM,GAAY,KAAK,CAAC;QACxB,iBAAY,GAAsB,EAAE,CAAC;IAmDjD,CAAC;IAjDG;;OAEG;IACU,WAAW;;YACpB,0CAA0C;YAC1C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;YAC7B,CAAC;YAED,gCAAgC;YAChC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;gBACzB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;IAED;;OAEG;IACI,WAAW;QACd,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACzC,CAAC;QAED,mEAAmE;QACnE,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAG,CAAC;YAC/C,0DAA0D;YAC1D,mEAAmE;YACnE,WAAW,EAAE,CAAC;QAClB,CAAC;aAAM,CAAC;YACJ,+BAA+B;YAC/B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACxB,CAAC;IACL,CAAC;IAED;;OAEG;IACH,IAAW,QAAQ;QACf,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,IAAW,WAAW;QAClB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;IACpC,CAAC;CACJ;AArDD,4BAqDC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./BasicEventModel"), exports);
|
|
18
|
+
__exportStar(require("./MessageRouter"), exports);
|
|
19
|
+
__exportStar(require("./QueueEventScheduler"), exports);
|
|
20
|
+
__exportStar(require("./Spinlock"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,kDAAgC;AAChC,wDAAsC;AACtC,6CAA2B"}
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vbstyle-event-models",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A TypeScript library providing event models, message routing, and event scheduling utilities with thread safety",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"events",
|
|
12
|
+
"event-model",
|
|
13
|
+
"message-router",
|
|
14
|
+
"event-scheduler",
|
|
15
|
+
"spinlock",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"author": "pac-dessert1436",
|
|
19
|
+
"license": "BSD-3-Clause",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/yourusername/vbstyle-event-models.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/yourusername/vbstyle-event-models#readme",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"typescript": "^5.9.3"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2016",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"rootDir": "./",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"*.ts"
|
|
15
|
+
],
|
|
16
|
+
"exclude": [
|
|
17
|
+
"node_modules",
|
|
18
|
+
"dist"
|
|
19
|
+
]
|
|
20
|
+
}
|