alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
575 lines • 21.4 kB
TypeScript
import { Alepha, AlephaError, KIND, PipelinePrimitive, PipelinePrimitiveOptions, Primitive, Service, Static, TSchema } from "alepha";
import { DateTimeProvider, DurationLike } from "alepha/datetime";
//#region ../../src/topic/core/errors/TopicTimeoutError.d.ts
declare class TopicTimeoutError extends AlephaError {
readonly topic: string;
readonly timeout: number;
constructor(topic: string, timeout: number);
}
//#endregion
//#region ../../src/topic/core/providers/TopicProvider.d.ts
/**
* Base class for topic providers.
*/
declare abstract class TopicProvider {
protected readonly alepha: Alepha;
protected readonly log: import("alepha/logger").Logger;
protected readonly dateTimeProvider: DateTimeProvider;
/**
* Publish a raw message to a topic.
*
* @param topic - The topic to publish to.
* @param message - The message to publish.
*/
abstract publish(topic: string, message: string, options?: TopicPublishOptions): Promise<void>;
/**
* Subscribe to a topic with a raw callback.
*
* @param topic - The topic to subscribe to.
* @param callback - The callback to call when a message is received.
*/
abstract subscribe(topic: string, callback: SubscribeCallback, options?: TopicSubscribeOptions): Promise<UnSubscribeFn>;
/**
* Unsubscribe from a topic.
*
* @param topic - The topic to unsubscribe from.
*/
abstract unsubscribe(topic: string): Promise<void>;
/**
* Encode and publish a typed message to a topic.
*/
publishMessage<T extends TopicMessageSchema>(name: string, schema: T["payload"], payload: TopicMessage<T>["payload"], options?: TopicPublishOptions): Promise<void>;
/**
* Parse a raw message string into a typed topic message.
*/
parseMessage<T extends TopicMessageSchema>(schema: T["payload"], message: string): TopicMessage<T>;
/**
* Subscribe a typed handler to a topic, with error wrapping and message parsing.
*/
subscribeHandler<T extends TopicMessageSchema>(name: string, schema: T["payload"], handler: TopicHandler<T>, options?: TopicSubscribeOptions): Promise<UnSubscribeFn>;
/**
* Wait for a single message matching an optional filter, with timeout.
*/
waitForMessage<T extends TopicMessageSchema>(name: string, schema: T["payload"], options?: TopicWaitOptions<T>): Promise<TopicMessage<T>>;
/**
* The wildcard character used for pattern subscriptions.
* Override in subclasses for provider-specific wildcards.
*/
protected wildcardChar(): string;
/**
* Returns the list of $subscribers for this provider.
*/
protected subscribers(): Array<() => Promise<unknown>>;
}
type SubscribeCallback = (message: string, topic?: string) => Promise<void> | void;
type UnSubscribeFn = () => Promise<void>;
interface TopicPublishOptions {
retain?: boolean;
params?: Record<string, string>;
}
interface TopicSubscribeOptions {}
//#endregion
//#region ../../src/topic/core/primitives/$topic.d.ts
/**
* Creates a topic primitive for publish/subscribe messaging and event-driven architecture.
*
* Enables decoupled communication through a pub/sub pattern where publishers send messages
* and multiple subscribers receive them. Supports type-safe messages, real-time delivery,
* event filtering, and pluggable backends (memory, Redis, custom providers).
*
* **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication
*
* @example
* ```ts
* class NotificationService {
* userActivity = $topic({
* name: "user-activity",
* schema: {
* payload: z.object({
* userId: z.text(),
* action: z.enum(["login", "logout", "purchase"]),
* timestamp: z.number()
* })
* },
* handler: async (message) => {
* console.log(`User ${message.payload.userId}: ${message.payload.action}`);
* }
* });
*
* async trackLogin(userId: string) {
* await this.userActivity.publish({ userId, action: "login", timestamp: Date.now() });
* }
*
* async subscribeToEvents() {
* await this.userActivity.subscribe(async (message) => {
* // Additional subscriber logic
* });
* }
* }
* ```
*/
declare const $topic: {
<T extends TopicMessageSchema>(options: TopicPrimitiveOptions<T>): TopicPrimitive<T>;
[KIND]: typeof TopicPrimitive;
};
interface TopicPrimitiveOptions<T extends TopicMessageSchema> {
/**
* Unique name identifier for the topic.
*
* This name is used for:
* - Topic identification across the pub/sub system
* - Message routing between publishers and subscribers
* - Logging and debugging topic-related operations
* - Provider-specific topic management (channels, keys, etc.)
*
* If not provided, defaults to the property key where the topic is declared.
*
* **Naming Conventions**:
* - Use descriptive, hierarchical names: "user.activity", "order.events"
* - Avoid spaces and special characters
* - Consider using dot notation for categorization
* - Keep names concise but meaningful
*
* @example "user-activity"
* @example "chat.messages"
* @example "system.health.checks"
* @example "payment.webhooks"
*/
name?: string;
/**
* Human-readable description of the topic's purpose and usage.
*
* Used for:
* - Documentation generation and API references
* - Developer onboarding and understanding
* - Monitoring dashboards and admin interfaces
* - Team communication about system architecture
*
* **Description Best Practices**:
* - Explain what events/messages this topic handles
* - Mention key use cases and subscribers
* - Include any important timing or ordering guarantees
* - Note any special processing requirements
*
* @example "Real-time user activity events for analytics and notifications"
* @example "Order lifecycle events from creation to delivery"
* @example "Chat messages broadcast to all room participants"
* @example "System health checks and service status updates"
*/
description?: string;
/**
* Topic provider configuration for message storage and delivery.
*
* Options:
* - **"memory"**: In-memory provider (default for development, lost on restart)
* - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)
* - **undefined**: Uses the default topic provider from dependency injection
*
* **Provider Selection Guidelines**:
* - **Development**: Use "memory" for fast, simple testing without external dependencies
* - **Production**: Use Redis or message brokers for persistence and scalability
* - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication
* - **High-throughput**: Use specialized providers with connection pooling
* - **Real-time**: Ensure provider supports low-latency message delivery
*
* **Provider Capabilities**:
* - Message persistence and durability
* - Subscriber management and connection handling
* - Message ordering and delivery guarantees
* - Horizontal scaling and load distribution
*
* @default Uses injected TopicProvider
* @example "memory"
* @example RedisTopicProvider
* @example RabbitMQTopicProvider
*/
provider?: "memory" | Service<TopicProvider>;
/**
* TypeBox schema defining the structure of messages published to this topic.
*
* The schema must include:
* - **payload**: Required schema for the main message data
* - **headers**: Optional schema for message metadata
*
* This schema:
* - Validates all messages published to the topic
* - Provides full TypeScript type inference for subscribers
* - Ensures type safety between publishers and subscribers
* - Enables automatic serialization/deserialization
*
* **Schema Design Best Practices**:
* - Keep payload schemas focused and cohesive
* - Use optional fields for data that might not always be present
* - Include timestamp fields for event ordering
* - Consider versioning for schema evolution
* - Use union types for different event types in the same topic
*
* @example
* ```ts
* {
* payload: z.object({
* eventId: z.text(),
* eventType: z.enum(["created", "updated"]),
* data: z.record(z.text(), z.any()),
* timestamp: z.number(),
* userId: z.text().optional()
* }),
* headers: z.object({
* source: z.text(),
* correlationId: z.text()
* }).optional()
* }
* ```
*/
schema: T;
/**
* Default subscriber handler function that processes messages published to this topic.
*
* This handler:
* - Automatically subscribes when the topic is initialized
* - Receives all messages published to the topic
* - Runs for every message without additional subscription setup
* - Can be supplemented with additional subscribers via `subscribe()` method
* - Should handle errors gracefully to avoid breaking other subscribers
*
* **Handler Design Guidelines**:
* - Keep handlers focused on a single responsibility
* - Use proper error handling and logging
* - Consider performance impact for high-frequency topics
* - Make handlers idempotent when possible
* - Validate business rules within the handler logic
* - Log important processing steps for debugging
*
* **Error Handling Strategy**:
* - Log errors but don't re-throw to avoid affecting other subscribers
* - Use try-catch blocks for external service calls
* - Consider implementing circuit breakers for resilience
* - Monitor error rates and patterns for system health
*
* @param message - The topic message with validated payload and headers
* @param message.payload - The typed message data based on the schema
* @returns Promise that resolves when processing is complete
*
* @example
* ```ts
* handler: async (message) => {
* const { eventType, data, timestamp } = message.payload;
*
* try {
* // Log message receipt
* this.logger.info(`Processing ${eventType} event`, { timestamp, data });
*
* // Process based on event type
* switch (eventType) {
* case "created":
* await this.handleCreation(data);
* break;
* case "updated":
* await this.handleUpdate(data);
* break;
* default:
* this.logger.warn(`Unknown event type: ${eventType}`);
* }
*
* this.logger.info(`Successfully processed ${eventType} event`);
*
* } catch (error) {
* // Log error but don't re-throw to avoid affecting other subscribers
* this.logger.error(`Failed to process ${eventType} event`, {
* error: error.message,
* eventType,
* timestamp,
* data
* });
* }
* }
* ```
*/
handler?: TopicHandler<T>;
/**
* Whether the last published message should be retained and delivered to new subscribers.
*
* When enabled, the provider stores the last message for this topic.
* New subscribers immediately receive the retained message upon subscribing.
*
* Supported by Memory, Redis, and MQTT providers.
*
* @default false
*/
retain?: boolean;
}
declare class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<TopicPrimitiveOptions<T>> {
readonly provider: TopicProvider;
get name(): string;
publish(message: T extends {
params: TSchema;
} ? {
params: Static<T["params"]>;
payload: TopicMessage<T>["payload"];
} : TopicMessage<T>["payload"]): Promise<void>;
subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn>;
wait(options?: TopicWaitOptions<T>): Promise<TopicMessage<T>>;
protected $provider(): TopicProvider;
}
interface TopicMessage<T extends TopicMessageSchema> {
payload: Static<T["payload"]>;
params: T extends {
params: TSchema;
} ? Static<T["params"]> : never;
}
interface TopicWaitOptions<T extends TopicMessageSchema> {
timeout?: DurationLike;
filter?: (message: {
payload: Static<T["payload"]>;
}) => boolean;
}
interface TopicMessageSchema {
payload: TSchema;
params?: TSchema;
}
type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (message: TopicMessage<T>) => unknown;
//#endregion
//#region ../../src/topic/core/primitives/$subscriber.d.ts
/**
* Creates a subscriber primitive to listen for messages from a specific topic.
*
* Provides a dedicated message subscriber that connects to a topic and processes messages
* with custom handler logic, enabling scalable pub/sub architectures where multiple
* subscribers can react to the same events independently.
*
* **Key Features**
* - Seamless integration with any $topic primitive
* - Full type safety inherited from topic schema
* - Real-time message delivery when events are published
* - Error isolation between subscribers
* - Support for multiple independent subscribers per topic
*
* **Common Use Cases**
* - Notification services and audit logging
* - Analytics and metrics collection
* - Data synchronization and real-time UI updates
*
* @example
* ```ts
* class UserActivityService {
* userEvents = $topic({
* name: "user-activity",
* schema: {
* payload: z.object({
* userId: z.text(),
* action: z.enum(["login", "logout", "purchase"]),
* timestamp: z.number()
* })
* }
* });
*
* activityLogger = $subscriber({
* topic: this.userEvents,
* handler: async (message) => {
* const { userId, action, timestamp } = message.payload;
* await this.auditLogger.log({
* userId,
* action,
* timestamp
* });
* }
* });
*
* async trackUserLogin(userId: string) {
* await this.userEvents.publish({
* userId,
* action: "login",
* timestamp: Date.now()
* });
* }
* }
* ```
*/
declare const $subscriber: {
<T extends TopicMessageSchema>(options: SubscriberPrimitiveOptions<T>): SubscriberPrimitive<T>;
[KIND]: typeof SubscriberPrimitive;
};
interface SubscriberPrimitiveOptions<T extends TopicMessageSchema> extends PipelinePrimitiveOptions {
/**
* The topic primitive that this subscriber will listen to for messages.
*
* This establishes the connection between the subscriber and its source topic:
* - The subscriber inherits the topic's message schema for type safety
* - Messages published to the topic will be automatically delivered to this subscriber
* - Multiple subscribers can listen to the same topic independently
* - The subscriber will use the topic's provider and configuration settings
*
* **Topic Integration Benefits**:
* - Type safety: Subscriber handler gets fully typed message payloads
* - Schema validation: Messages are validated before reaching the subscriber
* - Real-time delivery: Messages are delivered immediately upon publication
* - Error isolation: Subscriber errors don't affect the topic or other subscribers
* - Monitoring: Topic metrics include subscriber processing statistics
*
* @example
* ```ts
* // First, define a topic
* userEvents = $topic({
* name: "user-activity",
* schema: {
* payload: z.object({ userId: z.text(), action: z.text() })
* }
* });
*
* // Then, create a subscriber for that topic
* activitySubscriber = $subscriber({
* topic: this.userEvents, // Reference the topic primitive
* handler: async (message) => { } // Process messages here
* });
* ```
*/
topic: TopicPrimitive<T>;
/**
* Message handler function that processes individual messages from the topic.
*
* This function:
* - Receives fully typed and validated message payloads from the connected topic
* - Executes immediately when messages are published to the topic
* - Should implement the core business logic for reacting to these events
* - Runs independently of other subscribers to the same topic
* - Should handle errors gracefully to avoid affecting other subscribers
* - Has access to the full Alepha dependency injection container
*
* **Handler Design Guidelines**:
* - Keep handlers focused on a single responsibility
* - Use proper error handling and logging
* - Consider performance impact for high-frequency topics
* - Make handlers idempotent when possible for reliability
* - Validate business rules within the handler logic
* - Log important processing steps for debugging and monitoring
*
* **Error Handling Strategy**:
* - Log errors but don't re-throw to avoid affecting other subscribers
* - Use try-catch blocks for external service calls
* - Implement circuit breakers for resilience with external systems
* - Monitor error rates and patterns for system health
* - Consider implementing retry logic for temporary failures
*
* **Performance Considerations**:
* - Keep handler execution time minimal for high-throughput topics
* - Use background queues for heavy processing triggered by events
* - Implement batching for efficiency when processing many similar events
* - Consider async processing patterns for non-critical operations
*
* @param message - The topic message with validated payload and optional headers
* @param message.payload - The typed message data based on the topic's schema
* @returns Promise that resolves when processing is complete
*
* @example
* ```ts
* handler: async (message) => {
* const { userId, eventType, timestamp } = message.payload;
*
* try {
* // Log event receipt
* this.logger.info(`Processing ${eventType} event for user ${userId}`, {
* timestamp,
* userId,
* eventType
* });
*
* // Perform event-specific processing
* switch (eventType) {
* case 'user.login':
* await this.updateLastLogin(userId, timestamp);
* await this.sendWelcomeNotification(userId);
* break;
* case 'user.logout':
* await this.updateSessionDuration(userId, timestamp);
* break;
* case 'user.purchase':
* await this.updateRewardsPoints(userId, message.payload.purchaseAmount);
* await this.triggerRecommendations(userId);
* break;
* default:
* this.logger.warn(`Unknown event type: ${eventType}`);
* }
*
* // Update analytics
* await this.analytics.track(eventType, {
* userId,
* timestamp,
* source: 'topic-subscriber'
* });
*
* this.logger.info(`Successfully processed ${eventType} for user ${userId}`);
*
* } catch (error) {
* // Log error but don't re-throw to avoid affecting other subscribers
* this.logger.error(`Failed to process ${eventType} for user ${userId}`, {
* error: error.message,
* stack: error.stack,
* userId,
* eventType,
* timestamp
* });
*
* // Optionally send to error tracking service
* await this.errorTracker.captureException(error, {
* context: { userId, eventType, timestamp },
* tags: { component: 'topic-subscriber' }
* });
* }
* }
* ```
*/
handler: TopicHandler<T>;
}
declare class SubscriberPrimitive<T extends TopicMessageSchema> extends PipelinePrimitive<SubscriberPrimitiveOptions<T>> {}
//#endregion
//#region ../../src/topic/core/providers/MemoryTopicProvider.d.ts
declare class MemoryTopicProvider extends TopicProvider {
protected readonly log: import("alepha/logger").Logger;
protected readonly subscriptions: Record<string, SubscribeCallback[]>;
protected readonly retained: Record<string, string>;
protected readonly start: import("alepha").HookPrimitive<"start">;
/**
* Publish a message to a topic.
*
* @param topic
* @param message
* @param options
*/
publish(topic: string, message: string, options?: TopicPublishOptions): Promise<void>;
/**
* Subscribe to a topic.
*
* @param topic - The topic to subscribe to.
* @param callback
*/
subscribe(topic: string, callback: SubscribeCallback, _options?: TopicSubscribeOptions): Promise<UnSubscribeFn>;
/**
* Unsubscribe from a topic.
*
* @param topic - The topic to unsubscribe from.
*/
unsubscribe(topic: string): Promise<void>;
/**
* Check if a topic matches a subscription pattern.
* Supports `+` single-level wildcard.
*/
protected topicMatches(pattern: string, topic: string): boolean;
}
//#endregion
//#region ../../src/topic/core/index.d.ts
/**
* Publish/subscribe messaging for event-driven architectures.
*
* **Features:**
* - Pub/sub topics with type-safe messages
* - Topic subscription handlers
* - Multiple subscriber support
* - Message filtering and routing
* - Providers: Memory (dev), Redis (production)
*
* @module alepha.topic
*/
declare const AlephaTopic: import("alepha").Service<import("alepha").Module>;
//#endregion
export { $subscriber, $topic, AlephaTopic, MemoryTopicProvider, SubscribeCallback, SubscriberPrimitive, SubscriberPrimitiveOptions, TopicHandler, TopicMessage, TopicMessageSchema, TopicPrimitive, TopicPrimitiveOptions, TopicProvider, TopicPublishOptions, TopicSubscribeOptions, TopicTimeoutError, TopicWaitOptions, UnSubscribeFn };
//# sourceMappingURL=index.d.ts.map