@crossdelta/cloudevents 0.5.7 → 0.6.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.
Files changed (70) hide show
  1. package/README.md +20 -0
  2. package/dist/index.cjs +1602 -0
  3. package/dist/index.d.mts +812 -0
  4. package/dist/index.d.ts +812 -9
  5. package/dist/index.js +1574 -6
  6. package/package.json +20 -18
  7. package/dist/adapters/cloudevents/cloudevents.d.ts +0 -14
  8. package/dist/adapters/cloudevents/cloudevents.js +0 -58
  9. package/dist/adapters/cloudevents/index.d.ts +0 -8
  10. package/dist/adapters/cloudevents/index.js +0 -7
  11. package/dist/adapters/cloudevents/parsers/binary-mode.d.ts +0 -5
  12. package/dist/adapters/cloudevents/parsers/binary-mode.js +0 -32
  13. package/dist/adapters/cloudevents/parsers/pubsub.d.ts +0 -5
  14. package/dist/adapters/cloudevents/parsers/pubsub.js +0 -54
  15. package/dist/adapters/cloudevents/parsers/raw-event.d.ts +0 -5
  16. package/dist/adapters/cloudevents/parsers/raw-event.js +0 -17
  17. package/dist/adapters/cloudevents/parsers/structured-mode.d.ts +0 -5
  18. package/dist/adapters/cloudevents/parsers/structured-mode.js +0 -18
  19. package/dist/adapters/cloudevents/types.d.ts +0 -29
  20. package/dist/adapters/cloudevents/types.js +0 -1
  21. package/dist/domain/contract-helper.d.ts +0 -63
  22. package/dist/domain/contract-helper.js +0 -61
  23. package/dist/domain/discovery.d.ts +0 -24
  24. package/dist/domain/discovery.js +0 -201
  25. package/dist/domain/handler-factory.d.ts +0 -49
  26. package/dist/domain/handler-factory.js +0 -169
  27. package/dist/domain/index.d.ts +0 -6
  28. package/dist/domain/index.js +0 -4
  29. package/dist/domain/types.d.ts +0 -108
  30. package/dist/domain/types.js +0 -6
  31. package/dist/domain/validation.d.ts +0 -37
  32. package/dist/domain/validation.js +0 -53
  33. package/dist/infrastructure/errors.d.ts +0 -53
  34. package/dist/infrastructure/errors.js +0 -54
  35. package/dist/infrastructure/index.d.ts +0 -4
  36. package/dist/infrastructure/index.js +0 -2
  37. package/dist/infrastructure/logging.d.ts +0 -18
  38. package/dist/infrastructure/logging.js +0 -27
  39. package/dist/middlewares/cloudevents-middleware.d.ts +0 -171
  40. package/dist/middlewares/cloudevents-middleware.js +0 -276
  41. package/dist/middlewares/index.d.ts +0 -1
  42. package/dist/middlewares/index.js +0 -1
  43. package/dist/processing/dlq-safe.d.ts +0 -82
  44. package/dist/processing/dlq-safe.js +0 -108
  45. package/dist/processing/handler-cache.d.ts +0 -36
  46. package/dist/processing/handler-cache.js +0 -94
  47. package/dist/processing/idempotency.d.ts +0 -51
  48. package/dist/processing/idempotency.js +0 -112
  49. package/dist/processing/index.d.ts +0 -4
  50. package/dist/processing/index.js +0 -4
  51. package/dist/processing/validation.d.ts +0 -41
  52. package/dist/processing/validation.js +0 -48
  53. package/dist/publishing/index.d.ts +0 -2
  54. package/dist/publishing/index.js +0 -2
  55. package/dist/publishing/nats.publisher.d.ts +0 -19
  56. package/dist/publishing/nats.publisher.js +0 -115
  57. package/dist/publishing/pubsub.publisher.d.ts +0 -39
  58. package/dist/publishing/pubsub.publisher.js +0 -84
  59. package/dist/transports/nats/base-message-processor.d.ts +0 -44
  60. package/dist/transports/nats/base-message-processor.js +0 -118
  61. package/dist/transports/nats/index.d.ts +0 -5
  62. package/dist/transports/nats/index.js +0 -5
  63. package/dist/transports/nats/jetstream-consumer.d.ts +0 -217
  64. package/dist/transports/nats/jetstream-consumer.js +0 -367
  65. package/dist/transports/nats/jetstream-message-processor.d.ts +0 -9
  66. package/dist/transports/nats/jetstream-message-processor.js +0 -32
  67. package/dist/transports/nats/nats-consumer.d.ts +0 -36
  68. package/dist/transports/nats/nats-consumer.js +0 -84
  69. package/dist/transports/nats/nats-message-processor.d.ts +0 -11
  70. package/dist/transports/nats/nats-message-processor.js +0 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/cloudevents",
