@emmett-community/emmett-google-pubsub 0.2.0 → 0.4.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
@@ -378,6 +378,65 @@ JavaScript `Date` objects are preserved through serialization:
378
378
 
379
379
  See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) for design decisions.
380
380
 
381
+ ## Observability
382
+
383
+ The package supports optional observability through structured logging and OpenTelemetry tracing.
384
+
385
+ ### Logging
386
+
387
+ Logging is opt-in and completely silent by default. To enable logging, provide a logger that implements the canonical `(context, message)` contract:
388
+
389
+ ```typescript
390
+ const messageBus = getPubSubMessageBus({
391
+ pubsub,
392
+ observability: {
393
+ logger: {
394
+ debug: (context, message) => console.debug(message, context),
395
+ info: (context, message) => console.info(message, context),
396
+ warn: (context, message) => console.warn(message, context),
397
+ error: (context, message) => console.error(message, context),
398
+ },
399
+ },
400
+ });
401
+ ```
402
+
403
+ **Logger Contract:**
404
+
405
+ The logger MUST implement the canonical `(context, message)` contract:
406
+
407
+ - `context`: Structured data as `Record<string, unknown>` (first parameter)
408
+ - `message`: Human-readable log message (second parameter, optional)
409
+
410
+ Pino is natively compatible. For Winston, use an adapter.
411
+
412
+ **Log Levels:**
413
+
414
+ - `info` - Lifecycle events (start, stop)
415
+ - `debug` - External I/O operations (publish, subscribe)
416
+ - `warn` - Recoverable failures (timeouts, retries)
417
+ - `error` - Failures (with Error objects in `{ err: error }` format)
418
+
419
+ ### Tracing (OpenTelemetry)
420
+
421
+ The package creates OpenTelemetry spans for key operations. Tracing is passive - the `@opentelemetry/api` is a no-op by default.
422
+
423
+ To enable tracing, configure OpenTelemetry in your application:
424
+
425
+ ```typescript
426
+ import { NodeSDK } from '@opentelemetry/sdk-node';
427
+ const sdk = new NodeSDK({ /* config */ });
428
+ sdk.start();
429
+
430
+ // Spans from emmett-google-pubsub are now captured
431
+ const messageBus = getPubSubMessageBus({ pubsub });
432
+ ```
433
+
434
+ **Notes:**
435
+
436
+ - The package never initializes OpenTelemetry
437
+ - No tracing flags needed - spans are always created (no-op if SDK not initialized)
438
+ - Message types and payloads are never included in spans or logs
439
+
381
440
  ## Compatibility
382
441
 
383
442
  - **Node.js**: >= 18.0.0
package/dist/index.d.mts CHANGED
@@ -1,6 +1,45 @@
1
1
  import { PubSub, Topic, Subscription, Message as Message$1 } from '@google-cloud/pubsub';
2
2
  import { Message, SingleMessageHandler, Command, Event, SingleRawMessageHandlerWithoutContext, AnyMessage, MessageBus, EventSubscription, CommandProcessor, ScheduledMessageProcessor } from '@event-driven-io/emmett';
3
3
 
4
+ /**
5
+ * Canonical Logger contract for the Emmett ecosystem.
6
+ *
7
+ * DO NOT MODIFY this interface without updating ALL packages in the ecosystem.
8
+ *
9
+ * This package defines the canonical Logger interface.
10
+ * Implementations (Pino, Winston, etc.) MUST adapt to this contract.
11
+ * This contract MUST NOT adapt to any specific implementation.
12
+ *
13
+ * Semantic Rules:
14
+ * - context (first parameter): ALWAYS structured data as Record<string, unknown>
15
+ * - message (second parameter): ALWAYS the human-readable log message
16
+ * - The order is NEVER inverted
17
+ * - The (message, data) form is NOT valid for this contract
18
+ * - Error objects MUST use the 'err' key (frozen semantic)
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Pino - native compatibility
23
+ * import pino from 'pino';
24
+ * const logger = pino();
25
+ * // logger.info({ orderId }, 'Order created') matches our contract
26
+ * ```
27
+ */
28
+ interface Logger {
29
+ debug(context: Record<string, unknown>, message?: string): void;
30
+ info(context: Record<string, unknown>, message?: string): void;
31
+ warn(context: Record<string, unknown>, message?: string): void;
32
+ error(context: Record<string, unknown>, message?: string): void;
33
+ }
34
+ /**
35
+ * Observability configuration options.
36
+ */
37
+ interface ObservabilityOptions {
38
+ /**
39
+ * Optional logger instance. When not provided, the library operates silently.
40
+ */
41
+ logger?: Logger;
42
+ }
4
43
  /**
5
44
  * Configuration for PubSub MessageBus
6
45
  */
