@emmett-community/emmett-google-pubsub 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -2,20 +2,18 @@
2
2
 
3
3
  Google Cloud Pub/Sub message bus implementation for [Emmett](https://event-driven-io.github.io/emmett/), the Node.js event sourcing framework.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@emmett-community/emmett-google-pubsub.svg)](https://www.npmjs.com/package/@emmett-community/emmett-google-pubsub)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![npm version](https://img.shields.io/npm/v/@emmett-community/emmett-google-pubsub.svg)](https://www.npmjs.com/package/@emmett-community/emmett-google-pubsub) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
6
 
8
7
  ## Features
9
8
 
10
- - **Distributed Message Bus** - Scale command/event handling across multiple instances
11
- - **Type-Safe** - Full TypeScript support with comprehensive types
12
- - **Automatic Topic Management** - Auto-creates topics and subscriptions
13
- - **Message Scheduling** - Schedule commands/events for future execution
14
- - **Error Handling** - Built-in retry logic and dead letter queue support
15
- - **Emulator Support** - Local development with PubSub emulator
16
- - **Testing Utilities** - Helper functions for easy testing
17
- - **Emmett Compatible** - Drop-in replacement for in-memory message bus
18
- - **Producer-Only Mode** - Use without starting consumers
9
+ - **Distributed Message Bus** - Scale command/event handling across multiple instances
10
+ - **Type-Safe** - Full TypeScript support with comprehensive types
11
+ - **Automatic Topic Management** - Auto-creates topics and subscriptions
12
+ - **Message Scheduling** - Schedule commands/events for future execution
13
+ - **Error Handling** - Built-in retry logic and dead letter queue support
14
+ - **Emulator Support** - Local development with PubSub emulator
15
+ - **Emmett Compatible** - Drop-in replacement for in-memory message bus
16
+ - **Producer-Only Mode** - Use without starting consumers
19
17
 
20
18
  ## Installation
21
19
 
@@ -23,6 +21,10 @@ Google Cloud Pub/Sub message bus implementation for [Emmett](https://event-drive
23
21
  npm install @emmett-community/emmett-google-pubsub @google-cloud/pubsub
24
22
  ```
25
23
 
24
+ ### Peer Dependencies
25
+
26
+ - `@event-driven-io/emmett` ^0.39.0
27
+
26
28
  ## Quick Start
27
29
 
28
30
  ```typescript
@@ -259,20 +261,21 @@ describe('My Tests', () => {
259
261
  ### Running Tests
260
262
 
261
263
  ```bash
262
- # Start PubSub emulator
263
- gcloud beta emulators pubsub start --project=test-project
264
+ # Unit tests
265
+ npm run test:unit
264
266
 
265
- # Or use Docker
266
- docker run -p 8085:8085 gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators \
267
- gcloud beta emulators pubsub start --host-port=0.0.0.0:8085
267
+ # Integration tests (in-memory)
268
+ npm run test:int
268
269
 
269
- # Set environment variable
270
- export PUBSUB_EMULATOR_HOST=localhost:8085
270
+ # E2E tests (PubSub emulator via Testcontainers, requires Docker)
271
+ npm run test:e2e
271
272
 
272
- # Run tests
273
+ # All tests
273
274
  npm test
274
275
  ```
275
276
 
277
+ E2E tests start the emulator automatically via Testcontainers.
278
+
276
279
  ## Examples
277
280
 
278
281
  ### Complete Shopping Cart Example
@@ -406,9 +409,12 @@ npm test
406
409
  # Run unit tests only
407
410
  npm run test:unit
408
411
 
409
- # Run integration tests (requires emulator)
412
+ # Run integration tests (in-memory)
410
413
  npm run test:int
411
414
 
415
+ # Run E2E tests (requires Docker)
416
+ npm run test:e2e
417
+
412
418
  # Lint
413
419
  npm run lint
414
420
 
@@ -420,19 +426,18 @@ npm run format
420
426
 
421
427
  MIT
422
428
 
423
- ## Resources
429
+ ## Related Packages
424
430
 
425
- - [Emmett Documentation](https://event-driven-io.github.io/emmett/)
426
- - [Google Cloud Pub/Sub Docs](https://cloud.google.com/pubsub/docs)
427
- - [GitHub Repository](https://github.com/emmett-community/emmett-google-pubsub)
431
+ - [@event-driven-io/emmett](https://github.com/event-driven-io/emmett) - Core Emmett framework
432
+ - [@emmett-community/emmett-google-firestore](https://github.com/emmett-community/emmett-google-firestore) - Firestore event store
433
+ - [@emmett-community/emmett-google-realtime-db](https://github.com/emmett-community/emmett-google-realtime-db) - Realtime Database inline projections
434
+ - [@event-driven-io/emmett-mongodb](https://github.com/event-driven-io/emmett/tree/main/src/packages/emmett-mongodb) - MongoDB event store
428
435
 
429
436
  ## Support
430
437
 
431
- - **Issues**: [GitHub Issues](https://github.com/emmett-community/emmett-google-pubsub/issues)
432
- - **Discussions**: [GitHub Discussions](https://github.com/emmett-community/emmett-google-pubsub/discussions)
433
- - **Emmett Discord**: [Join Discord](https://discord.gg/fTpqUTMmVa)
438
+ - [GitHub Issues](https://github.com/emmett-community/emmett-google-pubsub/issues)
439
+ - [Emmett Documentation](https://event-driven-io.github.io/emmett/)
434
440
 
435
- ## Acknowledgments
441
+ ---
436
442
 
437
- - Built for the [Emmett](https://event-driven-io.github.io/emmett/) framework by [Oskar Dudycz](https://github.com/oskardudycz)
438
- - Part of the [Emmett Community](https://github.com/emmett-community)
443
+ Made with ❤️ by the Emmett Community
package/dist/index.js CHANGED
@@ -735,13 +735,22 @@ function getPubSubMessageBus(config) {
735
735
  }
736
736
  const timeout = 3e4;
737
737
  const waitStart = Date.now();
738
+ let timeoutId = null;
738
739
  const closePromises = subscriptions.map(
739
740
  ({ subscription }) => subscription.close()
740
741
  );
741
- await Promise.race([
742
- Promise.all(closePromises),
743
- new Promise((resolve) => setTimeout(resolve, timeout))
744
- ]);
742
+ try {
743
+ await Promise.race([
744
+ Promise.all(closePromises),
745
+ new Promise((resolve) => {
746
+ timeoutId = setTimeout(resolve, timeout);
747
+ })
748
+ ]);
749
+ } finally {
750
+ if (timeoutId) {
751
+ clearTimeout(timeoutId);
752
+ }
753
+ }
745
754
  const waitTime = Date.now() - waitStart;
746
755
  if (waitTime >= timeout) {
747
756
  console.warn(
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/messageBus/serialization.ts","../src/messageBus/topicManager.ts","../src/messageBus/utils.ts","../src/messageBus/scheduler.ts","../src/messageBus/messageHandler.ts","../src/messageBus/pubsubMessageBus.ts"],"names":["randomUUID","cryptoRandomUUID","EmmettError"],"mappings":"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","file":"index.js","sourcesContent":["import { randomUUID } from 'crypto';\nimport type { Command, Event, Message } from '@event-driven-io/emmett';\nimport type { PubSubMessageEnvelope } from './types';\n\n/**\n * Date marker for JSON serialization\n */\ninterface DateMarker {\n __type: 'Date';\n value: string;\n}\n\n/**\n * Recursively transform Dates to DateMarkers in an object\n */\nfunction transformDatesToMarkers(obj: unknown): unknown {\n if (obj instanceof Date) {\n return {\n __type: 'Date',\n value: obj.toISOString(),\n } satisfies DateMarker;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(transformDatesToMarkers);\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = transformDatesToMarkers(value);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Check if a value is a Date marker\n */\nfunction isDateMarker(value: unknown): value is DateMarker {\n return (\n typeof value === 'object' &&\n value !== null &&\n '__type' in value &&\n (value as DateMarker).__type === 'Date' &&\n 'value' in value &&\n typeof (value as DateMarker).value === 'string'\n );\n}\n\n/**\n * JSON reviver that converts Date markers back to Dates\n */\nfunction dateReviver(_key: string, value: unknown): unknown {\n if (isDateMarker(value)) {\n return new Date(value.value);\n }\n return value;\n}\n\n/**\n * Determine if a message is a command or event based on its type\n */\nfunction getMessageKind(message: Message): 'command' | 'event' {\n // Simple heuristic: if the message type contains \"Command\", it's a command\n // Otherwise, it's an event\n // This can be customized based on your naming conventions\n const typeStr = message.type.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Serialize a Command or Event to a Buffer for PubSub transport\n *\n * @param message - The message to serialize\n * @returns Buffer containing the serialized message envelope\n */\nexport function serialize(message: Command | Event): Buffer {\n const envelope = {\n type: message.type,\n kind: getMessageKind(message),\n data: transformDatesToMarkers(message.data),\n metadata: 'metadata' in message ? transformDatesToMarkers(message.metadata) : undefined,\n timestamp: new Date().toISOString(),\n messageId: randomUUID(),\n };\n\n const json = JSON.stringify(envelope);\n return Buffer.from(json);\n}\n\n/**\n * Deserialize a Buffer from PubSub into a Command or Event\n *\n * @param buffer - The buffer containing the serialized message\n * @returns The deserialized message\n * @throws Error if the buffer cannot be deserialized\n */\nexport function deserialize<T extends Command | Event>(buffer: Buffer): T {\n try {\n const json = buffer.toString('utf-8');\n const envelope = JSON.parse(json, dateReviver) as PubSubMessageEnvelope;\n\n const message = {\n type: envelope.type,\n data: envelope.data,\n ...(envelope.metadata ? { metadata: envelope.metadata } : {}),\n } as Message;\n\n return message as T;\n } catch (error) {\n throw new Error(\n `Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Attach a message ID to a message for idempotency tracking\n *\n * @param message - The message to attach ID to\n * @param messageId - The message ID\n * @returns The message with the ID attached\n */\nexport function attachMessageId<T extends Message>(\n message: T,\n messageId: string,\n): T & { __messageId: string } {\n return {\n ...message,\n __messageId: messageId,\n };\n}\n\n/**\n * Extract message ID from a message\n *\n * @param message - The message to extract ID from\n * @returns The message ID if present, undefined otherwise\n */\nexport function extractMessageId(message: Message): string | undefined {\n return '__messageId' in message\n ? (message as { __messageId: string }).__messageId\n : undefined;\n}\n","import type { PubSub, Subscription, Topic } from '@google-cloud/pubsub';\nimport type { SubscriptionOptions } from './types';\n\n/**\n * Get command topic name\n */\nexport function getCommandTopicName(\n commandType: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}`;\n}\n\n/**\n * Get event topic name\n */\nexport function getEventTopicName(eventType: string, prefix = 'emmett'): string {\n return `${prefix}-evt-${eventType}`;\n}\n\n/**\n * Get command subscription name\n */\nexport function getCommandSubscriptionName(\n commandType: string,\n instanceId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}-${instanceId}`;\n}\n\n/**\n * Get event subscription name\n */\nexport function getEventSubscriptionName(\n eventType: string,\n subscriptionId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-evt-${eventType}-${subscriptionId}`;\n}\n\n/**\n * Get or create a topic\n *\n * @param pubsub - PubSub client\n * @param topicName - Name of the topic\n * @returns The topic instance\n */\nexport async function getOrCreateTopic(\n pubsub: PubSub,\n topicName: string,\n): Promise<Topic> {\n const topic = pubsub.topic(topicName);\n\n try {\n const [exists] = await topic.exists();\n\n if (!exists) {\n try {\n await topic.create();\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return topic;\n } catch (error) {\n throw new Error(\n `Failed to get or create topic ${topicName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Get or create a subscription\n *\n * @param topic - The topic to subscribe to\n * @param subscriptionName - Name of the subscription\n * @param options - Subscription options\n * @returns The subscription instance\n */\nexport async function getOrCreateSubscription(\n topic: Topic,\n subscriptionName: string,\n options?: SubscriptionOptions,\n): Promise<Subscription> {\n const subscription = topic.subscription(subscriptionName);\n\n try {\n const [exists] = await subscription.exists();\n\n if (!exists) {\n const config = {\n ...(options?.ackDeadlineSeconds && {\n ackDeadlineSeconds: options.ackDeadlineSeconds,\n }),\n ...(options?.retryPolicy && {\n retryPolicy: {\n ...(options.retryPolicy.minimumBackoff && {\n minimumBackoff: options.retryPolicy.minimumBackoff,\n }),\n ...(options.retryPolicy.maximumBackoff && {\n maximumBackoff: options.retryPolicy.maximumBackoff,\n }),\n },\n }),\n ...(options?.deadLetterPolicy && {\n deadLetterPolicy: {\n ...(options.deadLetterPolicy.deadLetterTopic && {\n deadLetterTopic: options.deadLetterPolicy.deadLetterTopic,\n }),\n ...(options.deadLetterPolicy.maxDeliveryAttempts && {\n maxDeliveryAttempts: options.deadLetterPolicy.maxDeliveryAttempts,\n }),\n },\n }),\n };\n\n try {\n await subscription.create(config);\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return subscription;\n } catch (error) {\n throw new Error(\n `Failed to get or create subscription ${subscriptionName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete a subscription\n *\n * @param subscription - The subscription to delete\n */\nexport async function deleteSubscription(\n subscription: Subscription,\n): Promise<void> {\n try {\n const [exists] = await subscription.exists();\n\n if (exists) {\n await subscription.delete();\n }\n } catch (error) {\n // Log but don't throw - cleanup is best effort\n console.warn(\n `Failed to delete subscription: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete multiple subscriptions\n *\n * @param subscriptions - Array of subscriptions to delete\n */\nexport async function deleteSubscriptions(\n subscriptions: Subscription[],\n): Promise<void> {\n await Promise.all(subscriptions.map((sub) => deleteSubscription(sub)));\n}\n","import { randomUUID as cryptoRandomUUID } from 'crypto';\n\n/**\n * Generate a random UUID\n */\nexport function generateUUID(): string {\n return cryptoRandomUUID();\n}\n\n/**\n * Validate that a string is not empty\n */\nexport function assertNotEmptyString(\n value: unknown,\n name: string,\n): asserts value is string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new Error(`${name} must be a non-empty string`);\n }\n}\n\n/**\n * Validate that a number is positive\n */\nexport function assertPositiveNumber(\n value: unknown,\n name: string,\n): asserts value is number {\n if (typeof value !== 'number' || value <= 0 || Number.isNaN(value)) {\n throw new Error(`${name} must be a positive number`);\n }\n}\n","import type { PubSub, Topic } from '@google-cloud/pubsub';\nimport type { Message } from '@event-driven-io/emmett';\nimport type { ScheduledMessageInfo } from './types';\nimport { serialize } from './serialization';\n\n/**\n * Schedule options for messages\n */\nexport type ScheduleOptions = { afterInMs: number } | { at: Date };\n\n/**\n * Scheduled message returned from dequeue\n */\nexport interface ScheduledMessage {\n message: Message;\n options?: ScheduleOptions;\n}\n\n/**\n * Scheduler configuration\n */\nexport interface SchedulerConfig {\n /**\n * Whether running in emulator mode\n */\n useEmulator: boolean;\n\n /**\n * PubSub client instance\n */\n pubsub: PubSub;\n\n /**\n * Topic for scheduled messages (optional, created if needed)\n */\n scheduledTopic?: Topic;\n\n /**\n * Topic prefix for naming\n * @default \"emmett\"\n */\n topicPrefix?: string;\n}\n\n/**\n * Calculate scheduled time from options\n *\n * @param options - Schedule options (afterInMs or at)\n * @returns The calculated scheduled time\n */\nexport function calculateScheduledTime(options?: ScheduleOptions): Date {\n if (!options) {\n return new Date();\n }\n\n if ('afterInMs' in options) {\n const now = new Date();\n return new Date(now.getTime() + options.afterInMs);\n }\n\n if ('at' in options) {\n return options.at;\n }\n\n return new Date();\n}\n\n/**\n * Filter messages that are ready for delivery\n *\n * @param pending - Array of pending scheduled messages\n * @param now - Current time\n * @returns Messages ready for delivery\n */\nexport function filterReadyMessages(\n pending: ScheduledMessageInfo[],\n now: Date,\n): ScheduledMessageInfo[] {\n return pending.filter((msg) => msg.scheduledAt <= now);\n}\n\n/**\n * Message scheduler with dual mode support (production/emulator)\n */\nexport class MessageScheduler {\n private pendingMessages: ScheduledMessageInfo[] = [];\n private readonly useEmulator: boolean;\n private readonly pubsub: PubSub;\n private readonly topicPrefix: string;\n private scheduledTopic?: Topic;\n\n constructor(config: SchedulerConfig) {\n this.useEmulator = config.useEmulator;\n this.pubsub = config.pubsub;\n this.scheduledTopic = config.scheduledTopic;\n this.topicPrefix = config.topicPrefix ?? 'emmett';\n }\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Publishes to PubSub with publishTime attribute\n * In emulator mode: Stores in memory for later dequeue (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param options - When to deliver the message\n */\n async schedule(message: Message, options?: ScheduleOptions): Promise<void> {\n const scheduledAt = calculateScheduledTime(options);\n\n if (this.useEmulator) {\n // Emulator mode: store in memory\n this.pendingMessages.push({\n message,\n options,\n scheduledAt,\n });\n } else {\n // Production mode: publish to PubSub with publishTime attribute\n await this.publishScheduledMessage(message, scheduledAt);\n }\n }\n\n /**\n * Dequeue ready scheduled messages (emulator mode only)\n *\n * Returns messages whose scheduled time has passed and removes them from pending queue\n *\n * @returns Array of scheduled messages ready for delivery\n */\n dequeue(): ScheduledMessage[] {\n if (!this.useEmulator) {\n // In production mode, PubSub handles scheduling, so dequeue returns empty\n return [];\n }\n\n const now = new Date();\n const ready = filterReadyMessages(this.pendingMessages, now);\n\n // Remove ready messages from pending queue\n this.pendingMessages = this.pendingMessages.filter(\n (msg) => msg.scheduledAt > now,\n );\n\n // Convert to ScheduledMessage format\n return ready.map((info) => ({\n message: info.message,\n options: info.options,\n }));\n }\n\n /**\n * Publish a scheduled message to PubSub (production mode)\n *\n * @param message - The message to publish\n * @param scheduledAt - When the message should be delivered\n */\n private async publishScheduledMessage(\n message: Message,\n scheduledAt: Date,\n ): Promise<void> {\n try {\n // Get or create the scheduled messages topic\n if (!this.scheduledTopic) {\n const topicName = `${this.topicPrefix}-scheduled-messages`;\n this.scheduledTopic = this.pubsub.topic(topicName);\n\n const [exists] = await this.scheduledTopic.exists();\n if (!exists) {\n await this.scheduledTopic.create();\n }\n }\n\n // Serialize the message\n const buffer = serialize(message);\n\n // Publish with custom attributes including publish time\n await this.scheduledTopic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n publishTime: scheduledAt.toISOString(),\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish scheduled message ${message.type}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n /**\n * Get count of pending scheduled messages (emulator mode only)\n *\n * @returns Number of pending scheduled messages\n */\n getPendingCount(): number {\n return this.useEmulator ? this.pendingMessages.length : 0;\n }\n\n /**\n * Clear all pending scheduled messages (emulator mode only, useful for testing)\n */\n clearPending(): void {\n if (this.useEmulator) {\n this.pendingMessages = [];\n }\n }\n}\n","import type { Message as PubSubMessage, Subscription } from '@google-cloud/pubsub';\nimport type {\n AnyMessage,\n Command,\n Event,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport { deserialize } from './serialization';\n\n/**\n * Determine if an error should trigger a retry (nack) or be considered permanent (ack)\n *\n * @param error - The error to classify\n * @returns true if the error is retriable (should nack), false if permanent (should ack)\n */\nexport function shouldRetry(error: unknown): boolean {\n if (!(error instanceof Error)) {\n // Unknown error types - retry to be safe\n return true;\n }\n\n const errorMessage = error.message.toLowerCase();\n\n // Network/timeout errors - retry\n if (\n errorMessage.includes('network') ||\n errorMessage.includes('timeout') ||\n errorMessage.includes('econnrefused') ||\n errorMessage.includes('enotfound') ||\n errorMessage.includes('unavailable')\n ) {\n return true;\n }\n\n // EmmettError and validation errors - don't retry (business logic errors)\n if (error instanceof EmmettError) {\n return false;\n }\n\n if (\n errorMessage.includes('validation') ||\n errorMessage.includes('invalid') ||\n errorMessage.includes('not found') ||\n errorMessage.includes('already exists')\n ) {\n return false;\n }\n\n // Default to retry for unknown errors\n return true;\n}\n\n/**\n * Process an incoming command message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param commandType - The command type being processed\n * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleCommandMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n commandType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this command type\n const commandHandlers = handlers.get(commandType);\n\n if (!commandHandlers || commandHandlers.length === 0) {\n throw new EmmettError(\n `No handler registered for command ${commandType}!`,\n );\n }\n\n // Commands must have exactly one handler\n if (commandHandlers.length > 1) {\n throw new EmmettError(\n `Multiple handlers registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Deserialize the command\n const command = deserialize<Command>(message.data);\n\n // Execute the handler\n const handler = commandHandlers[0];\n await handler(command);\n\n return 'ack';\n } catch (error) {\n console.error(\n `Error handling command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // Determine if we should retry\n if (shouldRetry(error)) {\n console.info(\n `Nacking command ${commandType} for retry (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n } else {\n console.warn(\n `Acking command ${commandType} despite error (permanent failure)`,\n );\n return 'ack';\n }\n }\n}\n\n/**\n * Process an incoming event message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param eventType - The event type being processed\n * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleEventMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n eventType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this event type\n const eventHandlers = handlers.get(eventType);\n\n if (!eventHandlers || eventHandlers.length === 0) {\n // Events without handlers are silently ignored (valid scenario)\n console.debug(`No handlers registered for event ${eventType}, skipping`);\n return 'ack';\n }\n\n // Deserialize the event\n const event = deserialize<Event>(message.data);\n\n // Execute all handlers sequentially\n for (const handler of eventHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(\n `Error in event handler for ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // If any handler fails with a retriable error, nack the whole message\n if (shouldRetry(error)) {\n console.info(\n `Nacking event ${eventType} for retry due to handler failure (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n }\n // Otherwise continue to next handler\n console.warn(\n `Continuing event ${eventType} processing despite handler error (permanent failure)`,\n );\n }\n }\n\n return 'ack';\n } catch (error) {\n // Error deserializing or other unexpected error\n console.error(\n `Error handling event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n if (shouldRetry(error)) {\n return 'nack';\n } else {\n return 'ack';\n }\n }\n}\n\n/**\n * Create a message listener for a PubSub subscription\n *\n * @param subscription - The PubSub subscription to listen on\n * @param messageType - The message type (command or event type)\n * @param kind - Whether this is a command or event\n * @param handlers - Map of message type to handlers\n */\nexport function createMessageListener(\n subscription: Subscription,\n messageType: string,\n kind: 'command' | 'event',\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n): void {\n subscription.on('message', async (message: PubSubMessage) => {\n try {\n // Route to appropriate handler based on kind\n const result =\n kind === 'command'\n ? await handleCommandMessage(message, handlers, messageType)\n : await handleEventMessage(message, handlers, messageType);\n\n // Acknowledge or nack based on result\n if (result === 'ack') {\n message.ack();\n } else {\n message.nack();\n }\n } catch (error) {\n // Unexpected error in listener itself - log and nack\n console.error(\n `Unexpected error in message listener for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n message.nack();\n }\n });\n\n subscription.on('error', (error) => {\n console.error(\n `Subscription error for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n}\n","import type {\n AnyMessage,\n Command,\n CommandProcessor,\n Event,\n EventSubscription,\n Message,\n MessageBus,\n ScheduledMessageProcessor,\n SingleMessageHandler,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport type {\n PubSubMessageBusConfig,\n PubSubMessageBusLifecycle,\n SubscriptionInfo,\n} from './types';\nimport type { ScheduledMessage } from './scheduler';\nimport { MessageScheduler } from './scheduler';\nimport { serialize } from './serialization';\nimport {\n getCommandSubscriptionName,\n getCommandTopicName,\n getEventSubscriptionName,\n getEventTopicName,\n getOrCreateSubscription,\n getOrCreateTopic,\n deleteSubscriptions,\n} from './topicManager';\nimport { createMessageListener } from './messageHandler';\nimport { generateUUID } from './utils';\n\n/**\n * Determine message kind based on message type naming convention (fallback)\n *\n * @param messageType - The message type string\n * @returns 'command' if type contains 'Command', otherwise 'event'\n */\nfunction determineMessageKindFallback(messageType: string): 'command' | 'event' {\n const typeStr = messageType.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Create a Google Cloud Pub/Sub based message bus for Emmett\n *\n * @param config - Configuration for the PubSub message bus\n * @returns A message bus implementation using Google Cloud Pub/Sub\n *\n * @example\n * ```typescript\n * import { PubSub } from '@google-cloud/pubsub';\n * import { getPubSubMessageBus } from '@emmett-community/emmett-google-pubsub';\n *\n * const pubsub = new PubSub({ projectId: 'my-project' });\n * const messageBus = getPubSubMessageBus({ pubsub });\n *\n * // Register handlers\n * messageBus.handle(async (command) => {\n * // Handle command\n * }, 'MyCommand');\n *\n * // Subscribe to events\n * messageBus.subscribe(async (event) => {\n * // Handle event\n * }, 'MyEvent');\n *\n * // Start the message bus\n * await messageBus.start();\n *\n * // Send commands and publish events\n * await messageBus.send({ type: 'MyCommand', data: { ... } });\n * await messageBus.publish({ type: 'MyEvent', data: { ... } });\n *\n * // Close gracefully\n * await messageBus.close();\n * ```\n */\nexport function getPubSubMessageBus(\n config: PubSubMessageBusConfig,\n): MessageBus &\n EventSubscription &\n CommandProcessor &\n ScheduledMessageProcessor &\n PubSubMessageBusLifecycle {\n // Internal state\n const instanceId = config.instanceId ?? generateUUID();\n const topicPrefix = config.topicPrefix ?? 'emmett';\n const autoCreateResources = config.autoCreateResources ?? true;\n const cleanupOnClose = config.cleanupOnClose ?? false;\n const closePubSubClient = config.closePubSubClient;\n\n // Map of message type to handlers\n const handlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n\n // Map of subscription ID to specific handler (for event subscriptions)\n const subscriptionHandlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>\n >();\n\n // Map of message type to subscription IDs (for events with multiple subscriptions)\n const eventSubscriptionIds = new Map<string, string[]>();\n\n // Track which message types are commands vs events\n const commandTypes = new Set<string>();\n const eventTypes = new Set<string>();\n\n // Active subscriptions\n const subscriptions: SubscriptionInfo[] = [];\n\n // Scheduler for delayed messages\n const scheduler = new MessageScheduler({\n useEmulator: config.useEmulator ?? false,\n pubsub: config.pubsub,\n topicPrefix,\n });\n\n // Lifecycle state\n let started = false;\n\n /**\n * Determine message kind based on how it was registered\n *\n * @param messageType - The message type string\n * @returns 'command' if registered with handle(), 'event' if registered with subscribe()\n */\n function determineMessageKind(messageType: string): 'command' | 'event' {\n // Check explicit registration first\n if (commandTypes.has(messageType)) {\n return 'command';\n }\n if (eventTypes.has(messageType)) {\n return 'event';\n }\n // Fallback to name-based heuristic\n return determineMessageKindFallback(messageType);\n }\n\n /**\n * Create subscription for a specific message type\n *\n * @param messageType - The message type\n * @param kind - Whether this is a command or event\n * @param subscriptionId - Optional subscription ID for events\n */\n async function createSubscriptionForType(\n messageType: string,\n kind: 'command' | 'event',\n subscriptionId?: string,\n ): Promise<void> {\n // Get topic name based on kind\n const topicName =\n kind === 'command'\n ? getCommandTopicName(messageType, topicPrefix)\n : getEventTopicName(messageType, topicPrefix);\n\n // Get or create topic\n const topic = await getOrCreateTopic(config.pubsub, topicName);\n\n // Get subscription name\n const subName =\n kind === 'command'\n ? getCommandSubscriptionName(messageType, instanceId, topicPrefix)\n : getEventSubscriptionName(\n messageType,\n subscriptionId ?? instanceId,\n topicPrefix,\n );\n\n // Create subscription\n const subscription = await getOrCreateSubscription(\n topic,\n subName,\n config.subscriptionOptions,\n );\n\n // Create message listener with appropriate handlers\n if (kind === 'event' && subscriptionId) {\n // For events, create a map with only this subscription's handler\n const handler = subscriptionHandlers.get(subscriptionId);\n if (handler) {\n const singleHandlerMap = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n singleHandlerMap.set(messageType, [handler]);\n createMessageListener(subscription, messageType, kind, singleHandlerMap);\n }\n } else {\n // For commands, use the handlers map as before\n createMessageListener(subscription, messageType, kind, handlers);\n }\n\n // Track subscription\n subscriptions.push({\n topic,\n subscription,\n messageType,\n kind,\n });\n }\n\n /**\n * Publish a message to a PubSub topic\n *\n * @param message - The message to publish\n * @param kind - Whether this is a command or event\n */\n async function publishMessage(\n message: Message,\n kind: 'command' | 'event',\n ): Promise<void> {\n // Publishing without start() is allowed (producer-only mode)\n // start() is only required for consumers (handlers/subscribers)\n\n // Get topic name\n const topicName =\n kind === 'command'\n ? getCommandTopicName(message.type, topicPrefix)\n : getEventTopicName(message.type, topicPrefix);\n\n try {\n // Get topic\n const topic = config.pubsub.topic(topicName);\n\n // Check if topic exists if auto-create is disabled\n if (!autoCreateResources) {\n const [exists] = await topic.exists();\n if (!exists) {\n throw new Error(\n `Topic ${topicName} does not exist and autoCreateResources is disabled`,\n );\n }\n } else {\n // Create topic if it doesn't exist\n const [exists] = await topic.exists();\n if (!exists) {\n await topic.create();\n }\n }\n\n // Serialize message\n const buffer = serialize(message);\n\n // Publish\n await topic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n messageKind: kind,\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish ${kind} ${message.type} to topic ${topicName}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n // Return the message bus implementation\n return {\n // ===== MessageBus Interface =====\n\n /**\n * Send a command to the message bus\n *\n * Commands are routed to exactly one handler via PubSub topics\n *\n * @param command - The command to send\n */\n async send<CommandType extends Command>(\n command: CommandType,\n ): Promise<void> {\n await publishMessage(command, 'command');\n },\n\n /**\n * Publish an event to the message bus\n *\n * Events are delivered to all registered subscribers via PubSub topics\n *\n * @param event - The event to publish\n */\n async publish<EventType extends Event>(event: EventType): Promise<void> {\n await publishMessage(event, 'event');\n },\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Uses PubSub native scheduling\n * In emulator mode: Stores in memory (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param when - When to deliver the message (afterInMs or at)\n */\n schedule<MessageType extends Message>(\n message: MessageType,\n when?: { afterInMs: number } | { at: Date },\n ): void {\n scheduler.schedule(message, when);\n },\n\n // ===== CommandProcessor Interface =====\n\n /**\n * Register a command handler\n *\n * Commands must have exactly one handler. Attempting to register multiple\n * handlers for the same command will throw an EmmettError.\n *\n * @param commandHandler - The handler function\n * @param commandTypes - Command types this handler processes\n * @throws EmmettError if a handler is already registered for any command type\n *\n * @example\n * ```typescript\n * messageBus.handle(\n * async (command: AddProductItemCommand) => {\n * // Handle command\n * },\n * 'AddProductItem'\n * );\n * ```\n */\n handle<CommandType extends Command>(\n commandHandler: SingleMessageHandler<CommandType>,\n ...commandTypeNames: CommandType['type'][]\n ): void {\n for (const commandType of commandTypeNames) {\n // Validate no duplicate handlers\n if (handlers.has(commandType)) {\n throw new EmmettError(\n `Handler already registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Track as command type\n commandTypes.add(commandType);\n\n // Store handler (single handler in array for consistency)\n handlers.set(commandType, [\n commandHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(commandType, 'command').catch((error) => {\n console.error(\n `Failed to create subscription for command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n }\n }\n },\n\n // ===== EventSubscription Interface =====\n\n /**\n * Subscribe to events\n *\n * Events can have multiple subscribers. Each subscription gets its own\n * PubSub subscription to ensure all handlers receive all events.\n *\n * @param eventHandler - The handler function\n * @param eventTypes - Event types to subscribe to\n *\n * @example\n * ```typescript\n * messageBus.subscribe(\n * async (event: ProductItemAddedEvent) => {\n * // Handle event\n * },\n * 'ProductItemAdded'\n * );\n * ```\n */\n subscribe<EventType extends Event>(\n eventHandler: SingleMessageHandler<EventType>,\n ...eventTypeNames: EventType['type'][]\n ): void {\n for (const eventType of eventTypeNames) {\n // Track as event type\n eventTypes.add(eventType);\n\n // Generate unique subscription ID for this subscriber\n const subscriptionId = generateUUID();\n\n // Store handler associated with this subscription ID\n subscriptionHandlers.set(\n subscriptionId,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n );\n\n // Get existing handlers or create new array\n const existing = handlers.get(eventType) ?? [];\n\n // Add handler to array (for compatibility)\n handlers.set(eventType, [\n ...existing,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // Track subscription ID for this event type\n const existingIds = eventSubscriptionIds.get(eventType) ?? [];\n eventSubscriptionIds.set(eventType, [...existingIds, subscriptionId]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(eventType, 'event', subscriptionId).catch(\n (error) => {\n console.error(\n `Failed to create subscription for event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n },\n );\n }\n }\n },\n\n // ===== ScheduledMessageProcessor Interface =====\n\n /**\n * Dequeue scheduled messages that are ready for delivery\n *\n * Only used in emulator mode. In production, PubSub handles scheduling.\n *\n * @returns Array of scheduled messages ready for delivery\n *\n * @example\n * ```typescript\n * // In emulator mode, periodically call dequeue\n * setInterval(() => {\n * const ready = messageBus.dequeue();\n * for (const { message } of ready) {\n * // Process message\n * }\n * }, 1000);\n * ```\n */\n dequeue(): ScheduledMessage[] {\n return scheduler.dequeue();\n },\n\n // ===== PubSubMessageBusLifecycle Interface =====\n\n /**\n * Start the message bus\n *\n * Creates topics and subscriptions for all registered handlers and begins\n * listening for messages.\n *\n * This method is idempotent - calling it multiple times is safe.\n *\n * @throws Error if topic/subscription creation fails\n *\n * @example\n * ```typescript\n * // Register all handlers first\n * messageBus.handle(commandHandler, 'MyCommand');\n * messageBus.subscribe(eventHandler, 'MyEvent');\n *\n * // Then start\n * await messageBus.start();\n * ```\n */\n async start(): Promise<void> {\n if (started) {\n console.debug('Message bus already started, skipping');\n return;\n }\n\n console.info('Starting PubSub message bus...');\n\n try {\n // Create subscriptions for all registered handlers\n const subscriptionPromises: Promise<void>[] = [];\n\n for (const [messageType] of handlers.entries()) {\n const kind = determineMessageKind(messageType);\n\n if (kind === 'command') {\n // Commands: one subscription per instance\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'command'),\n );\n } else {\n // Events: one subscription per handler (multiple allowed)\n const subIds = eventSubscriptionIds.get(messageType) ?? [\n instanceId,\n ];\n for (const subId of subIds) {\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'event', subId),\n );\n }\n }\n }\n\n // Wait for all subscriptions to be created\n await Promise.all(subscriptionPromises);\n\n started = true;\n\n console.info(\n `PubSub message bus started with ${subscriptions.length} subscription(s)`,\n );\n } catch (error) {\n throw new Error(\n `Failed to start message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Close the message bus gracefully\n *\n * Stops accepting new messages, waits for in-flight messages to complete,\n * optionally cleans up subscriptions, and closes the PubSub client.\n *\n * @throws Error if cleanup fails\n *\n * @example\n * ```typescript\n * // Graceful shutdown\n * process.on('SIGTERM', async () => {\n * await messageBus.close();\n * process.exit(0);\n * });\n * ```\n */\n async close(): Promise<void> {\n console.info('Closing PubSub message bus...');\n\n try {\n // Only cleanup subscriptions if started\n if (started) {\n // Stop accepting new messages\n for (const { subscription } of subscriptions) {\n subscription.removeAllListeners('message');\n subscription.removeAllListeners('error');\n }\n\n // Wait for in-flight messages with timeout (30 seconds)\n const timeout = 30000;\n const waitStart = Date.now();\n\n // Close all subscriptions\n const closePromises = subscriptions.map(({ subscription }) =>\n subscription.close(),\n );\n await Promise.race([\n Promise.all(closePromises),\n new Promise((resolve) => setTimeout(resolve, timeout)),\n ]);\n\n const waitTime = Date.now() - waitStart;\n if (waitTime >= timeout) {\n console.warn(\n `Timeout waiting for in-flight messages after ${timeout}ms`,\n );\n }\n\n // Cleanup subscriptions if configured\n if (cleanupOnClose) {\n console.info('Cleaning up subscriptions...');\n await deleteSubscriptions(subscriptions.map((s) => s.subscription));\n }\n\n started = false;\n }\n\n // Always close PubSub client (even if not started, for producer-only mode)\n if (closePubSubClient !== false) {\n await config.pubsub.close();\n }\n\n console.info('PubSub message bus closed');\n } catch (error) {\n throw new Error(\n `Failed to close message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Check if the message bus is started\n *\n * @returns true if the message bus is started and ready to process messages\n */\n isStarted(): boolean {\n return started;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/messageBus/serialization.ts","../src/messageBus/topicManager.ts","../src/messageBus/utils.ts","../src/messageBus/scheduler.ts","../src/messageBus/messageHandler.ts","../src/messageBus/pubsubMessageBus.ts"],"names":["randomUUID","cryptoRandomUUID","EmmettError"],"mappings":"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","file":"index.js","sourcesContent":["import { randomUUID } from 'crypto';\nimport type { Command, Event, Message } from '@event-driven-io/emmett';\nimport type { PubSubMessageEnvelope } from './types';\n\n/**\n * Date marker for JSON serialization\n */\ninterface DateMarker {\n __type: 'Date';\n value: string;\n}\n\n/**\n * Recursively transform Dates to DateMarkers in an object\n */\nfunction transformDatesToMarkers(obj: unknown): unknown {\n if (obj instanceof Date) {\n return {\n __type: 'Date',\n value: obj.toISOString(),\n } satisfies DateMarker;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(transformDatesToMarkers);\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = transformDatesToMarkers(value);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Check if a value is a Date marker\n */\nfunction isDateMarker(value: unknown): value is DateMarker {\n return (\n typeof value === 'object' &&\n value !== null &&\n '__type' in value &&\n (value as DateMarker).__type === 'Date' &&\n 'value' in value &&\n typeof (value as DateMarker).value === 'string'\n );\n}\n\n/**\n * JSON reviver that converts Date markers back to Dates\n */\nfunction dateReviver(_key: string, value: unknown): unknown {\n if (isDateMarker(value)) {\n return new Date(value.value);\n }\n return value;\n}\n\n/**\n * Determine if a message is a command or event based on its type\n */\nfunction getMessageKind(message: Message): 'command' | 'event' {\n // Simple heuristic: if the message type contains \"Command\", it's a command\n // Otherwise, it's an event\n // This can be customized based on your naming conventions\n const typeStr = message.type.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Serialize a Command or Event to a Buffer for PubSub transport\n *\n * @param message - The message to serialize\n * @returns Buffer containing the serialized message envelope\n */\nexport function serialize(message: Command | Event): Buffer {\n const envelope = {\n type: message.type,\n kind: getMessageKind(message),\n data: transformDatesToMarkers(message.data),\n metadata: 'metadata' in message ? transformDatesToMarkers(message.metadata) : undefined,\n timestamp: new Date().toISOString(),\n messageId: randomUUID(),\n };\n\n const json = JSON.stringify(envelope);\n return Buffer.from(json);\n}\n\n/**\n * Deserialize a Buffer from PubSub into a Command or Event\n *\n * @param buffer - The buffer containing the serialized message\n * @returns The deserialized message\n * @throws Error if the buffer cannot be deserialized\n */\nexport function deserialize<T extends Command | Event>(buffer: Buffer): T {\n try {\n const json = buffer.toString('utf-8');\n const envelope = JSON.parse(json, dateReviver) as PubSubMessageEnvelope;\n\n const message = {\n type: envelope.type,\n data: envelope.data,\n ...(envelope.metadata ? { metadata: envelope.metadata } : {}),\n } as Message;\n\n return message as T;\n } catch (error) {\n throw new Error(\n `Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Attach a message ID to a message for idempotency tracking\n *\n * @param message - The message to attach ID to\n * @param messageId - The message ID\n * @returns The message with the ID attached\n */\nexport function attachMessageId<T extends Message>(\n message: T,\n messageId: string,\n): T & { __messageId: string } {\n return {\n ...message,\n __messageId: messageId,\n };\n}\n\n/**\n * Extract message ID from a message\n *\n * @param message - The message to extract ID from\n * @returns The message ID if present, undefined otherwise\n */\nexport function extractMessageId(message: Message): string | undefined {\n return '__messageId' in message\n ? (message as { __messageId: string }).__messageId\n : undefined;\n}\n","import type { PubSub, Subscription, Topic } from '@google-cloud/pubsub';\nimport type { SubscriptionOptions } from './types';\n\n/**\n * Get command topic name\n */\nexport function getCommandTopicName(\n commandType: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}`;\n}\n\n/**\n * Get event topic name\n */\nexport function getEventTopicName(eventType: string, prefix = 'emmett'): string {\n return `${prefix}-evt-${eventType}`;\n}\n\n/**\n * Get command subscription name\n */\nexport function getCommandSubscriptionName(\n commandType: string,\n instanceId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}-${instanceId}`;\n}\n\n/**\n * Get event subscription name\n */\nexport function getEventSubscriptionName(\n eventType: string,\n subscriptionId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-evt-${eventType}-${subscriptionId}`;\n}\n\n/**\n * Get or create a topic\n *\n * @param pubsub - PubSub client\n * @param topicName - Name of the topic\n * @returns The topic instance\n */\nexport async function getOrCreateTopic(\n pubsub: PubSub,\n topicName: string,\n): Promise<Topic> {\n const topic = pubsub.topic(topicName);\n\n try {\n const [exists] = await topic.exists();\n\n if (!exists) {\n try {\n await topic.create();\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return topic;\n } catch (error) {\n throw new Error(\n `Failed to get or create topic ${topicName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Get or create a subscription\n *\n * @param topic - The topic to subscribe to\n * @param subscriptionName - Name of the subscription\n * @param options - Subscription options\n * @returns The subscription instance\n */\nexport async function getOrCreateSubscription(\n topic: Topic,\n subscriptionName: string,\n options?: SubscriptionOptions,\n): Promise<Subscription> {\n const subscription = topic.subscription(subscriptionName);\n\n try {\n const [exists] = await subscription.exists();\n\n if (!exists) {\n const config = {\n ...(options?.ackDeadlineSeconds && {\n ackDeadlineSeconds: options.ackDeadlineSeconds,\n }),\n ...(options?.retryPolicy && {\n retryPolicy: {\n ...(options.retryPolicy.minimumBackoff && {\n minimumBackoff: options.retryPolicy.minimumBackoff,\n }),\n ...(options.retryPolicy.maximumBackoff && {\n maximumBackoff: options.retryPolicy.maximumBackoff,\n }),\n },\n }),\n ...(options?.deadLetterPolicy && {\n deadLetterPolicy: {\n ...(options.deadLetterPolicy.deadLetterTopic && {\n deadLetterTopic: options.deadLetterPolicy.deadLetterTopic,\n }),\n ...(options.deadLetterPolicy.maxDeliveryAttempts && {\n maxDeliveryAttempts: options.deadLetterPolicy.maxDeliveryAttempts,\n }),\n },\n }),\n };\n\n try {\n await subscription.create(config);\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return subscription;\n } catch (error) {\n throw new Error(\n `Failed to get or create subscription ${subscriptionName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete a subscription\n *\n * @param subscription - The subscription to delete\n */\nexport async function deleteSubscription(\n subscription: Subscription,\n): Promise<void> {\n try {\n const [exists] = await subscription.exists();\n\n if (exists) {\n await subscription.delete();\n }\n } catch (error) {\n // Log but don't throw - cleanup is best effort\n console.warn(\n `Failed to delete subscription: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete multiple subscriptions\n *\n * @param subscriptions - Array of subscriptions to delete\n */\nexport async function deleteSubscriptions(\n subscriptions: Subscription[],\n): Promise<void> {\n await Promise.all(subscriptions.map((sub) => deleteSubscription(sub)));\n}\n","import { randomUUID as cryptoRandomUUID } from 'crypto';\n\n/**\n * Generate a random UUID\n */\nexport function generateUUID(): string {\n return cryptoRandomUUID();\n}\n\n/**\n * Validate that a string is not empty\n */\nexport function assertNotEmptyString(\n value: unknown,\n name: string,\n): asserts value is string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new Error(`${name} must be a non-empty string`);\n }\n}\n\n/**\n * Validate that a number is positive\n */\nexport function assertPositiveNumber(\n value: unknown,\n name: string,\n): asserts value is number {\n if (typeof value !== 'number' || value <= 0 || Number.isNaN(value)) {\n throw new Error(`${name} must be a positive number`);\n }\n}\n","import type { PubSub, Topic } from '@google-cloud/pubsub';\nimport type { Message } from '@event-driven-io/emmett';\nimport type { ScheduledMessageInfo } from './types';\nimport { serialize } from './serialization';\n\n/**\n * Schedule options for messages\n */\nexport type ScheduleOptions = { afterInMs: number } | { at: Date };\n\n/**\n * Scheduled message returned from dequeue\n */\nexport interface ScheduledMessage {\n message: Message;\n options?: ScheduleOptions;\n}\n\n/**\n * Scheduler configuration\n */\nexport interface SchedulerConfig {\n /**\n * Whether running in emulator mode\n */\n useEmulator: boolean;\n\n /**\n * PubSub client instance\n */\n pubsub: PubSub;\n\n /**\n * Topic for scheduled messages (optional, created if needed)\n */\n scheduledTopic?: Topic;\n\n /**\n * Topic prefix for naming\n * @default \"emmett\"\n */\n topicPrefix?: string;\n}\n\n/**\n * Calculate scheduled time from options\n *\n * @param options - Schedule options (afterInMs or at)\n * @returns The calculated scheduled time\n */\nexport function calculateScheduledTime(options?: ScheduleOptions): Date {\n if (!options) {\n return new Date();\n }\n\n if ('afterInMs' in options) {\n const now = new Date();\n return new Date(now.getTime() + options.afterInMs);\n }\n\n if ('at' in options) {\n return options.at;\n }\n\n return new Date();\n}\n\n/**\n * Filter messages that are ready for delivery\n *\n * @param pending - Array of pending scheduled messages\n * @param now - Current time\n * @returns Messages ready for delivery\n */\nexport function filterReadyMessages(\n pending: ScheduledMessageInfo[],\n now: Date,\n): ScheduledMessageInfo[] {\n return pending.filter((msg) => msg.scheduledAt <= now);\n}\n\n/**\n * Message scheduler with dual mode support (production/emulator)\n */\nexport class MessageScheduler {\n private pendingMessages: ScheduledMessageInfo[] = [];\n private readonly useEmulator: boolean;\n private readonly pubsub: PubSub;\n private readonly topicPrefix: string;\n private scheduledTopic?: Topic;\n\n constructor(config: SchedulerConfig) {\n this.useEmulator = config.useEmulator;\n this.pubsub = config.pubsub;\n this.scheduledTopic = config.scheduledTopic;\n this.topicPrefix = config.topicPrefix ?? 'emmett';\n }\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Publishes to PubSub with publishTime attribute\n * In emulator mode: Stores in memory for later dequeue (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param options - When to deliver the message\n */\n async schedule(message: Message, options?: ScheduleOptions): Promise<void> {\n const scheduledAt = calculateScheduledTime(options);\n\n if (this.useEmulator) {\n // Emulator mode: store in memory\n this.pendingMessages.push({\n message,\n options,\n scheduledAt,\n });\n } else {\n // Production mode: publish to PubSub with publishTime attribute\n await this.publishScheduledMessage(message, scheduledAt);\n }\n }\n\n /**\n * Dequeue ready scheduled messages (emulator mode only)\n *\n * Returns messages whose scheduled time has passed and removes them from pending queue\n *\n * @returns Array of scheduled messages ready for delivery\n */\n dequeue(): ScheduledMessage[] {\n if (!this.useEmulator) {\n // In production mode, PubSub handles scheduling, so dequeue returns empty\n return [];\n }\n\n const now = new Date();\n const ready = filterReadyMessages(this.pendingMessages, now);\n\n // Remove ready messages from pending queue\n this.pendingMessages = this.pendingMessages.filter(\n (msg) => msg.scheduledAt > now,\n );\n\n // Convert to ScheduledMessage format\n return ready.map((info) => ({\n message: info.message,\n options: info.options,\n }));\n }\n\n /**\n * Publish a scheduled message to PubSub (production mode)\n *\n * @param message - The message to publish\n * @param scheduledAt - When the message should be delivered\n */\n private async publishScheduledMessage(\n message: Message,\n scheduledAt: Date,\n ): Promise<void> {\n try {\n // Get or create the scheduled messages topic\n if (!this.scheduledTopic) {\n const topicName = `${this.topicPrefix}-scheduled-messages`;\n this.scheduledTopic = this.pubsub.topic(topicName);\n\n const [exists] = await this.scheduledTopic.exists();\n if (!exists) {\n await this.scheduledTopic.create();\n }\n }\n\n // Serialize the message\n const buffer = serialize(message);\n\n // Publish with custom attributes including publish time\n await this.scheduledTopic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n publishTime: scheduledAt.toISOString(),\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish scheduled message ${message.type}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n /**\n * Get count of pending scheduled messages (emulator mode only)\n *\n * @returns Number of pending scheduled messages\n */\n getPendingCount(): number {\n return this.useEmulator ? this.pendingMessages.length : 0;\n }\n\n /**\n * Clear all pending scheduled messages (emulator mode only, useful for testing)\n */\n clearPending(): void {\n if (this.useEmulator) {\n this.pendingMessages = [];\n }\n }\n}\n","import type { Message as PubSubMessage, Subscription } from '@google-cloud/pubsub';\nimport type {\n AnyMessage,\n Command,\n Event,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport { deserialize } from './serialization';\n\n/**\n * Determine if an error should trigger a retry (nack) or be considered permanent (ack)\n *\n * @param error - The error to classify\n * @returns true if the error is retriable (should nack), false if permanent (should ack)\n */\nexport function shouldRetry(error: unknown): boolean {\n if (!(error instanceof Error)) {\n // Unknown error types - retry to be safe\n return true;\n }\n\n const errorMessage = error.message.toLowerCase();\n\n // Network/timeout errors - retry\n if (\n errorMessage.includes('network') ||\n errorMessage.includes('timeout') ||\n errorMessage.includes('econnrefused') ||\n errorMessage.includes('enotfound') ||\n errorMessage.includes('unavailable')\n ) {\n return true;\n }\n\n // EmmettError and validation errors - don't retry (business logic errors)\n if (error instanceof EmmettError) {\n return false;\n }\n\n if (\n errorMessage.includes('validation') ||\n errorMessage.includes('invalid') ||\n errorMessage.includes('not found') ||\n errorMessage.includes('already exists')\n ) {\n return false;\n }\n\n // Default to retry for unknown errors\n return true;\n}\n\n/**\n * Process an incoming command message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param commandType - The command type being processed\n * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleCommandMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n commandType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this command type\n const commandHandlers = handlers.get(commandType);\n\n if (!commandHandlers || commandHandlers.length === 0) {\n throw new EmmettError(\n `No handler registered for command ${commandType}!`,\n );\n }\n\n // Commands must have exactly one handler\n if (commandHandlers.length > 1) {\n throw new EmmettError(\n `Multiple handlers registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Deserialize the command\n const command = deserialize<Command>(message.data);\n\n // Execute the handler\n const handler = commandHandlers[0];\n await handler(command);\n\n return 'ack';\n } catch (error) {\n console.error(\n `Error handling command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // Determine if we should retry\n if (shouldRetry(error)) {\n console.info(\n `Nacking command ${commandType} for retry (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n } else {\n console.warn(\n `Acking command ${commandType} despite error (permanent failure)`,\n );\n return 'ack';\n }\n }\n}\n\n/**\n * Process an incoming event message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param eventType - The event type being processed\n * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleEventMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n eventType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this event type\n const eventHandlers = handlers.get(eventType);\n\n if (!eventHandlers || eventHandlers.length === 0) {\n // Events without handlers are silently ignored (valid scenario)\n console.debug(`No handlers registered for event ${eventType}, skipping`);\n return 'ack';\n }\n\n // Deserialize the event\n const event = deserialize<Event>(message.data);\n\n // Execute all handlers sequentially\n for (const handler of eventHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(\n `Error in event handler for ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // If any handler fails with a retriable error, nack the whole message\n if (shouldRetry(error)) {\n console.info(\n `Nacking event ${eventType} for retry due to handler failure (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n }\n // Otherwise continue to next handler\n console.warn(\n `Continuing event ${eventType} processing despite handler error (permanent failure)`,\n );\n }\n }\n\n return 'ack';\n } catch (error) {\n // Error deserializing or other unexpected error\n console.error(\n `Error handling event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n if (shouldRetry(error)) {\n return 'nack';\n } else {\n return 'ack';\n }\n }\n}\n\n/**\n * Create a message listener for a PubSub subscription\n *\n * @param subscription - The PubSub subscription to listen on\n * @param messageType - The message type (command or event type)\n * @param kind - Whether this is a command or event\n * @param handlers - Map of message type to handlers\n */\nexport function createMessageListener(\n subscription: Subscription,\n messageType: string,\n kind: 'command' | 'event',\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n): void {\n subscription.on('message', async (message: PubSubMessage) => {\n try {\n // Route to appropriate handler based on kind\n const result =\n kind === 'command'\n ? await handleCommandMessage(message, handlers, messageType)\n : await handleEventMessage(message, handlers, messageType);\n\n // Acknowledge or nack based on result\n if (result === 'ack') {\n message.ack();\n } else {\n message.nack();\n }\n } catch (error) {\n // Unexpected error in listener itself - log and nack\n console.error(\n `Unexpected error in message listener for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n message.nack();\n }\n });\n\n subscription.on('error', (error) => {\n console.error(\n `Subscription error for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n}\n","import type {\n AnyMessage,\n Command,\n CommandProcessor,\n Event,\n EventSubscription,\n Message,\n MessageBus,\n ScheduledMessageProcessor,\n SingleMessageHandler,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport type {\n PubSubMessageBusConfig,\n PubSubMessageBusLifecycle,\n SubscriptionInfo,\n} from './types';\nimport type { ScheduledMessage } from './scheduler';\nimport { MessageScheduler } from './scheduler';\nimport { serialize } from './serialization';\nimport {\n getCommandSubscriptionName,\n getCommandTopicName,\n getEventSubscriptionName,\n getEventTopicName,\n getOrCreateSubscription,\n getOrCreateTopic,\n deleteSubscriptions,\n} from './topicManager';\nimport { createMessageListener } from './messageHandler';\nimport { generateUUID } from './utils';\n\n/**\n * Determine message kind based on message type naming convention (fallback)\n *\n * @param messageType - The message type string\n * @returns 'command' if type contains 'Command', otherwise 'event'\n */\nfunction determineMessageKindFallback(messageType: string): 'command' | 'event' {\n const typeStr = messageType.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Create a Google Cloud Pub/Sub based message bus for Emmett\n *\n * @param config - Configuration for the PubSub message bus\n * @returns A message bus implementation using Google Cloud Pub/Sub\n *\n * @example\n * ```typescript\n * import { PubSub } from '@google-cloud/pubsub';\n * import { getPubSubMessageBus } from '@emmett-community/emmett-google-pubsub';\n *\n * const pubsub = new PubSub({ projectId: 'my-project' });\n * const messageBus = getPubSubMessageBus({ pubsub });\n *\n * // Register handlers\n * messageBus.handle(async (command) => {\n * // Handle command\n * }, 'MyCommand');\n *\n * // Subscribe to events\n * messageBus.subscribe(async (event) => {\n * // Handle event\n * }, 'MyEvent');\n *\n * // Start the message bus\n * await messageBus.start();\n *\n * // Send commands and publish events\n * await messageBus.send({ type: 'MyCommand', data: { ... } });\n * await messageBus.publish({ type: 'MyEvent', data: { ... } });\n *\n * // Close gracefully\n * await messageBus.close();\n * ```\n */\nexport function getPubSubMessageBus(\n config: PubSubMessageBusConfig,\n): MessageBus &\n EventSubscription &\n CommandProcessor &\n ScheduledMessageProcessor &\n PubSubMessageBusLifecycle {\n // Internal state\n const instanceId = config.instanceId ?? generateUUID();\n const topicPrefix = config.topicPrefix ?? 'emmett';\n const autoCreateResources = config.autoCreateResources ?? true;\n const cleanupOnClose = config.cleanupOnClose ?? false;\n const closePubSubClient = config.closePubSubClient;\n\n // Map of message type to handlers\n const handlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n\n // Map of subscription ID to specific handler (for event subscriptions)\n const subscriptionHandlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>\n >();\n\n // Map of message type to subscription IDs (for events with multiple subscriptions)\n const eventSubscriptionIds = new Map<string, string[]>();\n\n // Track which message types are commands vs events\n const commandTypes = new Set<string>();\n const eventTypes = new Set<string>();\n\n // Active subscriptions\n const subscriptions: SubscriptionInfo[] = [];\n\n // Scheduler for delayed messages\n const scheduler = new MessageScheduler({\n useEmulator: config.useEmulator ?? false,\n pubsub: config.pubsub,\n topicPrefix,\n });\n\n // Lifecycle state\n let started = false;\n\n /**\n * Determine message kind based on how it was registered\n *\n * @param messageType - The message type string\n * @returns 'command' if registered with handle(), 'event' if registered with subscribe()\n */\n function determineMessageKind(messageType: string): 'command' | 'event' {\n // Check explicit registration first\n if (commandTypes.has(messageType)) {\n return 'command';\n }\n if (eventTypes.has(messageType)) {\n return 'event';\n }\n // Fallback to name-based heuristic\n return determineMessageKindFallback(messageType);\n }\n\n /**\n * Create subscription for a specific message type\n *\n * @param messageType - The message type\n * @param kind - Whether this is a command or event\n * @param subscriptionId - Optional subscription ID for events\n */\n async function createSubscriptionForType(\n messageType: string,\n kind: 'command' | 'event',\n subscriptionId?: string,\n ): Promise<void> {\n // Get topic name based on kind\n const topicName =\n kind === 'command'\n ? getCommandTopicName(messageType, topicPrefix)\n : getEventTopicName(messageType, topicPrefix);\n\n // Get or create topic\n const topic = await getOrCreateTopic(config.pubsub, topicName);\n\n // Get subscription name\n const subName =\n kind === 'command'\n ? getCommandSubscriptionName(messageType, instanceId, topicPrefix)\n : getEventSubscriptionName(\n messageType,\n subscriptionId ?? instanceId,\n topicPrefix,\n );\n\n // Create subscription\n const subscription = await getOrCreateSubscription(\n topic,\n subName,\n config.subscriptionOptions,\n );\n\n // Create message listener with appropriate handlers\n if (kind === 'event' && subscriptionId) {\n // For events, create a map with only this subscription's handler\n const handler = subscriptionHandlers.get(subscriptionId);\n if (handler) {\n const singleHandlerMap = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n singleHandlerMap.set(messageType, [handler]);\n createMessageListener(subscription, messageType, kind, singleHandlerMap);\n }\n } else {\n // For commands, use the handlers map as before\n createMessageListener(subscription, messageType, kind, handlers);\n }\n\n // Track subscription\n subscriptions.push({\n topic,\n subscription,\n messageType,\n kind,\n });\n }\n\n /**\n * Publish a message to a PubSub topic\n *\n * @param message - The message to publish\n * @param kind - Whether this is a command or event\n */\n async function publishMessage(\n message: Message,\n kind: 'command' | 'event',\n ): Promise<void> {\n // Publishing without start() is allowed (producer-only mode)\n // start() is only required for consumers (handlers/subscribers)\n\n // Get topic name\n const topicName =\n kind === 'command'\n ? getCommandTopicName(message.type, topicPrefix)\n : getEventTopicName(message.type, topicPrefix);\n\n try {\n // Get topic\n const topic = config.pubsub.topic(topicName);\n\n // Check if topic exists if auto-create is disabled\n if (!autoCreateResources) {\n const [exists] = await topic.exists();\n if (!exists) {\n throw new Error(\n `Topic ${topicName} does not exist and autoCreateResources is disabled`,\n );\n }\n } else {\n // Create topic if it doesn't exist\n const [exists] = await topic.exists();\n if (!exists) {\n await topic.create();\n }\n }\n\n // Serialize message\n const buffer = serialize(message);\n\n // Publish\n await topic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n messageKind: kind,\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish ${kind} ${message.type} to topic ${topicName}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n // Return the message bus implementation\n return {\n // ===== MessageBus Interface =====\n\n /**\n * Send a command to the message bus\n *\n * Commands are routed to exactly one handler via PubSub topics\n *\n * @param command - The command to send\n */\n async send<CommandType extends Command>(\n command: CommandType,\n ): Promise<void> {\n await publishMessage(command, 'command');\n },\n\n /**\n * Publish an event to the message bus\n *\n * Events are delivered to all registered subscribers via PubSub topics\n *\n * @param event - The event to publish\n */\n async publish<EventType extends Event>(event: EventType): Promise<void> {\n await publishMessage(event, 'event');\n },\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Uses PubSub native scheduling\n * In emulator mode: Stores in memory (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param when - When to deliver the message (afterInMs or at)\n */\n schedule<MessageType extends Message>(\n message: MessageType,\n when?: { afterInMs: number } | { at: Date },\n ): void {\n scheduler.schedule(message, when);\n },\n\n // ===== CommandProcessor Interface =====\n\n /**\n * Register a command handler\n *\n * Commands must have exactly one handler. Attempting to register multiple\n * handlers for the same command will throw an EmmettError.\n *\n * @param commandHandler - The handler function\n * @param commandTypes - Command types this handler processes\n * @throws EmmettError if a handler is already registered for any command type\n *\n * @example\n * ```typescript\n * messageBus.handle(\n * async (command: AddProductItemCommand) => {\n * // Handle command\n * },\n * 'AddProductItem'\n * );\n * ```\n */\n handle<CommandType extends Command>(\n commandHandler: SingleMessageHandler<CommandType>,\n ...commandTypeNames: CommandType['type'][]\n ): void {\n for (const commandType of commandTypeNames) {\n // Validate no duplicate handlers\n if (handlers.has(commandType)) {\n throw new EmmettError(\n `Handler already registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Track as command type\n commandTypes.add(commandType);\n\n // Store handler (single handler in array for consistency)\n handlers.set(commandType, [\n commandHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(commandType, 'command').catch((error) => {\n console.error(\n `Failed to create subscription for command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n }\n }\n },\n\n // ===== EventSubscription Interface =====\n\n /**\n * Subscribe to events\n *\n * Events can have multiple subscribers. Each subscription gets its own\n * PubSub subscription to ensure all handlers receive all events.\n *\n * @param eventHandler - The handler function\n * @param eventTypes - Event types to subscribe to\n *\n * @example\n * ```typescript\n * messageBus.subscribe(\n * async (event: ProductItemAddedEvent) => {\n * // Handle event\n * },\n * 'ProductItemAdded'\n * );\n * ```\n */\n subscribe<EventType extends Event>(\n eventHandler: SingleMessageHandler<EventType>,\n ...eventTypeNames: EventType['type'][]\n ): void {\n for (const eventType of eventTypeNames) {\n // Track as event type\n eventTypes.add(eventType);\n\n // Generate unique subscription ID for this subscriber\n const subscriptionId = generateUUID();\n\n // Store handler associated with this subscription ID\n subscriptionHandlers.set(\n subscriptionId,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n );\n\n // Get existing handlers or create new array\n const existing = handlers.get(eventType) ?? [];\n\n // Add handler to array (for compatibility)\n handlers.set(eventType, [\n ...existing,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // Track subscription ID for this event type\n const existingIds = eventSubscriptionIds.get(eventType) ?? [];\n eventSubscriptionIds.set(eventType, [...existingIds, subscriptionId]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(eventType, 'event', subscriptionId).catch(\n (error) => {\n console.error(\n `Failed to create subscription for event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n },\n );\n }\n }\n },\n\n // ===== ScheduledMessageProcessor Interface =====\n\n /**\n * Dequeue scheduled messages that are ready for delivery\n *\n * Only used in emulator mode. In production, PubSub handles scheduling.\n *\n * @returns Array of scheduled messages ready for delivery\n *\n * @example\n * ```typescript\n * // In emulator mode, periodically call dequeue\n * setInterval(() => {\n * const ready = messageBus.dequeue();\n * for (const { message } of ready) {\n * // Process message\n * }\n * }, 1000);\n * ```\n */\n dequeue(): ScheduledMessage[] {\n return scheduler.dequeue();\n },\n\n // ===== PubSubMessageBusLifecycle Interface =====\n\n /**\n * Start the message bus\n *\n * Creates topics and subscriptions for all registered handlers and begins\n * listening for messages.\n *\n * This method is idempotent - calling it multiple times is safe.\n *\n * @throws Error if topic/subscription creation fails\n *\n * @example\n * ```typescript\n * // Register all handlers first\n * messageBus.handle(commandHandler, 'MyCommand');\n * messageBus.subscribe(eventHandler, 'MyEvent');\n *\n * // Then start\n * await messageBus.start();\n * ```\n */\n async start(): Promise<void> {\n if (started) {\n console.debug('Message bus already started, skipping');\n return;\n }\n\n console.info('Starting PubSub message bus...');\n\n try {\n // Create subscriptions for all registered handlers\n const subscriptionPromises: Promise<void>[] = [];\n\n for (const [messageType] of handlers.entries()) {\n const kind = determineMessageKind(messageType);\n\n if (kind === 'command') {\n // Commands: one subscription per instance\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'command'),\n );\n } else {\n // Events: one subscription per handler (multiple allowed)\n const subIds = eventSubscriptionIds.get(messageType) ?? [\n instanceId,\n ];\n for (const subId of subIds) {\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'event', subId),\n );\n }\n }\n }\n\n // Wait for all subscriptions to be created\n await Promise.all(subscriptionPromises);\n\n started = true;\n\n console.info(\n `PubSub message bus started with ${subscriptions.length} subscription(s)`,\n );\n } catch (error) {\n throw new Error(\n `Failed to start message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Close the message bus gracefully\n *\n * Stops accepting new messages, waits for in-flight messages to complete,\n * optionally cleans up subscriptions, and closes the PubSub client.\n *\n * @throws Error if cleanup fails\n *\n * @example\n * ```typescript\n * // Graceful shutdown\n * process.on('SIGTERM', async () => {\n * await messageBus.close();\n * process.exit(0);\n * });\n * ```\n */\n async close(): Promise<void> {\n console.info('Closing PubSub message bus...');\n\n try {\n // Only cleanup subscriptions if started\n if (started) {\n // Stop accepting new messages\n for (const { subscription } of subscriptions) {\n subscription.removeAllListeners('message');\n subscription.removeAllListeners('error');\n }\n\n // Wait for in-flight messages with timeout (30 seconds)\n const timeout = 30000;\n const waitStart = Date.now();\n let timeoutId: NodeJS.Timeout | null = null;\n\n // Close all subscriptions\n const closePromises = subscriptions.map(({ subscription }) =>\n subscription.close(),\n );\n try {\n await Promise.race([\n Promise.all(closePromises),\n new Promise((resolve) => {\n timeoutId = setTimeout(resolve, timeout);\n }),\n ]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n\n const waitTime = Date.now() - waitStart;\n if (waitTime >= timeout) {\n console.warn(\n `Timeout waiting for in-flight messages after ${timeout}ms`,\n );\n }\n\n // Cleanup subscriptions if configured\n if (cleanupOnClose) {\n console.info('Cleaning up subscriptions...');\n await deleteSubscriptions(subscriptions.map((s) => s.subscription));\n }\n\n started = false;\n }\n\n // Always close PubSub client (even if not started, for producer-only mode)\n if (closePubSubClient !== false) {\n await config.pubsub.close();\n }\n\n console.info('PubSub message bus closed');\n } catch (error) {\n throw new Error(\n `Failed to close message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Check if the message bus is started\n *\n * @returns true if the message bus is started and ready to process messages\n */\n isStarted(): boolean {\n return started;\n },\n };\n}\n"]}
package/dist/index.mjs CHANGED
@@ -733,13 +733,22 @@ function getPubSubMessageBus(config) {
733
733
  }
734
734
  const timeout = 3e4;
735
735
  const waitStart = Date.now();
736
+ let timeoutId = null;
736
737
  const closePromises = subscriptions.map(
737
738
  ({ subscription }) => subscription.close()
738
739
  );
739
- await Promise.race([
740
- Promise.all(closePromises),
741
- new Promise((resolve) => setTimeout(resolve, timeout))
742
- ]);
740
+ try {
741
+ await Promise.race([
742
+ Promise.all(closePromises),
743
+ new Promise((resolve) => {
744
+ timeoutId = setTimeout(resolve, timeout);
745
+ })
746
+ ]);
747
+ } finally {
748
+ if (timeoutId) {
749
+ clearTimeout(timeoutId);
750
+ }
751
+ }
743
752
  const waitTime = Date.now() - waitStart;
744
753
  if (waitTime >= timeout) {
745
754
  console.warn(
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/messageBus/serialization.ts","../src/messageBus/topicManager.ts","../src/messageBus/utils.ts","../src/messageBus/scheduler.ts","../src/messageBus/messageHandler.ts","../src/messageBus/pubsubMessageBus.ts"],"names":["cryptoRandomUUID","EmmettError"],"mappings":"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","file":"index.mjs","sourcesContent":["import { randomUUID } from 'crypto';\nimport type { Command, Event, Message } from '@event-driven-io/emmett';\nimport type { PubSubMessageEnvelope } from './types';\n\n/**\n * Date marker for JSON serialization\n */\ninterface DateMarker {\n __type: 'Date';\n value: string;\n}\n\n/**\n * Recursively transform Dates to DateMarkers in an object\n */\nfunction transformDatesToMarkers(obj: unknown): unknown {\n if (obj instanceof Date) {\n return {\n __type: 'Date',\n value: obj.toISOString(),\n } satisfies DateMarker;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(transformDatesToMarkers);\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = transformDatesToMarkers(value);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Check if a value is a Date marker\n */\nfunction isDateMarker(value: unknown): value is DateMarker {\n return (\n typeof value === 'object' &&\n value !== null &&\n '__type' in value &&\n (value as DateMarker).__type === 'Date' &&\n 'value' in value &&\n typeof (value as DateMarker).value === 'string'\n );\n}\n\n/**\n * JSON reviver that converts Date markers back to Dates\n */\nfunction dateReviver(_key: string, value: unknown): unknown {\n if (isDateMarker(value)) {\n return new Date(value.value);\n }\n return value;\n}\n\n/**\n * Determine if a message is a command or event based on its type\n */\nfunction getMessageKind(message: Message): 'command' | 'event' {\n // Simple heuristic: if the message type contains \"Command\", it's a command\n // Otherwise, it's an event\n // This can be customized based on your naming conventions\n const typeStr = message.type.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Serialize a Command or Event to a Buffer for PubSub transport\n *\n * @param message - The message to serialize\n * @returns Buffer containing the serialized message envelope\n */\nexport function serialize(message: Command | Event): Buffer {\n const envelope = {\n type: message.type,\n kind: getMessageKind(message),\n data: transformDatesToMarkers(message.data),\n metadata: 'metadata' in message ? transformDatesToMarkers(message.metadata) : undefined,\n timestamp: new Date().toISOString(),\n messageId: randomUUID(),\n };\n\n const json = JSON.stringify(envelope);\n return Buffer.from(json);\n}\n\n/**\n * Deserialize a Buffer from PubSub into a Command or Event\n *\n * @param buffer - The buffer containing the serialized message\n * @returns The deserialized message\n * @throws Error if the buffer cannot be deserialized\n */\nexport function deserialize<T extends Command | Event>(buffer: Buffer): T {\n try {\n const json = buffer.toString('utf-8');\n const envelope = JSON.parse(json, dateReviver) as PubSubMessageEnvelope;\n\n const message = {\n type: envelope.type,\n data: envelope.data,\n ...(envelope.metadata ? { metadata: envelope.metadata } : {}),\n } as Message;\n\n return message as T;\n } catch (error) {\n throw new Error(\n `Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Attach a message ID to a message for idempotency tracking\n *\n * @param message - The message to attach ID to\n * @param messageId - The message ID\n * @returns The message with the ID attached\n */\nexport function attachMessageId<T extends Message>(\n message: T,\n messageId: string,\n): T & { __messageId: string } {\n return {\n ...message,\n __messageId: messageId,\n };\n}\n\n/**\n * Extract message ID from a message\n *\n * @param message - The message to extract ID from\n * @returns The message ID if present, undefined otherwise\n */\nexport function extractMessageId(message: Message): string | undefined {\n return '__messageId' in message\n ? (message as { __messageId: string }).__messageId\n : undefined;\n}\n","import type { PubSub, Subscription, Topic } from '@google-cloud/pubsub';\nimport type { SubscriptionOptions } from './types';\n\n/**\n * Get command topic name\n */\nexport function getCommandTopicName(\n commandType: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}`;\n}\n\n/**\n * Get event topic name\n */\nexport function getEventTopicName(eventType: string, prefix = 'emmett'): string {\n return `${prefix}-evt-${eventType}`;\n}\n\n/**\n * Get command subscription name\n */\nexport function getCommandSubscriptionName(\n commandType: string,\n instanceId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}-${instanceId}`;\n}\n\n/**\n * Get event subscription name\n */\nexport function getEventSubscriptionName(\n eventType: string,\n subscriptionId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-evt-${eventType}-${subscriptionId}`;\n}\n\n/**\n * Get or create a topic\n *\n * @param pubsub - PubSub client\n * @param topicName - Name of the topic\n * @returns The topic instance\n */\nexport async function getOrCreateTopic(\n pubsub: PubSub,\n topicName: string,\n): Promise<Topic> {\n const topic = pubsub.topic(topicName);\n\n try {\n const [exists] = await topic.exists();\n\n if (!exists) {\n try {\n await topic.create();\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return topic;\n } catch (error) {\n throw new Error(\n `Failed to get or create topic ${topicName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Get or create a subscription\n *\n * @param topic - The topic to subscribe to\n * @param subscriptionName - Name of the subscription\n * @param options - Subscription options\n * @returns The subscription instance\n */\nexport async function getOrCreateSubscription(\n topic: Topic,\n subscriptionName: string,\n options?: SubscriptionOptions,\n): Promise<Subscription> {\n const subscription = topic.subscription(subscriptionName);\n\n try {\n const [exists] = await subscription.exists();\n\n if (!exists) {\n const config = {\n ...(options?.ackDeadlineSeconds && {\n ackDeadlineSeconds: options.ackDeadlineSeconds,\n }),\n ...(options?.retryPolicy && {\n retryPolicy: {\n ...(options.retryPolicy.minimumBackoff && {\n minimumBackoff: options.retryPolicy.minimumBackoff,\n }),\n ...(options.retryPolicy.maximumBackoff && {\n maximumBackoff: options.retryPolicy.maximumBackoff,\n }),\n },\n }),\n ...(options?.deadLetterPolicy && {\n deadLetterPolicy: {\n ...(options.deadLetterPolicy.deadLetterTopic && {\n deadLetterTopic: options.deadLetterPolicy.deadLetterTopic,\n }),\n ...(options.deadLetterPolicy.maxDeliveryAttempts && {\n maxDeliveryAttempts: options.deadLetterPolicy.maxDeliveryAttempts,\n }),\n },\n }),\n };\n\n try {\n await subscription.create(config);\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return subscription;\n } catch (error) {\n throw new Error(\n `Failed to get or create subscription ${subscriptionName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete a subscription\n *\n * @param subscription - The subscription to delete\n */\nexport async function deleteSubscription(\n subscription: Subscription,\n): Promise<void> {\n try {\n const [exists] = await subscription.exists();\n\n if (exists) {\n await subscription.delete();\n }\n } catch (error) {\n // Log but don't throw - cleanup is best effort\n console.warn(\n `Failed to delete subscription: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete multiple subscriptions\n *\n * @param subscriptions - Array of subscriptions to delete\n */\nexport async function deleteSubscriptions(\n subscriptions: Subscription[],\n): Promise<void> {\n await Promise.all(subscriptions.map((sub) => deleteSubscription(sub)));\n}\n","import { randomUUID as cryptoRandomUUID } from 'crypto';\n\n/**\n * Generate a random UUID\n */\nexport function generateUUID(): string {\n return cryptoRandomUUID();\n}\n\n/**\n * Validate that a string is not empty\n */\nexport function assertNotEmptyString(\n value: unknown,\n name: string,\n): asserts value is string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new Error(`${name} must be a non-empty string`);\n }\n}\n\n/**\n * Validate that a number is positive\n */\nexport function assertPositiveNumber(\n value: unknown,\n name: string,\n): asserts value is number {\n if (typeof value !== 'number' || value <= 0 || Number.isNaN(value)) {\n throw new Error(`${name} must be a positive number`);\n }\n}\n","import type { PubSub, Topic } from '@google-cloud/pubsub';\nimport type { Message } from '@event-driven-io/emmett';\nimport type { ScheduledMessageInfo } from './types';\nimport { serialize } from './serialization';\n\n/**\n * Schedule options for messages\n */\nexport type ScheduleOptions = { afterInMs: number } | { at: Date };\n\n/**\n * Scheduled message returned from dequeue\n */\nexport interface ScheduledMessage {\n message: Message;\n options?: ScheduleOptions;\n}\n\n/**\n * Scheduler configuration\n */\nexport interface SchedulerConfig {\n /**\n * Whether running in emulator mode\n */\n useEmulator: boolean;\n\n /**\n * PubSub client instance\n */\n pubsub: PubSub;\n\n /**\n * Topic for scheduled messages (optional, created if needed)\n */\n scheduledTopic?: Topic;\n\n /**\n * Topic prefix for naming\n * @default \"emmett\"\n */\n topicPrefix?: string;\n}\n\n/**\n * Calculate scheduled time from options\n *\n * @param options - Schedule options (afterInMs or at)\n * @returns The calculated scheduled time\n */\nexport function calculateScheduledTime(options?: ScheduleOptions): Date {\n if (!options) {\n return new Date();\n }\n\n if ('afterInMs' in options) {\n const now = new Date();\n return new Date(now.getTime() + options.afterInMs);\n }\n\n if ('at' in options) {\n return options.at;\n }\n\n return new Date();\n}\n\n/**\n * Filter messages that are ready for delivery\n *\n * @param pending - Array of pending scheduled messages\n * @param now - Current time\n * @returns Messages ready for delivery\n */\nexport function filterReadyMessages(\n pending: ScheduledMessageInfo[],\n now: Date,\n): ScheduledMessageInfo[] {\n return pending.filter((msg) => msg.scheduledAt <= now);\n}\n\n/**\n * Message scheduler with dual mode support (production/emulator)\n */\nexport class MessageScheduler {\n private pendingMessages: ScheduledMessageInfo[] = [];\n private readonly useEmulator: boolean;\n private readonly pubsub: PubSub;\n private readonly topicPrefix: string;\n private scheduledTopic?: Topic;\n\n constructor(config: SchedulerConfig) {\n this.useEmulator = config.useEmulator;\n this.pubsub = config.pubsub;\n this.scheduledTopic = config.scheduledTopic;\n this.topicPrefix = config.topicPrefix ?? 'emmett';\n }\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Publishes to PubSub with publishTime attribute\n * In emulator mode: Stores in memory for later dequeue (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param options - When to deliver the message\n */\n async schedule(message: Message, options?: ScheduleOptions): Promise<void> {\n const scheduledAt = calculateScheduledTime(options);\n\n if (this.useEmulator) {\n // Emulator mode: store in memory\n this.pendingMessages.push({\n message,\n options,\n scheduledAt,\n });\n } else {\n // Production mode: publish to PubSub with publishTime attribute\n await this.publishScheduledMessage(message, scheduledAt);\n }\n }\n\n /**\n * Dequeue ready scheduled messages (emulator mode only)\n *\n * Returns messages whose scheduled time has passed and removes them from pending queue\n *\n * @returns Array of scheduled messages ready for delivery\n */\n dequeue(): ScheduledMessage[] {\n if (!this.useEmulator) {\n // In production mode, PubSub handles scheduling, so dequeue returns empty\n return [];\n }\n\n const now = new Date();\n const ready = filterReadyMessages(this.pendingMessages, now);\n\n // Remove ready messages from pending queue\n this.pendingMessages = this.pendingMessages.filter(\n (msg) => msg.scheduledAt > now,\n );\n\n // Convert to ScheduledMessage format\n return ready.map((info) => ({\n message: info.message,\n options: info.options,\n }));\n }\n\n /**\n * Publish a scheduled message to PubSub (production mode)\n *\n * @param message - The message to publish\n * @param scheduledAt - When the message should be delivered\n */\n private async publishScheduledMessage(\n message: Message,\n scheduledAt: Date,\n ): Promise<void> {\n try {\n // Get or create the scheduled messages topic\n if (!this.scheduledTopic) {\n const topicName = `${this.topicPrefix}-scheduled-messages`;\n this.scheduledTopic = this.pubsub.topic(topicName);\n\n const [exists] = await this.scheduledTopic.exists();\n if (!exists) {\n await this.scheduledTopic.create();\n }\n }\n\n // Serialize the message\n const buffer = serialize(message);\n\n // Publish with custom attributes including publish time\n await this.scheduledTopic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n publishTime: scheduledAt.toISOString(),\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish scheduled message ${message.type}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n /**\n * Get count of pending scheduled messages (emulator mode only)\n *\n * @returns Number of pending scheduled messages\n */\n getPendingCount(): number {\n return this.useEmulator ? this.pendingMessages.length : 0;\n }\n\n /**\n * Clear all pending scheduled messages (emulator mode only, useful for testing)\n */\n clearPending(): void {\n if (this.useEmulator) {\n this.pendingMessages = [];\n }\n }\n}\n","import type { Message as PubSubMessage, Subscription } from '@google-cloud/pubsub';\nimport type {\n AnyMessage,\n Command,\n Event,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport { deserialize } from './serialization';\n\n/**\n * Determine if an error should trigger a retry (nack) or be considered permanent (ack)\n *\n * @param error - The error to classify\n * @returns true if the error is retriable (should nack), false if permanent (should ack)\n */\nexport function shouldRetry(error: unknown): boolean {\n if (!(error instanceof Error)) {\n // Unknown error types - retry to be safe\n return true;\n }\n\n const errorMessage = error.message.toLowerCase();\n\n // Network/timeout errors - retry\n if (\n errorMessage.includes('network') ||\n errorMessage.includes('timeout') ||\n errorMessage.includes('econnrefused') ||\n errorMessage.includes('enotfound') ||\n errorMessage.includes('unavailable')\n ) {\n return true;\n }\n\n // EmmettError and validation errors - don't retry (business logic errors)\n if (error instanceof EmmettError) {\n return false;\n }\n\n if (\n errorMessage.includes('validation') ||\n errorMessage.includes('invalid') ||\n errorMessage.includes('not found') ||\n errorMessage.includes('already exists')\n ) {\n return false;\n }\n\n // Default to retry for unknown errors\n return true;\n}\n\n/**\n * Process an incoming command message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param commandType - The command type being processed\n * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleCommandMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n commandType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this command type\n const commandHandlers = handlers.get(commandType);\n\n if (!commandHandlers || commandHandlers.length === 0) {\n throw new EmmettError(\n `No handler registered for command ${commandType}!`,\n );\n }\n\n // Commands must have exactly one handler\n if (commandHandlers.length > 1) {\n throw new EmmettError(\n `Multiple handlers registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Deserialize the command\n const command = deserialize<Command>(message.data);\n\n // Execute the handler\n const handler = commandHandlers[0];\n await handler(command);\n\n return 'ack';\n } catch (error) {\n console.error(\n `Error handling command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // Determine if we should retry\n if (shouldRetry(error)) {\n console.info(\n `Nacking command ${commandType} for retry (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n } else {\n console.warn(\n `Acking command ${commandType} despite error (permanent failure)`,\n );\n return 'ack';\n }\n }\n}\n\n/**\n * Process an incoming event message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param eventType - The event type being processed\n * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleEventMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n eventType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this event type\n const eventHandlers = handlers.get(eventType);\n\n if (!eventHandlers || eventHandlers.length === 0) {\n // Events without handlers are silently ignored (valid scenario)\n console.debug(`No handlers registered for event ${eventType}, skipping`);\n return 'ack';\n }\n\n // Deserialize the event\n const event = deserialize<Event>(message.data);\n\n // Execute all handlers sequentially\n for (const handler of eventHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(\n `Error in event handler for ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // If any handler fails with a retriable error, nack the whole message\n if (shouldRetry(error)) {\n console.info(\n `Nacking event ${eventType} for retry due to handler failure (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n }\n // Otherwise continue to next handler\n console.warn(\n `Continuing event ${eventType} processing despite handler error (permanent failure)`,\n );\n }\n }\n\n return 'ack';\n } catch (error) {\n // Error deserializing or other unexpected error\n console.error(\n `Error handling event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n if (shouldRetry(error)) {\n return 'nack';\n } else {\n return 'ack';\n }\n }\n}\n\n/**\n * Create a message listener for a PubSub subscription\n *\n * @param subscription - The PubSub subscription to listen on\n * @param messageType - The message type (command or event type)\n * @param kind - Whether this is a command or event\n * @param handlers - Map of message type to handlers\n */\nexport function createMessageListener(\n subscription: Subscription,\n messageType: string,\n kind: 'command' | 'event',\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n): void {\n subscription.on('message', async (message: PubSubMessage) => {\n try {\n // Route to appropriate handler based on kind\n const result =\n kind === 'command'\n ? await handleCommandMessage(message, handlers, messageType)\n : await handleEventMessage(message, handlers, messageType);\n\n // Acknowledge or nack based on result\n if (result === 'ack') {\n message.ack();\n } else {\n message.nack();\n }\n } catch (error) {\n // Unexpected error in listener itself - log and nack\n console.error(\n `Unexpected error in message listener for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n message.nack();\n }\n });\n\n subscription.on('error', (error) => {\n console.error(\n `Subscription error for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n}\n","import type {\n AnyMessage,\n Command,\n CommandProcessor,\n Event,\n EventSubscription,\n Message,\n MessageBus,\n ScheduledMessageProcessor,\n SingleMessageHandler,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport type {\n PubSubMessageBusConfig,\n PubSubMessageBusLifecycle,\n SubscriptionInfo,\n} from './types';\nimport type { ScheduledMessage } from './scheduler';\nimport { MessageScheduler } from './scheduler';\nimport { serialize } from './serialization';\nimport {\n getCommandSubscriptionName,\n getCommandTopicName,\n getEventSubscriptionName,\n getEventTopicName,\n getOrCreateSubscription,\n getOrCreateTopic,\n deleteSubscriptions,\n} from './topicManager';\nimport { createMessageListener } from './messageHandler';\nimport { generateUUID } from './utils';\n\n/**\n * Determine message kind based on message type naming convention (fallback)\n *\n * @param messageType - The message type string\n * @returns 'command' if type contains 'Command', otherwise 'event'\n */\nfunction determineMessageKindFallback(messageType: string): 'command' | 'event' {\n const typeStr = messageType.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Create a Google Cloud Pub/Sub based message bus for Emmett\n *\n * @param config - Configuration for the PubSub message bus\n * @returns A message bus implementation using Google Cloud Pub/Sub\n *\n * @example\n * ```typescript\n * import { PubSub } from '@google-cloud/pubsub';\n * import { getPubSubMessageBus } from '@emmett-community/emmett-google-pubsub';\n *\n * const pubsub = new PubSub({ projectId: 'my-project' });\n * const messageBus = getPubSubMessageBus({ pubsub });\n *\n * // Register handlers\n * messageBus.handle(async (command) => {\n * // Handle command\n * }, 'MyCommand');\n *\n * // Subscribe to events\n * messageBus.subscribe(async (event) => {\n * // Handle event\n * }, 'MyEvent');\n *\n * // Start the message bus\n * await messageBus.start();\n *\n * // Send commands and publish events\n * await messageBus.send({ type: 'MyCommand', data: { ... } });\n * await messageBus.publish({ type: 'MyEvent', data: { ... } });\n *\n * // Close gracefully\n * await messageBus.close();\n * ```\n */\nexport function getPubSubMessageBus(\n config: PubSubMessageBusConfig,\n): MessageBus &\n EventSubscription &\n CommandProcessor &\n ScheduledMessageProcessor &\n PubSubMessageBusLifecycle {\n // Internal state\n const instanceId = config.instanceId ?? generateUUID();\n const topicPrefix = config.topicPrefix ?? 'emmett';\n const autoCreateResources = config.autoCreateResources ?? true;\n const cleanupOnClose = config.cleanupOnClose ?? false;\n const closePubSubClient = config.closePubSubClient;\n\n // Map of message type to handlers\n const handlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n\n // Map of subscription ID to specific handler (for event subscriptions)\n const subscriptionHandlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>\n >();\n\n // Map of message type to subscription IDs (for events with multiple subscriptions)\n const eventSubscriptionIds = new Map<string, string[]>();\n\n // Track which message types are commands vs events\n const commandTypes = new Set<string>();\n const eventTypes = new Set<string>();\n\n // Active subscriptions\n const subscriptions: SubscriptionInfo[] = [];\n\n // Scheduler for delayed messages\n const scheduler = new MessageScheduler({\n useEmulator: config.useEmulator ?? false,\n pubsub: config.pubsub,\n topicPrefix,\n });\n\n // Lifecycle state\n let started = false;\n\n /**\n * Determine message kind based on how it was registered\n *\n * @param messageType - The message type string\n * @returns 'command' if registered with handle(), 'event' if registered with subscribe()\n */\n function determineMessageKind(messageType: string): 'command' | 'event' {\n // Check explicit registration first\n if (commandTypes.has(messageType)) {\n return 'command';\n }\n if (eventTypes.has(messageType)) {\n return 'event';\n }\n // Fallback to name-based heuristic\n return determineMessageKindFallback(messageType);\n }\n\n /**\n * Create subscription for a specific message type\n *\n * @param messageType - The message type\n * @param kind - Whether this is a command or event\n * @param subscriptionId - Optional subscription ID for events\n */\n async function createSubscriptionForType(\n messageType: string,\n kind: 'command' | 'event',\n subscriptionId?: string,\n ): Promise<void> {\n // Get topic name based on kind\n const topicName =\n kind === 'command'\n ? getCommandTopicName(messageType, topicPrefix)\n : getEventTopicName(messageType, topicPrefix);\n\n // Get or create topic\n const topic = await getOrCreateTopic(config.pubsub, topicName);\n\n // Get subscription name\n const subName =\n kind === 'command'\n ? getCommandSubscriptionName(messageType, instanceId, topicPrefix)\n : getEventSubscriptionName(\n messageType,\n subscriptionId ?? instanceId,\n topicPrefix,\n );\n\n // Create subscription\n const subscription = await getOrCreateSubscription(\n topic,\n subName,\n config.subscriptionOptions,\n );\n\n // Create message listener with appropriate handlers\n if (kind === 'event' && subscriptionId) {\n // For events, create a map with only this subscription's handler\n const handler = subscriptionHandlers.get(subscriptionId);\n if (handler) {\n const singleHandlerMap = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n singleHandlerMap.set(messageType, [handler]);\n createMessageListener(subscription, messageType, kind, singleHandlerMap);\n }\n } else {\n // For commands, use the handlers map as before\n createMessageListener(subscription, messageType, kind, handlers);\n }\n\n // Track subscription\n subscriptions.push({\n topic,\n subscription,\n messageType,\n kind,\n });\n }\n\n /**\n * Publish a message to a PubSub topic\n *\n * @param message - The message to publish\n * @param kind - Whether this is a command or event\n */\n async function publishMessage(\n message: Message,\n kind: 'command' | 'event',\n ): Promise<void> {\n // Publishing without start() is allowed (producer-only mode)\n // start() is only required for consumers (handlers/subscribers)\n\n // Get topic name\n const topicName =\n kind === 'command'\n ? getCommandTopicName(message.type, topicPrefix)\n : getEventTopicName(message.type, topicPrefix);\n\n try {\n // Get topic\n const topic = config.pubsub.topic(topicName);\n\n // Check if topic exists if auto-create is disabled\n if (!autoCreateResources) {\n const [exists] = await topic.exists();\n if (!exists) {\n throw new Error(\n `Topic ${topicName} does not exist and autoCreateResources is disabled`,\n );\n }\n } else {\n // Create topic if it doesn't exist\n const [exists] = await topic.exists();\n if (!exists) {\n await topic.create();\n }\n }\n\n // Serialize message\n const buffer = serialize(message);\n\n // Publish\n await topic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n messageKind: kind,\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish ${kind} ${message.type} to topic ${topicName}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n // Return the message bus implementation\n return {\n // ===== MessageBus Interface =====\n\n /**\n * Send a command to the message bus\n *\n * Commands are routed to exactly one handler via PubSub topics\n *\n * @param command - The command to send\n */\n async send<CommandType extends Command>(\n command: CommandType,\n ): Promise<void> {\n await publishMessage(command, 'command');\n },\n\n /**\n * Publish an event to the message bus\n *\n * Events are delivered to all registered subscribers via PubSub topics\n *\n * @param event - The event to publish\n */\n async publish<EventType extends Event>(event: EventType): Promise<void> {\n await publishMessage(event, 'event');\n },\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Uses PubSub native scheduling\n * In emulator mode: Stores in memory (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param when - When to deliver the message (afterInMs or at)\n */\n schedule<MessageType extends Message>(\n message: MessageType,\n when?: { afterInMs: number } | { at: Date },\n ): void {\n scheduler.schedule(message, when);\n },\n\n // ===== CommandProcessor Interface =====\n\n /**\n * Register a command handler\n *\n * Commands must have exactly one handler. Attempting to register multiple\n * handlers for the same command will throw an EmmettError.\n *\n * @param commandHandler - The handler function\n * @param commandTypes - Command types this handler processes\n * @throws EmmettError if a handler is already registered for any command type\n *\n * @example\n * ```typescript\n * messageBus.handle(\n * async (command: AddProductItemCommand) => {\n * // Handle command\n * },\n * 'AddProductItem'\n * );\n * ```\n */\n handle<CommandType extends Command>(\n commandHandler: SingleMessageHandler<CommandType>,\n ...commandTypeNames: CommandType['type'][]\n ): void {\n for (const commandType of commandTypeNames) {\n // Validate no duplicate handlers\n if (handlers.has(commandType)) {\n throw new EmmettError(\n `Handler already registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Track as command type\n commandTypes.add(commandType);\n\n // Store handler (single handler in array for consistency)\n handlers.set(commandType, [\n commandHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(commandType, 'command').catch((error) => {\n console.error(\n `Failed to create subscription for command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n }\n }\n },\n\n // ===== EventSubscription Interface =====\n\n /**\n * Subscribe to events\n *\n * Events can have multiple subscribers. Each subscription gets its own\n * PubSub subscription to ensure all handlers receive all events.\n *\n * @param eventHandler - The handler function\n * @param eventTypes - Event types to subscribe to\n *\n * @example\n * ```typescript\n * messageBus.subscribe(\n * async (event: ProductItemAddedEvent) => {\n * // Handle event\n * },\n * 'ProductItemAdded'\n * );\n * ```\n */\n subscribe<EventType extends Event>(\n eventHandler: SingleMessageHandler<EventType>,\n ...eventTypeNames: EventType['type'][]\n ): void {\n for (const eventType of eventTypeNames) {\n // Track as event type\n eventTypes.add(eventType);\n\n // Generate unique subscription ID for this subscriber\n const subscriptionId = generateUUID();\n\n // Store handler associated with this subscription ID\n subscriptionHandlers.set(\n subscriptionId,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n );\n\n // Get existing handlers or create new array\n const existing = handlers.get(eventType) ?? [];\n\n // Add handler to array (for compatibility)\n handlers.set(eventType, [\n ...existing,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // Track subscription ID for this event type\n const existingIds = eventSubscriptionIds.get(eventType) ?? [];\n eventSubscriptionIds.set(eventType, [...existingIds, subscriptionId]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(eventType, 'event', subscriptionId).catch(\n (error) => {\n console.error(\n `Failed to create subscription for event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n },\n );\n }\n }\n },\n\n // ===== ScheduledMessageProcessor Interface =====\n\n /**\n * Dequeue scheduled messages that are ready for delivery\n *\n * Only used in emulator mode. In production, PubSub handles scheduling.\n *\n * @returns Array of scheduled messages ready for delivery\n *\n * @example\n * ```typescript\n * // In emulator mode, periodically call dequeue\n * setInterval(() => {\n * const ready = messageBus.dequeue();\n * for (const { message } of ready) {\n * // Process message\n * }\n * }, 1000);\n * ```\n */\n dequeue(): ScheduledMessage[] {\n return scheduler.dequeue();\n },\n\n // ===== PubSubMessageBusLifecycle Interface =====\n\n /**\n * Start the message bus\n *\n * Creates topics and subscriptions for all registered handlers and begins\n * listening for messages.\n *\n * This method is idempotent - calling it multiple times is safe.\n *\n * @throws Error if topic/subscription creation fails\n *\n * @example\n * ```typescript\n * // Register all handlers first\n * messageBus.handle(commandHandler, 'MyCommand');\n * messageBus.subscribe(eventHandler, 'MyEvent');\n *\n * // Then start\n * await messageBus.start();\n * ```\n */\n async start(): Promise<void> {\n if (started) {\n console.debug('Message bus already started, skipping');\n return;\n }\n\n console.info('Starting PubSub message bus...');\n\n try {\n // Create subscriptions for all registered handlers\n const subscriptionPromises: Promise<void>[] = [];\n\n for (const [messageType] of handlers.entries()) {\n const kind = determineMessageKind(messageType);\n\n if (kind === 'command') {\n // Commands: one subscription per instance\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'command'),\n );\n } else {\n // Events: one subscription per handler (multiple allowed)\n const subIds = eventSubscriptionIds.get(messageType) ?? [\n instanceId,\n ];\n for (const subId of subIds) {\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'event', subId),\n );\n }\n }\n }\n\n // Wait for all subscriptions to be created\n await Promise.all(subscriptionPromises);\n\n started = true;\n\n console.info(\n `PubSub message bus started with ${subscriptions.length} subscription(s)`,\n );\n } catch (error) {\n throw new Error(\n `Failed to start message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Close the message bus gracefully\n *\n * Stops accepting new messages, waits for in-flight messages to complete,\n * optionally cleans up subscriptions, and closes the PubSub client.\n *\n * @throws Error if cleanup fails\n *\n * @example\n * ```typescript\n * // Graceful shutdown\n * process.on('SIGTERM', async () => {\n * await messageBus.close();\n * process.exit(0);\n * });\n * ```\n */\n async close(): Promise<void> {\n console.info('Closing PubSub message bus...');\n\n try {\n // Only cleanup subscriptions if started\n if (started) {\n // Stop accepting new messages\n for (const { subscription } of subscriptions) {\n subscription.removeAllListeners('message');\n subscription.removeAllListeners('error');\n }\n\n // Wait for in-flight messages with timeout (30 seconds)\n const timeout = 30000;\n const waitStart = Date.now();\n\n // Close all subscriptions\n const closePromises = subscriptions.map(({ subscription }) =>\n subscription.close(),\n );\n await Promise.race([\n Promise.all(closePromises),\n new Promise((resolve) => setTimeout(resolve, timeout)),\n ]);\n\n const waitTime = Date.now() - waitStart;\n if (waitTime >= timeout) {\n console.warn(\n `Timeout waiting for in-flight messages after ${timeout}ms`,\n );\n }\n\n // Cleanup subscriptions if configured\n if (cleanupOnClose) {\n console.info('Cleaning up subscriptions...');\n await deleteSubscriptions(subscriptions.map((s) => s.subscription));\n }\n\n started = false;\n }\n\n // Always close PubSub client (even if not started, for producer-only mode)\n if (closePubSubClient !== false) {\n await config.pubsub.close();\n }\n\n console.info('PubSub message bus closed');\n } catch (error) {\n throw new Error(\n `Failed to close message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Check if the message bus is started\n *\n * @returns true if the message bus is started and ready to process messages\n */\n isStarted(): boolean {\n return started;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/messageBus/serialization.ts","../src/messageBus/topicManager.ts","../src/messageBus/utils.ts","../src/messageBus/scheduler.ts","../src/messageBus/messageHandler.ts","../src/messageBus/pubsubMessageBus.ts"],"names":["cryptoRandomUUID","EmmettError"],"mappings":"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","file":"index.mjs","sourcesContent":["import { randomUUID } from 'crypto';\nimport type { Command, Event, Message } from '@event-driven-io/emmett';\nimport type { PubSubMessageEnvelope } from './types';\n\n/**\n * Date marker for JSON serialization\n */\ninterface DateMarker {\n __type: 'Date';\n value: string;\n}\n\n/**\n * Recursively transform Dates to DateMarkers in an object\n */\nfunction transformDatesToMarkers(obj: unknown): unknown {\n if (obj instanceof Date) {\n return {\n __type: 'Date',\n value: obj.toISOString(),\n } satisfies DateMarker;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(transformDatesToMarkers);\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = transformDatesToMarkers(value);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Check if a value is a Date marker\n */\nfunction isDateMarker(value: unknown): value is DateMarker {\n return (\n typeof value === 'object' &&\n value !== null &&\n '__type' in value &&\n (value as DateMarker).__type === 'Date' &&\n 'value' in value &&\n typeof (value as DateMarker).value === 'string'\n );\n}\n\n/**\n * JSON reviver that converts Date markers back to Dates\n */\nfunction dateReviver(_key: string, value: unknown): unknown {\n if (isDateMarker(value)) {\n return new Date(value.value);\n }\n return value;\n}\n\n/**\n * Determine if a message is a command or event based on its type\n */\nfunction getMessageKind(message: Message): 'command' | 'event' {\n // Simple heuristic: if the message type contains \"Command\", it's a command\n // Otherwise, it's an event\n // This can be customized based on your naming conventions\n const typeStr = message.type.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Serialize a Command or Event to a Buffer for PubSub transport\n *\n * @param message - The message to serialize\n * @returns Buffer containing the serialized message envelope\n */\nexport function serialize(message: Command | Event): Buffer {\n const envelope = {\n type: message.type,\n kind: getMessageKind(message),\n data: transformDatesToMarkers(message.data),\n metadata: 'metadata' in message ? transformDatesToMarkers(message.metadata) : undefined,\n timestamp: new Date().toISOString(),\n messageId: randomUUID(),\n };\n\n const json = JSON.stringify(envelope);\n return Buffer.from(json);\n}\n\n/**\n * Deserialize a Buffer from PubSub into a Command or Event\n *\n * @param buffer - The buffer containing the serialized message\n * @returns The deserialized message\n * @throws Error if the buffer cannot be deserialized\n */\nexport function deserialize<T extends Command | Event>(buffer: Buffer): T {\n try {\n const json = buffer.toString('utf-8');\n const envelope = JSON.parse(json, dateReviver) as PubSubMessageEnvelope;\n\n const message = {\n type: envelope.type,\n data: envelope.data,\n ...(envelope.metadata ? { metadata: envelope.metadata } : {}),\n } as Message;\n\n return message as T;\n } catch (error) {\n throw new Error(\n `Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Attach a message ID to a message for idempotency tracking\n *\n * @param message - The message to attach ID to\n * @param messageId - The message ID\n * @returns The message with the ID attached\n */\nexport function attachMessageId<T extends Message>(\n message: T,\n messageId: string,\n): T & { __messageId: string } {\n return {\n ...message,\n __messageId: messageId,\n };\n}\n\n/**\n * Extract message ID from a message\n *\n * @param message - The message to extract ID from\n * @returns The message ID if present, undefined otherwise\n */\nexport function extractMessageId(message: Message): string | undefined {\n return '__messageId' in message\n ? (message as { __messageId: string }).__messageId\n : undefined;\n}\n","import type { PubSub, Subscription, Topic } from '@google-cloud/pubsub';\nimport type { SubscriptionOptions } from './types';\n\n/**\n * Get command topic name\n */\nexport function getCommandTopicName(\n commandType: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}`;\n}\n\n/**\n * Get event topic name\n */\nexport function getEventTopicName(eventType: string, prefix = 'emmett'): string {\n return `${prefix}-evt-${eventType}`;\n}\n\n/**\n * Get command subscription name\n */\nexport function getCommandSubscriptionName(\n commandType: string,\n instanceId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-cmd-${commandType}-${instanceId}`;\n}\n\n/**\n * Get event subscription name\n */\nexport function getEventSubscriptionName(\n eventType: string,\n subscriptionId: string,\n prefix = 'emmett',\n): string {\n return `${prefix}-evt-${eventType}-${subscriptionId}`;\n}\n\n/**\n * Get or create a topic\n *\n * @param pubsub - PubSub client\n * @param topicName - Name of the topic\n * @returns The topic instance\n */\nexport async function getOrCreateTopic(\n pubsub: PubSub,\n topicName: string,\n): Promise<Topic> {\n const topic = pubsub.topic(topicName);\n\n try {\n const [exists] = await topic.exists();\n\n if (!exists) {\n try {\n await topic.create();\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return topic;\n } catch (error) {\n throw new Error(\n `Failed to get or create topic ${topicName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Get or create a subscription\n *\n * @param topic - The topic to subscribe to\n * @param subscriptionName - Name of the subscription\n * @param options - Subscription options\n * @returns The subscription instance\n */\nexport async function getOrCreateSubscription(\n topic: Topic,\n subscriptionName: string,\n options?: SubscriptionOptions,\n): Promise<Subscription> {\n const subscription = topic.subscription(subscriptionName);\n\n try {\n const [exists] = await subscription.exists();\n\n if (!exists) {\n const config = {\n ...(options?.ackDeadlineSeconds && {\n ackDeadlineSeconds: options.ackDeadlineSeconds,\n }),\n ...(options?.retryPolicy && {\n retryPolicy: {\n ...(options.retryPolicy.minimumBackoff && {\n minimumBackoff: options.retryPolicy.minimumBackoff,\n }),\n ...(options.retryPolicy.maximumBackoff && {\n maximumBackoff: options.retryPolicy.maximumBackoff,\n }),\n },\n }),\n ...(options?.deadLetterPolicy && {\n deadLetterPolicy: {\n ...(options.deadLetterPolicy.deadLetterTopic && {\n deadLetterTopic: options.deadLetterPolicy.deadLetterTopic,\n }),\n ...(options.deadLetterPolicy.maxDeliveryAttempts && {\n maxDeliveryAttempts: options.deadLetterPolicy.maxDeliveryAttempts,\n }),\n },\n }),\n };\n\n try {\n await subscription.create(config);\n } catch (createError: any) {\n // Ignore ALREADY_EXISTS errors (race condition)\n if (createError.code !== 6) {\n throw createError;\n }\n }\n }\n\n return subscription;\n } catch (error) {\n throw new Error(\n `Failed to get or create subscription ${subscriptionName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete a subscription\n *\n * @param subscription - The subscription to delete\n */\nexport async function deleteSubscription(\n subscription: Subscription,\n): Promise<void> {\n try {\n const [exists] = await subscription.exists();\n\n if (exists) {\n await subscription.delete();\n }\n } catch (error) {\n // Log but don't throw - cleanup is best effort\n console.warn(\n `Failed to delete subscription: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Delete multiple subscriptions\n *\n * @param subscriptions - Array of subscriptions to delete\n */\nexport async function deleteSubscriptions(\n subscriptions: Subscription[],\n): Promise<void> {\n await Promise.all(subscriptions.map((sub) => deleteSubscription(sub)));\n}\n","import { randomUUID as cryptoRandomUUID } from 'crypto';\n\n/**\n * Generate a random UUID\n */\nexport function generateUUID(): string {\n return cryptoRandomUUID();\n}\n\n/**\n * Validate that a string is not empty\n */\nexport function assertNotEmptyString(\n value: unknown,\n name: string,\n): asserts value is string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new Error(`${name} must be a non-empty string`);\n }\n}\n\n/**\n * Validate that a number is positive\n */\nexport function assertPositiveNumber(\n value: unknown,\n name: string,\n): asserts value is number {\n if (typeof value !== 'number' || value <= 0 || Number.isNaN(value)) {\n throw new Error(`${name} must be a positive number`);\n }\n}\n","import type { PubSub, Topic } from '@google-cloud/pubsub';\nimport type { Message } from '@event-driven-io/emmett';\nimport type { ScheduledMessageInfo } from './types';\nimport { serialize } from './serialization';\n\n/**\n * Schedule options for messages\n */\nexport type ScheduleOptions = { afterInMs: number } | { at: Date };\n\n/**\n * Scheduled message returned from dequeue\n */\nexport interface ScheduledMessage {\n message: Message;\n options?: ScheduleOptions;\n}\n\n/**\n * Scheduler configuration\n */\nexport interface SchedulerConfig {\n /**\n * Whether running in emulator mode\n */\n useEmulator: boolean;\n\n /**\n * PubSub client instance\n */\n pubsub: PubSub;\n\n /**\n * Topic for scheduled messages (optional, created if needed)\n */\n scheduledTopic?: Topic;\n\n /**\n * Topic prefix for naming\n * @default \"emmett\"\n */\n topicPrefix?: string;\n}\n\n/**\n * Calculate scheduled time from options\n *\n * @param options - Schedule options (afterInMs or at)\n * @returns The calculated scheduled time\n */\nexport function calculateScheduledTime(options?: ScheduleOptions): Date {\n if (!options) {\n return new Date();\n }\n\n if ('afterInMs' in options) {\n const now = new Date();\n return new Date(now.getTime() + options.afterInMs);\n }\n\n if ('at' in options) {\n return options.at;\n }\n\n return new Date();\n}\n\n/**\n * Filter messages that are ready for delivery\n *\n * @param pending - Array of pending scheduled messages\n * @param now - Current time\n * @returns Messages ready for delivery\n */\nexport function filterReadyMessages(\n pending: ScheduledMessageInfo[],\n now: Date,\n): ScheduledMessageInfo[] {\n return pending.filter((msg) => msg.scheduledAt <= now);\n}\n\n/**\n * Message scheduler with dual mode support (production/emulator)\n */\nexport class MessageScheduler {\n private pendingMessages: ScheduledMessageInfo[] = [];\n private readonly useEmulator: boolean;\n private readonly pubsub: PubSub;\n private readonly topicPrefix: string;\n private scheduledTopic?: Topic;\n\n constructor(config: SchedulerConfig) {\n this.useEmulator = config.useEmulator;\n this.pubsub = config.pubsub;\n this.scheduledTopic = config.scheduledTopic;\n this.topicPrefix = config.topicPrefix ?? 'emmett';\n }\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Publishes to PubSub with publishTime attribute\n * In emulator mode: Stores in memory for later dequeue (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param options - When to deliver the message\n */\n async schedule(message: Message, options?: ScheduleOptions): Promise<void> {\n const scheduledAt = calculateScheduledTime(options);\n\n if (this.useEmulator) {\n // Emulator mode: store in memory\n this.pendingMessages.push({\n message,\n options,\n scheduledAt,\n });\n } else {\n // Production mode: publish to PubSub with publishTime attribute\n await this.publishScheduledMessage(message, scheduledAt);\n }\n }\n\n /**\n * Dequeue ready scheduled messages (emulator mode only)\n *\n * Returns messages whose scheduled time has passed and removes them from pending queue\n *\n * @returns Array of scheduled messages ready for delivery\n */\n dequeue(): ScheduledMessage[] {\n if (!this.useEmulator) {\n // In production mode, PubSub handles scheduling, so dequeue returns empty\n return [];\n }\n\n const now = new Date();\n const ready = filterReadyMessages(this.pendingMessages, now);\n\n // Remove ready messages from pending queue\n this.pendingMessages = this.pendingMessages.filter(\n (msg) => msg.scheduledAt > now,\n );\n\n // Convert to ScheduledMessage format\n return ready.map((info) => ({\n message: info.message,\n options: info.options,\n }));\n }\n\n /**\n * Publish a scheduled message to PubSub (production mode)\n *\n * @param message - The message to publish\n * @param scheduledAt - When the message should be delivered\n */\n private async publishScheduledMessage(\n message: Message,\n scheduledAt: Date,\n ): Promise<void> {\n try {\n // Get or create the scheduled messages topic\n if (!this.scheduledTopic) {\n const topicName = `${this.topicPrefix}-scheduled-messages`;\n this.scheduledTopic = this.pubsub.topic(topicName);\n\n const [exists] = await this.scheduledTopic.exists();\n if (!exists) {\n await this.scheduledTopic.create();\n }\n }\n\n // Serialize the message\n const buffer = serialize(message);\n\n // Publish with custom attributes including publish time\n await this.scheduledTopic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n publishTime: scheduledAt.toISOString(),\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish scheduled message ${message.type}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n /**\n * Get count of pending scheduled messages (emulator mode only)\n *\n * @returns Number of pending scheduled messages\n */\n getPendingCount(): number {\n return this.useEmulator ? this.pendingMessages.length : 0;\n }\n\n /**\n * Clear all pending scheduled messages (emulator mode only, useful for testing)\n */\n clearPending(): void {\n if (this.useEmulator) {\n this.pendingMessages = [];\n }\n }\n}\n","import type { Message as PubSubMessage, Subscription } from '@google-cloud/pubsub';\nimport type {\n AnyMessage,\n Command,\n Event,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport { deserialize } from './serialization';\n\n/**\n * Determine if an error should trigger a retry (nack) or be considered permanent (ack)\n *\n * @param error - The error to classify\n * @returns true if the error is retriable (should nack), false if permanent (should ack)\n */\nexport function shouldRetry(error: unknown): boolean {\n if (!(error instanceof Error)) {\n // Unknown error types - retry to be safe\n return true;\n }\n\n const errorMessage = error.message.toLowerCase();\n\n // Network/timeout errors - retry\n if (\n errorMessage.includes('network') ||\n errorMessage.includes('timeout') ||\n errorMessage.includes('econnrefused') ||\n errorMessage.includes('enotfound') ||\n errorMessage.includes('unavailable')\n ) {\n return true;\n }\n\n // EmmettError and validation errors - don't retry (business logic errors)\n if (error instanceof EmmettError) {\n return false;\n }\n\n if (\n errorMessage.includes('validation') ||\n errorMessage.includes('invalid') ||\n errorMessage.includes('not found') ||\n errorMessage.includes('already exists')\n ) {\n return false;\n }\n\n // Default to retry for unknown errors\n return true;\n}\n\n/**\n * Process an incoming command message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param commandType - The command type being processed\n * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleCommandMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n commandType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this command type\n const commandHandlers = handlers.get(commandType);\n\n if (!commandHandlers || commandHandlers.length === 0) {\n throw new EmmettError(\n `No handler registered for command ${commandType}!`,\n );\n }\n\n // Commands must have exactly one handler\n if (commandHandlers.length > 1) {\n throw new EmmettError(\n `Multiple handlers registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Deserialize the command\n const command = deserialize<Command>(message.data);\n\n // Execute the handler\n const handler = commandHandlers[0];\n await handler(command);\n\n return 'ack';\n } catch (error) {\n console.error(\n `Error handling command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // Determine if we should retry\n if (shouldRetry(error)) {\n console.info(\n `Nacking command ${commandType} for retry (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n } else {\n console.warn(\n `Acking command ${commandType} despite error (permanent failure)`,\n );\n return 'ack';\n }\n }\n}\n\n/**\n * Process an incoming event message from PubSub\n *\n * @param message - The PubSub message\n * @param handlers - Map of message type to handlers\n * @param eventType - The event type being processed\n * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure\n */\nexport async function handleEventMessage(\n message: PubSubMessage,\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n eventType: string,\n): Promise<'ack' | 'nack'> {\n try {\n // Get handlers for this event type\n const eventHandlers = handlers.get(eventType);\n\n if (!eventHandlers || eventHandlers.length === 0) {\n // Events without handlers are silently ignored (valid scenario)\n console.debug(`No handlers registered for event ${eventType}, skipping`);\n return 'ack';\n }\n\n // Deserialize the event\n const event = deserialize<Event>(message.data);\n\n // Execute all handlers sequentially\n for (const handler of eventHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(\n `Error in event handler for ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n // If any handler fails with a retriable error, nack the whole message\n if (shouldRetry(error)) {\n console.info(\n `Nacking event ${eventType} for retry due to handler failure (delivery attempt: ${message.deliveryAttempt})`,\n );\n return 'nack';\n }\n // Otherwise continue to next handler\n console.warn(\n `Continuing event ${eventType} processing despite handler error (permanent failure)`,\n );\n }\n }\n\n return 'ack';\n } catch (error) {\n // Error deserializing or other unexpected error\n console.error(\n `Error handling event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n\n if (shouldRetry(error)) {\n return 'nack';\n } else {\n return 'ack';\n }\n }\n}\n\n/**\n * Create a message listener for a PubSub subscription\n *\n * @param subscription - The PubSub subscription to listen on\n * @param messageType - The message type (command or event type)\n * @param kind - Whether this is a command or event\n * @param handlers - Map of message type to handlers\n */\nexport function createMessageListener(\n subscription: Subscription,\n messageType: string,\n kind: 'command' | 'event',\n handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>,\n): void {\n subscription.on('message', async (message: PubSubMessage) => {\n try {\n // Route to appropriate handler based on kind\n const result =\n kind === 'command'\n ? await handleCommandMessage(message, handlers, messageType)\n : await handleEventMessage(message, handlers, messageType);\n\n // Acknowledge or nack based on result\n if (result === 'ack') {\n message.ack();\n } else {\n message.nack();\n }\n } catch (error) {\n // Unexpected error in listener itself - log and nack\n console.error(\n `Unexpected error in message listener for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n message.nack();\n }\n });\n\n subscription.on('error', (error) => {\n console.error(\n `Subscription error for ${messageType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n}\n","import type {\n AnyMessage,\n Command,\n CommandProcessor,\n Event,\n EventSubscription,\n Message,\n MessageBus,\n ScheduledMessageProcessor,\n SingleMessageHandler,\n SingleRawMessageHandlerWithoutContext,\n} from '@event-driven-io/emmett';\nimport { EmmettError } from '@event-driven-io/emmett';\nimport type {\n PubSubMessageBusConfig,\n PubSubMessageBusLifecycle,\n SubscriptionInfo,\n} from './types';\nimport type { ScheduledMessage } from './scheduler';\nimport { MessageScheduler } from './scheduler';\nimport { serialize } from './serialization';\nimport {\n getCommandSubscriptionName,\n getCommandTopicName,\n getEventSubscriptionName,\n getEventTopicName,\n getOrCreateSubscription,\n getOrCreateTopic,\n deleteSubscriptions,\n} from './topicManager';\nimport { createMessageListener } from './messageHandler';\nimport { generateUUID } from './utils';\n\n/**\n * Determine message kind based on message type naming convention (fallback)\n *\n * @param messageType - The message type string\n * @returns 'command' if type contains 'Command', otherwise 'event'\n */\nfunction determineMessageKindFallback(messageType: string): 'command' | 'event' {\n const typeStr = messageType.toLowerCase();\n return typeStr.includes('command') ? 'command' : 'event';\n}\n\n/**\n * Create a Google Cloud Pub/Sub based message bus for Emmett\n *\n * @param config - Configuration for the PubSub message bus\n * @returns A message bus implementation using Google Cloud Pub/Sub\n *\n * @example\n * ```typescript\n * import { PubSub } from '@google-cloud/pubsub';\n * import { getPubSubMessageBus } from '@emmett-community/emmett-google-pubsub';\n *\n * const pubsub = new PubSub({ projectId: 'my-project' });\n * const messageBus = getPubSubMessageBus({ pubsub });\n *\n * // Register handlers\n * messageBus.handle(async (command) => {\n * // Handle command\n * }, 'MyCommand');\n *\n * // Subscribe to events\n * messageBus.subscribe(async (event) => {\n * // Handle event\n * }, 'MyEvent');\n *\n * // Start the message bus\n * await messageBus.start();\n *\n * // Send commands and publish events\n * await messageBus.send({ type: 'MyCommand', data: { ... } });\n * await messageBus.publish({ type: 'MyEvent', data: { ... } });\n *\n * // Close gracefully\n * await messageBus.close();\n * ```\n */\nexport function getPubSubMessageBus(\n config: PubSubMessageBusConfig,\n): MessageBus &\n EventSubscription &\n CommandProcessor &\n ScheduledMessageProcessor &\n PubSubMessageBusLifecycle {\n // Internal state\n const instanceId = config.instanceId ?? generateUUID();\n const topicPrefix = config.topicPrefix ?? 'emmett';\n const autoCreateResources = config.autoCreateResources ?? true;\n const cleanupOnClose = config.cleanupOnClose ?? false;\n const closePubSubClient = config.closePubSubClient;\n\n // Map of message type to handlers\n const handlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n\n // Map of subscription ID to specific handler (for event subscriptions)\n const subscriptionHandlers = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>\n >();\n\n // Map of message type to subscription IDs (for events with multiple subscriptions)\n const eventSubscriptionIds = new Map<string, string[]>();\n\n // Track which message types are commands vs events\n const commandTypes = new Set<string>();\n const eventTypes = new Set<string>();\n\n // Active subscriptions\n const subscriptions: SubscriptionInfo[] = [];\n\n // Scheduler for delayed messages\n const scheduler = new MessageScheduler({\n useEmulator: config.useEmulator ?? false,\n pubsub: config.pubsub,\n topicPrefix,\n });\n\n // Lifecycle state\n let started = false;\n\n /**\n * Determine message kind based on how it was registered\n *\n * @param messageType - The message type string\n * @returns 'command' if registered with handle(), 'event' if registered with subscribe()\n */\n function determineMessageKind(messageType: string): 'command' | 'event' {\n // Check explicit registration first\n if (commandTypes.has(messageType)) {\n return 'command';\n }\n if (eventTypes.has(messageType)) {\n return 'event';\n }\n // Fallback to name-based heuristic\n return determineMessageKindFallback(messageType);\n }\n\n /**\n * Create subscription for a specific message type\n *\n * @param messageType - The message type\n * @param kind - Whether this is a command or event\n * @param subscriptionId - Optional subscription ID for events\n */\n async function createSubscriptionForType(\n messageType: string,\n kind: 'command' | 'event',\n subscriptionId?: string,\n ): Promise<void> {\n // Get topic name based on kind\n const topicName =\n kind === 'command'\n ? getCommandTopicName(messageType, topicPrefix)\n : getEventTopicName(messageType, topicPrefix);\n\n // Get or create topic\n const topic = await getOrCreateTopic(config.pubsub, topicName);\n\n // Get subscription name\n const subName =\n kind === 'command'\n ? getCommandSubscriptionName(messageType, instanceId, topicPrefix)\n : getEventSubscriptionName(\n messageType,\n subscriptionId ?? instanceId,\n topicPrefix,\n );\n\n // Create subscription\n const subscription = await getOrCreateSubscription(\n topic,\n subName,\n config.subscriptionOptions,\n );\n\n // Create message listener with appropriate handlers\n if (kind === 'event' && subscriptionId) {\n // For events, create a map with only this subscription's handler\n const handler = subscriptionHandlers.get(subscriptionId);\n if (handler) {\n const singleHandlerMap = new Map<\n string,\n SingleRawMessageHandlerWithoutContext<AnyMessage>[]\n >();\n singleHandlerMap.set(messageType, [handler]);\n createMessageListener(subscription, messageType, kind, singleHandlerMap);\n }\n } else {\n // For commands, use the handlers map as before\n createMessageListener(subscription, messageType, kind, handlers);\n }\n\n // Track subscription\n subscriptions.push({\n topic,\n subscription,\n messageType,\n kind,\n });\n }\n\n /**\n * Publish a message to a PubSub topic\n *\n * @param message - The message to publish\n * @param kind - Whether this is a command or event\n */\n async function publishMessage(\n message: Message,\n kind: 'command' | 'event',\n ): Promise<void> {\n // Publishing without start() is allowed (producer-only mode)\n // start() is only required for consumers (handlers/subscribers)\n\n // Get topic name\n const topicName =\n kind === 'command'\n ? getCommandTopicName(message.type, topicPrefix)\n : getEventTopicName(message.type, topicPrefix);\n\n try {\n // Get topic\n const topic = config.pubsub.topic(topicName);\n\n // Check if topic exists if auto-create is disabled\n if (!autoCreateResources) {\n const [exists] = await topic.exists();\n if (!exists) {\n throw new Error(\n `Topic ${topicName} does not exist and autoCreateResources is disabled`,\n );\n }\n } else {\n // Create topic if it doesn't exist\n const [exists] = await topic.exists();\n if (!exists) {\n await topic.create();\n }\n }\n\n // Serialize message\n const buffer = serialize(message);\n\n // Publish\n await topic.publishMessage({\n data: buffer,\n attributes: {\n messageType: message.type,\n messageKind: kind,\n },\n });\n } catch (error) {\n throw new Error(\n `Failed to publish ${kind} ${message.type} to topic ${topicName}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n // Return the message bus implementation\n return {\n // ===== MessageBus Interface =====\n\n /**\n * Send a command to the message bus\n *\n * Commands are routed to exactly one handler via PubSub topics\n *\n * @param command - The command to send\n */\n async send<CommandType extends Command>(\n command: CommandType,\n ): Promise<void> {\n await publishMessage(command, 'command');\n },\n\n /**\n * Publish an event to the message bus\n *\n * Events are delivered to all registered subscribers via PubSub topics\n *\n * @param event - The event to publish\n */\n async publish<EventType extends Event>(event: EventType): Promise<void> {\n await publishMessage(event, 'event');\n },\n\n /**\n * Schedule a message for future delivery\n *\n * In production mode: Uses PubSub native scheduling\n * In emulator mode: Stores in memory (emulator doesn't support scheduling)\n *\n * @param message - The message to schedule\n * @param when - When to deliver the message (afterInMs or at)\n */\n schedule<MessageType extends Message>(\n message: MessageType,\n when?: { afterInMs: number } | { at: Date },\n ): void {\n scheduler.schedule(message, when);\n },\n\n // ===== CommandProcessor Interface =====\n\n /**\n * Register a command handler\n *\n * Commands must have exactly one handler. Attempting to register multiple\n * handlers for the same command will throw an EmmettError.\n *\n * @param commandHandler - The handler function\n * @param commandTypes - Command types this handler processes\n * @throws EmmettError if a handler is already registered for any command type\n *\n * @example\n * ```typescript\n * messageBus.handle(\n * async (command: AddProductItemCommand) => {\n * // Handle command\n * },\n * 'AddProductItem'\n * );\n * ```\n */\n handle<CommandType extends Command>(\n commandHandler: SingleMessageHandler<CommandType>,\n ...commandTypeNames: CommandType['type'][]\n ): void {\n for (const commandType of commandTypeNames) {\n // Validate no duplicate handlers\n if (handlers.has(commandType)) {\n throw new EmmettError(\n `Handler already registered for command ${commandType}. ` +\n `Commands must have exactly one handler.`,\n );\n }\n\n // Track as command type\n commandTypes.add(commandType);\n\n // Store handler (single handler in array for consistency)\n handlers.set(commandType, [\n commandHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(commandType, 'command').catch((error) => {\n console.error(\n `Failed to create subscription for command ${commandType}:`,\n error instanceof Error ? error.message : String(error),\n );\n });\n }\n }\n },\n\n // ===== EventSubscription Interface =====\n\n /**\n * Subscribe to events\n *\n * Events can have multiple subscribers. Each subscription gets its own\n * PubSub subscription to ensure all handlers receive all events.\n *\n * @param eventHandler - The handler function\n * @param eventTypes - Event types to subscribe to\n *\n * @example\n * ```typescript\n * messageBus.subscribe(\n * async (event: ProductItemAddedEvent) => {\n * // Handle event\n * },\n * 'ProductItemAdded'\n * );\n * ```\n */\n subscribe<EventType extends Event>(\n eventHandler: SingleMessageHandler<EventType>,\n ...eventTypeNames: EventType['type'][]\n ): void {\n for (const eventType of eventTypeNames) {\n // Track as event type\n eventTypes.add(eventType);\n\n // Generate unique subscription ID for this subscriber\n const subscriptionId = generateUUID();\n\n // Store handler associated with this subscription ID\n subscriptionHandlers.set(\n subscriptionId,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n );\n\n // Get existing handlers or create new array\n const existing = handlers.get(eventType) ?? [];\n\n // Add handler to array (for compatibility)\n handlers.set(eventType, [\n ...existing,\n eventHandler as SingleRawMessageHandlerWithoutContext<AnyMessage>,\n ]);\n\n // Track subscription ID for this event type\n const existingIds = eventSubscriptionIds.get(eventType) ?? [];\n eventSubscriptionIds.set(eventType, [...existingIds, subscriptionId]);\n\n // If already started, create subscription immediately\n if (started) {\n createSubscriptionForType(eventType, 'event', subscriptionId).catch(\n (error) => {\n console.error(\n `Failed to create subscription for event ${eventType}:`,\n error instanceof Error ? error.message : String(error),\n );\n },\n );\n }\n }\n },\n\n // ===== ScheduledMessageProcessor Interface =====\n\n /**\n * Dequeue scheduled messages that are ready for delivery\n *\n * Only used in emulator mode. In production, PubSub handles scheduling.\n *\n * @returns Array of scheduled messages ready for delivery\n *\n * @example\n * ```typescript\n * // In emulator mode, periodically call dequeue\n * setInterval(() => {\n * const ready = messageBus.dequeue();\n * for (const { message } of ready) {\n * // Process message\n * }\n * }, 1000);\n * ```\n */\n dequeue(): ScheduledMessage[] {\n return scheduler.dequeue();\n },\n\n // ===== PubSubMessageBusLifecycle Interface =====\n\n /**\n * Start the message bus\n *\n * Creates topics and subscriptions for all registered handlers and begins\n * listening for messages.\n *\n * This method is idempotent - calling it multiple times is safe.\n *\n * @throws Error if topic/subscription creation fails\n *\n * @example\n * ```typescript\n * // Register all handlers first\n * messageBus.handle(commandHandler, 'MyCommand');\n * messageBus.subscribe(eventHandler, 'MyEvent');\n *\n * // Then start\n * await messageBus.start();\n * ```\n */\n async start(): Promise<void> {\n if (started) {\n console.debug('Message bus already started, skipping');\n return;\n }\n\n console.info('Starting PubSub message bus...');\n\n try {\n // Create subscriptions for all registered handlers\n const subscriptionPromises: Promise<void>[] = [];\n\n for (const [messageType] of handlers.entries()) {\n const kind = determineMessageKind(messageType);\n\n if (kind === 'command') {\n // Commands: one subscription per instance\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'command'),\n );\n } else {\n // Events: one subscription per handler (multiple allowed)\n const subIds = eventSubscriptionIds.get(messageType) ?? [\n instanceId,\n ];\n for (const subId of subIds) {\n subscriptionPromises.push(\n createSubscriptionForType(messageType, 'event', subId),\n );\n }\n }\n }\n\n // Wait for all subscriptions to be created\n await Promise.all(subscriptionPromises);\n\n started = true;\n\n console.info(\n `PubSub message bus started with ${subscriptions.length} subscription(s)`,\n );\n } catch (error) {\n throw new Error(\n `Failed to start message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Close the message bus gracefully\n *\n * Stops accepting new messages, waits for in-flight messages to complete,\n * optionally cleans up subscriptions, and closes the PubSub client.\n *\n * @throws Error if cleanup fails\n *\n * @example\n * ```typescript\n * // Graceful shutdown\n * process.on('SIGTERM', async () => {\n * await messageBus.close();\n * process.exit(0);\n * });\n * ```\n */\n async close(): Promise<void> {\n console.info('Closing PubSub message bus...');\n\n try {\n // Only cleanup subscriptions if started\n if (started) {\n // Stop accepting new messages\n for (const { subscription } of subscriptions) {\n subscription.removeAllListeners('message');\n subscription.removeAllListeners('error');\n }\n\n // Wait for in-flight messages with timeout (30 seconds)\n const timeout = 30000;\n const waitStart = Date.now();\n let timeoutId: NodeJS.Timeout | null = null;\n\n // Close all subscriptions\n const closePromises = subscriptions.map(({ subscription }) =>\n subscription.close(),\n );\n try {\n await Promise.race([\n Promise.all(closePromises),\n new Promise((resolve) => {\n timeoutId = setTimeout(resolve, timeout);\n }),\n ]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n\n const waitTime = Date.now() - waitStart;\n if (waitTime >= timeout) {\n console.warn(\n `Timeout waiting for in-flight messages after ${timeout}ms`,\n );\n }\n\n // Cleanup subscriptions if configured\n if (cleanupOnClose) {\n console.info('Cleaning up subscriptions...');\n await deleteSubscriptions(subscriptions.map((s) => s.subscription));\n }\n\n started = false;\n }\n\n // Always close PubSub client (even if not started, for producer-only mode)\n if (closePubSubClient !== false) {\n await config.pubsub.close();\n }\n\n console.info('PubSub message bus closed');\n } catch (error) {\n throw new Error(\n `Failed to close message bus: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n /**\n * Check if the message bus is started\n *\n * @returns true if the message bus is started and ready to process messages\n */\n isStarted(): boolean {\n return started;\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emmett-community/emmett-google-pubsub",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Google Cloud Pub/Sub message bus for Emmett - Event Sourcing development made simple",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -15,16 +15,6 @@
15
15
  "types": "./dist/index.d.ts",
16
16
  "default": "./dist/index.js"
17
17
  }
18
- },
19
- "./testing": {
20
- "import": {
21
- "types": "./dist/testing/index.d.mts",
22
- "default": "./dist/testing/index.mjs"
23
- },
24
- "require": {
25
- "types": "./dist/testing/index.d.ts",
26
- "default": "./dist/testing/index.js"
27
- }
28
18
  }
29
19
  },
30
20
  "files": [
@@ -33,11 +23,13 @@
33
23
  "scripts": {
34
24
  "build": "tsup",
35
25
  "build:ts": "tsc --noEmit",
36
- "test": "jest",
37
- "test:unit": "jest test/unit",
38
- "test:integration": "jest test/integration",
39
- "test:watch": "jest --watch",
40
- "test:coverage": "jest --coverage",
26
+ "test": "npm run test:unit && npm run test:int && npm run test:e2e",
27
+ "test:unit": "jest --testMatch \"**/*.unit.spec.ts\" --watchman=false",
28
+ "test:int": "jest --testMatch \"**/*.int.spec.ts\" --watchman=false",
29
+ "test:integration": "npm run test:int",
30
+ "test:e2e": "jest --testMatch \"**/*.e2e.spec.ts\" --watchman=false",
31
+ "test:watch": "jest --watch --watchman=false",
32
+ "test:coverage": "jest --coverage --watchman=false",
41
33
  "lint": "eslint . --ext .ts",
42
34
  "lint:fix": "eslint . --ext .ts --fix",
43
35
  "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
@@ -82,6 +74,7 @@
82
74
  "eslint-plugin-prettier": "^5.2.1",
83
75
  "jest": "^29.7.0",
84
76
  "prettier": "^3.4.2",
77
+ "testcontainers": "^10.28.0",
85
78
  "ts-jest": "^29.2.5",
86
79
  "ts-node": "^10.9.2",
87
80
  "tsup": "^8.3.5",
@@ -1,2 +0,0 @@
1
-
2
- export { }
@@ -1,2 +0,0 @@
1
-
2
- export { }
@@ -1,4 +0,0 @@
1
- 'use strict';
2
-
3
- //# sourceMappingURL=index.js.map
4
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1,3 +0,0 @@
1
-
2
- //# sourceMappingURL=index.mjs.map
3
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}