vbstyle-event-models 1.0.5

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.
@@ -0,0 +1,32 @@
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 declare class BasicEventModel {
15
+ #eventHandlers: EventHandler[];
16
+ /**
17
+ * Add an event handler to the event model.
18
+ * @param {EventHandler} handler - The event handler to add.
19
+ */
20
+ addHandler(handler: EventHandler): void;
21
+ /**
22
+ * Remove an event handler from the event model.
23
+ * @param {EventHandler} handler - The event handler to remove.
24
+ */
25
+ removeHandler(handler: EventHandler): void;
26
+ /**
27
+ * Raise the event from the event model, invoking all registered event handlers.
28
+ * @param {any} sender - The event sender.
29
+ * @param {EventArgs} e - The event arguments.
30
+ */
31
+ raiseEvent(sender: any, e: EventArgs): void;
32
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * @typedef {any[]} EventArgs A type for event arguments that can be any array of values.
4
+ */
5
+
6
+ /**
7
+ * @typedef {(sender: any, e: EventArgs) => void} EventHandler
8
+ * A delegate type for event handlers that take any sender and event arguments.
9
+ */
10
+
11
+ /**
12
+ * A basic event model that allows registration and invocation of event handlers.
13
+ */
14
+ export class BasicEventModel {
15
+ #eventHandlers = [];
16
+
17
+ /**
18
+ * Add an event handler to the event model.
19
+ */
20
+ addHandler(handler) {
21
+ this.#eventHandlers.push(handler);
22
+ }
23
+
24
+ /**
25
+ * Remove an event handler from the event model.
26
+ */
27
+ removeHandler(handler) {
28
+ this.#eventHandlers = this.#eventHandlers.filter(h => h !== handler);
29
+ }
30
+
31
+ /**
32
+ * Raise the event from the event model, invoking all registered event handlers.
33
+ */
34
+ raiseEvent(sender, e) {
35
+ this.#eventHandlers.forEach(handler => handler(sender, e));
36
+ }
37
+ }
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.
@@ -0,0 +1,50 @@
1
+ import { MutexLock } from './MutexLock';
2
+
3
+ /**
4
+ * Type alias for a message handler function.
5
+ */
6
+ export type MessageHandler = (content: any) => void;
7
+
8
+ /**
9
+ * A singleton class for message routing, using a mutex lock for thread safety.
10
+ */
11
+ export declare class MessageRouter {
12
+ static #instance: MessageRouter | null;
13
+ #subscribers: Map<string, Set<MessageHandler>>;
14
+ #lock: MutexLock;
15
+ constructor();
16
+
17
+ /**
18
+ * Get the singleton instance of the MessageRouter class.
19
+ * @returns {MessageRouter} The singleton instance.
20
+ */
21
+ static get instance(): MessageRouter;
22
+ /**
23
+ * Subscribe to a message type.
24
+ * @param {string} messageType - The type of message to subscribe to.
25
+ * @param {MessageHandler} handler - The message handler to call when a message of the specified type is sent.
26
+ */
27
+ subscribe(messageType: string, handler: MessageHandler): Promise<void>;
28
+ /**
29
+ * Send a message of a specific type.
30
+ * @param {string} messageType - The type of message to send.
31
+ * @param {*} content - The content of the message to send.
32
+ */
33
+ send(messageType: string, content: any): Promise<void>;
34
+ /**
35
+ * Unsubscribe from a message type.
36
+ * @param {string} messageType - The type of message to unsubscribe from.
37
+ * @param {MessageHandler} handler - The message handler to remove from the subscribers list.
38
+ */
39
+ unsubscribe(messageType: string, handler: MessageHandler): Promise<void>;
40
+ /**
41
+ * Clear all subscribers for a specific message type.
42
+ * @param {string} messageType - The type of message to clear subscribers for.
43
+ */
44
+ clearSubscribers(messageType: string): Promise<void>;
45
+ /**
46
+ * Get the total number of subscribers across all message types.
47
+ * @returns {number} The total number of subscribers.
48
+ */
49
+ getSubscriberCount(): Promise<number>;
50
+ }
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ import { MutexLock } from "./MutexLock.js";
3
+
4
+ /**
5
+ * @typedef {(content: any) => void} MessageHandler Type alias for a message handler function.
6
+ */
7
+
8
+ /**
9
+ * A singleton class for message routing, using a mutex lock for thread safety.
10
+ */
11
+ export class MessageRouter {
12
+ static #instance = null;
13
+ #subscribers = new Map();
14
+ #lock = new MutexLock();
15
+
16
+ constructor() {
17
+ if (MessageRouter.#instance)
18
+ throw new Error("Use MessageRouter.instance to get the singleton instance.");
19
+ }
20
+
21
+ /**
22
+ * Get the singleton instance of the MessageRouter class.
23
+ * @returns {MessageRouter} The singleton instance.
24
+ */
25
+ static get instance() {
26
+ if (!MessageRouter.#instance)
27
+ MessageRouter.#instance = new MessageRouter();
28
+
29
+ return MessageRouter.#instance;
30
+ }
31
+
32
+ /**
33
+ * Subscribe to a message type.
34
+ * @param {string} messageType - The type of message to subscribe to.
35
+ * @param {MessageHandler} handler - The message handler to call when a message of the specified type is sent.
36
+ */
37
+ async subscribe(messageType, handler) {
38
+ await this.#lock.acquireLock();
39
+ try {
40
+ if (!messageType) throw new Error("Message type cannot be empty.");
41
+ if (typeof handler !== "function") throw new Error("Handler must be a function.");
42
+
43
+ if (!this.#subscribers.has(messageType)) {
44
+ this.#subscribers.set(messageType, new Set());
45
+ }
46
+ this.#subscribers.get(messageType).add(handler);
47
+ } finally {
48
+ this.#lock.releaseLock();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Send a message of a specific type.
54
+ * @param {string} messageType - The type of message to send.
55
+ * @param {*} content - The content of the message to send.
56
+ */
57
+ async send(messageType, content) {
58
+ let handlers = null;
59
+
60
+ await this.#lock.acquireLock();
61
+ try {
62
+ if (!messageType) throw new Error("Message type cannot be empty.");
63
+
64
+ if (this.#subscribers.has(messageType)) {
65
+ handlers = this.#subscribers.get(messageType);
66
+ }
67
+ } finally {
68
+ this.#lock.releaseLock();
69
+ }
70
+
71
+ if (handlers) {
72
+ for (const handler of handlers) {
73
+ try {
74
+ handler(content);
75
+ } catch (ex) {
76
+ // Log but don't crash the message pump
77
+ console.debug(`Handler error: ${ex.message}`);
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Unsubscribe from a message type.
85
+ * @param {string} messageType - The type of message to unsubscribe from.
86
+ * @param {MessageHandler} handler - The message handler to remove from the subscribers list.
87
+ */
88
+ async unsubscribe(messageType, handler) {
89
+ await this.#lock.acquireLock();
90
+ try {
91
+ if (this.#subscribers.has(messageType)) {
92
+ /** @type {Set<MessageHandler>} */
93
+ const handlers = this.#subscribers.get(messageType);
94
+ if (handlers.has(handler))
95
+ handlers.delete(handler);
96
+ }
97
+ } finally {
98
+ this.#lock.releaseLock();
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Clear all subscribers for a specific message type.
104
+ * @param {string} messageType - The type of message to clear subscribers for.
105
+ */
106
+ async clearSubscribers(messageType) {
107
+ await this.#lock.acquireLock();
108
+ try {
109
+ if (this.#subscribers.has(messageType)) {
110
+ /** @type {Set<MessageHandler>} */
111
+ const handlers = this.#subscribers.get(messageType);
112
+ // Clear the handlers set
113
+ handlers.clear();
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
+ get subscriberCount() {
125
+ let count = 0;
126
+ for (const handlers of this.#subscribers.values()) {
127
+ count += handlers.length;
128
+ }
129
+ return count;
130
+ }
131
+ }
package/MutexLock.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A simple mutex lock implementation using a queue.
3
+ */
4
+ export declare class MutexLock {
5
+ #locked: boolean;
6
+ #waitingQueue: Promise<void>[];
7
+
8
+ /**
9
+ * Acquire the lock. If the lock is already held, queue the request.
10
+ */
11
+ async acquireLock(): Promise<void>;
12
+ /**
13
+ * Release the lock. If there are waiting acquisitions, pass the lock to the next in line.
14
+ */
15
+ releaseLock(): void;
16
+ /**
17
+ * Check if the lock is currently held.
18
+ */
19
+ get isLocked(): boolean;
20
+ /**
21
+ * Get the number of waiters.
22
+ */
23
+ get waiterCount(): number;
24
+ }
package/MutexLock.js ADDED
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * A simple mutex lock implementation using a queue.
5
+ */
6
+ export class MutexLock {
7
+ #locked = false;
8
+ #waitingQueue = [];
9
+
10
+ /**
11
+ * Acquire the lock. If the lock is already held, queue the request.
12
+ */
13
+ async acquireLock() {
14
+ // If lock is free, acquire it immediately
15
+ if (!this.#locked) {
16
+ this.#locked = true;
17
+ return Promise.resolve();
18
+ }
19
+
20
+ // Otherwise, queue this request
21
+ return new Promise(resolve => {
22
+ this.#waitingQueue.push(resolve);
23
+ });
24
+ }
25
+
26
+ /**
27
+ * Release the lock. If there are waiting acquisitions, pass the lock to the next in line.
28
+ */
29
+ releaseLock() {
30
+ if (!this.#locked && this.#waitingQueue.length === 0) {
31
+ throw new Error("Lock is not held.");
32
+ }
33
+
34
+ // If there are waiting acquisitions, pass the lock to the next one
35
+ if (this.#waitingQueue.length > 0) {
36
+ /** @type {() => void} */
37
+ const nextResolve = this.#waitingQueue.shift();
38
+ // The lock remains locked, but now the next waiter has it
39
+ // We don't set locked = false because it's immediately re-acquired
40
+ nextResolve();
41
+ } else {
42
+ // No waiters, release the lock
43
+ this.#locked = false;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Check if the lock is currently held.
49
+ */
50
+ get isLocked() {
51
+ return this.#locked;
52
+ }
53
+
54
+ /**
55
+ * Get the number of waiters.
56
+ */
57
+ get waiterCount() {
58
+ return this.#waitingQueue.length;
59
+ }
60
+ }
@@ -0,0 +1,29 @@
1
+ import { EventArgs, EventHandler } from "./BasicEventModel";
2
+ import { MutexLock } from "./MutexLock";
3
+
4
+ /**
5
+ * A simple event scheduler that uses a queue and a mutex lock for thread safety.
6
+ */
7
+ export declare class QueueEventScheduler {
8
+ #eventQueue: EventHandler[];
9
+ #lock: MutexLock;
10
+
11
+ /**
12
+ * Schedule an event to be processed.
13
+ * @param {EventHandler} event - The event to be processed.
14
+ */
15
+ scheduleEvent(event: EventHandler): Promise<void>;
16
+ /**
17
+ * Process all events in the queue.
18
+ */
19
+ processEvents(sender: any, e: EventArgs): Promise<void>;
20
+ /**
21
+ * Clear all pending events.
22
+ */
23
+ clearEvents(): Promise<void>;
24
+ /**
25
+ * Get the number of pending events.
26
+ * @returns {number} The number of pending events.
27
+ */
28
+ get pendingEventCount(): number;
29
+ }
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ import { MutexLock } from "./MutexLock.js";
3
+
4
+ /**
5
+ * @import { EventArgs, EventHandler } from "./BasicEventModel.js";
6
+ */
7
+
8
+ /**
9
+ * A simple event scheduler that uses a queue and a mutex lock for thread safety.
10
+ */
11
+ export class QueueEventScheduler {
12
+ #eventQueue = [];
13
+ #lock = new MutexLock();
14
+
15
+ constructor() {
16
+ }
17
+
18
+ /**
19
+ * Schedule an event to be processed.
20
+ * @param {EventHandler} event - The event to be processed.
21
+ */
22
+ async scheduleEvent(event) {
23
+ await this.#lock.acquireLock();
24
+ try {
25
+ this.#eventQueue.push(event);
26
+ } finally {
27
+ this.#lock.releaseLock();
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Process all events in the queue.
33
+ * @param {*} sender - The sender of the event.
34
+ * @param {EventArgs} e - The event arguments.
35
+ */
36
+ async processEvents(sender, e) {
37
+ if (this.#eventQueue.length === 0) {
38
+ return;
39
+ }
40
+
41
+ await this.#lock.acquireLock();
42
+ try {
43
+ while (this.#eventQueue.length > 0) {
44
+ const event = this.#eventQueue.shift();
45
+ event?.(sender, e);
46
+ }
47
+ } finally {
48
+ this.#lock.releaseLock();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Clear all pending events.
54
+ */
55
+ async clearEvents() {
56
+ await this.#lock.acquireLock();
57
+ try {
58
+ this.#eventQueue = [];
59
+ } finally {
60
+ this.#lock.releaseLock();
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get the number of pending events.
66
+ * @returns {number} The number of pending events.
67
+ */
68
+ get pendingEventCount() {
69
+ return this.#eventQueue.length;
70
+ }
71
+ }
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # VB.NET-Style Event Models (JavaScript/TypeScript Edition)
2
+
3
+ A JavaScript/TypeScript library providing event models, message routing, and event scheduling utilities with thread safety, inspired by VB.NET's event model.
4
+
5
+ NOTE: This library was written in pure TypeScript initially but underwent packaging issues, so it's now rewritten in JavaScript with TypeScript declaration files provided for type support.
6
+
7
+ Install this package via NPM: `npm install vbstyle-event-models` (no other dependencies required, current version 1.0.5)
8
+
9
+ ## Features
10
+
11
+ - **BasicEventModel**: A simple event model for registering and invoking event handlers
12
+ - **MessageRouter**: A singleton message router for publish-subscribe pattern
13
+ - **QueueEventScheduler**: A queue-based event scheduler with thread safety
14
+ - **MutexLock**: A simple mutex lock implementation for thread safety
15
+
16
+ ## Usage
17
+
18
+ ### BasicEventModel
19
+
20
+ ```typescript
21
+ import { BasicEventModel, EventHandler, EventArgs } from 'vbstyle-event-models';
22
+
23
+ // Create an event model
24
+ const eventModel = new BasicEventModel();
25
+
26
+ // Define an event handler
27
+ const handler: EventHandler = (sender, e) => {
28
+ console.log('Event triggered:', { sender, args: e });
29
+ };
30
+
31
+ // Add the handler
32
+ eventModel.addHandler(handler);
33
+
34
+ // Raise the event
35
+ eventModel.raiseEvent('sender', ['arg1', 'arg2']);
36
+
37
+ // Remove the handler
38
+ eventModel.removeHandler(handler);
39
+ ```
40
+
41
+ ### MessageRouter
42
+
43
+ ```typescript
44
+ import { MessageRouter } from 'vbstyle-event-models';
45
+
46
+ // Get the singleton instance
47
+ const router = MessageRouter.instance;
48
+
49
+ // Subscribe to a message type
50
+ await router.subscribe('userCreated', (user) => {
51
+ console.log('User created:', user);
52
+ });
53
+
54
+ // Send a message
55
+ await router.send('userCreated', { id: 1, name: 'John' });
56
+
57
+ // Unsubscribe from a message type
58
+ await router.unsubscribe('userCreated', handler);
59
+
60
+ // Clear all subscribers for a message type
61
+ await router.clearSubscribers('userCreated');
62
+
63
+ // Get total subscriber count
64
+ const count = await router.getSubscriberCount();
65
+ console.log('Total subscribers:', count);
66
+ ```
67
+
68
+ ### QueueEventScheduler
69
+
70
+ ```typescript
71
+ import { QueueEventScheduler } from 'vbstyle-event-models';
72
+
73
+ // Create an event scheduler
74
+ const scheduler = new QueueEventScheduler();
75
+
76
+ // Schedule events
77
+ await scheduler.scheduleEvent(() => {
78
+ console.log('Event 1 executed');
79
+ });
80
+
81
+ await scheduler.scheduleEvent(() => {
82
+ console.log('Event 2 executed');
83
+ });
84
+
85
+ // Process all events
86
+ await scheduler.processEvents();
87
+
88
+ // Clear pending events
89
+ await scheduler.clearEvents();
90
+
91
+ // Get pending event count
92
+ const count = scheduler.pendingEventCount;
93
+ console.log('Pending events:', count);
94
+ ```
95
+
96
+ ### MutexLock
97
+
98
+ ```typescript
99
+ import { MutexLock } from 'vbstyle-event-models';
100
+
101
+ // Create a mutex lock
102
+ const lock = new MutexLock();
103
+
104
+ // Acquire the lock
105
+ await lock.acquireLock();
106
+ try {
107
+ // Critical section
108
+ console.log('Lock acquired');
109
+ } finally {
110
+ // Release the lock
111
+ lock.releaseLock();
112
+ console.log('Lock released');
113
+ }
114
+
115
+ // Check if locked
116
+ console.log('Is locked:', lock.isLocked);
117
+
118
+ // Get waiter count
119
+ console.log('Waiters:', lock.waiterCount);
120
+ ```
121
+
122
+ ## License
123
+
124
+ [BSD 3-Clause](LICENSE)
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './BasicEventModel';
2
+ export * from './MessageRouter';
3
+ export * from './QueueEventScheduler';
4
+ export * from './MutexLock';
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './BasicEventModel.js';
2
+ export * from './MessageRouter.js';
3
+ export * from './QueueEventScheduler.js';
4
+ export * from './MutexLock.js';
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "vbstyle-event-models",
3
+ "version": "1.0.5",
4
+ "description": "A thread-safe JS/TS library, providing event models, message routing, and event scheduling utilities.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {},
8
+ "keywords": [
9
+ "events",
10
+ "event-model",
11
+ "message-router",
12
+ "event-scheduler",
13
+ "mutex",
14
+ "mutex-lock",
15
+ "javascript"
16
+ ],
17
+ "author": "pac-dessert1436",
18
+ "license": "BSD-3-Clause",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/Pac-Dessert1436/vbstyle-event-models.git"
22
+ },
23
+ "homepage": "https://github.com/Pac-Dessert1436/vbstyle-event-models",
24
+ "dependencies": {}
25
+ }