@signaltree/events 7.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ import { BaseEvent, EventActor, EventMetadata, EventPriority, EventVersion } from './types';
2
+ /**
3
+ * Event Factory - Create events with proper structure
4
+ *
5
+ * Provides:
6
+ * - UUID v7 generation (time-sortable)
7
+ * - Correlation ID management
8
+ * - Event creation with defaults
9
+ * - Type-safe event factories
10
+ */
11
+ /**
12
+ * Configuration for event factory
13
+ */
14
+ export interface EventFactoryConfig {
15
+ /** Default source for events */
16
+ source: string;
17
+ /** Environment (production, staging, development) */
18
+ environment: string;
19
+ /** Default actor for system events */
20
+ systemActor?: EventActor;
21
+ /** Custom ID generator */
22
+ generateId?: () => string;
23
+ /** Custom correlation ID generator */
24
+ generateCorrelationId?: () => string;
25
+ }
26
+ /**
27
+ * Event factory for creating typed events
28
+ */
29
+ export interface EventFactory<TEvents extends BaseEvent = BaseEvent> {
30
+ /**
31
+ * Create an event with full control
32
+ */
33
+ create<T extends TEvents>(type: T['type'], data: T['data'], options?: CreateEventOptions): T;
34
+ /**
35
+ * Create event with correlation from existing event
36
+ */
37
+ createFromCause<T extends TEvents>(type: T['type'], data: T['data'], cause: BaseEvent, options?: Omit<CreateEventOptions, 'correlationId' | 'causationId'>): T;
38
+ /**
39
+ * Get current correlation ID (for request context)
40
+ */
41
+ getCorrelationId(): string | undefined;
42
+ /**
43
+ * Set correlation ID for current context
44
+ */
45
+ setCorrelationId(id: string): void;
46
+ /**
47
+ * Clear correlation ID
48
+ */
49
+ clearCorrelationId(): void;
50
+ }
51
+ /**
52
+ * Options for creating an event
53
+ */
54
+ export interface CreateEventOptions {
55
+ /** Override event ID */
56
+ id?: string;
57
+ /** Correlation ID (request trace) */
58
+ correlationId?: string;
59
+ /** Causation ID (parent event) */
60
+ causationId?: string;
61
+ /** Event actor */
62
+ actor?: EventActor;
63
+ /** Additional metadata */
64
+ metadata?: Partial<EventMetadata>;
65
+ /** Event priority */
66
+ priority?: EventPriority;
67
+ /** Aggregate info */
68
+ aggregate?: {
69
+ type: string;
70
+ id: string;
71
+ };
72
+ /** Schema version override */
73
+ version?: EventVersion;
74
+ /** Timestamp override (for testing/replay) */
75
+ timestamp?: string;
76
+ }
77
+ /**
78
+ * Generate a UUID v7 (time-sortable)
79
+ *
80
+ * UUID v7 format: timestamp (48 bits) + version (4 bits) + random (12 bits) + variant (2 bits) + random (62 bits)
81
+ */
82
+ export declare function generateEventId(): string;
83
+ /**
84
+ * Generate a correlation ID (also UUID v7 for traceability)
85
+ */
86
+ export declare function generateCorrelationId(): string;
87
+ /**
88
+ * Create a single event
89
+ */
90
+ export declare function createEvent<TType extends string, TData>(type: TType, data: TData, options: CreateEventOptions & {
91
+ source: string;
92
+ environment: string;
93
+ }): BaseEvent<TType, TData>;
94
+ /**
95
+ * Create an event factory with default configuration
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const factory = createEventFactory({
100
+ * source: 'trade-service',
101
+ * environment: process.env.NODE_ENV || 'development',
102
+ * });
103
+ *
104
+ * const event = factory.create('TradeProposalCreated', {
105
+ * tradeId: '123',
106
+ * initiatorId: 'user-1',
107
+ * recipientId: 'user-2',
108
+ * }, {
109
+ * actor: { id: 'user-1', type: 'user' },
110
+ * priority: 'high',
111
+ * });
112
+ * ```
113
+ */
114
+ export declare function createEventFactory<TEvents extends BaseEvent = BaseEvent>(config: EventFactoryConfig): EventFactory<TEvents>;
@@ -0,0 +1,209 @@
1
+ import { BaseEvent } from './types';
2
+ /**
3
+ * Idempotency Store - Prevent duplicate event processing
4
+ *
5
+ * Provides:
6
+ * - Idempotency key checking
7
+ * - In-memory implementation (for testing/development)
8
+ * - Redis implementation (production)
9
+ * - Distributed lock support
10
+ */
11
+ /**
12
+ * Result of checking idempotency
13
+ */
14
+ export interface IdempotencyCheckResult {
15
+ /** Whether this event has already been processed */
16
+ isDuplicate: boolean;
17
+ /** If duplicate, when was it originally processed */
18
+ processedAt?: Date;
19
+ /** If duplicate, what was the result */
20
+ result?: unknown;
21
+ /** Lock acquired for processing */
22
+ lockAcquired?: boolean;
23
+ }
24
+ /**
25
+ * Record of a processed event
26
+ */
27
+ export interface ProcessedEventRecord {
28
+ /** Event ID */
29
+ eventId: string;
30
+ /** Event type */
31
+ eventType: string;
32
+ /** When processing started */
33
+ startedAt: Date;
34
+ /** When processing completed */
35
+ completedAt?: Date;
36
+ /** Processing result (success/failure) */
37
+ status: 'processing' | 'completed' | 'failed';
38
+ /** Result data (for idempotent responses) */
39
+ result?: unknown;
40
+ /** Error if failed */
41
+ error?: string;
42
+ /** Consumer/subscriber that processed the event */
43
+ consumer: string;
44
+ /** Number of processing attempts */
45
+ attempts: number;
46
+ }
47
+ /**
48
+ * Idempotency store interface
49
+ *
50
+ * Implementations must be atomic and handle concurrent access
51
+ */
52
+ export interface IdempotencyStore {
53
+ /**
54
+ * Check if event has been processed and optionally acquire a processing lock
55
+ *
56
+ * @param event - Event to check
57
+ * @param consumer - Consumer/subscriber identifier
58
+ * @param options - Check options
59
+ * @returns Check result with duplicate status and optional lock
60
+ */
61
+ check(event: BaseEvent, consumer: string, options?: IdempotencyCheckOptions): Promise<IdempotencyCheckResult>;
62
+ /**
63
+ * Mark event as being processed (acquire lock)
64
+ *
65
+ * @param event - Event being processed
66
+ * @param consumer - Consumer/subscriber identifier
67
+ * @param ttlMs - Lock TTL in milliseconds
68
+ */
69
+ markProcessing(event: BaseEvent, consumer: string, ttlMs?: number): Promise<boolean>;
70
+ /**
71
+ * Mark event as successfully processed
72
+ *
73
+ * @param event - Processed event
74
+ * @param consumer - Consumer/subscriber identifier
75
+ * @param result - Optional result data for idempotent responses
76
+ * @param ttlMs - How long to keep the record
77
+ */
78
+ markCompleted(event: BaseEvent, consumer: string, result?: unknown, ttlMs?: number): Promise<void>;
79
+ /**
80
+ * Mark event as failed
81
+ *
82
+ * @param event - Failed event
83
+ * @param consumer - Consumer/subscriber identifier
84
+ * @param error - Error information
85
+ */
86
+ markFailed(event: BaseEvent, consumer: string, error: unknown): Promise<void>;
87
+ /**
88
+ * Release processing lock without marking completed
89
+ * (Used when you want to allow retry)
90
+ *
91
+ * @param event - Event to release
92
+ * @param consumer - Consumer/subscriber identifier
93
+ */
94
+ releaseLock(event: BaseEvent, consumer: string): Promise<void>;
95
+ /**
96
+ * Get processing record for an event
97
+ */
98
+ getRecord(eventId: string, consumer: string): Promise<ProcessedEventRecord | null>;
99
+ /**
100
+ * Clean up expired records (for stores that don't auto-expire)
101
+ */
102
+ cleanup?(): Promise<number>;
103
+ }
104
+ /**
105
+ * Options for idempotency check
106
+ */
107
+ export interface IdempotencyCheckOptions {
108
+ /** Acquire a processing lock if not duplicate */
109
+ acquireLock?: boolean;
110
+ /** Lock TTL in milliseconds (default: 30000) */
111
+ lockTtlMs?: number;
112
+ }
113
+ /**
114
+ * Configuration for in-memory idempotency store
115
+ */
116
+ export interface InMemoryIdempotencyStoreConfig {
117
+ /** Default TTL for records in milliseconds (default: 24 hours) */
118
+ defaultTtlMs?: number;
119
+ /** Default lock TTL in milliseconds (default: 30 seconds) */
120
+ defaultLockTtlMs?: number;
121
+ /** Maximum number of records to keep (LRU eviction) */
122
+ maxRecords?: number;
123
+ /** Cleanup interval in milliseconds (default: 60 seconds) */
124
+ cleanupIntervalMs?: number;
125
+ }
126
+ /**
127
+ * In-memory idempotency store
128
+ *
129
+ * Best for:
130
+ * - Development and testing
131
+ * - Single-instance deployments
132
+ * - Short-lived processes
133
+ *
134
+ * NOT recommended for:
135
+ * - Production multi-instance deployments
136
+ * - Long-running processes with many events
137
+ */
138
+ export declare class InMemoryIdempotencyStore implements IdempotencyStore {
139
+ private records;
140
+ private cleanupTimer?;
141
+ private readonly defaultTtlMs;
142
+ private readonly defaultLockTtlMs;
143
+ private readonly maxRecords;
144
+ constructor(config?: InMemoryIdempotencyStoreConfig);
145
+ private makeKey;
146
+ check(event: BaseEvent, consumer: string, options?: IdempotencyCheckOptions): Promise<IdempotencyCheckResult>;
147
+ markProcessing(event: BaseEvent, consumer: string, ttlMs?: number): Promise<boolean>;
148
+ markCompleted(event: BaseEvent, consumer: string, result?: unknown, ttlMs?: number): Promise<void>;
149
+ markFailed(event: BaseEvent, consumer: string, error: unknown): Promise<void>;
150
+ releaseLock(event: BaseEvent, consumer: string): Promise<void>;
151
+ getRecord(eventId: string, consumer: string): Promise<ProcessedEventRecord | null>;
152
+ cleanup(): Promise<number>;
153
+ /**
154
+ * Enforce max records limit using LRU-like eviction
155
+ */
156
+ private enforceMaxRecords;
157
+ /**
158
+ * Stop cleanup timer (for graceful shutdown)
159
+ */
160
+ dispose(): void;
161
+ /**
162
+ * Clear all records (for testing)
163
+ */
164
+ clear(): void;
165
+ /**
166
+ * Get stats (for monitoring)
167
+ */
168
+ getStats(): {
169
+ size: number;
170
+ maxRecords: number;
171
+ };
172
+ }
173
+ /**
174
+ * Create an in-memory idempotency store
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const store = createInMemoryIdempotencyStore({
179
+ * defaultTtlMs: 60 * 60 * 1000, // 1 hour
180
+ * maxRecords: 50000,
181
+ * });
182
+ *
183
+ * // In your subscriber
184
+ * const result = await store.check(event, 'my-subscriber');
185
+ * if (result.isDuplicate) {
186
+ * return result.result; // Return cached result
187
+ * }
188
+ *
189
+ * try {
190
+ * const processResult = await processEvent(event);
191
+ * await store.markCompleted(event, 'my-subscriber', processResult);
192
+ * return processResult;
193
+ * } catch (error) {
194
+ * await store.markFailed(event, 'my-subscriber', error);
195
+ * throw error;
196
+ * }
197
+ * ```
198
+ */
199
+ export declare function createInMemoryIdempotencyStore(config?: InMemoryIdempotencyStoreConfig): InMemoryIdempotencyStore;
200
+ /**
201
+ * Generate an idempotency key from event
202
+ * Useful for custom implementations
203
+ */
204
+ export declare function generateIdempotencyKey(event: BaseEvent, consumer: string): string;
205
+ /**
206
+ * Generate an idempotency key from correlation ID
207
+ * Useful for request-level idempotency
208
+ */
209
+ export declare function generateCorrelationKey(correlationId: string, operation: string): string;
@@ -0,0 +1,147 @@
1
+ import { ZodTypeAny } from 'zod';
2
+ import { BaseEvent, EventPriority } from './types';
3
+ /**
4
+ * Event Registry - Central catalog of all registered event types
5
+ *
6
+ * Provides:
7
+ * - Type-safe event registration
8
+ * - Schema lookup by event type
9
+ * - Validation helpers
10
+ * - Event catalog for documentation
11
+ */
12
+ /**
13
+ * Configuration for a registered event
14
+ */
15
+ export interface RegisteredEvent<T extends ZodTypeAny = ZodTypeAny> {
16
+ /** Event type string */
17
+ type: string;
18
+ /** Zod schema for validation */
19
+ schema: T;
20
+ /** Default priority for this event type */
21
+ priority: EventPriority;
22
+ /** Human-readable description */
23
+ description?: string;
24
+ /** Event category for grouping */
25
+ category?: string;
26
+ /** Whether this event is deprecated */
27
+ deprecated?: boolean;
28
+ /** Deprecation message if deprecated */
29
+ deprecationMessage?: string;
30
+ }
31
+ /**
32
+ * Event registry configuration
33
+ */
34
+ export interface EventRegistryConfig {
35
+ /** Strict mode - throw on unknown events */
36
+ strict?: boolean;
37
+ /** Log warnings for deprecated events */
38
+ warnOnDeprecated?: boolean;
39
+ }
40
+ /**
41
+ * Event catalog entry for documentation
42
+ */
43
+ export interface EventCatalogEntry {
44
+ type: string;
45
+ category?: string;
46
+ priority: EventPriority;
47
+ description?: string;
48
+ deprecated: boolean;
49
+ }
50
+ /**
51
+ * Full event catalog
52
+ */
53
+ export type EventCatalog = EventCatalogEntry[];
54
+ /**
55
+ * Event Registry class - manages all registered event types
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const registry = createEventRegistry();
60
+ *
61
+ * // Register events
62
+ * registry.register({
63
+ * type: 'TradeProposalCreated',
64
+ * schema: TradeProposalCreatedSchema,
65
+ * priority: 'high',
66
+ * description: 'Emitted when a user proposes a trade',
67
+ * category: 'trade',
68
+ * });
69
+ *
70
+ * // Validate events
71
+ * const validated = registry.validate(rawEvent);
72
+ *
73
+ * // Get schema for event type
74
+ * const schema = registry.getSchema('TradeProposalCreated');
75
+ * ```
76
+ */
77
+ export declare class EventRegistry {
78
+ private readonly events;
79
+ private readonly config;
80
+ constructor(config?: EventRegistryConfig);
81
+ /**
82
+ * Register an event type with its schema
83
+ */
84
+ register<T extends ZodTypeAny>(event: RegisteredEvent<T>): this;
85
+ /**
86
+ * Register multiple events at once
87
+ */
88
+ registerMany(events: RegisteredEvent[]): this;
89
+ /**
90
+ * Get schema for an event type
91
+ */
92
+ getSchema(type: string): ZodTypeAny | undefined;
93
+ /**
94
+ * Get registered event info
95
+ */
96
+ getEvent(type: string): RegisteredEvent | undefined;
97
+ /**
98
+ * Check if event type is registered
99
+ */
100
+ has(type: string): boolean;
101
+ /**
102
+ * Get default priority for event type
103
+ */
104
+ getPriority(type: string): EventPriority;
105
+ /**
106
+ * Validate an event against its registered schema
107
+ *
108
+ * @throws Error if event type is unknown (in strict mode) or validation fails
109
+ */
110
+ validate<T extends BaseEvent = BaseEvent>(event: unknown): T;
111
+ /**
112
+ * Check if event is valid without throwing
113
+ */
114
+ isValid(event: unknown): boolean;
115
+ /**
116
+ * Get all registered event types
117
+ */
118
+ getAllTypes(): string[];
119
+ /**
120
+ * Get all registered events
121
+ */
122
+ getAll(): RegisteredEvent[];
123
+ /**
124
+ * Get events by category
125
+ */
126
+ getByCategory(category: string): RegisteredEvent[];
127
+ /**
128
+ * Get event catalog for documentation
129
+ */
130
+ getCatalog(): EventCatalog;
131
+ /**
132
+ * Export registry as JSON schema (for external tools)
133
+ */
134
+ toJSONSchema(): Record<string, unknown>;
135
+ }
136
+ /**
137
+ * Create a new event registry
138
+ */
139
+ export declare function createEventRegistry(config?: EventRegistryConfig): EventRegistry;
140
+ /**
141
+ * Get schema for an event type from a registry
142
+ */
143
+ export declare function getEventSchema(registry: EventRegistry, type: string): ZodTypeAny | undefined;
144
+ /**
145
+ * Get all registered event types from a registry
146
+ */
147
+ export declare function getAllEventTypes(registry: EventRegistry): string[];
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Core event types - framework-agnostic definitions
3
+ */
4
+ /**
5
+ * Event priority levels for queue routing and SLA management
6
+ */
7
+ export type EventPriority = 'critical' | 'high' | 'normal' | 'low' | 'bulk';
8
+ /**
9
+ * Priority configuration with SLA targets
10
+ */
11
+ export declare const EVENT_PRIORITIES: Record<EventPriority, {
12
+ sla: number;
13
+ weight: number;
14
+ }>;
15
+ /**
16
+ * Event schema version for evolution tracking
17
+ */
18
+ export interface EventVersion {
19
+ major: number;
20
+ minor: number;
21
+ }
22
+ export declare const DEFAULT_EVENT_VERSION: EventVersion;
23
+ /**
24
+ * Actor who triggered the event
25
+ */
26
+ export interface EventActor {
27
+ /** User, system, or admin ID */
28
+ id: string;
29
+ /** Type of actor */
30
+ type: 'user' | 'system' | 'admin' | 'webhook';
31
+ /** Optional display name for audit */
32
+ name?: string;
33
+ }
34
+ /**
35
+ * Event metadata for tracing, audit, and debugging
36
+ */
37
+ export interface EventMetadata {
38
+ /** Service/module that emitted the event */
39
+ source: string;
40
+ /** Environment (production, staging, development) */
41
+ environment: string;
42
+ /** Client IP address (for audit) */
43
+ ip?: string;
44
+ /** User agent string (for analytics) */
45
+ userAgent?: string;
46
+ /** Additional context */
47
+ [key: string]: unknown;
48
+ }
49
+ /**
50
+ * Base event interface - all events must extend this
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * interface TradeProposalCreated extends BaseEvent {
55
+ * type: 'TradeProposalCreated';
56
+ * data: {
57
+ * tradeId: string;
58
+ * initiatorId: string;
59
+ * recipientId: string;
60
+ * };
61
+ * }
62
+ * ```
63
+ */
64
+ export interface BaseEvent<TType extends string = string, TData = unknown> {
65
+ /**
66
+ * Unique event ID (UUID v7 recommended - time-sortable)
67
+ */
68
+ id: string;
69
+ /**
70
+ * Event type in PascalCase (e.g., 'TradeProposalCreated')
71
+ * Must be past tense - events are facts that happened
72
+ */
73
+ type: TType;
74
+ /**
75
+ * Schema version for event evolution
76
+ */
77
+ version: EventVersion;
78
+ /**
79
+ * When the event occurred (ISO 8601)
80
+ */
81
+ timestamp: string;
82
+ /**
83
+ * Request/trace ID for distributed tracing
84
+ * All events from the same user action share this ID
85
+ */
86
+ correlationId: string;
87
+ /**
88
+ * ID of the event that caused this event (for event chains)
89
+ */
90
+ causationId?: string;
91
+ /**
92
+ * Who/what triggered this event
93
+ */
94
+ actor: EventActor;
95
+ /**
96
+ * Event metadata for tracing and audit
97
+ */
98
+ metadata: EventMetadata;
99
+ /**
100
+ * Event-specific payload
101
+ */
102
+ data: TData;
103
+ /**
104
+ * Priority for queue routing
105
+ * @default 'normal'
106
+ */
107
+ priority?: EventPriority;
108
+ /**
109
+ * Aggregate information for event sourcing
110
+ */
111
+ aggregate?: {
112
+ type: string;
113
+ id: string;
114
+ };
115
+ }
116
+ /**
117
+ * Type helper to extract event data type
118
+ */
119
+ export type EventData<T extends BaseEvent> = T['data'];
120
+ /**
121
+ * Type helper to extract event type string
122
+ */
123
+ export type EventType<T extends BaseEvent> = T['type'];
124
+ /**
125
+ * Union type of all registered events (to be extended by apps)
126
+ */
127
+ export type AnyEvent = BaseEvent<string, unknown>;