ngx-signal-hub
Version:
[](https://badge.fury.io/js/ngx-signal-hub) [](https://opensource.org/licenses/MIT) A lightweight, reactive signal hub service f
343 lines (342 loc) • 13.5 kB
TypeScript
import { DestroyRef, Injector, Signal } from '@angular/core';
import * as i0 from "@angular/core";
/**
* Represents an event in the signal hub with a key, data, and timestamp.
*/
export interface HubEvent<T = unknown> {
key: string;
data: T;
timestamp: number;
}
/**
* Represents the options for subscribing to a signal hub event.
*/
export interface HubEventOptions<T> {
/** The key/query string to match events against (e.g., 'user:login', 'user:*'). */
key: string;
/** The callback function to execute when an event matches the query. */
callback: (event: HubEvent<T>) => void | Promise<void>;
/** Optional DestroyRef for auto-cleaning subscriptions. */
destroyRef?: DestroyRef;
/** If true, replays the latest event for the key on subscription. */
replayLatest?: boolean;
/** Optional error handler for callback errors. */
onError?: (error: unknown, event: HubEvent<T>) => void;
}
/**
* Represents a subscription to the signal hub.
*/
export interface HubSubscription {
unsubscribe: () => void;
}
/**
* A hybrid signal hub service for publishing and observing events using Signals and callbacks.
* Supports pub/sub with key-based routing, pattern matching, and Signal-based event observation.
*
* @example
* ```typescript
* @Component({...})
* export class MyComponent {
* constructor(private signalHub: SignalHubService, private destroyRef: DestroyRef) {
* // Synchronous subscription
* this.signalHub.on({
* key: 'user:*',
* callback: (event) => console.log(event.data),
* destroyRef: this.destroyRef,
* replayLatest: true,
* });
*
* // Asynchronous subscription
* this.signalHub.onAsync({
* key: 'user:login',
* callback: async (event) => {
* await fetch('/log', { method: 'POST', body: JSON.stringify(event.data) });
* console.log(event.data);
* },
* replayLatest: true,
* onError: (err) => console.error('Event error:', err),
* });
*
* // Publish an event
* this.signalHub.publish('user:login', { id: 123 });
*
* // Clear a specific event
* this.signalHub.clearEvent('user:login');
*
* // Reset everything
* this.signalHub.reset({ clearSubscribers: true });
* }
* }
* ```
*/
export declare class SignalHubService {
private readonly eventQueue;
private readonly eventRegistry;
private readonly subscribers;
constructor(injector: Injector);
/**
* Publishes an event with the given key and data, triggering subscribers and updating the registry.
*
* @param key - The event key (e.g., 'user:login').
* @param data - The event data (must not be null or undefined).
* @throws Error if key is empty or data is null/undefined.
*
* @example
* ```typescript
* signalHub.publish('user:login', { id: 123 });
* // Triggers subscribers to 'user:login' or 'user:*'
* ```
*/
publish<T>(key: string, data: T): void;
/**
* Publishes an event with the given key, triggering subscribers and updating the registry.
*
* @param key - The event key (e.g., 'user:login').
* @throws Error if key is empty.
*
* @example
* ```typescript
* signalHub.publish('user:login');
* // Triggers subscribers to 'user:login' or 'user:*'
* ```
*/
publishNoData(key: string): void;
/**
* Subscribes to events matching a query with a synchronous callback, optionally auto-cleaning with DestroyRef.
* Supports pattern matching (e.g., 'user:*') and optional replay of the latest event.
*
* @param options - Subscription options including key, callback, and optional settings.
* @returns A HubSubscription object with an unsubscribe method.
* @throws Error if key is empty or callback is not a function.
*
* @example
* ```typescript
* signalHub.subscribe({
* key: 'user:*',
* callback: (event) => console.log(event.data),
* replayLatest: true,
* destroyRef: inject(DestroyRef),
* onError: (err) => console.error('Error:', err),
* });
* ```
*/
subscribe<T>({ key, callback, destroyRef, replayLatest, onError, }: HubEventOptions<T>): HubSubscription;
/**
* Subscribes to events matching a query with an asynchronous callback, optionally auto-cleaning with DestroyRef.
* Supports pattern matching (e.g., 'user:*') and optional replay of the latest event.
*
* @param options - Subscription options including key, callback, and optional settings.
* @returns A Promise resolving to a HubSubscription object with an unsubscribe method.
* @throws Error if key is empty or callback is not a function.
*
* @example
* ```typescript
* await signalHub.subscribeAsync({
* key: 'user:*',
* callback: async (event) => console.log(event.data),
* replayLatest: true,
* destroyRef: inject(DestroyRef),
* onError: (err) => console.error('Error:', err),
* });
* ```
*/
subscribeAsync<T>({ key, callback, destroyRef, replayLatest, onError, }: HubEventOptions<T>): Promise<HubSubscription>;
/**
* A shorthand for subscribing to events with a synchronous callback, with optional replay and auto-cleanup.
*
* @param options - Options including key, callback, and optional replayLatest, destroyRef, and onError.
* @returns A HubSubscription object with an unsubscribe method.
*
* @example
* ```typescript
* signalHub.on({
* key: 'user:login',
* callback: (event) => console.log(event.data),
* replayLatest: true,
* destroyRef: inject(DestroyRef),
* onError: (err) => console.error('Error:', err),
* });
* ```
*/
on<T>({ key, callback, replayLatest, destroyRef, onError }: HubEventOptions<T>): HubSubscription;
/**
* A shorthand for subscribing to events with an asynchronous callback, with optional replay and auto-cleanup.
*
* @param options - Options including key, callback, and optional replayLatest, destroyRef, and onError.
* @returns A Promise resolving to a HubSubscription object with an unsubscribe method.
*
* @example
* ```typescript
* await signalHub.onAsync({
* key: 'user:login',
* callback: async (event) => console.log(event.data),
* replayLatest: true,
* destroyRef: inject(DestroyRef),
* onError: (err) => console.error('Error:', err),
* });
* ```
*/
onAsync<T>({ key, callback, replayLatest, destroyRef, onError, }: HubEventOptions<T>): Promise<HubSubscription>;
/**
* Subscribes to a single event matching the key with a synchronous callback, auto-unsubscribing after the first match.
*
* @param options - Options including key, callback, and optional replayLatest and onError.
* @returns A HubSubscription object with an unsubscribe method.
*
* @example
* ```typescript
* signalHub.once({
* key: 'user:login',
* callback: (event) => console.log('Logged in:', event.data),
* replayLatest: true,
* onError: (err) => console.error('Error:', err),
* });
* ```
*/
once<T>({ key, callback, replayLatest, onError }: HubEventOptions<T>): HubSubscription;
/**
* Subscribes to a single event matching the key with an asynchronous callback, auto-unsubscribing after the first match.
*
* @param options - Options including key, callback, and optional replayLatest and onError.
* @returns A Promise resolving to a HubSubscription object with an unsubscribe method.
*
* @example
* ```typescript
* await signalHub.onceAsync({
* key: 'user:login',
* callback: async (event) => console.log('Logged in:', event.data),
* replayLatest: true,
* onError: (err) => console.error('Error:', err),
* });
* ```
*/
onceAsync<T>({ key, callback, replayLatest, onError, }: HubEventOptions<T>): Promise<HubSubscription>;
/**
* Subscribes to the latest events from multiple keys, invoking the callback with an array of events
* whenever any key emits a new event, provided all keys have emitted at least once.
*
* @param options - Options including keys, callback, and optional settings.
* @returns A HubSubscription object with an unsubscribe method.
* @throws Error if keys array is empty or contains invalid keys.
*
* @example
* ```typescript
* signalHub.onCombineLatest({
* keys: ['user:login', 'user:logout'],
* callback: (events) => console.log(events.map(e => e.data)),
* replayLatest: true,
* destroyRef: inject(DestroyRef),
* onError: (err, events) => console.error('Error:', err, events),
* });
* ```
*/
onCombineLatest<T>({ keys, callback, destroyRef, replayLatest, onError, }: {
keys: string[];
callback: (events: HubEvent<T>[]) => void | Promise<void>;
destroyRef?: DestroyRef;
replayLatest?: boolean;
onError?: (error: unknown, events: HubEvent<T>[]) => void;
}): HubSubscription;
/**
* Subscribes to the latest events from multiple keys with an asynchronous callback,
* invoking the callback with an array of events whenever any key emits a new event,
* provided all keys have emitted at least once.
*
* @param options - Options including keys, callback, and optional settings.
* @returns A Promise resolving to a HubSubscription object with an unsubscribe method.
* @throws Error if keys array is empty or contains invalid keys.
*/
onCombineLatestAsync<T>({ keys, callback, destroyRef, replayLatest, onError, }: {
keys: string[];
callback: (events: HubEvent<T>[]) => Promise<void>;
destroyRef?: DestroyRef;
replayLatest?: boolean;
onError?: (error: unknown, events: HubEvent<T>[]) => void;
}): Promise<HubSubscription>;
/**
* Returns a Signal for observing the latest event for a specific key, or null if none exists.
*
* @param key - The event key to observe (e.g., 'user:login').
* @returns A Signal emitting the latest HubEvent or null.
* @throws Error if key is empty.
*
* @example
* ```typescript
* const userLogin = signalHub.toSignal('user:login');
* effect(() => console.log(userLogin()?.data));
* ```
*/
toSignal<T>(key: string): Signal<HubEvent<T> | null>;
/**
* Returns a Signal for observing the latest events for multiple keys or wildcard patterns,
* returning an array of matching events.
*
* @param keys - Array of event keys or wildcard patterns to observe (e.g., `['user:login', 'user:*']`).
* @param options - Optional configuration for sorting the returned events.
* @param options.sortBy - Sorts the events by `'timestamp'` (descending, latest first) or `'key'` (alphabetically).
* @returns A Signal emitting an array of matching HubEvents, deduplicated by key.
* @throws Error if the keys array is empty or contains invalid (empty) keys.
*
* @example
* ```typescript
* // Observe specific and wildcard keys
* const events = signalHub.toSignalMultiple(['user:login', 'user:*'], { sortBy: 'timestamp' });
* effect(() => console.log(events().map(e => `${e.key}: ${e.data}`)));
*
* // Publish events
* signalHub.publish('user:login', { id: 123 });
* signalHub.publish('user:logout', { id: 456 });
*
* // Output might be: ["user:logout: {id: 456}", "user:login: {id: 123}"] (sorted by timestamp)
* ```
*/
toSignalMultiple<T>(keys: string[], options?: {
sortBy?: 'timestamp' | 'key';
}): Signal<HubEvent<T>[]>;
/**
* Clears a specific event from the registry by its key.
*
* @param key - The event key to clear.
* @throws Error if key is empty.
*
* @example
* ```typescript
* signalHub.clearEvent('user:login');
* // Removes 'user:login' from the registry, affecting replayLatest and signals
* ```
*/
clearEvent(key: string): void;
/**
* Resets the event registry, optionally clearing all subscribers.
*
* @param options - Optional settings for the reset operation.
* @param options.clearSubscribers - If true, also clears all subscribers (default: false).
*
* @example
* ```typescript
* signalHub.reset(); // Clears event registry only
* signalHub.reset({ clearSubscribers: true }); // Clears registry and subscribers
* ```
*/
reset(options?: {
clearSubscribers?: boolean;
}): void;
/**
* Unsubscribes all callbacks for a specific key or all subscribers if no key is provided.
*
* @param key - Optional key to unsubscribe all callbacks for. If omitted, all subscribers are removed.
*
* @example
* ```typescript
* signalHub.unsubscribeAll('user:login'); // Remove all 'user:login' subscribers
* signalHub.unsubscribeAll(); // Remove all subscribers
* ```
*/
unsubscribeAll(key?: string): void;
private internalSubscribe;
private notifySubscribers;
private unsubscribe;
private matchQuery;
static ɵfac: i0.ɵɵFactoryDeclaration<SignalHubService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<SignalHubService>;
}