UNPKG

@mastra/core

Version:
171 lines 6.75 kB
import type { MastraServerCache } from '../cache/base.js'; import type { IMastraLogger } from '../logger/index.js'; import { PubSub } from './pubsub.js'; import type { LeaseProvider } from './pubsub.js'; import type { Event, EventCallback, SubscribeOptions } from './types.js'; /** * Options for CachingPubSub */ export interface CachingPubSubOptions { /** * Optional prefix for cache keys to namespace events. * Defaults to 'pubsub:'. */ keyPrefix?: string; /** * Optional logger for structured logging. * Falls back to console.error if not provided. */ logger?: IMastraLogger; } /** * A PubSub decorator that adds event caching and replay capabilities. * * Wraps any PubSub implementation and uses MastraServerCache to: * - Cache all published events per topic * - Enable replay of cached events for late subscribers * * This enables resumable streams - clients can disconnect and reconnect * without missing events. * * ## Batching * * `CachingPubSub` is transparent to `options.batch`: `subscribe()` forwards * the option to the inner PubSub, and `supportsNativeBatching` mirrors the * inner's value. Wrapping a non-native inner with `{ batch: {...} }` results * in unbatched delivery — use an inner that returns * `supportsNativeBatching === true` (e.g. `EventEmitterPubSub`) if you need * batched delivery. * * @example * ```typescript * import { EventEmitterPubSub, CachingPubSub } from '@mastra/core/events'; * import { InMemoryServerCache } from '@mastra/core/cache'; * * const cache = new InMemoryServerCache(); * const pubsub = new CachingPubSub(new EventEmitterPubSub(), cache); * * // Subscribe with replay - receives cached events first, then live * await pubsub.subscribeWithReplay('my-topic', (event) => { * console.log(event); * }); * ``` */ export declare class CachingPubSub extends PubSub { private readonly inner; private readonly cache; private readonly keyPrefix; private readonly logger?; /** Maps original callbacks to their wrapped versions for proper unsubscribe */ private callbackMap; constructor(inner: PubSub, cache: MastraServerCache, options?: CachingPubSubOptions); get supportsNativeBatching(): boolean; /** * Log an error message using the configured logger or console.error. */ private logError; /** * Stable key used to deduplicate an event across the cache-replay and * live-delivery paths. * * We cannot dedup on `event.id`: `CachingPubSub.publish` assigns the id and * caches the event with it, but inner PubSub implementations * (EventEmitterPubSub, UnixSocketPubSub, …) regenerate `id` inside their own * `publish`, so the cached copy and the live copy of the SAME publish carry * different ids. The sequential `index` is assigned here and is preserved by * every inner implementation, so it matches across both paths. Events without * an index are never cached (see `publish`), so they can't be replay/live * duplicated — falling back to `id` for them is safe. */ private dedupKey; /** * Get the cache key for a topic's event list */ private getCacheKey; /** * Get the cache key for a topic's index counter */ private getCounterKey; /** * Publish an event to a topic. * The event is cached with a sequential index before being published to the inner PubSub. * * Uses atomic increment for index assignment to prevent race conditions * when multiple events are published concurrently. */ publish(topic: string, event: Omit<Event, 'id' | 'createdAt' | 'index'>, options?: { localOnly?: boolean; }): Promise<void>; /** * Subscribe to live events on a topic (no replay). */ subscribe(topic: string, cb: EventCallback, options?: SubscribeOptions): Promise<void>; /** * Subscribe to a topic with automatic replay of cached events. * Delegates to {@link subscribeFromOffset} with offset 0. */ subscribeWithReplay(topic: string, cb: EventCallback): Promise<void>; /** * Subscribe to a topic with replay starting from a specific index. * More efficient than full replay when the client knows their last position. * * Order of operations: * 1. Subscribe to live events FIRST — buffer deliveries during bootstrap * 2. Fetch and deliver cached history in order * 3. Drain the buffer, skipping events already delivered via history * 4. Switch to passthrough with an index watermark for steady-state dedup * * @param topic - The topic to subscribe to * @param offset - Start replaying from this index (0-based) * @param cb - Callback invoked for each event */ subscribeFromOffset(topic: string, offset: number, cb: EventCallback): Promise<void>; /** * Unsubscribe from a topic. */ unsubscribe(topic: string, cb: EventCallback): Promise<void>; /** * Get historical events for a topic from cache. */ getHistory(topic: string, offset?: number): Promise<Event[]>; /** * Flush any pending operations on the inner PubSub. */ flush(): Promise<void>; /** * Expose the inner's {@link LeaseProvider} when it has one, otherwise * `undefined`. Leasing is a capability of the underlying backend * (e.g. Redis), not of the caching decorator itself — so rather than * unconditionally declaring lease methods (which would make * {@link isLeaseProvider} report `true` even when the inner can't * coordinate a lock), we surface the inner's capability directly. The * signals runtime unwraps this so wrapping with caching preserves real * distributed lease semantics without faking them. */ getLeaseProvider(): LeaseProvider | undefined; /** * Clear cached events for a specific topic. * Call this when a stream completes to free memory. * Also clears the index counter. */ clearTopic(topic: string): Promise<void>; /** * Get the inner PubSub instance. * Useful for accessing implementation-specific methods like close(). */ getInner(): PubSub; } /** * Factory function to wrap a PubSub with caching capabilities. * * @example * ```typescript * import { withCaching, EventEmitterPubSub } from '@mastra/core/events'; * import { InMemoryServerCache } from '@mastra/core/cache'; * * const cache = new InMemoryServerCache(); * const pubsub = withCaching(new EventEmitterPubSub(), cache); * ``` */ export declare function withCaching(pubsub: PubSub, cache: MastraServerCache, options?: CachingPubSubOptions): CachingPubSub; //# sourceMappingURL=caching-pubsub.d.ts.map