@mastra/core
Version:
174 lines • 8.42 kB
TypeScript
import type { Event, EventCallback, SubscribeOptions } from './types.js';
/**
* Delivery model for a PubSub implementation.
*
* - `pull`: consumers actively read from the broker (e.g. Redis Streams
* XREADGROUP, GCP Pub/Sub streamingPull, SQS ReceiveMessage). Mastra runs
* a long-lived `OrchestrationWorker` that owns a subscription loop.
*
* - `push`: events arrive without the consumer asking — either in-process
* (EventEmitter dispatching to a registered listener) or out-of-process
* (the broker POSTs to an HTTP endpoint, e.g. GCP Pub/Sub push, SNS,
* EventBridge). Mastra wires the workflow handler directly to the pubsub
* for in-process push, or relies on `POST /api/workers/events` for
* broker push delivered over HTTP.
*/
export type PubSubDeliveryMode = 'pull' | 'push';
export declare abstract class PubSub {
abstract publish(topic: string, event: Omit<Event, 'id' | 'createdAt'>, options?: {
localOnly?: boolean;
}): Promise<void>;
abstract subscribe(topic: string, cb: EventCallback, options?: SubscribeOptions): Promise<void>;
abstract unsubscribe(topic: string, cb: EventCallback): Promise<void>;
/**
* Drain any buffered or in-flight deliveries before resolving.
*
* Best-effort: a `flush()` that resolves successfully does not guarantee
* every subscriber callback succeeded — implementations surface per-event
* delivery errors via their configured logger rather than re-throwing,
* so a single failed callback does not mask later cleanup work.
*/
abstract flush(): Promise<void>;
/**
* Delivery modes this PubSub implementation supports.
*
* Defaults to `['pull']` for backward compatibility — third-party
* implementations that don't override this property are treated as
* pull-mode, which preserves today's behavior.
*
* Implementations that deliver events without an active read loop (e.g.
* EventEmitter, GCP Pub/Sub push subscriptions) should declare `'push'`.
* Implementations that support both modes should declare both.
*/
get supportedModes(): ReadonlyArray<PubSubDeliveryMode>;
/**
* Whether this implementation honors `options.batch` on `subscribe()`
* natively. Defaults to `false`.
*
* Implementations that integrate batching internally (e.g. against their
* own broker retention or via an `AckHandleBuffer`) override this getter
* and return `true`.
*/
get supportsNativeBatching(): boolean;
/**
* Get historical events for a topic.
* Default implementation returns empty array (no history support).
* Override in implementations that support event caching.
*
* @param topic - The topic to get history for
* @param offset - Starting index (0-based), defaults to 0
* @returns Array of events from the specified index
*/
getHistory(_topic: string, _offset?: number): Promise<Event[]>;
/**
* Subscribe to a topic with automatic replay of cached events.
* First replays any cached history, then subscribes to live events.
* Default implementation falls back to regular subscribe (no replay).
* Override in implementations that support event caching.
*
* @param topic - The topic to subscribe to
* @param cb - Callback invoked for each event (both cached and live)
*/
subscribeWithReplay(topic: string, cb: EventCallback): Promise<void>;
/**
* Subscribe to a topic with replay starting from a specific index.
* This is more efficient than full replay when the client knows their last position.
* Default implementation falls back to subscribeWithReplay (full replay).
* Override in implementations that support indexed event caching.
*
* @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>;
}
/**
* Distributed leasing capability, separate from event delivery (`PubSub`).
*
* Used by the signals layer to elect a single owner across multiple
* processes (e.g. serverless invocations) for a given resource — most
* commonly a thread-key, where the owner is the process that will wake
* and run the agent stream.
*
* Leasing is a distinct concern from pub/sub: a backend only implements
* this when it can genuinely coordinate a lock (Redis via SET-NX, an
* in-memory map for single-process). Backends that cannot lease simply do
* not implement `LeaseProvider`; the signals runtime feature-detects and
* falls back to {@link NoopLeaseProvider} (always-win / no-op), preserving
* single-process behavior.
*/
export interface LeaseProvider {
/**
* Atomically try to acquire a lease on a key.
*
* Returns `{ acquired: true, owner }` if the caller claimed the lease,
* or `{ acquired: false, owner }` where `owner` is the current holder
* (so the caller can route follow-up work to them). `owner` may be
* `undefined` if the holder could not be read (rare).
*
* @param key - The lease key (e.g. thread key)
* @param owner - Identifier for the owner (e.g. runId) — used so the
* same owner can call `acquireLease` idempotently and renew/release.
* @param ttlMs - Time-to-live in milliseconds for the lease
*/
acquireLease(key: string, owner: string, ttlMs: number): Promise<{
acquired: boolean;
owner?: string;
}>;
/**
* Read the current owner of a lease, or `undefined` if no lease is held.
*/
getLeaseOwner(key: string): Promise<string | undefined>;
/**
* Release a lease. No-op if the caller is not the current owner
* (implementations should atomically check ownership before releasing
* to avoid clobbering a renewal that happened concurrently).
*/
releaseLease(key: string, owner: string): Promise<void>;
/**
* Renew an existing lease owned by `owner`, extending its TTL.
*
* Returns `true` if the renewal succeeded (caller still owns it),
* `false` if the lease was lost (TTL expired or another owner took it).
*/
renewLease(key: string, owner: string, ttlMs: number): Promise<boolean>;
/**
* Atomically hand a held lease from `fromOwner` to `toOwner`, refreshing
* its TTL, without ever releasing the key in between.
*
* This is the gap-free primitive used when one owner finishes but a
* follow-up owner must take over the *same* lease key immediately (e.g. a
* thread run completes and a queued follow-up run drains on the same
* thread). A naive release-then-acquire would briefly leave the key empty,
* letting a racing process win the freed lease and start a competing run.
*
* Returns `true` if `fromOwner` still held the lease and ownership moved to
* `toOwner`; `false` if the lease was already lost (expired or taken by a
* third owner), in which case the caller should fall back to a fresh
* `acquireLease`.
*
* Backends that cannot perform this atomically must still implement it —
* as a best-effort `releaseLease(from)` followed by `acquireLease(to)` — and
* document that the swap is non-atomic (a racing process can win the key in
* the gap). Keeping it required means callers have a single code path and the
* atomicity guarantee is an explicit per-backend decision rather than a
* silent caller-side fallback.
*/
transferLease(key: string, fromOwner: string, toOwner: string, ttlMs: number): Promise<boolean>;
}
/**
* Duck-typed check for whether a value implements {@link LeaseProvider}.
*
* Uses structural detection rather than `instanceof` so it works across
* package boundaries (e.g. a separately-published pubsub backend resolving
* a different copy of `@mastra/core`).
*/
export declare function isLeaseProvider(value: unknown): value is LeaseProvider;
/**
* Always-win / no-op {@link LeaseProvider}. Used by the signals runtime
* when the configured pubsub does not implement `LeaseProvider` — this
* preserves single-process behavior where every caller "wins" its own
* lease race and release/renew are inert.
*/
export declare const NoopLeaseProvider: LeaseProvider;
//# sourceMappingURL=pubsub.d.ts.map