3
- "version": "0.5.7",
3
+ "version": "0.6.0",
4
4
  "description": "CloudEvents toolkit for TypeScript - Zod validation, handler discovery, NATS JetStream",
5
5
  "author": "crossdelta",
6
6
  "license": "MIT",
@@ -15,9 +15,9 @@
15
15
  "nestjs",
16
16
  "nest"
17
17
  ],
18
- "type": "module",
19
- "main": "dist/index.js",
20
- "types": "dist/index.d.ts",
18
+ "main": "./dist/index.cjs",
19
+ "module": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
21
  "files": [
22
22
  "dist"
23
23
  ],
@@ -25,25 +25,18 @@
25
25
  ".": {
26
26
  "types": "./dist/index.d.ts",
27
27
  "import": "./dist/index.js",
28
- "require": "./dist/index.js",
28
+ "require": "./dist/index.cjs",
29
29
  "default": "./dist/index.js"
30
- },
31
- "./transports/nats": {
32
- "types": "./dist/src/transports/nats/index.d.ts",
33
- "import": "./dist/src/transports/nats/index.js",
34
- "require": "./dist/src/transports/nats/index.js",
35
- "default": "./dist/src/transports/nats/index.js"
36
30
  }
37
31
  },
38
32
  "publishConfig": {
39
33
  "access": "public"
40
34
  },
41
35
  "scripts": {
42
- "build": "tsc -p tsconfig.json",
43
- "start:dev": "tsc -p tsconfig.json --watch",
36
+ "build": "tsup",
37
+ "start:dev": "tsup --watch",
44
38
  "stryker": "bun stryker run",
45
39
  "lint": "biome lint --fix",
46
- "pretest": "rm -rf dist",
47
40
  "test": "bun test",
48
41
  "prepublishOnly": "bun run build"
49
42
  },
@@ -52,18 +45,27 @@
52
45
  "glob": "^11.1.0",
53
46
  "nats": "^2.29.3"
54
47
  },
55
- "optionalDependencies": {
56
- "@google-cloud/pubsub": "5.2.0"
57
- },
58
48
  "peerDependencies": {
49
+ "@google-cloud/pubsub": ">=4.0.0",
59
50
  "hono": "^4.6.0",
60
51
  "zod": "^4.0.0"
61
52
  },
53
+ "peerDependenciesMeta": {
54
+ "@google-cloud/pubsub": {
55
+ "optional": true
56
+ },
57
+ "hono": {
58
+ "optional": true
59
+ }
60
+ },
62
61
  "trustedDependencies": [],
63
62
  "devDependencies": {
64
- "@types/bun": "^1.3.3",
63
+ "@google-cloud/pubsub": "^4.9.0",
65
64
  "@stryker-mutator/core": "^9.1.1",
65
+ "@types/bun": "^1.3.3",
66
+ "hono": "^4.6.11",
66
67
  "stryker-mutator-bun-runner": "^0.4.0",
68
+ "tsup": "^8.3.5",
67
69
  "typescript": "^5.9.2"
68
70
  }
69
71
  }