@@ -43,6 +82,11 @@ interface PubSubMessageBusConfig {
43
82
  * @default true
44
83
  */
45
84
  closePubSubClient?: boolean;
85
+ /**
86
+ * Observability options (logging).
87
+ * If not provided, the package remains silent.
88
+ */
89
+ observability?: ObservabilityOptions;
46
90
  }
47
91
  /**
48
92
  * Subscription configuration options
@@ -217,14 +261,16 @@ declare function getOrCreateSubscription(topic: Topic, subscriptionName: string,
217
261
  * Delete a subscription
218
262
  *
219
263
  * @param subscription - The subscription to delete
264
+ * @param logger - Optional logger for observability
220
265
  */
221
- declare function deleteSubscription(subscription: Subscription): Promise<void>;
266
+ declare function deleteSubscription(subscription: Subscription, logger?: Logger): Promise<void>;
222
267
  /**
223
268
  * Delete multiple subscriptions
224
269
  *
225
270
  * @param subscriptions - Array of subscriptions to delete
271
+ * @param logger - Optional logger for observability
226
272
  */
227
- declare function deleteSubscriptions(subscriptions: Subscription[]): Promise<void>;
273
+ declare function deleteSubscriptions(subscriptions: Subscription[], logger?: Logger): Promise<void>;
228
274
 
229
275
  /**
230
276
  * Generate a random UUID
@@ -351,18 +397,20 @@ declare function shouldRetry(error: unknown): boolean;
351
397
  * @param message - The PubSub message
352
398
  * @param handlers - Map of message type to handlers
353
399
  * @param commandType - The command type being processed
400
+ * @param logger - Optional logger for observability
354
401
  * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure
355
402
  */
356
- declare function handleCommandMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, commandType: string): Promise<'ack' | 'nack'>;
403
+ declare function handleCommandMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, commandType: string, logger?: Logger): Promise<'ack' | 'nack'>;
357
404
  /**
358
405
  * Process an incoming event message from PubSub
359
406
  *
360
407
  * @param message - The PubSub message
361
408
  * @param handlers - Map of message type to handlers
362
409
  * @param eventType - The event type being processed
410
+ * @param logger - Optional logger for observability
363
411
  * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure
364
412
  */
365
- declare function handleEventMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, eventType: string): Promise<'ack' | 'nack'>;
413
+ declare function handleEventMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, eventType: string, logger?: Logger): Promise<'ack' | 'nack'>;
366
414
  /**
367
415
  * Create a message listener for a PubSub subscription
368
416
  *
@@ -370,8 +418,9 @@ declare function handleEventMessage(message: Message$1, handlers: Map<string, Si
370
418
  * @param messageType - The message type (command or event type)
371
419
  * @param kind - Whether this is a command or event
372
420
  * @param handlers - Map of message type to handlers
421
+ * @param logger - Optional logger for observability
373
422
  */
374
- declare function createMessageListener(subscription: Subscription, messageType: string, kind: 'command' | 'event', handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>): void;
423
+ declare function createMessageListener(subscription: Subscription, messageType: string, kind: 'command' | 'event', handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, logger?: Logger): void;
375
424
 
376
425
  /**
377
426
  * Create a Google Cloud Pub/Sub based message bus for Emmett
@@ -410,4 +459,4 @@ declare function createMessageListener(subscription: Subscription, messageType:
410
459
  */
411
460
  declare function getPubSubMessageBus(config: PubSubMessageBusConfig): MessageBus & EventSubscription & CommandProcessor & ScheduledMessageProcessor & PubSubMessageBusLifecycle;
412
461
 
413
- export { type HandlerRegistration, MessageScheduler, type PubSubMessageBusConfig, type PubSubMessageBusLifecycle, type PubSubMessageEnvelope, type ScheduleOptions, type ScheduledMessage, type ScheduledMessageInfo, type SchedulerConfig, type SubscriptionInfo, type SubscriptionOptions, assertNotEmptyString, assertPositiveNumber, attachMessageId, calculateScheduledTime, createMessageListener, deleteSubscription, deleteSubscriptions, deserialize, extractMessageId, filterReadyMessages, generateUUID, getCommandSubscriptionName, getCommandTopicName, getEventSubscriptionName, getEventTopicName, getOrCreateSubscription, getOrCreateTopic, getPubSubMessageBus, handleCommandMessage, handleEventMessage, serialize, shouldRetry };
462
+ export { type HandlerRegistration, type Logger, MessageScheduler, type ObservabilityOptions, type PubSubMessageBusConfig, type PubSubMessageBusLifecycle, type PubSubMessageEnvelope, type ScheduleOptions, type ScheduledMessage, type ScheduledMessageInfo, type SchedulerConfig, type SubscriptionInfo, type SubscriptionOptions, assertNotEmptyString, assertPositiveNumber, attachMessageId, calculateScheduledTime, createMessageListener, deleteSubscription, deleteSubscriptions, deserialize, extractMessageId, filterReadyMessages, generateUUID, getCommandSubscriptionName, getCommandTopicName, getEventSubscriptionName, getEventTopicName, getOrCreateSubscription, getOrCreateTopic, getPubSubMessageBus, handleCommandMessage, handleEventMessage, serialize, shouldRetry };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,45 @@
1
1
  import { PubSub, Topic, Subscription, Message as Message$1 } from '@google-cloud/pubsub';
2
2
  import { Message, SingleMessageHandler, Command, Event, SingleRawMessageHandlerWithoutContext, AnyMessage, MessageBus, EventSubscription, CommandProcessor, ScheduledMessageProcessor } from '@event-driven-io/emmett';
3
3
 
4
+ /**
5
+ * Canonical Logger contract for the Emmett ecosystem.
6
+ *
7
+ * DO NOT MODIFY this interface without updating ALL packages in the ecosystem.
8
+ *
9
+ * This package defines the canonical Logger interface.
10
+ * Implementations (Pino, Winston, etc.) MUST adapt to this contract.
11
+ * This contract MUST NOT adapt to any specific implementation.
12
+ *
13
+ * Semantic Rules:
14
+ * - context (first parameter): ALWAYS structured data as Record<string, unknown>
15
+ * - message (second parameter): ALWAYS the human-readable log message
16
+ * - The order is NEVER inverted
17
+ * - The (message, data) form is NOT valid for this contract
18
+ * - Error objects MUST use the 'err' key (frozen semantic)
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Pino - native compatibility
23
+ * import pino from 'pino';
24
+ * const logger = pino();
25
+ * // logger.info({ orderId }, 'Order created') matches our contract
26
+ * ```
27
+ */
28
+ interface Logger {
29
+ debug(context: Record<string, unknown>, message?: string): void;
30
+ info(context: Record<string, unknown>, message?: string): void;
31
+ warn(context: Record<string, unknown>, message?: string): void;
32
+ error(context: Record<string, unknown>, message?: string): void;
33
+ }
34
+ /**
35
+ * Observability configuration options.
36
+ */
37
+ interface ObservabilityOptions {
38
+ /**
39
+ * Optional logger instance. When not provided, the library operates silently.
40
+ */
41
+ logger?: Logger;
42
+ }
4
43
  /**
5
44
  * Configuration for PubSub MessageBus
6
45
  */
@@ -43,6 +82,11 @@ interface PubSubMessageBusConfig {
43
82
  * @default true
44
83
  */
45
84
  closePubSubClient?: boolean;
85
+ /**
86
+ * Observability options (logging).
87
+ * If not provided, the package remains silent.
88
+ */
89
+ observability?: ObservabilityOptions;
46
90
  }
47
91
  /**
48
92
  * Subscription configuration options
@@ -217,14 +261,16 @@ declare function getOrCreateSubscription(topic: Topic, subscriptionName: string,
217
261
  * Delete a subscription
218
262
  *
219
263
  * @param subscription - The subscription to delete
264
+ * @param logger - Optional logger for observability
220
265
  */
221
- declare function deleteSubscription(subscription: Subscription): Promise<void>;
266
+ declare function deleteSubscription(subscription: Subscription, logger?: Logger): Promise<void>;
222
267
  /**
223
268
  * Delete multiple subscriptions
224
269
  *
225
270
  * @param subscriptions - Array of subscriptions to delete
271
+ * @param logger - Optional logger for observability
226
272
  */
