@emmett-community/emmett-google-pubsub 0.1.0

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,413 @@
1
+ import { PubSub, Topic, Subscription, Message as Message$1 } from '@google-cloud/pubsub';
2
+ import { Message, SingleMessageHandler, Command, Event, SingleRawMessageHandlerWithoutContext, AnyMessage, MessageBus, EventSubscription, CommandProcessor, ScheduledMessageProcessor } from '@event-driven-io/emmett';
3
+
4
+ /**
5
+ * Configuration for PubSub MessageBus
6
+ */
7
+ interface PubSubMessageBusConfig {
8
+ /**
9
+ * Google Cloud PubSub client instance
10
+ */
11
+ pubsub: PubSub;
12
+ /**
13
+ * Instance identifier for subscriptions (auto-generated if not provided)
14
+ */
15
+ instanceId?: string;
16
+ /**
17
+ * Topic/subscription name prefix
18
+ * @default "emmett"
19
+ */
20
+ topicPrefix?: string;
21
+ /**
22
+ * Enable emulator mode (disables Cloud Scheduler features)
23
+ * @default false
24
+ */
25
+ useEmulator?: boolean;
26
+ /**
27
+ * Subscription configuration options
28
+ */
29
+ subscriptionOptions?: SubscriptionOptions;
30
+ /**
31
+ * Auto-create topics/subscriptions
32
+ * @default true
33
+ */
34
+ autoCreateResources?: boolean;
35
+ /**
36
+ * Cleanup subscriptions on close
37
+ * @default false
38
+ */
39
+ cleanupOnClose?: boolean;
40
+ /**
41
+ * Close the PubSub client on close
42
+ * Set to false if you want to reuse the client (e.g., in tests)
43
+ * @default true
44
+ */
45
+ closePubSubClient?: boolean;
46
+ }
47
+ /**
48
+ * Subscription configuration options
49
+ */
50
+ interface SubscriptionOptions {
51
+ /**
52
+ * Acknowledgment deadline in seconds
53
+ * @default 60
54
+ */
55
+ ackDeadlineSeconds?: number;
56
+ /**
57
+ * Retry policy for failed messages
58
+ */
59
+ retryPolicy?: {
60
+ minimumBackoff?: {
61
+ seconds: number;
62
+ };
63
+ maximumBackoff?: {
64
+ seconds: number;
65
+ };
66
+ };
67
+ /**
68
+ * Dead letter policy
69
+ */
70
+ deadLetterPolicy?: {
71
+ deadLetterTopic?: string;
72
+ maxDeliveryAttempts?: number;
73
+ };
74
+ }
75
+ /**
76
+ * Message envelope for PubSub transport
77
+ */
78
+ interface PubSubMessageEnvelope {
79
+ /**
80
+ * Message type (e.g., "AddProductItem")
81
+ */
82
+ type: string;
83
+ /**
84
+ * Message kind (command or event)
85
+ */
86
+ kind: 'command' | 'event';
87
+ /**
88
+ * Serialized message data
89
+ */
90
+ data: unknown;
91
+ /**
92
+ * Serialized message metadata
93
+ */
94
+ metadata?: unknown;
95
+ /**
96
+ * ISO 8601 timestamp
97
+ */
98
+ timestamp: string;
99
+ /**
100
+ * UUID for idempotency
101
+ */
102
+ messageId: string;
103
+ }
104
+ /**
105
+ * Internal handler registration
106
+ */
107
+ interface HandlerRegistration<T extends Message = Message> {
108
+ id: string;
109
+ handler: SingleMessageHandler<T>;
110
+ }
111
+ /**
112
+ * Subscription information
113
+ */
114
+ interface SubscriptionInfo {
115
+ topic: Topic;
116
+ subscription: Subscription;
117
+ messageType: string;
118
+ kind: 'command' | 'event';
119
+ }
120
+ /**
121
+ * Scheduled message with timing information
122
+ */
123
+ interface ScheduledMessageInfo {
124
+ message: Message;
125
+ options?: {
126
+ afterInMs: number;
127
+ } | {
128
+ at: Date;
129
+ };
130
+ scheduledAt: Date;
131
+ }
132
+ /**
133
+ * PubSub message bus lifecycle
134
+ */
135
+ interface PubSubMessageBusLifecycle {
136
+ /**
137
+ * Start the message bus (create topics/subscriptions and start listening)
138
+ */
139
+ start(): Promise<void>;
140
+ /**
141
+ * Close the message bus gracefully
142
+ */
143
+ close(): Promise<void>;
144
+ /**
145
+ * Check if the message bus is started
146
+ */
147
+ isStarted(): boolean;
148
+ }
149
+
150
+ /**
151
+ * Serialize a Command or Event to a Buffer for PubSub transport
152
+ *
153
+ * @param message - The message to serialize
154
+ * @returns Buffer containing the serialized message envelope
155
+ */
156
+ declare function serialize(message: Command | Event): Buffer;
157
+ /**
158
+ * Deserialize a Buffer from PubSub into a Command or Event
159
+ *
160
+ * @param buffer - The buffer containing the serialized message
161
+ * @returns The deserialized message
162
+ * @throws Error if the buffer cannot be deserialized
163
+ */
164
+ declare function deserialize<T extends Command | Event>(buffer: Buffer): T;
165
+ /**
166
+ * Attach a message ID to a message for idempotency tracking
167
+ *
168
+ * @param message - The message to attach ID to
169
+ * @param messageId - The message ID
170
+ * @returns The message with the ID attached
171
+ */
172
+ declare function attachMessageId<T extends Message>(message: T, messageId: string): T & {
173
+ __messageId: string;
174
+ };
175
+ /**
176
+ * Extract message ID from a message
177
+ *
178
+ * @param message - The message to extract ID from
179
+ * @returns The message ID if present, undefined otherwise
180
+ */
181
+ declare function extractMessageId(message: Message): string | undefined;
182
+
183
+ /**
184
+ * Get command topic name
185
+ */
186
+ declare function getCommandTopicName(commandType: string, prefix?: string): string;
187
+ /**
188
+ * Get event topic name
189
+ */
190
+ declare function getEventTopicName(eventType: string, prefix?: string): string;
191
+ /**
192
+ * Get command subscription name
193
+ */
194
+ declare function getCommandSubscriptionName(commandType: string, instanceId: string, prefix?: string): string;
195
+ /**
196
+ * Get event subscription name
197
+ */
198
+ declare function getEventSubscriptionName(eventType: string, subscriptionId: string, prefix?: string): string;
199
+ /**
200
+ * Get or create a topic
201
+ *
202
+ * @param pubsub - PubSub client
203
+ * @param topicName - Name of the topic
204
+ * @returns The topic instance
205
+ */
206
+ declare function getOrCreateTopic(pubsub: PubSub, topicName: string): Promise<Topic>;
207
+ /**
208
+ * Get or create a subscription
209
+ *
210
+ * @param topic - The topic to subscribe to
211
+ * @param subscriptionName - Name of the subscription
212
+ * @param options - Subscription options
213
+ * @returns The subscription instance
214
+ */
215
+ declare function getOrCreateSubscription(topic: Topic, subscriptionName: string, options?: SubscriptionOptions): Promise<Subscription>;
216
+ /**
217
+ * Delete a subscription
218
+ *
219
+ * @param subscription - The subscription to delete
220
+ */
221
+ declare function deleteSubscription(subscription: Subscription): Promise<void>;
222
+ /**
223
+ * Delete multiple subscriptions
224
+ *
225
+ * @param subscriptions - Array of subscriptions to delete
226
+ */
227
+ declare function deleteSubscriptions(subscriptions: Subscription[]): Promise<void>;
228
+
229
+ /**
230
+ * Generate a random UUID
231
+ */
232
+ declare function generateUUID(): string;
233
+ /**
234
+ * Validate that a string is not empty
235
+ */
236
+ declare function assertNotEmptyString(value: unknown, name: string): asserts value is string;
237
+ /**
238
+ * Validate that a number is positive
239
+ */
240
+ declare function assertPositiveNumber(value: unknown, name: string): asserts value is number;
241
+
242
+ /**
243
+ * Schedule options for messages
244
+ */
245
+ type ScheduleOptions = {
246
+ afterInMs: number;
247
+ } | {
248
+ at: Date;
249
+ };
250
+ /**
251
+ * Scheduled message returned from dequeue
252
+ */
253
+ interface ScheduledMessage {
254
+ message: Message;
255
+ options?: ScheduleOptions;
256
+ }
257
+ /**
258
+ * Scheduler configuration
259
+ */
260
+ interface SchedulerConfig {
261
+ /**
262
+ * Whether running in emulator mode
263
+ */
264
+ useEmulator: boolean;
265
+ /**
266
+ * PubSub client instance
267
+ */
268
+ pubsub: PubSub;
269
+ /**
270
+ * Topic for scheduled messages (optional, created if needed)
271
+ */
272
+ scheduledTopic?: Topic;
273
+ /**
274
+ * Topic prefix for naming
275
+ * @default "emmett"
276
+ */
277
+ topicPrefix?: string;
278
+ }
279
+ /**
280
+ * Calculate scheduled time from options
281
+ *
282
+ * @param options - Schedule options (afterInMs or at)
283
+ * @returns The calculated scheduled time
284
+ */
285
+ declare function calculateScheduledTime(options?: ScheduleOptions): Date;
286
+ /**
287
+ * Filter messages that are ready for delivery
288
+ *
289
+ * @param pending - Array of pending scheduled messages
290
+ * @param now - Current time
291
+ * @returns Messages ready for delivery
292
+ */
293
+ declare function filterReadyMessages(pending: ScheduledMessageInfo[], now: Date): ScheduledMessageInfo[];
294
+ /**
295
+ * Message scheduler with dual mode support (production/emulator)
296
+ */
297
+ declare class MessageScheduler {
298
+ private pendingMessages;
299
+ private readonly useEmulator;
300
+ private readonly pubsub;
301
+ private readonly topicPrefix;
302
+ private scheduledTopic?;
303
+ constructor(config: SchedulerConfig);
304
+ /**
305
+ * Schedule a message for future delivery
306
+ *
307
+ * In production mode: Publishes to PubSub with publishTime attribute
308
+ * In emulator mode: Stores in memory for later dequeue (emulator doesn't support scheduling)
309
+ *
310
+ * @param message - The message to schedule
311
+ * @param options - When to deliver the message
312
+ */
313
+ schedule(message: Message, options?: ScheduleOptions): Promise<void>;
314
+ /**
315
+ * Dequeue ready scheduled messages (emulator mode only)
316
+ *
317
+ * Returns messages whose scheduled time has passed and removes them from pending queue
318
+ *
319
+ * @returns Array of scheduled messages ready for delivery
320
+ */
321
+ dequeue(): ScheduledMessage[];
322
+ /**
323
+ * Publish a scheduled message to PubSub (production mode)
324
+ *
325
+ * @param message - The message to publish
326
+ * @param scheduledAt - When the message should be delivered
327
+ */
328
+ private publishScheduledMessage;
329
+ /**
330
+ * Get count of pending scheduled messages (emulator mode only)
331
+ *
332
+ * @returns Number of pending scheduled messages
333
+ */
334
+ getPendingCount(): number;
335
+ /**
336
+ * Clear all pending scheduled messages (emulator mode only, useful for testing)
337
+ */
338
+ clearPending(): void;
339
+ }
340
+
341
+ /**
342
+ * Determine if an error should trigger a retry (nack) or be considered permanent (ack)
343
+ *
344
+ * @param error - The error to classify
345
+ * @returns true if the error is retriable (should nack), false if permanent (should ack)
346
+ */
347
+ declare function shouldRetry(error: unknown): boolean;
348
+ /**
349
+ * Process an incoming command message from PubSub
350
+ *
351
+ * @param message - The PubSub message
352
+ * @param handlers - Map of message type to handlers
353
+ * @param commandType - The command type being processed
354
+ * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure
355
+ */
356
+ declare function handleCommandMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, commandType: string): Promise<'ack' | 'nack'>;
357
+ /**
358
+ * Process an incoming event message from PubSub
359
+ *
360
+ * @param message - The PubSub message
361
+ * @param handlers - Map of message type to handlers
362
+ * @param eventType - The event type being processed
363
+ * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure
364
+ */
365
+ declare function handleEventMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, eventType: string): Promise<'ack' | 'nack'>;
366
+ /**
367
+ * Create a message listener for a PubSub subscription
368
+ *
369
+ * @param subscription - The PubSub subscription to listen on
370
+ * @param messageType - The message type (command or event type)
371
+ * @param kind - Whether this is a command or event
372
+ * @param handlers - Map of message type to handlers
373
+ */
374
+ declare function createMessageListener(subscription: Subscription, messageType: string, kind: 'command' | 'event', handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>): void;
375
+
376
+ /**
377
+ * Create a Google Cloud Pub/Sub based message bus for Emmett
378
+ *
379
+ * @param config - Configuration for the PubSub message bus
380
+ * @returns A message bus implementation using Google Cloud Pub/Sub
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * import { PubSub } from '@google-cloud/pubsub';
385
+ * import { getPubSubMessageBus } from '@emmett-community/emmett-google-pubsub';
386
+ *
387
+ * const pubsub = new PubSub({ projectId: 'my-project' });
388
+ * const messageBus = getPubSubMessageBus({ pubsub });
389
+ *
390
+ * // Register handlers
391
+ * messageBus.handle(async (command) => {
392
+ * // Handle command
393
+ * }, 'MyCommand');
394
+ *
395
+ * // Subscribe to events
396
+ * messageBus.subscribe(async (event) => {
397
+ * // Handle event
398
+ * }, 'MyEvent');
399
+ *
400
+ * // Start the message bus
401
+ * await messageBus.start();
402
+ *
403
+ * // Send commands and publish events
404
+ * await messageBus.send({ type: 'MyCommand', data: { ... } });
405
+ * await messageBus.publish({ type: 'MyEvent', data: { ... } });
406
+ *
407
+ * // Close gracefully
408
+ * await messageBus.close();
409
+ * ```
410
+ */
411
+ declare function getPubSubMessageBus(config: PubSubMessageBusConfig): MessageBus & EventSubscription & CommandProcessor & ScheduledMessageProcessor & PubSubMessageBusLifecycle;
412
+
413
+ export { type HandlerRegistration, MessageScheduler, type PubSubMessageBusConfig, type PubSubMessageBusLifecycle, type PubSubMessageEnvelope, type ScheduleOptions, type ScheduledMessage, type ScheduledMessageInfo, type SchedulerConfig, type SubscriptionInfo, type SubscriptionOptions, assertNotEmptyString, assertPositiveNumber, attachMessageId, calculateScheduledTime, createMessageListener, deleteSubscription, deleteSubscriptions, deserialize, extractMessageId, filterReadyMessages, generateUUID, getCommandSubscriptionName, getCommandTopicName, getEventSubscriptionName, getEventTopicName, getOrCreateSubscription, getOrCreateTopic, getPubSubMessageBus, handleCommandMessage, handleEventMessage, serialize, shouldRetry };