@yuva-devlab/events 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +297 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +425 -0
- package/dist/index.d.ts +425 -0
- package/dist/index.js +288 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -3
- package/README.md +0 -3
|
@@ -0,0 +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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuva-devlab/events",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Standardized domain event envelopes, in-memory event bus, transactional outbox pattern, and idempotency for DevLab",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": {
|
|
12
|
+
"import": "./dist/index.d.ts",
|
|
13
|
+
"require": "./dist/index.d.cts"
|
|
14
|
+
},
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@yuva-devlab/errors": "0.2.0",
|
|
29
|
+
"@yuva-devlab/logger": "1.2.0",
|
|
30
|
+
"@yuva-devlab/regex": "0.3.0",
|
|
31
|
+
"zod": "^3.25.76"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
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",
|
|
38
|
+
"eslint": "^9.39.1",
|
|
39
|
+
"prettier": "^3.9.8",
|
|
40
|
+
"tsup": "^8.5.1",
|
|
41
|
+
"typescript": "^6.0.3"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup",
|
|
45
|
+
"dev": "tsup --watch",
|
|
46
|
+
"lint": "eslint src",
|
|
47
|
+
"lint:fix": "eslint src --fix",
|
|
48
|
+
"format": "prettier --write .",
|
|
49
|
+
"format:check": "prettier --check .",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"clean": "rm -rf dist node_modules .turbo"
|
|
52
|
+
}
|
|
6
53
|
}
|
package/README.md
DELETED