@yuva-devlab/events 0.2.0 → 0.2.2

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 ADDED
@@ -0,0 +1,144 @@
1
+ # 📬 @yuva-devlab/events
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@yuva-devlab/events.svg)](https://www.npmjs.com/package/@yuva-devlab/events)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![TypeScript 6](https://img.shields.io/badge/TypeScript-6.x-3178c6.svg)](https://www.typescriptlang.org)
6
+
7
+ > Standardized domain event envelopes, in-memory event bus, transactional outbox pattern, and distributed idempotency stores for reliable event-driven architecture across the DevLab platform.
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - ✉️ **Standardized Event Envelopes (`DomainEvent`)**: Uniform structure with event ID, timestamp, topic, correlation context, and typed payload.
14
+ - 🚌 **In-Memory Event Bus (`InMemoryEventBus`)**: Decoupled publish/subscribe bus with asynchronous dispatch, wildcard topic matching, and error isolation.
15
+ - 📦 **Transactional Outbox Pattern (`OutboxRelay`, `IOutboxStore`)**: Guarantee at-least-once message delivery without dual-write race conditions.
16
+ - 🛡️ **Distributed Idempotency (`IdempotencyStore`)**: Deduplicate concurrent webhook deliveries, command executions, and payment callbacks.
17
+ - 🔍 **Correlation & Trace Propagation**: Carries `correlationId`, `causationId`, `tenantId`, and `userId` across event chains.
18
+ - 📦 **Dual ESM & CJS Build**: Clean consumption across Node.js event subscribers, serverless workers, and agent runtimes.
19
+
20
+ ---
21
+
22
+ ## Table of Contents
23
+
24
+ 1. [Installation](#installation)
25
+ 2. [Quick Start](#quick-start)
26
+ 3. [Domain Events & Envelopes](#domain-events--envelopes)
27
+ 4. [In-Memory Event Bus](#in-memory-event-bus)
28
+ 5. [Transactional Outbox Pattern](#transactional-outbox-pattern)
29
+ 6. [Idempotency Guard](#idempotency-guard)
30
+ 7. [Author & Attribution](#author--attribution)
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ # pnpm (recommended)
38
+ pnpm add @yuva-devlab/events
39
+
40
+ # npm
41
+ npm install @yuva-devlab/events
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ### Publishing and Subscribing to Events
49
+
50
+ ```typescript
51
+ import { InMemoryEventBus, createDomainEvent } from "@yuva-devlab/events";
52
+
53
+ // 1. Initialize bus
54
+ const bus = new InMemoryEventBus();
55
+
56
+ // 2. Subscribe to user signup events
57
+ bus.subscribe("user.created", async (event) => {
58
+ console.log(`Sending welcome email to: ${event.payload.email}`);
59
+ });
60
+
61
+ // 3. Publish an event
62
+ const event = createDomainEvent({
63
+ topic: "user.created",
64
+ source: "auth-service",
65
+ tenantId: "tenant_apex",
66
+ payload: {
67
+ userId: "usr_9912",
68
+ email: "alex@example.com",
69
+ role: "DEVELOPER",
70
+ },
71
+ });
72
+
73
+ await bus.publish(event);
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Domain Events & Envelopes
79
+
80
+ Every event follows a strict, observable schema:
81
+
82
+ ```typescript
83
+ export interface DomainEvent<T = unknown> {
84
+ id: string; // Unique event UUIDv4
85
+ topic: string; // Dot-separated topic (e.g., "billing.invoice.paid")
86
+ source: string; // Service origin (e.g., "billing-service")
87
+ timestamp: string; // ISO-8601 creation time
88
+ correlationId?: string; // Trace correlation across microservices
89
+ tenantId?: string; // Tenant boundary
90
+ payload: T; // Strongly typed payload
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Transactional Outbox Pattern
97
+
98
+ Eliminate the distributed dual-write problem by saving outbox records inside your primary database transaction and relaying them asynchronously:
99
+
100
+ ```typescript
101
+ import { OutboxRelay, IOutboxStore } from "@yuva-devlab/events";
102
+
103
+ // Implement or inject your database store (PostgreSQL / Prisma)
104
+ const store: IOutboxStore = getDatabaseOutboxStore();
105
+
106
+ const relay = new OutboxRelay(store, bus, {
107
+ batchSize: 50,
108
+ pollIntervalMs: 1000,
109
+ maxRetries: 5,
110
+ });
111
+
112
+ // Start background poller
113
+ await relay.start();
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Idempotency Guard
119
+
120
+ Prevent duplicate execution when webhooks or requests are retried:
121
+
122
+ ```typescript
123
+ import { InMemoryIdempotencyStore } from "@yuva-devlab/events";
124
+
125
+ const idempotency = new InMemoryIdempotencyStore({ ttlSeconds: 86400 });
126
+
127
+ async function handleWebhook(idempotencyKey: string, payload: unknown) {
128
+ const isDuplicate = await idempotency.has(idempotencyKey);
129
+ if (isDuplicate) {
130
+ console.warn(`Duplicate event received for key: ${idempotencyKey}`);
131
+ return;
132
+ }
133
+
134
+ await idempotency.record(idempotencyKey);
135
+ await processPayment(payload);
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Author & Attribution
142
+
143
+ - **Author**: Yuva DevLab (`yuva.devlab@gmail.com`)
144
+ - **License**: MIT
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contracts/envelope.ts","../src/bus/in-memory-event-bus.ts","../src/outbox/outbox-record.ts","../src/outbox/outbox-relay.ts","../src/idempotency/in-memory-idempotency-store.ts"],"names":["z","UUID_REGEX","logger"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @file packages/events/src/contracts/envelope.ts\n * @description Standard transactional outbox and event bus envelope schema for DevLab.\n *\n * Guarantees cross-service traceability, event ordering, and idempotency across all services.\n *\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { UUID_REGEX } from \"@yuva-devlab/regex\";\n\n/**\n * Standard Zod validator for RFC UUID strings.\n */\nexport const UuidSchema = z\n .string()\n .regex(UUID_REGEX, \"Must be a valid RFC UUID format\");\n\n/**\n * Zod schema defining the canonical metadata envelope wrapping all domain events.\n */\nexport const DomainEventEnvelopeSchema = z\n .object({\n /** Unique event occurrence UUID for deduplication */\n eventId: UuidSchema,\n /** Qualified dot-notated or uppercase event type (e.g. AGENT.EXECUTION.COMPLETED) */\n eventType: z.string().min(1, \"Event type must not be empty\"),\n /** Optional execution or correlation identifier */\n executionId: z.string().optional(),\n /** Optional tenant partition identifier */\n tenantId: z.string().optional(),\n /** Distributed trace identifier for end-to-end telemetry */\n traceId: z.string().optional(),\n /** ISO-8601 string or Date object representing event generation time */\n timestamp: z\n .string()\n .datetime()\n .default(() => new Date().toISOString()),\n /** Domain-specific event payload matching the eventType */\n payload: z.record(z.string(), z.unknown()),\n })\n .describe(\n \"Universal envelope for domain events transported via outbox, memory, or stream\",\n );\n\n/**\n * Inferred type of the canonical domain event envelope.\n */\nexport type DomainEventEnvelope<\n TPayload extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<z.infer<typeof DomainEventEnvelopeSchema>, \"payload\"> & {\n payload: TPayload;\n};\n\n/**\n * Factory options for generating a new DomainEventEnvelope.\n */\nexport interface CreateEventEnvelopeOptions<\n TPayload extends Record<string, unknown>,\n> {\n eventId?: string;\n eventType: string;\n payload: TPayload;\n executionId?: string;\n tenantId?: string;\n traceId?: string;\n timestamp?: string;\n}\n\n/**\n * Generates a valid domain event envelope with defaults.\n *\n * @param options - Factory configuration options.\n * @returns Fully constructed DomainEventEnvelope.\n */\nexport function createEventEnvelope<TPayload extends Record<string, unknown>>(\n options: CreateEventEnvelopeOptions<TPayload>,\n): DomainEventEnvelope<TPayload> {\n const envelope: DomainEventEnvelope<TPayload> = {\n eventId: options.eventId ?? crypto.randomUUID(),\n eventType: options.eventType,\n payload: options.payload,\n executionId: options.executionId,\n tenantId: options.tenantId,\n traceId: options.traceId,\n timestamp: options.timestamp ?? new Date().toISOString(),\n };\n\n // Validate envelope constraints against schema\n DomainEventEnvelopeSchema.parse(envelope);\n return envelope;\n}\n","/**\n * @file packages/events/src/bus/in-memory-event-bus.ts\n * @description Fast, non-blocking in-memory event bus implementation supporting wildcards.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type {\n DomainEventEnvelope,\n EventHandler,\n EventSubscription,\n IEventBus,\n} from \"../contracts\";\n\n/**\n * In-memory event bus with pattern matching, asynchronous execution, and fault isolation.\n */\nexport class InMemoryEventBus implements IEventBus {\n /** Map of event pattern to set of subscribed callback handlers */\n private readonly subscribers = new Map<string, Set<EventHandler>>();\n\n /**\n * Emits a single domain event envelope to all matching listeners.\n *\n * @param event - The validated event envelope.\n */\n public async publish(event: DomainEventEnvelope): Promise<void> {\n const matchingHandlers = this.getMatchingHandlers(event.eventType);\n\n // If no handlers are subscribed, return early\n if (matchingHandlers.length === 0) {\n return;\n }\n\n // Execute all handlers concurrently with isolated failure boundaries\n await Promise.allSettled(\n matchingHandlers.map(async (handler) => {\n try {\n await handler(event);\n } catch (error) {\n logger.error(\"Event handler threw an unhandled exception\", {\n eventType: event.eventType,\n eventId: event.eventId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }),\n );\n }\n\n /**\n * Emits an array of domain events sequentially or concurrently.\n *\n * @param events - List of event envelopes to publish.\n */\n public async publishBatch(events: DomainEventEnvelope[]): Promise<void> {\n for (const event of events) {\n await this.publish(event);\n }\n }\n\n /**\n * Registers a listener for an exact event type or wildcard pattern (*, domain.*).\n *\n * @param eventPattern - Topic pattern to subscribe to.\n * @param handler - Asynchronous callback handler.\n * @returns Unsubscribe handle.\n */\n public subscribe(\n eventPattern: string,\n handler: EventHandler,\n ): EventSubscription {\n if (!this.subscribers.has(eventPattern)) {\n this.subscribers.set(eventPattern, new Set());\n }\n\n const handlerSet = this.subscribers.get(eventPattern)!;\n handlerSet.add(handler);\n\n return {\n unsubscribe: () => {\n handlerSet.delete(handler);\n // Clean up empty key\n if (handlerSet.size === 0) {\n this.subscribers.delete(eventPattern);\n }\n },\n };\n }\n\n /**\n * Clears all active subscribers from memory.\n */\n public clear(): void {\n this.subscribers.clear();\n }\n\n /**\n * Resolves all handlers matching the given concrete event type.\n *\n * @param concreteType - The incoming eventType string.\n * @returns Array of unique matching handlers.\n */\n private getMatchingHandlers(concreteType: string): EventHandler[] {\n const matching = new Set<EventHandler>();\n\n for (const [pattern, handlers] of this.subscribers.entries()) {\n if (this.matchesPattern(pattern, concreteType)) {\n for (const handler of handlers) {\n matching.add(handler);\n }\n }\n }\n\n return Array.from(matching);\n }\n\n /**\n * Evaluates if a concrete event type matches a subscription pattern.\n * Supports global wildcard \"*\" and prefix wildcards \"prefix.*\".\n *\n * @param pattern - Registered pattern.\n * @param target - Concrete event type.\n * @returns True if matched.\n */\n private matchesPattern(pattern: string, target: string): boolean {\n // Exact match or universal catch-all\n if (pattern === \"*\" || pattern === target) {\n return true;\n }\n\n // Prefix wildcard match (e.g. \"AGENT.*\")\n if (pattern.endsWith(\".*\")) {\n const prefix = pattern.slice(0, -2);\n return target.startsWith(prefix + \".\");\n }\n\n return false;\n }\n}\n","/**\n * @file packages/events/src/outbox/outbox-record.ts\n * @description Outbox table record model and validation schema for the Transactional Outbox pattern.\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { DomainEventEnvelopeSchema } from \"../contracts/envelope\";\n\n/**\n * Lifecycle status of an outbox record awaiting dispatch.\n */\nexport const OutboxStatus = {\n PENDING: \"PENDING\",\n PROCESSING: \"PROCESSING\",\n PUBLISHED: \"PUBLISHED\",\n FAILED: \"FAILED\",\n} as const;\n\nexport type OutboxStatus = (typeof OutboxStatus)[keyof typeof OutboxStatus];\n\n/**\n * Zod schema validating a persisted outbox record.\n */\nexport const OutboxRecordSchema = z.object({\n id: z.string().uuid(\"Outbox record ID must be an RFC UUID\"),\n envelope: DomainEventEnvelopeSchema,\n status: z.enum([\n OutboxStatus.PENDING,\n OutboxStatus.PROCESSING,\n OutboxStatus.PUBLISHED,\n OutboxStatus.FAILED,\n ]),\n attempts: z.number().int().nonnegative().default(0),\n maxAttempts: z.number().int().positive().default(5),\n lastError: z.string().optional(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime(),\n publishedAt: z.string().datetime().optional(),\n});\n\nexport type OutboxRecord = z.infer<typeof OutboxRecordSchema>;\n","/**\n * @file packages/events/src/outbox/outbox-relay.ts\n * @description Background polling relay engine delivering transactional outbox events to the event bus.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type { IEventPublisher } from \"../contracts/event-bus.interface\";\nimport type { IOutboxStore } from \"./outbox-store.interface\";\n\n/**\n * Configuration options for the background outbox dispatch relay.\n */\nexport interface OutboxRelayOptions {\n /** Polling interval in milliseconds (default: 1000ms) */\n pollIntervalMs?: number;\n /** Max records to drain in each tick (default: 50) */\n batchSize?: number;\n}\n\n/**\n * Background poller ensuring guaranteed at-least-once delivery of outbox records.\n */\nexport class OutboxRelay {\n private isRunning = false;\n private timer: NodeJS.Timeout | null = null;\n private readonly pollIntervalMs: number;\n private readonly batchSize: number;\n\n constructor(\n private readonly store: IOutboxStore,\n private readonly publisher: IEventPublisher,\n options: OutboxRelayOptions = {},\n ) {\n this.pollIntervalMs = options.pollIntervalMs ?? 1000;\n this.batchSize = options.batchSize ?? 50;\n }\n\n /**\n * Starts the background relay polling loop.\n */\n public start(): void {\n if (this.isRunning) {\n return;\n }\n this.isRunning = true;\n this.scheduleNextTick();\n logger.info(\"OutboxRelay started\", {\n pollIntervalMs: this.pollIntervalMs,\n batchSize: this.batchSize,\n });\n }\n\n /**\n * Stops the background relay polling loop.\n */\n public stop(): void {\n this.isRunning = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n logger.info(\"OutboxRelay stopped\");\n }\n\n /**\n * Executes a single polling tick, draining pending outbox records.\n *\n * @returns Number of events successfully processed during this tick.\n */\n public async drainOnce(): Promise<number> {\n try {\n const records = await this.store.fetchPending(this.batchSize);\n if (records.length === 0) {\n return 0;\n }\n\n let processedCount = 0;\n for (const record of records) {\n try {\n await this.publisher.publish(record.envelope);\n await this.store.markPublished(record.id);\n processedCount++;\n } catch (publishError) {\n const errMsg =\n publishError instanceof Error\n ? publishError.message\n : String(publishError);\n logger.error(\"OutboxRelay failed to publish event\", {\n recordId: record.id,\n error: errMsg,\n });\n await this.store.markFailed(record.id, errMsg);\n }\n }\n\n return processedCount;\n } catch (storeError) {\n logger.error(\"OutboxRelay error fetching pending records\", {\n error:\n storeError instanceof Error ? storeError.message : String(storeError),\n });\n return 0;\n }\n }\n\n /**\n * Schedules the next execution tick with timer guard.\n */\n private scheduleNextTick(): void {\n if (!this.isRunning) {\n return;\n }\n this.timer = setTimeout(async () => {\n await this.drainOnce();\n this.scheduleNextTick();\n }, this.pollIntervalMs);\n }\n}\n","/**\n * @file packages/events/src/idempotency/in-memory-idempotency-store.ts\n * @description In-memory TTL idempotency store implementation for testing and single-node services.\n * @module @yuva-devlab/events\n */\n\nimport type {\n IIdempotencyStore,\n IdempotencyAcquireResult,\n} from \"./idempotency-store.interface\";\n\ninterface RecordEntry {\n state: \"IN_FLIGHT\" | \"COMPLETED\";\n expiresAt: number;\n completedAt?: string;\n}\n\n/**\n * Single-process in-memory idempotency store with active expiration.\n */\nexport class InMemoryIdempotencyStore implements IIdempotencyStore {\n private readonly records = new Map<string, RecordEntry>();\n\n public async acquire(\n key: string,\n ttlSeconds = 60,\n ): Promise<IdempotencyAcquireResult> {\n this.cleanExpired();\n const existing = this.records.get(key);\n\n if (existing) {\n if (existing.state === \"COMPLETED\") {\n return {\n state: \"ALREADY_COMPLETED\",\n completedAt: existing.completedAt ?? new Date().toISOString(),\n };\n }\n return { state: \"IN_FLIGHT\" };\n }\n\n // Acquire new in-flight lock\n this.records.set(key, {\n state: \"IN_FLIGHT\",\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n\n return { state: \"ACQUIRED\" };\n }\n\n public async markCompleted(\n key: string,\n retentionSeconds = 86400,\n ): Promise<void> {\n this.records.set(key, {\n state: \"COMPLETED\",\n completedAt: new Date().toISOString(),\n expiresAt: Date.now() + retentionSeconds * 1000,\n });\n }\n\n public async release(key: string): Promise<void> {\n this.records.delete(key);\n }\n\n private cleanExpired(): void {\n const now = Date.now();\n for (const [key, entry] of this.records.entries()) {\n if (entry.expiresAt <= now) {\n this.records.delete(key);\n }\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/contracts/envelope.ts","../src/bus/in-memory-event-bus.ts","../src/outbox/outbox-record.ts","../src/outbox/outbox-relay.ts","../src/idempotency/in-memory-idempotency-store.ts"],"names":["z","UUID_REGEX","logger"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @file packages/events/src/contracts/envelope.ts\n * @description Standard transactional outbox and event bus envelope schema for DevLab.\n *\n * Guarantees cross-service traceability, event ordering, and idempotency across all services.\n *\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { UUID_REGEX } from \"@yuva-devlab/regex\";\n\n/**\n * Standard Zod validator for RFC UUID strings.\n */\nexport const UuidSchema = z\n .string()\n .regex(UUID_REGEX, \"Must be a valid RFC UUID format\");\n\n/**\n * Zod schema defining the canonical metadata envelope wrapping all domain events.\n */\nexport const DomainEventEnvelopeSchema = z\n .object({\n /** Unique event occurrence UUID for deduplication */\n eventId: UuidSchema,\n /** Qualified dot-notated or uppercase event type (e.g. AGENT.EXECUTION.COMPLETED) */\n eventType: z.string().min(1, \"Event type must not be empty\"),\n /** Optional execution or correlation identifier */\n executionId: z.string().optional(),\n /** Optional tenant partition identifier */\n tenantId: z.string().optional(),\n /** Distributed trace identifier for end-to-end telemetry */\n traceId: z.string().optional(),\n /** ISO-8601 string or Date object representing event generation time */\n timestamp: z\n .string()\n .datetime()\n .default(() => new Date().toISOString()),\n /** Domain-specific event payload matching the eventType */\n payload: z.record(z.string(), z.unknown()),\n })\n .describe(\n \"Universal envelope for domain events transported via outbox, memory, or stream\",\n );\n\n/**\n * Inferred type of the canonical domain event envelope.\n */\nexport type DomainEventEnvelope<\n TPayload extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<z.infer<typeof DomainEventEnvelopeSchema>, \"payload\"> & {\n payload: TPayload;\n};\n\n/**\n * Factory options for generating a new DomainEventEnvelope.\n */\nexport interface CreateEventEnvelopeOptions<\n TPayload extends Record<string, unknown>,\n> {\n eventId?: string;\n eventType: string;\n payload: TPayload;\n executionId?: string;\n tenantId?: string;\n traceId?: string;\n timestamp?: string;\n}\n\n/**\n * Generates a valid domain event envelope with defaults.\n *\n * @param options - Factory configuration options.\n * @returns Fully constructed DomainEventEnvelope.\n */\nexport function createEventEnvelope<TPayload extends Record<string, unknown>>(\n options: CreateEventEnvelopeOptions<TPayload>,\n): DomainEventEnvelope<TPayload> {\n const envelope: DomainEventEnvelope<TPayload> = {\n eventId: options.eventId ?? crypto.randomUUID(),\n eventType: options.eventType,\n payload: options.payload,\n executionId: options.executionId,\n tenantId: options.tenantId,\n traceId: options.traceId,\n timestamp: options.timestamp ?? new Date().toISOString(),\n };\n\n // Validate envelope constraints against schema\n DomainEventEnvelopeSchema.parse(envelope);\n return envelope;\n}\n","/**\n * @file packages/events/src/bus/in-memory-event-bus.ts\n * @description Fast, non-blocking in-memory event bus implementation supporting wildcards.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type {\n DomainEventEnvelope,\n EventHandler,\n EventSubscription,\n IEventBus,\n} from \"@/contracts\";\n\n/**\n * In-memory event bus with pattern matching, asynchronous execution, and fault isolation.\n */\nexport class InMemoryEventBus implements IEventBus {\n /** Map of event pattern to set of subscribed callback handlers */\n private readonly subscribers = new Map<string, Set<EventHandler>>();\n\n /**\n * Emits a single domain event envelope to all matching listeners.\n *\n * @param event - The validated event envelope.\n */\n public async publish(event: DomainEventEnvelope): Promise<void> {\n const matchingHandlers = this.getMatchingHandlers(event.eventType);\n\n // If no handlers are subscribed, return early\n if (matchingHandlers.length === 0) {\n return;\n }\n\n // Execute all handlers concurrently with isolated failure boundaries\n await Promise.allSettled(\n matchingHandlers.map(async (handler) => {\n try {\n await handler(event);\n } catch (error) {\n logger.error(\"Event handler threw an unhandled exception\", {\n eventType: event.eventType,\n eventId: event.eventId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }),\n );\n }\n\n /**\n * Emits an array of domain events sequentially or concurrently.\n *\n * @param events - List of event envelopes to publish.\n */\n public async publishBatch(events: DomainEventEnvelope[]): Promise<void> {\n for (const event of events) {\n await this.publish(event);\n }\n }\n\n /**\n * Registers a listener for an exact event type or wildcard pattern (*, domain.*).\n *\n * @param eventPattern - Topic pattern to subscribe to.\n * @param handler - Asynchronous callback handler.\n * @returns Unsubscribe handle.\n */\n public subscribe(\n eventPattern: string,\n handler: EventHandler,\n ): EventSubscription {\n if (!this.subscribers.has(eventPattern)) {\n this.subscribers.set(eventPattern, new Set());\n }\n\n const handlerSet = this.subscribers.get(eventPattern)!;\n handlerSet.add(handler);\n\n return {\n unsubscribe: () => {\n handlerSet.delete(handler);\n // Clean up empty key\n if (handlerSet.size === 0) {\n this.subscribers.delete(eventPattern);\n }\n },\n };\n }\n\n /**\n * Clears all active subscribers from memory.\n */\n public clear(): void {\n this.subscribers.clear();\n }\n\n /**\n * Resolves all handlers matching the given concrete event type.\n *\n * @param concreteType - The incoming eventType string.\n * @returns Array of unique matching handlers.\n */\n private getMatchingHandlers(concreteType: string): EventHandler[] {\n const matching = new Set<EventHandler>();\n\n for (const [pattern, handlers] of this.subscribers.entries()) {\n if (this.matchesPattern(pattern, concreteType)) {\n for (const handler of handlers) {\n matching.add(handler);\n }\n }\n }\n\n return Array.from(matching);\n }\n\n /**\n * Evaluates if a concrete event type matches a subscription pattern.\n * Supports global wildcard \"*\" and prefix wildcards \"prefix.*\".\n *\n * @param pattern - Registered pattern.\n * @param target - Concrete event type.\n * @returns True if matched.\n */\n private matchesPattern(pattern: string, target: string): boolean {\n // Exact match or universal catch-all\n if (pattern === \"*\" || pattern === target) {\n return true;\n }\n\n // Prefix wildcard match (e.g. \"AGENT.*\")\n if (pattern.endsWith(\".*\")) {\n const prefix = pattern.slice(0, -2);\n return target.startsWith(prefix + \".\");\n }\n\n return false;\n }\n}\n","/**\n * @file packages/events/src/outbox/outbox-record.ts\n * @description Outbox table record model and validation schema for the Transactional Outbox pattern.\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { DomainEventEnvelopeSchema } from \"@/contracts/envelope\";\n\n/**\n * Lifecycle status of an outbox record awaiting dispatch.\n */\nexport const OutboxStatus = {\n PENDING: \"PENDING\",\n PROCESSING: \"PROCESSING\",\n PUBLISHED: \"PUBLISHED\",\n FAILED: \"FAILED\",\n} as const;\n\nexport type OutboxStatus = (typeof OutboxStatus)[keyof typeof OutboxStatus];\n\n/**\n * Zod schema validating a persisted outbox record.\n */\nexport const OutboxRecordSchema = z.object({\n id: z.string().uuid(\"Outbox record ID must be an RFC UUID\"),\n envelope: DomainEventEnvelopeSchema,\n status: z.enum([\n OutboxStatus.PENDING,\n OutboxStatus.PROCESSING,\n OutboxStatus.PUBLISHED,\n OutboxStatus.FAILED,\n ]),\n attempts: z.number().int().nonnegative().default(0),\n maxAttempts: z.number().int().positive().default(5),\n lastError: z.string().optional(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime(),\n publishedAt: z.string().datetime().optional(),\n});\n\nexport type OutboxRecord = z.infer<typeof OutboxRecordSchema>;\n","/**\n * @file packages/events/src/outbox/outbox-relay.ts\n * @description Background polling relay engine delivering transactional outbox events to the event bus.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type { IEventPublisher } from \"@/contracts/event-bus.interface\";\nimport type { IOutboxStore } from \"./outbox-store.interface\";\n\n/**\n * Configuration options for the background outbox dispatch relay.\n */\nexport interface OutboxRelayOptions {\n /** Polling interval in milliseconds (default: 1000ms) */\n pollIntervalMs?: number;\n /** Max records to drain in each tick (default: 50) */\n batchSize?: number;\n}\n\n/**\n * Background poller ensuring guaranteed at-least-once delivery of outbox records.\n */\nexport class OutboxRelay {\n private isRunning = false;\n private timer: NodeJS.Timeout | null = null;\n private readonly pollIntervalMs: number;\n private readonly batchSize: number;\n\n constructor(\n private readonly store: IOutboxStore,\n private readonly publisher: IEventPublisher,\n options: OutboxRelayOptions = {},\n ) {\n this.pollIntervalMs = options.pollIntervalMs ?? 1000;\n this.batchSize = options.batchSize ?? 50;\n }\n\n /**\n * Starts the background relay polling loop.\n */\n public start(): void {\n if (this.isRunning) {\n return;\n }\n this.isRunning = true;\n this.scheduleNextTick();\n logger.info(\"OutboxRelay started\", {\n pollIntervalMs: this.pollIntervalMs,\n batchSize: this.batchSize,\n });\n }\n\n /**\n * Stops the background relay polling loop.\n */\n public stop(): void {\n this.isRunning = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n logger.info(\"OutboxRelay stopped\");\n }\n\n /**\n * Executes a single polling tick, draining pending outbox records.\n *\n * @returns Number of events successfully processed during this tick.\n */\n public async drainOnce(): Promise<number> {\n try {\n const records = await this.store.fetchPending(this.batchSize);\n if (records.length === 0) {\n return 0;\n }\n\n let processedCount = 0;\n for (const record of records) {\n try {\n await this.publisher.publish(record.envelope);\n await this.store.markPublished(record.id);\n processedCount++;\n } catch (publishError) {\n const errMsg =\n publishError instanceof Error\n ? publishError.message\n : String(publishError);\n logger.error(\"OutboxRelay failed to publish event\", {\n recordId: record.id,\n error: errMsg,\n });\n await this.store.markFailed(record.id, errMsg);\n }\n }\n\n return processedCount;\n } catch (storeError) {\n logger.error(\"OutboxRelay error fetching pending records\", {\n error:\n storeError instanceof Error ? storeError.message : String(storeError),\n });\n return 0;\n }\n }\n\n /**\n * Schedules the next execution tick with timer guard.\n */\n private scheduleNextTick(): void {\n if (!this.isRunning) {\n return;\n }\n this.timer = setTimeout(async () => {\n await this.drainOnce();\n this.scheduleNextTick();\n }, this.pollIntervalMs);\n }\n}\n","/**\n * @file packages/events/src/idempotency/in-memory-idempotency-store.ts\n * @description In-memory TTL idempotency store implementation for testing and single-node services.\n * @module @yuva-devlab/events\n */\n\nimport type {\n IIdempotencyStore,\n IdempotencyAcquireResult,\n} from \"./idempotency-store.interface\";\n\ninterface RecordEntry {\n state: \"IN_FLIGHT\" | \"COMPLETED\";\n expiresAt: number;\n completedAt?: string;\n}\n\n/**\n * Single-process in-memory idempotency store with active expiration.\n */\nexport class InMemoryIdempotencyStore implements IIdempotencyStore {\n private readonly records = new Map<string, RecordEntry>();\n\n public async acquire(\n key: string,\n ttlSeconds = 60,\n ): Promise<IdempotencyAcquireResult> {\n this.cleanExpired();\n const existing = this.records.get(key);\n\n if (existing) {\n if (existing.state === \"COMPLETED\") {\n return {\n state: \"ALREADY_COMPLETED\",\n completedAt: existing.completedAt ?? new Date().toISOString(),\n };\n }\n return { state: \"IN_FLIGHT\" };\n }\n\n // Acquire new in-flight lock\n this.records.set(key, {\n state: \"IN_FLIGHT\",\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n\n return { state: \"ACQUIRED\" };\n }\n\n public async markCompleted(\n key: string,\n retentionSeconds = 86400,\n ): Promise<void> {\n this.records.set(key, {\n state: \"COMPLETED\",\n completedAt: new Date().toISOString(),\n expiresAt: Date.now() + retentionSeconds * 1000,\n });\n }\n\n public async release(key: string): Promise<void> {\n this.records.delete(key);\n }\n\n private cleanExpired(): void {\n const now = Date.now();\n for (const [key, entry] of this.records.entries()) {\n if (entry.expiresAt <= now) {\n this.records.delete(key);\n }\n }\n }\n}\n"]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contracts/envelope.ts","../src/bus/in-memory-event-bus.ts","../src/outbox/outbox-record.ts","../src/outbox/outbox-relay.ts","../src/idempotency/in-memory-idempotency-store.ts"],"names":["z","logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file packages/events/src/contracts/envelope.ts\n * @description Standard transactional outbox and event bus envelope schema for DevLab.\n *\n * Guarantees cross-service traceability, event ordering, and idempotency across all services.\n *\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { UUID_REGEX } from \"@yuva-devlab/regex\";\n\n/**\n * Standard Zod validator for RFC UUID strings.\n */\nexport const UuidSchema = z\n .string()\n .regex(UUID_REGEX, \"Must be a valid RFC UUID format\");\n\n/**\n * Zod schema defining the canonical metadata envelope wrapping all domain events.\n */\nexport const DomainEventEnvelopeSchema = z\n .object({\n /** Unique event occurrence UUID for deduplication */\n eventId: UuidSchema,\n /** Qualified dot-notated or uppercase event type (e.g. AGENT.EXECUTION.COMPLETED) */\n eventType: z.string().min(1, \"Event type must not be empty\"),\n /** Optional execution or correlation identifier */\n executionId: z.string().optional(),\n /** Optional tenant partition identifier */\n tenantId: z.string().optional(),\n /** Distributed trace identifier for end-to-end telemetry */\n traceId: z.string().optional(),\n /** ISO-8601 string or Date object representing event generation time */\n timestamp: z\n .string()\n .datetime()\n .default(() => new Date().toISOString()),\n /** Domain-specific event payload matching the eventType */\n payload: z.record(z.string(), z.unknown()),\n })\n .describe(\n \"Universal envelope for domain events transported via outbox, memory, or stream\",\n );\n\n/**\n * Inferred type of the canonical domain event envelope.\n */\nexport type DomainEventEnvelope<\n TPayload extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<z.infer<typeof DomainEventEnvelopeSchema>, \"payload\"> & {\n payload: TPayload;\n};\n\n/**\n * Factory options for generating a new DomainEventEnvelope.\n */\nexport interface CreateEventEnvelopeOptions<\n TPayload extends Record<string, unknown>,\n> {\n eventId?: string;\n eventType: string;\n payload: TPayload;\n executionId?: string;\n tenantId?: string;\n traceId?: string;\n timestamp?: string;\n}\n\n/**\n * Generates a valid domain event envelope with defaults.\n *\n * @param options - Factory configuration options.\n * @returns Fully constructed DomainEventEnvelope.\n */\nexport function createEventEnvelope<TPayload extends Record<string, unknown>>(\n options: CreateEventEnvelopeOptions<TPayload>,\n): DomainEventEnvelope<TPayload> {\n const envelope: DomainEventEnvelope<TPayload> = {\n eventId: options.eventId ?? crypto.randomUUID(),\n eventType: options.eventType,\n payload: options.payload,\n executionId: options.executionId,\n tenantId: options.tenantId,\n traceId: options.traceId,\n timestamp: options.timestamp ?? new Date().toISOString(),\n };\n\n // Validate envelope constraints against schema\n DomainEventEnvelopeSchema.parse(envelope);\n return envelope;\n}\n","/**\n * @file packages/events/src/bus/in-memory-event-bus.ts\n * @description Fast, non-blocking in-memory event bus implementation supporting wildcards.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type {\n DomainEventEnvelope,\n EventHandler,\n EventSubscription,\n IEventBus,\n} from \"../contracts\";\n\n/**\n * In-memory event bus with pattern matching, asynchronous execution, and fault isolation.\n */\nexport class InMemoryEventBus implements IEventBus {\n /** Map of event pattern to set of subscribed callback handlers */\n private readonly subscribers = new Map<string, Set<EventHandler>>();\n\n /**\n * Emits a single domain event envelope to all matching listeners.\n *\n * @param event - The validated event envelope.\n */\n public async publish(event: DomainEventEnvelope): Promise<void> {\n const matchingHandlers = this.getMatchingHandlers(event.eventType);\n\n // If no handlers are subscribed, return early\n if (matchingHandlers.length === 0) {\n return;\n }\n\n // Execute all handlers concurrently with isolated failure boundaries\n await Promise.allSettled(\n matchingHandlers.map(async (handler) => {\n try {\n await handler(event);\n } catch (error) {\n logger.error(\"Event handler threw an unhandled exception\", {\n eventType: event.eventType,\n eventId: event.eventId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }),\n );\n }\n\n /**\n * Emits an array of domain events sequentially or concurrently.\n *\n * @param events - List of event envelopes to publish.\n */\n public async publishBatch(events: DomainEventEnvelope[]): Promise<void> {\n for (const event of events) {\n await this.publish(event);\n }\n }\n\n /**\n * Registers a listener for an exact event type or wildcard pattern (*, domain.*).\n *\n * @param eventPattern - Topic pattern to subscribe to.\n * @param handler - Asynchronous callback handler.\n * @returns Unsubscribe handle.\n */\n public subscribe(\n eventPattern: string,\n handler: EventHandler,\n ): EventSubscription {\n if (!this.subscribers.has(eventPattern)) {\n this.subscribers.set(eventPattern, new Set());\n }\n\n const handlerSet = this.subscribers.get(eventPattern)!;\n handlerSet.add(handler);\n\n return {\n unsubscribe: () => {\n handlerSet.delete(handler);\n // Clean up empty key\n if (handlerSet.size === 0) {\n this.subscribers.delete(eventPattern);\n }\n },\n };\n }\n\n /**\n * Clears all active subscribers from memory.\n */\n public clear(): void {\n this.subscribers.clear();\n }\n\n /**\n * Resolves all handlers matching the given concrete event type.\n *\n * @param concreteType - The incoming eventType string.\n * @returns Array of unique matching handlers.\n */\n private getMatchingHandlers(concreteType: string): EventHandler[] {\n const matching = new Set<EventHandler>();\n\n for (const [pattern, handlers] of this.subscribers.entries()) {\n if (this.matchesPattern(pattern, concreteType)) {\n for (const handler of handlers) {\n matching.add(handler);\n }\n }\n }\n\n return Array.from(matching);\n }\n\n /**\n * Evaluates if a concrete event type matches a subscription pattern.\n * Supports global wildcard \"*\" and prefix wildcards \"prefix.*\".\n *\n * @param pattern - Registered pattern.\n * @param target - Concrete event type.\n * @returns True if matched.\n */\n private matchesPattern(pattern: string, target: string): boolean {\n // Exact match or universal catch-all\n if (pattern === \"*\" || pattern === target) {\n return true;\n }\n\n // Prefix wildcard match (e.g. \"AGENT.*\")\n if (pattern.endsWith(\".*\")) {\n const prefix = pattern.slice(0, -2);\n return target.startsWith(prefix + \".\");\n }\n\n return false;\n }\n}\n","/**\n * @file packages/events/src/outbox/outbox-record.ts\n * @description Outbox table record model and validation schema for the Transactional Outbox pattern.\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { DomainEventEnvelopeSchema } from \"../contracts/envelope\";\n\n/**\n * Lifecycle status of an outbox record awaiting dispatch.\n */\nexport const OutboxStatus = {\n PENDING: \"PENDING\",\n PROCESSING: \"PROCESSING\",\n PUBLISHED: \"PUBLISHED\",\n FAILED: \"FAILED\",\n} as const;\n\nexport type OutboxStatus = (typeof OutboxStatus)[keyof typeof OutboxStatus];\n\n/**\n * Zod schema validating a persisted outbox record.\n */\nexport const OutboxRecordSchema = z.object({\n id: z.string().uuid(\"Outbox record ID must be an RFC UUID\"),\n envelope: DomainEventEnvelopeSchema,\n status: z.enum([\n OutboxStatus.PENDING,\n OutboxStatus.PROCESSING,\n OutboxStatus.PUBLISHED,\n OutboxStatus.FAILED,\n ]),\n attempts: z.number().int().nonnegative().default(0),\n maxAttempts: z.number().int().positive().default(5),\n lastError: z.string().optional(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime(),\n publishedAt: z.string().datetime().optional(),\n});\n\nexport type OutboxRecord = z.infer<typeof OutboxRecordSchema>;\n","/**\n * @file packages/events/src/outbox/outbox-relay.ts\n * @description Background polling relay engine delivering transactional outbox events to the event bus.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type { IEventPublisher } from \"../contracts/event-bus.interface\";\nimport type { IOutboxStore } from \"./outbox-store.interface\";\n\n/**\n * Configuration options for the background outbox dispatch relay.\n */\nexport interface OutboxRelayOptions {\n /** Polling interval in milliseconds (default: 1000ms) */\n pollIntervalMs?: number;\n /** Max records to drain in each tick (default: 50) */\n batchSize?: number;\n}\n\n/**\n * Background poller ensuring guaranteed at-least-once delivery of outbox records.\n */\nexport class OutboxRelay {\n private isRunning = false;\n private timer: NodeJS.Timeout | null = null;\n private readonly pollIntervalMs: number;\n private readonly batchSize: number;\n\n constructor(\n private readonly store: IOutboxStore,\n private readonly publisher: IEventPublisher,\n options: OutboxRelayOptions = {},\n ) {\n this.pollIntervalMs = options.pollIntervalMs ?? 1000;\n this.batchSize = options.batchSize ?? 50;\n }\n\n /**\n * Starts the background relay polling loop.\n */\n public start(): void {\n if (this.isRunning) {\n return;\n }\n this.isRunning = true;\n this.scheduleNextTick();\n logger.info(\"OutboxRelay started\", {\n pollIntervalMs: this.pollIntervalMs,\n batchSize: this.batchSize,\n });\n }\n\n /**\n * Stops the background relay polling loop.\n */\n public stop(): void {\n this.isRunning = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n logger.info(\"OutboxRelay stopped\");\n }\n\n /**\n * Executes a single polling tick, draining pending outbox records.\n *\n * @returns Number of events successfully processed during this tick.\n */\n public async drainOnce(): Promise<number> {\n try {\n const records = await this.store.fetchPending(this.batchSize);\n if (records.length === 0) {\n return 0;\n }\n\n let processedCount = 0;\n for (const record of records) {\n try {\n await this.publisher.publish(record.envelope);\n await this.store.markPublished(record.id);\n processedCount++;\n } catch (publishError) {\n const errMsg =\n publishError instanceof Error\n ? publishError.message\n : String(publishError);\n logger.error(\"OutboxRelay failed to publish event\", {\n recordId: record.id,\n error: errMsg,\n });\n await this.store.markFailed(record.id, errMsg);\n }\n }\n\n return processedCount;\n } catch (storeError) {\n logger.error(\"OutboxRelay error fetching pending records\", {\n error:\n storeError instanceof Error ? storeError.message : String(storeError),\n });\n return 0;\n }\n }\n\n /**\n * Schedules the next execution tick with timer guard.\n */\n private scheduleNextTick(): void {\n if (!this.isRunning) {\n return;\n }\n this.timer = setTimeout(async () => {\n await this.drainOnce();\n this.scheduleNextTick();\n }, this.pollIntervalMs);\n }\n}\n","/**\n * @file packages/events/src/idempotency/in-memory-idempotency-store.ts\n * @description In-memory TTL idempotency store implementation for testing and single-node services.\n * @module @yuva-devlab/events\n */\n\nimport type {\n IIdempotencyStore,\n IdempotencyAcquireResult,\n} from \"./idempotency-store.interface\";\n\ninterface RecordEntry {\n state: \"IN_FLIGHT\" | \"COMPLETED\";\n expiresAt: number;\n completedAt?: string;\n}\n\n/**\n * Single-process in-memory idempotency store with active expiration.\n */\nexport class InMemoryIdempotencyStore implements IIdempotencyStore {\n private readonly records = new Map<string, RecordEntry>();\n\n public async acquire(\n key: string,\n ttlSeconds = 60,\n ): Promise<IdempotencyAcquireResult> {\n this.cleanExpired();\n const existing = this.records.get(key);\n\n if (existing) {\n if (existing.state === \"COMPLETED\") {\n return {\n state: \"ALREADY_COMPLETED\",\n completedAt: existing.completedAt ?? new Date().toISOString(),\n };\n }\n return { state: \"IN_FLIGHT\" };\n }\n\n // Acquire new in-flight lock\n this.records.set(key, {\n state: \"IN_FLIGHT\",\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n\n return { state: \"ACQUIRED\" };\n }\n\n public async markCompleted(\n key: string,\n retentionSeconds = 86400,\n ): Promise<void> {\n this.records.set(key, {\n state: \"COMPLETED\",\n completedAt: new Date().toISOString(),\n expiresAt: Date.now() + retentionSeconds * 1000,\n });\n }\n\n public async release(key: string): Promise<void> {\n this.records.delete(key);\n }\n\n private cleanExpired(): void {\n const now = Date.now();\n for (const [key, entry] of this.records.entries()) {\n if (entry.expiresAt <= now) {\n this.records.delete(key);\n }\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/contracts/envelope.ts","../src/bus/in-memory-event-bus.ts","../src/outbox/outbox-record.ts","../src/outbox/outbox-relay.ts","../src/idempotency/in-memory-idempotency-store.ts"],"names":["z","logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file packages/events/src/contracts/envelope.ts\n * @description Standard transactional outbox and event bus envelope schema for DevLab.\n *\n * Guarantees cross-service traceability, event ordering, and idempotency across all services.\n *\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { UUID_REGEX } from \"@yuva-devlab/regex\";\n\n/**\n * Standard Zod validator for RFC UUID strings.\n */\nexport const UuidSchema = z\n .string()\n .regex(UUID_REGEX, \"Must be a valid RFC UUID format\");\n\n/**\n * Zod schema defining the canonical metadata envelope wrapping all domain events.\n */\nexport const DomainEventEnvelopeSchema = z\n .object({\n /** Unique event occurrence UUID for deduplication */\n eventId: UuidSchema,\n /** Qualified dot-notated or uppercase event type (e.g. AGENT.EXECUTION.COMPLETED) */\n eventType: z.string().min(1, \"Event type must not be empty\"),\n /** Optional execution or correlation identifier */\n executionId: z.string().optional(),\n /** Optional tenant partition identifier */\n tenantId: z.string().optional(),\n /** Distributed trace identifier for end-to-end telemetry */\n traceId: z.string().optional(),\n /** ISO-8601 string or Date object representing event generation time */\n timestamp: z\n .string()\n .datetime()\n .default(() => new Date().toISOString()),\n /** Domain-specific event payload matching the eventType */\n payload: z.record(z.string(), z.unknown()),\n })\n .describe(\n \"Universal envelope for domain events transported via outbox, memory, or stream\",\n );\n\n/**\n * Inferred type of the canonical domain event envelope.\n */\nexport type DomainEventEnvelope<\n TPayload extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<z.infer<typeof DomainEventEnvelopeSchema>, \"payload\"> & {\n payload: TPayload;\n};\n\n/**\n * Factory options for generating a new DomainEventEnvelope.\n */\nexport interface CreateEventEnvelopeOptions<\n TPayload extends Record<string, unknown>,\n> {\n eventId?: string;\n eventType: string;\n payload: TPayload;\n executionId?: string;\n tenantId?: string;\n traceId?: string;\n timestamp?: string;\n}\n\n/**\n * Generates a valid domain event envelope with defaults.\n *\n * @param options - Factory configuration options.\n * @returns Fully constructed DomainEventEnvelope.\n */\nexport function createEventEnvelope<TPayload extends Record<string, unknown>>(\n options: CreateEventEnvelopeOptions<TPayload>,\n): DomainEventEnvelope<TPayload> {\n const envelope: DomainEventEnvelope<TPayload> = {\n eventId: options.eventId ?? crypto.randomUUID(),\n eventType: options.eventType,\n payload: options.payload,\n executionId: options.executionId,\n tenantId: options.tenantId,\n traceId: options.traceId,\n timestamp: options.timestamp ?? new Date().toISOString(),\n };\n\n // Validate envelope constraints against schema\n DomainEventEnvelopeSchema.parse(envelope);\n return envelope;\n}\n","/**\n * @file packages/events/src/bus/in-memory-event-bus.ts\n * @description Fast, non-blocking in-memory event bus implementation supporting wildcards.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type {\n DomainEventEnvelope,\n EventHandler,\n EventSubscription,\n IEventBus,\n} from \"@/contracts\";\n\n/**\n * In-memory event bus with pattern matching, asynchronous execution, and fault isolation.\n */\nexport class InMemoryEventBus implements IEventBus {\n /** Map of event pattern to set of subscribed callback handlers */\n private readonly subscribers = new Map<string, Set<EventHandler>>();\n\n /**\n * Emits a single domain event envelope to all matching listeners.\n *\n * @param event - The validated event envelope.\n */\n public async publish(event: DomainEventEnvelope): Promise<void> {\n const matchingHandlers = this.getMatchingHandlers(event.eventType);\n\n // If no handlers are subscribed, return early\n if (matchingHandlers.length === 0) {\n return;\n }\n\n // Execute all handlers concurrently with isolated failure boundaries\n await Promise.allSettled(\n matchingHandlers.map(async (handler) => {\n try {\n await handler(event);\n } catch (error) {\n logger.error(\"Event handler threw an unhandled exception\", {\n eventType: event.eventType,\n eventId: event.eventId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }),\n );\n }\n\n /**\n * Emits an array of domain events sequentially or concurrently.\n *\n * @param events - List of event envelopes to publish.\n */\n public async publishBatch(events: DomainEventEnvelope[]): Promise<void> {\n for (const event of events) {\n await this.publish(event);\n }\n }\n\n /**\n * Registers a listener for an exact event type or wildcard pattern (*, domain.*).\n *\n * @param eventPattern - Topic pattern to subscribe to.\n * @param handler - Asynchronous callback handler.\n * @returns Unsubscribe handle.\n */\n public subscribe(\n eventPattern: string,\n handler: EventHandler,\n ): EventSubscription {\n if (!this.subscribers.has(eventPattern)) {\n this.subscribers.set(eventPattern, new Set());\n }\n\n const handlerSet = this.subscribers.get(eventPattern)!;\n handlerSet.add(handler);\n\n return {\n unsubscribe: () => {\n handlerSet.delete(handler);\n // Clean up empty key\n if (handlerSet.size === 0) {\n this.subscribers.delete(eventPattern);\n }\n },\n };\n }\n\n /**\n * Clears all active subscribers from memory.\n */\n public clear(): void {\n this.subscribers.clear();\n }\n\n /**\n * Resolves all handlers matching the given concrete event type.\n *\n * @param concreteType - The incoming eventType string.\n * @returns Array of unique matching handlers.\n */\n private getMatchingHandlers(concreteType: string): EventHandler[] {\n const matching = new Set<EventHandler>();\n\n for (const [pattern, handlers] of this.subscribers.entries()) {\n if (this.matchesPattern(pattern, concreteType)) {\n for (const handler of handlers) {\n matching.add(handler);\n }\n }\n }\n\n return Array.from(matching);\n }\n\n /**\n * Evaluates if a concrete event type matches a subscription pattern.\n * Supports global wildcard \"*\" and prefix wildcards \"prefix.*\".\n *\n * @param pattern - Registered pattern.\n * @param target - Concrete event type.\n * @returns True if matched.\n */\n private matchesPattern(pattern: string, target: string): boolean {\n // Exact match or universal catch-all\n if (pattern === \"*\" || pattern === target) {\n return true;\n }\n\n // Prefix wildcard match (e.g. \"AGENT.*\")\n if (pattern.endsWith(\".*\")) {\n const prefix = pattern.slice(0, -2);\n return target.startsWith(prefix + \".\");\n }\n\n return false;\n }\n}\n","/**\n * @file packages/events/src/outbox/outbox-record.ts\n * @description Outbox table record model and validation schema for the Transactional Outbox pattern.\n * @module @yuva-devlab/events\n */\n\nimport { z } from \"zod\";\nimport { DomainEventEnvelopeSchema } from \"@/contracts/envelope\";\n\n/**\n * Lifecycle status of an outbox record awaiting dispatch.\n */\nexport const OutboxStatus = {\n PENDING: \"PENDING\",\n PROCESSING: \"PROCESSING\",\n PUBLISHED: \"PUBLISHED\",\n FAILED: \"FAILED\",\n} as const;\n\nexport type OutboxStatus = (typeof OutboxStatus)[keyof typeof OutboxStatus];\n\n/**\n * Zod schema validating a persisted outbox record.\n */\nexport const OutboxRecordSchema = z.object({\n id: z.string().uuid(\"Outbox record ID must be an RFC UUID\"),\n envelope: DomainEventEnvelopeSchema,\n status: z.enum([\n OutboxStatus.PENDING,\n OutboxStatus.PROCESSING,\n OutboxStatus.PUBLISHED,\n OutboxStatus.FAILED,\n ]),\n attempts: z.number().int().nonnegative().default(0),\n maxAttempts: z.number().int().positive().default(5),\n lastError: z.string().optional(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime(),\n publishedAt: z.string().datetime().optional(),\n});\n\nexport type OutboxRecord = z.infer<typeof OutboxRecordSchema>;\n","/**\n * @file packages/events/src/outbox/outbox-relay.ts\n * @description Background polling relay engine delivering transactional outbox events to the event bus.\n * @module @yuva-devlab/events\n */\n\nimport { logger } from \"@yuva-devlab/logger\";\nimport type { IEventPublisher } from \"@/contracts/event-bus.interface\";\nimport type { IOutboxStore } from \"./outbox-store.interface\";\n\n/**\n * Configuration options for the background outbox dispatch relay.\n */\nexport interface OutboxRelayOptions {\n /** Polling interval in milliseconds (default: 1000ms) */\n pollIntervalMs?: number;\n /** Max records to drain in each tick (default: 50) */\n batchSize?: number;\n}\n\n/**\n * Background poller ensuring guaranteed at-least-once delivery of outbox records.\n */\nexport class OutboxRelay {\n private isRunning = false;\n private timer: NodeJS.Timeout | null = null;\n private readonly pollIntervalMs: number;\n private readonly batchSize: number;\n\n constructor(\n private readonly store: IOutboxStore,\n private readonly publisher: IEventPublisher,\n options: OutboxRelayOptions = {},\n ) {\n this.pollIntervalMs = options.pollIntervalMs ?? 1000;\n this.batchSize = options.batchSize ?? 50;\n }\n\n /**\n * Starts the background relay polling loop.\n */\n public start(): void {\n if (this.isRunning) {\n return;\n }\n this.isRunning = true;\n this.scheduleNextTick();\n logger.info(\"OutboxRelay started\", {\n pollIntervalMs: this.pollIntervalMs,\n batchSize: this.batchSize,\n });\n }\n\n /**\n * Stops the background relay polling loop.\n */\n public stop(): void {\n this.isRunning = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n logger.info(\"OutboxRelay stopped\");\n }\n\n /**\n * Executes a single polling tick, draining pending outbox records.\n *\n * @returns Number of events successfully processed during this tick.\n */\n public async drainOnce(): Promise<number> {\n try {\n const records = await this.store.fetchPending(this.batchSize);\n if (records.length === 0) {\n return 0;\n }\n\n let processedCount = 0;\n for (const record of records) {\n try {\n await this.publisher.publish(record.envelope);\n await this.store.markPublished(record.id);\n processedCount++;\n } catch (publishError) {\n const errMsg =\n publishError instanceof Error\n ? publishError.message\n : String(publishError);\n logger.error(\"OutboxRelay failed to publish event\", {\n recordId: record.id,\n error: errMsg,\n });\n await this.store.markFailed(record.id, errMsg);\n }\n }\n\n return processedCount;\n } catch (storeError) {\n logger.error(\"OutboxRelay error fetching pending records\", {\n error:\n storeError instanceof Error ? storeError.message : String(storeError),\n });\n return 0;\n }\n }\n\n /**\n * Schedules the next execution tick with timer guard.\n */\n private scheduleNextTick(): void {\n if (!this.isRunning) {\n return;\n }\n this.timer = setTimeout(async () => {\n await this.drainOnce();\n this.scheduleNextTick();\n }, this.pollIntervalMs);\n }\n}\n","/**\n * @file packages/events/src/idempotency/in-memory-idempotency-store.ts\n * @description In-memory TTL idempotency store implementation for testing and single-node services.\n * @module @yuva-devlab/events\n */\n\nimport type {\n IIdempotencyStore,\n IdempotencyAcquireResult,\n} from \"./idempotency-store.interface\";\n\ninterface RecordEntry {\n state: \"IN_FLIGHT\" | \"COMPLETED\";\n expiresAt: number;\n completedAt?: string;\n}\n\n/**\n * Single-process in-memory idempotency store with active expiration.\n */\nexport class InMemoryIdempotencyStore implements IIdempotencyStore {\n private readonly records = new Map<string, RecordEntry>();\n\n public async acquire(\n key: string,\n ttlSeconds = 60,\n ): Promise<IdempotencyAcquireResult> {\n this.cleanExpired();\n const existing = this.records.get(key);\n\n if (existing) {\n if (existing.state === \"COMPLETED\") {\n return {\n state: \"ALREADY_COMPLETED\",\n completedAt: existing.completedAt ?? new Date().toISOString(),\n };\n }\n return { state: \"IN_FLIGHT\" };\n }\n\n // Acquire new in-flight lock\n this.records.set(key, {\n state: \"IN_FLIGHT\",\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n\n return { state: \"ACQUIRED\" };\n }\n\n public async markCompleted(\n key: string,\n retentionSeconds = 86400,\n ): Promise<void> {\n this.records.set(key, {\n state: \"COMPLETED\",\n completedAt: new Date().toISOString(),\n expiresAt: Date.now() + retentionSeconds * 1000,\n });\n }\n\n public async release(key: string): Promise<void> {\n this.records.delete(key);\n }\n\n private cleanExpired(): void {\n const now = Date.now();\n for (const [key, entry] of this.records.entries()) {\n if (entry.expiresAt <= now) {\n this.records.delete(key);\n }\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuva-devlab/events",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Standardized domain event envelopes, in-memory event bus, transactional outbox pattern, and idempotency for DevLab",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -25,20 +25,20 @@
25
25
  },
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
- "zod": "^3.25.76",
29
28
  "@yuva-devlab/errors": "0.2.0",
30
- "@yuva-devlab/logger": "1.2.0",
31
- "@yuva-devlab/regex": "0.2.0"
29
+ "@yuva-devlab/logger": "1.3.0",
30
+ "@yuva-devlab/regex": "0.3.0",
31
+ "zod": "^3.25.76"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^24.10.1",
35
+ "@yuva-devlab/eslint-config": "0.1.1",
36
+ "@yuva-devlab/prettier-config": "0.1.1",
37
+ "@yuva-devlab/typescript-config": "0.1.1",
35
38
  "eslint": "^9.39.1",
36
39
  "prettier": "^3.9.8",
37
40
  "tsup": "^8.5.1",
38
- "typescript": "^6.0.3",
39
- "@yuva-devlab/eslint-config": "0.1.1",
40
- "@yuva-devlab/prettier-config": "0.1.1",
41
- "@yuva-devlab/typescript-config": "0.1.1"
41
+ "typescript": "^6.0.3"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup",