@crossdelta/cloudevents 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,15 +35,23 @@ bun add @crossdelta/cloudevents zod@4
35
35
  import { handleEvent } from '@crossdelta/cloudevents'
36
36
  import { z } from 'zod'
37
37
 
38
- export default handleEvent({
39
- type: 'orders.created',
40
- schema: z.object({
41
- orderId: z.string(),
42
- total: z.number(),
43
- }),
44
- }, async (data) => {
45
- console.log(`New order: ${data.orderId}, total: ${data.total}`)
38
+ const OrderCreatedSchema = z.object({
39
+ orderId: z.string(),
40
+ total: z.number(),
46
41
  })
42
+
43
+ // Export type for use in use-cases
44
+ export type OrderCreatedEvent = z.infer<typeof OrderCreatedSchema>
45
+
46
+ export default handleEvent(
47
+ {
48
+ schema: OrderCreatedSchema,
49
+ type: 'orders.created',
50
+ },
51
+ async (data) => {
52
+ console.log(`New order: ${data.orderId}, total: ${data.total}`)
53
+ },
54
+ )
47
55
  ```
48
56
 
49
57
  **2. Start consuming:**
@@ -84,18 +92,52 @@ That's it. Handlers are auto-discovered, validated with Zod, and messages persis
84
92
 
85
93
  ## Core Concepts
86
94
 
95
+ ### Event Type vs. Event Data
96
+
97
+ **Important distinction:**
98
+
99
+ - **Event Type** (`orders.created`): Lives in the CloudEvent **envelope** (`ce.type`). Used for routing and handler matching.
100
+ - **Event Data** (`{ orderId, total }`): The actual payload. Does **not** include the type.
101
+
102
+ ```typescript
103
+ const Schema = z.object({
104
+ orderId: z.string(),
105
+ })
106
+
107
+ export default handleEvent(
108
+ {
109
+ schema: Schema,
110
+ type: 'orders.created',
111
+ },
112
+ async (data) => { ... }
113
+ )
114
+ ```
115
+
87
116
  ### Handlers
88
117
 
89
118
  Drop a `*.event.ts` file anywhere — it's auto-registered:
90
119
 
91
120
  ```typescript
92
121
  // src/handlers/user-signup.event.ts
93
- export default handleEvent({
94
- type: 'users.signup',
95
- schema: z.object({ email: z.string().email() }),
96
- }, async (data) => {
97
- await sendWelcomeEmail(data.email)
122
+ import { z } from 'zod'
123
+
124
+ const UserSignupSchema = z.object({
125
+ email: z.string().email(),
126
+ name: z.string(),
98
127
  })
128
+
129
+ // Export type for use in use-cases
130
+ export type UserSignupEvent = z.infer<typeof UserSignupSchema>
131
+
132
+ export default handleEvent(
133
+ {
134
+ schema: UserSignupSchema,
135
+ type: 'users.signup',
136
+ },
137
+ async (data) => {
138
+ await sendWelcomeEmail(data.email)
139
+ },
140
+ )
99
141
  ```
100
142
 
101
143
  ### Publishing
@@ -19,6 +19,23 @@ export const quarantineMessage = async (processingContext, reason, options, erro
19
19
  return;
20
20
  }
21
21
  try {
22
+ // Serialize error properly - handle ValidationError specially
23
+ let serializedError;
24
+ if (error) {
25
+ if (typeof error === 'object' && error !== null && 'type' in error && error.type === 'ValidationError') {
26
+ serializedError = JSON.stringify(error, null, 2);
27
+ }
28
+ else if (error instanceof Error) {
29
+ serializedError = JSON.stringify({
30
+ name: error.name,
31
+ message: error.message,
32
+ stack: error.stack,
33
+ }, null, 2);
34
+ }
35
+ else {
36
+ serializedError = JSON.stringify(error, null, 2);
37
+ }
38
+ }
22
39
  const quarantineData = {
23
40
  originalMessageId: processingContext.messageId,
24
41
  originalEventType: processingContext.eventType,
@@ -27,7 +44,7 @@ export const quarantineMessage = async (processingContext, reason, options, erro
27
44
  originalCloudEvent: processingContext.originalCloudEvent,
28
45
  quarantinedAt: new Date().toISOString(),
29
46
  quarantineReason: reason,
30
- error: error ? String(error) : undefined,
47
+ error: serializedError,
31
48
  };
32
49
  await publishRawEvent(options.quarantineTopic, 'hono.cloudevents.quarantined', quarantineData, {
33
50
  projectId: options.projectId,
@@ -40,6 +40,8 @@ export function createBaseMessageProcessor(deps) {
40
40
  return { handled: true, shouldAck: true };
41
41
  };
42
42
  const handleValidationFailure = async (validationResult, handler, context) => {
43
+ // Log validation errors with full details for debugging
44
+ logger.error(`[${name}] validation failed for handler ${handler.name}`, JSON.stringify(validationResult.error, null, 2));
43
45
  if (dlqEnabled) {
44
46
  await quarantineMessage(context, 'validation_error', options, validationResult.error);
45
47
  return { handled: true, shouldAck: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/cloudevents",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "CloudEvents toolkit for TypeScript - Zod validation, handler discovery, NATS JetStream",
5
5
  "author": "crossdelta",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "prepublishOnly": "bun run build"
42
42
  },
43
43
  "dependencies": {
44
- "cloudevents": "7.0.2",
44
+ "cloudevents": "^10.0.0",
45
45
  "glob": "11.0.0",
46
46
  "nats": "^2.29.3"
47
47
  },