227
- declare function deleteSubscriptions(subscriptions: Subscription[]): Promise<void>;
273
+ declare function deleteSubscriptions(subscriptions: Subscription[], logger?: Logger): Promise<void>;
228
274
 
229
275
  /**
230
276
  * Generate a random UUID
@@ -351,18 +397,20 @@ declare function shouldRetry(error: unknown): boolean;
351
397
  * @param message - The PubSub message
352
398
  * @param handlers - Map of message type to handlers
353
399
  * @param commandType - The command type being processed
400
+ * @param logger - Optional logger for observability
354
401
  * @returns 'ack' if successful or permanent failure, 'nack' if retriable failure
355
402
  */
356
- declare function handleCommandMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, commandType: string): Promise<'ack' | 'nack'>;
403
+ declare function handleCommandMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, commandType: string, logger?: Logger): Promise<'ack' | 'nack'>;
357
404
  /**
358
405
  * Process an incoming event message from PubSub
359
406
  *
360
407
  * @param message - The PubSub message
361
408
  * @param handlers - Map of message type to handlers
362
409
  * @param eventType - The event type being processed
410
+ * @param logger - Optional logger for observability
363
411
  * @returns 'ack' if all handlers successful or permanent failure, 'nack' if retriable failure
364
412
  */
365
- declare function handleEventMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, eventType: string): Promise<'ack' | 'nack'>;
413
+ declare function handleEventMessage(message: Message$1, handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, eventType: string, logger?: Logger): Promise<'ack' | 'nack'>;
366
414
  /**
367
415
  * Create a message listener for a PubSub subscription
368
416
  *
@@ -370,8 +418,9 @@ declare function handleEventMessage(message: Message$1, handlers: Map<string, Si
370
418
  * @param messageType - The message type (command or event type)
371
419
  * @param kind - Whether this is a command or event
372
420
  * @param handlers - Map of message type to handlers
421
+ * @param logger - Optional logger for observability
373
422
  */
374
- declare function createMessageListener(subscription: Subscription, messageType: string, kind: 'command' | 'event', handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>): void;
423
+ declare function createMessageListener(subscription: Subscription, messageType: string, kind: 'command' | 'event', handlers: Map<string, SingleRawMessageHandlerWithoutContext<AnyMessage>[]>, logger?: Logger): void;
375
424
 
376
425
  /**
377
426
  * Create a Google Cloud Pub/Sub based message bus for Emmett
@@ -410,4 +459,4 @@ declare function createMessageListener(subscription: Subscription, messageType:
410
459
  */
411
460
  declare function getPubSubMessageBus(config: PubSubMessageBusConfig): MessageBus & EventSubscription & CommandProcessor & ScheduledMessageProcessor & PubSubMessageBusLifecycle;
412
461
 
413
- export { type HandlerRegistration, MessageScheduler, type PubSubMessageBusConfig, type PubSubMessageBusLifecycle, type PubSubMessageEnvelope, type ScheduleOptions, type ScheduledMessage, type ScheduledMessageInfo, type SchedulerConfig, type SubscriptionInfo, type SubscriptionOptions, assertNotEmptyString, assertPositiveNumber, attachMessageId, calculateScheduledTime, createMessageListener, deleteSubscription, deleteSubscriptions, deserialize, extractMessageId, filterReadyMessages, generateUUID, getCommandSubscriptionName, getCommandTopicName, getEventSubscriptionName, getEventTopicName, getOrCreateSubscription, getOrCreateTopic, getPubSubMessageBus, handleCommandMessage, handleEventMessage, serialize, shouldRetry };
462
+ export { type HandlerRegistration, type Logger, MessageScheduler, type ObservabilityOptions, type PubSubMessageBusConfig, type PubSubMessageBusLifecycle, type PubSubMessageEnvelope, type ScheduleOptions, type ScheduledMessage, type ScheduledMessageInfo, type SchedulerConfig, type SubscriptionInfo, type SubscriptionOptions, assertNotEmptyString, assertPositiveNumber, attachMessageId, calculateScheduledTime, createMessageListener, deleteSubscription, deleteSubscriptions, deserialize, extractMessageId, filterReadyMessages, generateUUID, getCommandSubscriptionName, getCommandTopicName, getEventSubscriptionName, getEventTopicName, getOrCreateSubscription, getOrCreateTopic, getPubSubMessageBus, handleCommandMessage, handleEventMessage, serialize, shouldRetry };