A TypeScript utility module that provides various classes and helpers for events, trees, data structures, string manipulation, object comparison, JSON utilities, and communication management through a realtime client.
A TypeScript utility module that provides various classes and helpers for
events, trees, data structures, string manipulation, object comparison, JSON
utilities, and communication management through a realtime client.
---
> Notice on exports
>
> Most utility classes (e.g. StringUtils, ObjectUtils) are exported as a
> singleton instance, not the class constructor.
>
> ``tsnew
> import { StringUtils } from "@commonmodule/ts";
> // StringUtils is already an instantiated object – no needed.`
>
>
> When you need the class itself (rare), import from its relative path instead.
---
1. Installation
2. API Reference
- Communication
- RealtimeClient
- MessageChannelManager
- Event
- EventRecord
- EventContainer
- Loaders
- ResourceLoader
- Tree
- TreeNode
- EventTreeNode
- Utils
- IntegerUtils
- StringUtils
- ArrayUtils
- ObjectUtils
- JsonUtils
- Debouncer
3. Usage Examples
4. Contributing
5. License
---
`bash`
npm install @commonmodule/tsor
yarn add @commonmodule/ts
---
---
#### RealtimeClient
An interface representing a realtime connection or transport layer.
Implementations should provide methods for sending and receiving messages.
`ts`
interface RealtimeClient {
send(message: string): void;
onMessage(handler: (message: string) => void): void;
}
Members:
| Member | Access | Description |
| ----------------------------------------------- | ---------- | ------------------------------------------------------------------ |
| send(message: string) | public | Sends a message (serialized string) over the connection. |onMessage(handler: (message: string) => void)
| | public | Registers a callback that executes whenever a message is received. |
---
#### MessageChannelManager
A class that manages message channels, actions, and optional request–response
flows via a RealtimeClient.
`ts`
class MessageChannelManager<
Handlers extends Record
> {
constructor(client: RealtimeClient) {/ ... /}
// ...
}
Constructor:
- client: RealtimeClient – The realtime client instance used for
communication.
Members:
| Member | Access | Description |
| ------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| on | public | Subscribes to a specific channel/action pair. |off
| | public | Unsubscribes from a channel/action. If no handler is specified, removes all handlers for that action. |send(channel: string, action: string, ...args: any[])
| | public | Sends a message on the specified channel with the given action and arguments. |request
| | public | Sends a request message (with a unique requestId) on the specified channel. Returns a Promise that resolves or rejects when the response or error is received. |
---
---
#### EventRecord
A simple type alias defining the structure of event-handler objects:
`ts`
type EventRecord = Record
Each event is identified by a string key, and each key’s value is a function
taking arbitrary arguments.
---
#### EventContainer
Manages subscriptions and emissions for a set of events described by an
EventRecord.
`ts`
class EventContainer
// ...
}
Members:
| Member | Access | Description |
| ----------------------------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| on | public | Registers an event handler for the specified event name. |off
| | public | Removes a handler from an event. If no handler is passed, removes all handlers for that event. |emit
| | protected | Triggers all handlers associated with an event name (asynchronously). Returns an array of results (including any awaited promises). |hasEvent
| | protected | Checks whether this container has any handlers for the given event name. |clearEvents()
| | protected | Removes all events and handlers from this container (used internally for cleanup). |
> Note: The emit() method is protected by default in the code above,await emit()
> but you can change it to public if needed in your own implementation.\
> For asynchronous event results, always if you need the
> aggregated return values.
---
---
#### ResourceLoader
An abstract class for loading and managing reference-counted resources.
`ts`
abstract class ResourceLoader
// ...
}
Members:
| Member | Access | Description |
| ------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| resources: Map | protected | Stores loaded resources by ID. |pendingLoads: Map
| | protected | Keeps track of pending load operations. |load(id: string, ...args: any[]): Promise
| | public | Increments the reference count for the resource. If already loaded or loading, returns the existing resource/promise; otherwise, calls loadResource. |isLoaded(id: string): boolean
| | public | Checks if a resource is currently loaded (i.e., exists in resources map). |release(id: string): void
| | public | Decrements the resource’s reference count. If it drops to 0, calls cleanup(resource, id) and removes the resource from resources. |isResourceInUse(id: string): boolean
| | protected | Checks if a resource is still in use (i.e., reference count > 0). |loadResource(id: string, ...args: any[]): Promise
| | protected abstract | Subclasses must implement the actual resource loading logic. |cleanup(resource: T, id: string): void
| | protected abstract | Subclasses must define how to clean up the resource (e.g. disposing of memory, closing connections) after the reference count drops to 0. |
---
---
#### TreeNode
An abstract class that represents a tree node structure with a parent-child
relationship.
`ts`
abstract class TreeNode
// ...
}
Key Properties:
- parent: T | undefined – The parent node.children: T[]
- – List of child nodes.removed: boolean
- – If true, the node has been removed from its parent.
Members:
| Member | Access | Description |
| -------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| appendTo(parent: T, index?: number): this | public | Appends the current node to a new parent node. If index is specified, the node is inserted at that position within the parent's children. |clear(...except: (T \| undefined)[]): this
| | public | Removes all children except for the specified ones. |remove(): void
| | public | Removes the current node from its parent and clears all of its children. |
---
#### EventTreeNode
Extends TreeNode to add event handling via an internal event container.
Particularly useful for hierarchical data structures that emit or listen to
events.
`ts`
abstract class EventTreeNode<
T extends EventTreeNode
E extends EventRecord,
> extends TreeNode
// ...
}
Key Differences from TreeNode:
- Has an internal EventContainer to handle its own events.
- Supports event subscription, emission, and cleanup on removal.
Members:
| Member | Access | Description |
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| on | public | Registers an event handler for the given event name. |off
| | public | Unregisters an event handler for the given event name. |subscribe
| | public | Subscribes to events from another EventTreeNode. Automatically unsubscribes if that target node is removed. |remove(): void
| | public | Removes this node, emits a remove event, clears its event handlers, and unsubscribes from any other nodes. Always call super.remove() if you override. |
---
---
#### IntegerUtils
Provides utility methods for integer-related operations (exported as a singleton
instance).
Members:
| Member | Access | Description |
| ---------------------------------- | ---------- | --------------------------------------------------------------- |
| random(min: number, max: number) | public | Generates a random integer between min and max (inclusive). |
---
#### StringUtils
Utility class (exported as a singleton instance) for common string
manipulations.
Members:
| Member | Access | Description |
| ------------------------------------------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| capitalize(input: string): string | public | Capitalizes the first letter of each word in a given string, making all other letters lowercase. |isKebabCase(str: string): boolean
| | public | Checks if a given string is in kebab-case (lowercase words separated by hyphens). |formatNumberWithCommas(value: string, decimals?: number): string
| | public | Inserts commas as thousands separators in a numeric string. If decimals is provided (and the number is within safe integer range), it formats to that many decimals. |
---
#### ArrayUtils
Provides utility methods for array manipulation (exported as a singleton
instance).
Members:
| Member | Access | Description |
| ----------------------------------------------- | ---------- | ---------------------------------------------------------------------------------- |
| pull | public | Removes the specified elements (removeList) from the given array, if they exist. |
---
#### ObjectUtils
Utility class (exported as a singleton instance) for object comparisons.
Members:
| Member | Access | Description |
| ---------------------------------------- | ---------- | ------------------------------------------------------------------ |
| isEqual(obj1: any, obj2: any): boolean | public | Performs a deep comparison to check if two objects are equivalent. |
---
#### JsonUtils
Utilities for JSON parsing and handling edge cases like null → undefined.
Members:
| Member | Access | Description |
| ---------------------------------------- | ---------- | ---------------------------------------------------------- |
| parseWithUndefined | public | Parses JSON data, converting null values to undefined. |
---
#### Debouncer
A class to debounce function execution. Useful for events that should only fire
after a certain time has elapsed without re-triggering.
`ts`
class Debouncer {
constructor(delayMs: number, callback: (...args: any[]) => void) {/ ... /}
// ...
}
Constructor:
- delayMs: number – The time in milliseconds to wait before calling thecallback: (...args: any[]) => void
callback. Must be >= 0.
- – The function to call after the delay.
Members:
| Member | Access | Description |
| ------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- |
| execute(...args: any[]): void | public | Schedules the callback to run after the specified delay. If called again before the delay expires, the timer resets. |cancel(): void
| | public | Cancels any pending debounced callback. |isPending(): boolean
| | public | Returns true if there is a pending execution that hasn’t yet fired. |
---
Below are some short usage examples illustrating how to use the provided classes
and functions.
`ts
import { StringUtils } from "@commonmodule/ts";
const text = "hello world from TS";
const capitalized = StringUtils.capitalize(text);
console.log(capitalized);
// Output: "Hello World From Ts"
console.log(StringUtils.isKebabCase("kebab-case-example")); // true
`
`ts
import { ObjectUtils } from "@commonmodule/ts";
const objA = { name: "Alice", details: { age: 30 } };
const objB = { name: "Alice", details: { age: 30 } };
console.log(ObjectUtils.isEqual(objA, objB)); // true
`
`ts
import { Debouncer } from "@commonmodule/ts";
function saveToServer(data: any) {
console.log("Saving data to server:", data);
}
const debouncedSave = new Debouncer(500, saveToServer);
debouncedSave.execute({ id: 1 });
// If "execute" is called again within 500ms, the old call is canceled
debouncedSave.execute({ id: 2 }); // Only this call will run after 500ms
`
`ts
import { EventContainer } from "@commonmodule/ts";
class MyEventContainer extends EventContainer<{
dataReceived: (data: any) => void;
}> {
constructor() {
super();
this.on("dataReceived", (data) => {
console.log("Data received:", data);
});
// Emit an event
this.emit("dataReceived", { message: "Hello" });
// => "Data received: { message: 'Hello' }"
}
}
`
`ts
import { MessageChannelManager, RealtimeClient } from "@commonmodule/ts";
class WebSocketClient implements RealtimeClient {
private socket: WebSocket;
private messageHandler?: (message: string) => void;
constructor(url: string) {
this.socket = new WebSocket(url);
this.socket.onmessage = (event) => {
if (this.messageHandler) {
this.messageHandler(event.data);
}
};
}
send(message: string): void {
this.socket.send(message);
}
onMessage(handler: (message: string) => void): void {
this.messageHandler = handler;
}
}
const client = new WebSocketClient("ws://example.com");
const manager = new MessageChannelManager<{
greet: (name: string) => void;
}>(client);
// Listen for 'greet' actions on channel "chat"
manager.on("chat", "greet", (name) => {
console.log("Hello,", name);
});
// Send a message
manager.send("chat", "greet", "Alice");
// => Console: "Hello, Alice"
// Request/Response pattern
(async () => {
try {
const response = await manager.request
console.log("Received welcome message:", response);
} catch (error) {
console.error("Failed to get welcome message:", error);
}
})();
`
---
1. Fork the repository.
2. Create a new branch: git checkout -b feature/my-feature.git commit -m 'Add my feature'
3. Make your changes and commit: .git push origin feature/my-feature`.
4. Push the changes:
5. Create a pull request.
---
This module is provided under the MIT License. For more details, please see
the LICENSE file in the repository.
---
Author: yj.gaia