@@ -1,14 +0,0 @@
1
- import type { Context } from 'hono';
2
- import type { CloudEventParseResult } from './types';
3
- /**
4
- * Parses CloudEvent from Hono context with automatic format detection.
5
- *
6
- * Supports:
7
- * 1. Structured CloudEvent (body with specversion)
8
- * 2. Pub/Sub push message format (body with message field)
9
- * 3. Binary CloudEvent (CloudEvent headers)
10
- * 4. Raw event data (fallback)
11
- *
12
- * Uses dynamic imports to load only necessary parsers for optimal performance.
13
- */
14
- export declare const parseEventFromContext: (context: Context) => Promise<CloudEventParseResult>;
@@ -1,58 +0,0 @@
1
- import { logger } from '../../infrastructure';
2
- /**
3
- * Checks if request has CloudEvent headers (for binary mode)
4
- */
5
- const hasCloudEventHeaders = (headers) => Object.keys(headers).some((key) => key.toLowerCase().startsWith('ce-'));
6
- const isRecord = (value) => typeof value === 'object' && value !== null;
7
- /**
8
- * Parses CloudEvent from Hono context with automatic format detection.
9
- *
10
- * Supports:
11
- * 1. Structured CloudEvent (body with specversion)
12
- * 2. Pub/Sub push message format (body with message field)
13
- * 3. Binary CloudEvent (CloudEvent headers)
14
- * 4. Raw event data (fallback)
15
- *
16
- * Uses dynamic imports to load only necessary parsers for optimal performance.
17
- */
18
- export const parseEventFromContext = async (context) => {
19
- try {
20
- const headers = context.req.header();
21
- const rawBody = await context.req.text();
22
- let parsedBody;
23
- if (rawBody?.length) {
24
- try {
25
- parsedBody = JSON.parse(rawBody);
26
- }
27
- catch {
28
- parsedBody = undefined;
29
- }
30
- }
31
- const bodyObject = isRecord(parsedBody) ? parsedBody : undefined;
32
- // 1. Structured CloudEvent (has specversion in body)
33
- if (bodyObject && 'specversion' in bodyObject) {
34
- const { parseStructuredMode } = await import('./parsers/structured-mode');
35
- return parseStructuredMode(bodyObject);
36
- }
37
- // 2. PubSub message format (has message field)
38
- if (bodyObject && 'message' in bodyObject) {
39
- const { parsePubSubMessage } = await import('./parsers/pubsub');
40
- return await parsePubSubMessage(bodyObject, headers);
41
- }
42
- // 3. Binary CloudEvent (CloudEvent headers, no specversion in body)
43
- if (hasCloudEventHeaders(headers)) {
44
- const { parseBinaryMode } = await import('./parsers/binary-mode');
45
- return await parseBinaryMode(headers, rawBody);
46
- }
47
- // 4. Raw event data (fallback)
48
- const { parseRawEvent } = await import('./parsers/raw-event');
49
- if (bodyObject) {
50
- return parseRawEvent(bodyObject);
51
- }
52
- return parseRawEvent({ raw: rawBody });
53
- }
54
- catch (error) {
55
- logger.error('Failed to parse event:', error);
56
- throw new Error(`Failed to parse CloudEvent: ${error instanceof Error ? error.message : 'Unknown error'}`);
57
- }
58
- };
@@ -1,8 +0,0 @@
1
- /**
2
- * Adapters Module - External Protocol Handlers
3
- *
4
- * Entry point for all protocol adapters with automatic format detection
5
- * and dynamic loading for optimal performance.
6
- */
7
- export * from './cloudevents';
8
- export type { CloudEventParseResult, EventContext } from './types';
@@ -1,7 +0,0 @@
1
- /**
2
- * Adapters Module - External Protocol Handlers
3
- *
4
- * Entry point for all protocol adapters with automatic format detection
5
- * and dynamic loading for optimal performance.
6
- */
7
- export * from './cloudevents';
@@ -1,5 +0,0 @@
1
- import type { CloudEventParseResult } from '../types';
2
- /**
3
- * Parse binary mode CloudEvent using CloudEvents SDK
4
- */
5
- export declare const parseBinaryMode: (headers: Record<string, string | undefined>, rawBody: string) => Promise<CloudEventParseResult>;
@@ -1,32 +0,0 @@
1
- import { HTTP } from 'cloudevents';
2
- import { createCloudEventParseError, logger } from '../../../infrastructure';
3
- /**
4
- * Parse binary mode CloudEvent using CloudEvents SDK
5
- */
6
- export const parseBinaryMode = async (headers, rawBody) => {
7
- const normalizedHeaders = {};
8
- Object.entries(headers).forEach(([key, value]) => {
9
- if (typeof value === 'string') {
10
- normalizedHeaders[key] = value;
11
- }
12
- });
13
- const body = rawBody ?? '';
14
- try {
15
- const event = HTTP.toEvent({ headers: normalizedHeaders, body });
16
- const finalEvent = Array.isArray(event) ? event[0] : event;
17
- const cloudEventData = {
18
- id: finalEvent.id,
19
- type: finalEvent.type,
20
- specversion: finalEvent.specversion,
21
- source: finalEvent.source,
22
- subject: finalEvent.subject,
23
- time: finalEvent.time || new Date().toISOString(),
24
- data: finalEvent.data,
25
- };
26
- return { event: finalEvent, cloudEventData };
27
- }
28
- catch (error) {
29
- logger.error('Binary mode parsing failed:', error);
30
- throw createCloudEventParseError(`Binary mode parsing failed: ${error}`);
31
- }
32
- };
@@ -1,5 +0,0 @@
1
- import type { CloudEventParseResult } from '../types';
2
- /**
3
- * Parses raw PubSub message into CloudEvent wrapper format
4
- */
5
- export declare const parsePubSubMessage: (rawBody: Record<string, unknown>, headers: Record<string, string | undefined>) => Promise<CloudEventParseResult>;
@@ -1,54 +0,0 @@
1
- import { logger } from '../../../infrastructure';
2
- /**
3
- * Parses raw PubSub message into CloudEvent wrapper format
4
- */
5
- export const parsePubSubMessage = async (rawBody, headers) => {
6
- const body = rawBody;
7
- if (!body.message?.data) {
8
- throw new Error('Invalid PubSub message: missing message.data');
9
- }
10
- // Try to extract real event type from message attributes or decoded data
11
- let eventType = 'google.cloud.pubsub.topic.v1.messagePublished';
12
- let decodedData = body;
13
- try {
14
- // Always try to decode the base64 data first
15
- const base64Data = body.message.data;
16
- const decodedString = Buffer.from(base64Data, 'base64').toString('utf8');
17
- const parsedData = JSON.parse(decodedString);
18
- // Check for event type in attributes first, then in parsed data
19
- if (body.message.attributes?.eventType) {
20
- eventType = body.message.attributes.eventType;
21
- decodedData = parsedData;
22
- }
23
- else if (body.message.attributes?.type) {
24
- eventType = body.message.attributes.type;
25
- decodedData = parsedData;
26
- }
27
- else if (parsedData.type) {
28
- eventType = parsedData.type;
29
- decodedData = parsedData;
30
- }
31
- else {
32
- // If no type found, use decoded data but keep default type
33
- decodedData = parsedData;
34
- }
35
- }
36
- catch (error) {
37
- // If decoding fails, keep original type and data
38
- logger.warn('Failed to extract event type from PubSub message:', error);
39
- }
40
- const messageId = body.message.messageId || 'unknown';
41
- const eventSource = headers['ce-source'] || '//pubsub.googleapis.com';
42
- const subject = headers['ce-subject'];
43
- const publishTime = headers['ce-time'];
44
- const cloudEventData = {
45
- id: messageId,
46
- type: eventType,
47
- specversion: '1.0',
48
- source: eventSource,
49
- subject: subject,
50
- time: publishTime || new Date().toISOString(),
51
- data: decodedData,
52
- };
53
- return { event: null, cloudEventData };
54
- };
@@ -1,5 +0,0 @@
1
- import type { CloudEventParseResult } from '../types';
2
- /**
3
- * Parse raw event data and wrap it in CloudEvent structure
4
- */
5
- export declare const parseRawEvent: (body: Record<string, unknown>) => CloudEventParseResult;
@@ -1,17 +0,0 @@
1
- import { logger } from '../../../infrastructure';
2
- /**
3
- * Parse raw event data and wrap it in CloudEvent structure
4
- */
5
- export const parseRawEvent = (body) => {
6
- logger.info('Creating CloudEvent wrapper for raw data');
7
- const cloudEventData = {
8
- id: `raw-${Date.now()}`,
9
- type: body.type || 'unknown.event',
10
- specversion: '1.0',
11
- source: '//direct.post',
12
- subject: undefined,
13
- time: new Date().toISOString(),
14
- data: body,
15
- };
16
- return { event: null, cloudEventData };
17
- };
@@ -1,5 +0,0 @@
1
- import type { CloudEventParseResult } from '../types';
2
- /**
3
- * Parse structured mode CloudEvent (CloudEvents JSON in body)
4
- */
5
- export declare const parseStructuredMode: (body: Record<string, unknown>) => CloudEventParseResult;
@@ -1,18 +0,0 @@
1
- import { logger } from '../../../infrastructure';
2
- /**
3
- * Parse structured mode CloudEvent (CloudEvents JSON in body)
4
- */
5
- export const parseStructuredMode = (body) => {
6
- logger.info('Structured mode CloudEvent detected');
7
- // For structured CloudEvents, use data directly since it's already in CloudEvent format
8
- const cloudEventData = {
9
- id: String(body.id || 'unknown'),
10
- type: String(body.type || 'unknown.event'),
11
- specversion: String(body.specversion || '1.0'),
12
- source: String(body.source || '//unknown'),
13
- subject: body.subject ? String(body.subject) : undefined,
14
- time: String(body.time) || new Date().toISOString(),
15
- data: body.data,
16
- };
17
- return { event: null, cloudEventData };
18
- };
@@ -1,29 +0,0 @@
1
- /**
2
- * Adapter types for external protocols
3
- */
4
- import type { CloudEventV1 } from 'cloudevents';
5
- /**
6
- * Simplified event context for handler consumption
7
- *
8
- * Provides essential CloudEvent metadata without the complexity of the full CloudEventV1 interface.
9
- * This is what handler functions receive as context parameter.
10
- */
11
- export interface EventContext {
12
- /** Event type identifier (from CloudEvent.type) */
13
- eventType: string;
14
- /** Event source (from CloudEvent.source) */
15
- source: string;
16
- /** Optional subject (from CloudEvent.subject) */
17
- subject?: string;
18
- /** Event timestamp (from CloudEvent.time) */
19
- time: string;
20
- /** Message ID for deduplication (from CloudEvent.id) */
21
- messageId?: string;
22
- }
23
- /**
24
- * Result of CloudEvent parsing - either CloudEvents SDK object or normalized data
25
- */
26
- export interface CloudEventParseResult {
27
- event: CloudEventV1<unknown> | null;
28
- cloudEventData: CloudEventV1<unknown> | null;
29
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,63 +0,0 @@
1
- import type { ZodTypeAny } from 'zod';
2
- import type { ChannelMetadata, HandleEventOptions } from './types';
3
- /**
4
- * Creates a type-safe event contract with optional channel metadata
5
- *
6
- * This helper ensures proper type inference when using contracts
7
- * with handleEvent across package boundaries.
8
- *
9
- * @example
10
- * Basic contract without channel:
11
- * ```typescript
12
- * export const OrderCreatedContract = createContract({
13
- * type: 'orders.created',
14
- * schema: z.object({
15
- * orderId: z.string(),
16
- * total: z.number(),
17
- * }),
18
- * })
19
- * ```
20
- *
21
- * @example
22
- * Contract with channel metadata:
23
- * ```typescript
24
- * export const OrderCreatedContract = createContract({
25
- * type: 'orders.created',
26
- * channel: {
27
- * stream: 'ORDERS',
28
- * // subject defaults to 'orders.created' if omitted
29
- * },
30
- * schema: z.object({
31
- * orderId: z.string(),
32
- * total: z.number(),
33
- * }),
34
- * })
35
- * ```
36
- *
37
- * @example
38
- * Contract with explicit subject:
39
- * ```typescript
40
- * export const OrderCreatedContract = createContract({
41
- * type: 'orders.created',
42
- * channel: {
43
- * stream: 'ORDERS',
44
- * subject: 'orders.v1.created', // Override default
45
- * },
46
- * schema: OrderSchema,
47
- * })
48
- * ```
49
- */
50
- export declare function createContract<TSchema extends ZodTypeAny>(options: {
51
- type: string;
52
- schema: TSchema;
53
- match?: HandleEventOptions['match'];
54
- safeParse?: boolean;
55
- tenantId?: string | string[];
56
- channel?: {
57
- stream: string;
58
- subject?: string;
59
- };
60
- }): HandleEventOptions<TSchema> & {
61
- _schema: TSchema;
62
- channel?: ChannelMetadata;
63
- };
@@ -1,61 +0,0 @@
1
- /**
2
- * Creates a type-safe event contract with optional channel metadata
3
- *
4
- * This helper ensures proper type inference when using contracts
5
- * with handleEvent across package boundaries.
6
- *
7
- * @example
8
- * Basic contract without channel:
9
- * ```typescript
10
- * export const OrderCreatedContract = createContract({
11
- * type: 'orders.created',
12
- * schema: z.object({
13
- * orderId: z.string(),
14
- * total: z.number(),
15
- * }),
16
- * })
17
- * ```
18
- *
19
- * @example
20
- * Contract with channel metadata:
21
- * ```typescript
22
- * export const OrderCreatedContract = createContract({
23
- * type: 'orders.created',
24
- * channel: {
25
- * stream: 'ORDERS',
26
- * // subject defaults to 'orders.created' if omitted
27
- * },
28
- * schema: z.object({
29
- * orderId: z.string(),
30
- * total: z.number(),
31
- * }),
32
- * })
33
- * ```
34
- *
35
- * @example
36
- * Contract with explicit subject:
37
- * ```typescript
38
- * export const OrderCreatedContract = createContract({
39
- * type: 'orders.created',
40
- * channel: {
41
- * stream: 'ORDERS',
42
- * subject: 'orders.v1.created', // Override default
43
- * },
44
- * schema: OrderSchema,
45
- * })
46
- * ```
47
- */
48
- export function createContract(options) {
49
- // Resolve channel metadata: default subject to type if not provided
50
- const resolvedChannel = options.channel
51
- ? {
52
- stream: options.channel.stream,
53
- subject: options.channel.subject ?? options.type,
54
- }
55
- : undefined;
56
- return {
57
- ...options,
58
- _schema: options.schema,
59
- channel: resolvedChannel,
60
- };
61
- }
@@ -1,24 +0,0 @@
1
- import type { HandlerConstructor } from './types';
2
- /**
3
- * Discovers event handlers from files matching the given glob pattern.
4
- *
5
- * @param pattern - Glob pattern to match handler files (e.g., 'events/*.handler.ts')
6
- * @param options - Configuration options
7
- * @returns Promise resolving to array of discovered handler constructors
8
- *
9
- * @example
10
- * ```typescript
11
- * // Discover all handlers in events directory
12
- * const handlers = await discoverHandlers('events/*.handler.ts')
13
- *
14
- * // With filtering and logging
15
- * const handlers = await discoverHandlers('events/*.handler.ts', {
16
- * filter: (name, handler) => name.includes('Customer'),
17
- * log: true
18
- * })
19
- * ```
20
- */
21
- export declare const discoverHandlers: (pattern: string, options?: {
22
- filter?: (name: string, handler: unknown) => boolean;
23
- log?: boolean;
24
- }) => Promise<HandlerConstructor[]>;