@crossdelta/cloudevents 0.5.1 → 0.5.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
@@ -29,7 +29,11 @@ bun add @crossdelta/cloudevents zod@4
29
29
 
30
30
  ## Quick Start
31
31
 
32
- **1. Create an event handler** (`src/events/orders-created.event.ts`):
32
+ > **Note:** Services **never create streams**. Streams must exist before services consume from them:
33
+ > - **Development**: Use [NATS CLI](https://docs.nats.io/running-a-nats-service/nats_admin/jetstream_admin/streams) or [Platform SDK](https://www.npmjs.com/package/@crossdelta/platform-sdk) (`pf dev` auto-creates from contracts)
34
+ > - **Production**: Use infrastructure-as-code (Pulumi, Terraform) or the [Stream Setup](#stream-setup) function below
35
+
36
+ **1. Create an event handler** (`src/events/orders-created.handler.ts`):
33
37
 
34
38
  ```typescript
35
39
  import { handleEvent } from '@crossdelta/cloudevents'
@@ -55,17 +59,7 @@ export default handleEvent(
55
59
  )
56
60
  ```
57
61
 
58
- **2. Ensure stream exists** (once, during setup):
59
-
60
- ```typescript
61
- import { ensureJetStreams } from '@crossdelta/cloudevents'
62
-
63
- await ensureJetStreams({
64
- streams: [{ stream: 'ORDERS', subjects: ['orders.*'] }],
65
- })
66
- ```
67
-
68
- **3. Start consuming:**
62
+ **2. Start consuming:**
69
63
 
70
64
  ```typescript
71
65
  import { consumeJetStreams } from '@crossdelta/cloudevents'
@@ -73,11 +67,11 @@ import { consumeJetStreams } from '@crossdelta/cloudevents'
73
67
  consumeJetStreams({
74
68
  streams: ['ORDERS'],
75
69
  consumer: 'my-service',
76
- discover: './src/events/**/*.event.ts',
70
+ discover: './src/events/**/*.handler.ts',
77
71
  })
78
72
  ```
79
73
 
80
- **4. Publish from another service:**
74
+ **3. Publish from another service:**
81
75
 
82
76
  ```typescript
83
77
  import { publish } from '@crossdelta/cloudevents'
@@ -87,6 +81,10 @@ await publish('orders.created', { orderId: 'ord_123', total: 99.99 })
87
81
 
88
82
  That's it. Handlers are auto-discovered, validated with Zod, and messages persist in JetStream.
89
83
 
84
+ > **Stream creation** is handled by your development environment or infrastructure — not by services:
85
+ > - Development: [NATS CLI](https://docs.nats.io/running-a-nats-service/nats_admin/jetstream_admin/streams), [@crossdelta/platform-sdk](https://www.npmjs.com/package/@crossdelta/platform-sdk), or manual setup
86
+ > - Production: See [Stream Setup](#stream-setup) below for `ensureJetStreams()` usage in infrastructure code
87
+
90
88
  ---
91
89
 
92
90
  ## Why use this?
@@ -125,10 +123,10 @@ export default handleEvent(
125
123
 
126
124
  ### Handlers
127
125
 
128
- Drop a `*.event.ts` file anywhere — it's auto-registered:
126
+ Drop a `*.handler.ts` file anywhere — it's auto-registered:
129
127
 
130
128
  ```typescript
131
- // src/events/user-signup.event.ts
129
+ // src/events/user-signup.handler.ts
132
130
  import { z } from 'zod'
133
131
 
134
132
  const UserSignupSchema = z.object({
@@ -158,7 +156,9 @@ await publish('orders.created', orderData)
158
156
 
159
157
  ### Stream Setup
160
158
 
161
- Create the stream once during infrastructure setup:
159
+ > **For infrastructure use only** - Services should never call this function.
160
+
161
+ Create streams during infrastructure setup (Pulumi, deployment scripts):
162
162
 
163
163
  ```typescript
164
164
  import { ensureJetStreams } from '@crossdelta/cloudevents'
@@ -175,6 +175,7 @@ await ensureJetStreams({
175
175
  },
176
176
  ],
177
177
  })
178
+ })
178
179
  ```
179
180
 
180
181
  ### Consuming
@@ -186,13 +187,13 @@ import { consumeJetStreams } from '@crossdelta/cloudevents'
186
187
  consumeJetStreams({
187
188
  streams: ['ORDERS'],
188
189
  consumer: 'billing',
189
- discover: './src/events/**/*.event.ts',
190
+ discover: './src/events/**/*.handler.ts',
190
191
  })
191
192
 
192
193
  // Core NATS — fire-and-forget, simpler
193
194
  await consumeNatsEvents({
194
195
  subjects: ['notifications.*'],
195
- discover: './src/events/**/*.event.ts',
196
+ discover: './src/events/**/*.handler.ts',
196
197
  })
197
198
  ```
198
199
 
@@ -239,7 +240,7 @@ consumeJetStreams({
239
240
  // Required
240
241
  streams: ['ORDERS'],
241
242
  consumer: 'my-service',
242
- discover: './src/events/**/*.event.ts',
243
+ discover: './src/events/**/*.handler.ts',
243
244
 
244
245
  // Optional
245
246
  servers: 'nats://localhost:4222',
@@ -254,6 +255,129 @@ consumeJetStreams({
254
255
 
255
256
  ## Advanced Features
256
257
 
258
+ <details>
259
+ <parameter name="summary"><b>Channel Metadata (Infrastructure Integration)</b></summary>
260
+
261
+ <br />
262
+
263
+ Contracts can include **channel metadata** to define which NATS JetStream stream they belong to. This enables infrastructure-as-code workflows where streams are materialized from contracts.
264
+
265
+ ### Basic Contract with Channel
266
+
267
+ ```typescript
268
+ import { createContract } from '@crossdelta/cloudevents'
269
+ import { z } from 'zod'
270
+
271
+ export const OrdersCreatedContract = createContract({
272
+ type: 'orders.created',
273
+ channel: {
274
+ stream: 'ORDERS',
275
+ // subject defaults to 'orders.created' if omitted
276
+ },
277
+ schema: z.object({
278
+ orderId: z.string(),
279
+ total: z.number(),
280
+ }),
281
+ })
282
+ ```
283
+
284
+ ### Channel Configuration
285
+
286
+ ```typescript
287
+ interface ChannelConfig {
288
+ stream: string // JetStream stream name (e.g., 'ORDERS')
289
+ subject?: string // NATS subject (defaults to event type)
290
+ }
291
+ ```
292
+
293
+ **Subject Defaulting:**
294
+ - If `subject` is **not provided**, it defaults to the event `type`
295
+ - This follows the convention: `orders.created` → subject `orders.created`
296
+ - Override when needed: `subject: 'orders.v1.created'`
297
+
298
+ ### Multiple Events, Same Stream
299
+
300
+ ```typescript
301
+ // orders.created → ORDERS stream
302
+ export const OrdersCreatedContract = createContract({
303
+ type: 'orders.created',
304
+ channel: { stream: 'ORDERS' },
305
+ schema: OrderCreatedSchema,
306
+ })
307
+
308
+ // orders.updated → ORDERS stream (same stream!)
309
+ export const OrdersUpdatedContract = createContract({
310
+ type: 'orders.updated',
311
+ channel: { stream: 'ORDERS' },
312
+ schema: OrderUpdatedSchema,
313
+ })
314
+ ```
315
+
316
+ **Result:** Both events share the `ORDERS` stream with subjects `orders.created` and `orders.updated`.
317
+
318
+ ### Infrastructure Materialization
319
+
320
+ Channel metadata is **conceptual** - it defines routing, not infrastructure policy.
321
+
322
+ **Contracts define:**
323
+ - Event type (`orders.created`)
324
+ - Stream routing (`ORDERS`)
325
+ - Subject mapping (`orders.created`)
326
+
327
+ **Infrastructure defines:**
328
+ - Retention (7 days, 14 days, etc.)
329
+ - Storage (disk, memory)
330
+ - Replicas (1, 3, etc.)
331
+ - Limits (max message size, etc.)
332
+
333
+ **Example Infrastructure (Pulumi):**
334
+
335
+ ```typescript
336
+ import { collectStreamDefinitions } from './helpers/streams'
337
+ import { ensureJetStreams } from '@crossdelta/cloudevents'
338
+
339
+ // Collect from contracts
340
+ const streams = collectStreamDefinitions()
341
+ // [{ stream: 'ORDERS', subjects: ['orders.created', 'orders.updated'] }]
342
+
343
+ // Materialize with policies
344
+ await ensureJetStreams({
345
+ streams: streams.map(({ stream, subjects }) => ({
346
+ stream,
347
+ subjects,
348
+ config: {
349
+ maxAge: 14 * 24 * 60 * 60 * 1000, // 14 days
350
+ replicas: 3, // High availability
351
+ storage: 'file', // Persistent
352
+ },
353
+ })),
354
+ })
355
+ ```
356
+
357
+ ### Development vs. Production
358
+
359
+ | Environment | Streams | How | Retention |
360
+ |-------------|---------|-----|-----------|
361
+ | **Development** | Ephemeral | Auto-created by services | None (memory) |
362
+ | **Production** | Persistent | Materialized via infrastructure | Explicit (7d, 14d, etc.) |
363
+
364
+ **Key Principle:** Contracts define **what** and **where**, infrastructure defines **how** and **how long**.
365
+
366
+ ### Backward Compatibility
367
+
368
+ Channel metadata is **optional** - contracts without `channel` work as before:
369
+
370
+ ```typescript
371
+ // Legacy contract (still works)
372
+ export const LegacyContract = createContract({
373
+ type: 'legacy.event',
374
+ schema: LegacySchema,
375
+ // No channel - handler still works
376
+ })
377
+ ```
378
+
379
+ </details>
380
+
257
381
  <details>
258
382
  <summary><b>Shared Contracts</b></summary>
259
383
 
@@ -340,8 +464,10 @@ const redisStore = {
340
464
  add: (id, ttl) => redis.set(`idem:${id}`, '1', 'PX', ttl),
341
465
  }
342
466
 
343
- await consumeJetStreamEvents({
344
- // ...
467
+ await consumeJetStreams({
468
+ streams: ['ORDERS'],
469
+ consumer: 'my-service',
470
+ discover: './src/events/**/*.handler.ts',
345
471
  idempotencyStore: redisStore,
346
472
  })
347
473
  ```
@@ -354,13 +480,17 @@ await consumeJetStreamEvents({
354
480
  Invalid messages are quarantined, not lost:
355
481
 
356
482
  ```typescript
357
- await consumeJetStreamEvents({
358
- // ...
359
- quarantineTopic: 'events.quarantine',
360
- errorTopic: 'events.errors',
483
+ await consumeJetStreams({
484
+ streams: ['ORDERS'],
485
+ consumer: 'my-service',
486
+ discover: './src/events/**/*.handler.ts',
487
+ quarantineTopic: 'events.quarantine', // For malformed messages
488
+ errorTopic: 'events.errors', // For handler errors
361
489
  })
362
490
  ```
363
491
 
492
+ Messages that fail validation go to `quarantineTopic`, messages that crash handlers go to `errorTopic`.
493
+
364
494
  </details>
365
495
 
366
496
  <details>
@@ -371,7 +501,7 @@ import { Hono } from 'hono'
371
501
  import { cloudEvents } from '@crossdelta/cloudevents'
372
502
 
373
503
  const app = new Hono()
374
- app.use('/events', cloudEvents({ discover: 'src/events/**/*.event.ts' }))
504
+ app.use('/events', cloudEvents({ discover: 'src/events/**/*.handler.ts' }))
375
505
  ```
376
506
 
377
507
  </details>
@@ -384,9 +514,8 @@ app.use('/events', cloudEvents({ discover: 'src/events/**/*.event.ts' }))
384
514
  |----------|---------|
385
515
  | `handleEvent(options, handler)` | Create a handler |
386
516
  | `createContract(options)` | Create shared event contract |
387
- | `ensureJetStreams(options)` | Create/update JetStream streams (preferred) |
388
- | `consumeJetStreams(options)` | Consume from multiple streams (preferred) |
389
- | `consumeJetStreamEvents(options)` | Consume single stream (legacy) |
517
+ | `ensureJetStreams(options)` | Create/update JetStream streams |
518
+ | `consumeJetStreams(options)` | Consume from multiple streams |
390
519
  | `consumeNatsEvents(options)` | Consume fire-and-forget |
391
520
  | `publish(type, data)` | Publish event |
392
521
 
@@ -1,26 +1,50 @@
1
1
  import type { ZodTypeAny } from 'zod';
2
- import type { HandleEventOptions } from './types';
2
+ import type { ChannelMetadata, HandleEventOptions } from './types';
3
3
  /**
4
- * Creates a type-safe event contract for Advanced Mode
4
+ * Creates a type-safe event contract with optional channel metadata
5
5
  *
6
6
  * This helper ensures proper type inference when using contracts
7
7
  * with handleEvent across package boundaries.
8
8
  *
9
9
  * @example
10
+ * Basic contract without channel:
10
11
  * ```typescript
11
- * import { createContract } from '@crossdelta/cloudevents'
12
- * import { z } from 'zod'
12
+ * export const OrderCreatedContract = createContract({
13
+ * type: 'orders.created',
14
+ * schema: z.object({
15
+ * orderId: z.string(),
16
+ * total: z.number(),
17
+ * }),
18
+ * })
19
+ * ```
13
20
  *
21
+ * @example
22
+ * Contract with channel metadata:
23
+ * ```typescript
14
24
  * export const OrderCreatedContract = createContract({
15
25
  * type: 'orders.created',
26
+ * channel: {
27
+ * stream: 'ORDERS',
28
+ * // subject defaults to 'orders.created' if omitted
29
+ * },
16
30
  * schema: z.object({
17
31
  * orderId: z.string(),
18
32
  * total: z.number(),
19
33
  * }),
20
34
  * })
35
+ * ```
21
36
  *
22
- * // Type is inferred: { orderId: string, total: number }
23
- * export type OrderCreatedData = typeof OrderCreatedContract._schema._output
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
+ * })
24
48
  * ```
25
49
  */
26
50
  export declare function createContract<TSchema extends ZodTypeAny>(options: {
@@ -29,6 +53,11 @@ export declare function createContract<TSchema extends ZodTypeAny>(options: {
29
53
  match?: HandleEventOptions['match'];
30
54
  safeParse?: boolean;
31
55
  tenantId?: string | string[];
56
+ channel?: {
57
+ stream: string;
58
+ subject?: string;
59
+ };
32
60
  }): HandleEventOptions<TSchema> & {
33
61
  _schema: TSchema;
62
+ channel?: ChannelMetadata;
34
63
  };
@@ -1,29 +1,61 @@
1
1
  /**
2
- * Creates a type-safe event contract for Advanced Mode
2
+ * Creates a type-safe event contract with optional channel metadata
3
3
  *
4
4
  * This helper ensures proper type inference when using contracts
5
5
  * with handleEvent across package boundaries.
6
6
  *
7
7
  * @example
8
+ * Basic contract without channel:
8
9
  * ```typescript
9
- * import { createContract } from '@crossdelta/cloudevents'
10
- * import { z } from 'zod'
10
+ * export const OrderCreatedContract = createContract({
11
+ * type: 'orders.created',
12
+ * schema: z.object({
13
+ * orderId: z.string(),
14
+ * total: z.number(),
15
+ * }),
16
+ * })
17
+ * ```
11
18
  *
19
+ * @example
20
+ * Contract with channel metadata:
21
+ * ```typescript
12
22
  * export const OrderCreatedContract = createContract({
13
23
  * type: 'orders.created',
24
+ * channel: {
25
+ * stream: 'ORDERS',
26
+ * // subject defaults to 'orders.created' if omitted
27
+ * },
14
28
  * schema: z.object({
15
29
  * orderId: z.string(),
16
30
  * total: z.number(),
17
31
  * }),
18
32
  * })
33
+ * ```
19
34
  *
20
- * // Type is inferred: { orderId: string, total: number }
21
- * export type OrderCreatedData = typeof OrderCreatedContract._schema._output
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
+ * })
22
46
  * ```
23
47
  */
24
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;
25
56
  return {
26
57
  ...options,
27
58
  _schema: options.schema,
59
+ channel: resolvedChannel,
28
60
  };
29
61
  }
@@ -2,17 +2,17 @@ import type { HandlerConstructor } from './types';
2
2
  /**
3
3
  * Discovers event handlers from files matching the given glob pattern.
4
4
  *
5
- * @param pattern - Glob pattern to match handler files (e.g., 'events/*.event.ts')
5
+ * @param pattern - Glob pattern to match handler files (e.g., 'events/*.handler.ts')
6
6
  * @param options - Configuration options
7
7
  * @returns Promise resolving to array of discovered handler constructors
8
8
  *
9
9
  * @example
10
10
  * ```typescript
11
11
  * // Discover all handlers in events directory
12
- * const handlers = await discoverHandlers('events/*.event.ts')
12
+ * const handlers = await discoverHandlers('events/*.handler.ts')
13
13
  *
14
14
  * // With filtering and logging
15
- * const handlers = await discoverHandlers('events/*.event.ts', {
15
+ * const handlers = await discoverHandlers('events/*.handler.ts', {
16
16
  * filter: (name, handler) => name.includes('Customer'),
17
17
  * log: true
18
18
  * })
@@ -144,17 +144,17 @@ const discoverFiles = async (pattern, basePath, preferCompiled) => {
144
144
  /**
145
145
  * Discovers event handlers from files matching the given glob pattern.
146
146
  *
147
- * @param pattern - Glob pattern to match handler files (e.g., 'events/*.event.ts')
147
+ * @param pattern - Glob pattern to match handler files (e.g., 'events/*.handler.ts')
148
148
  * @param options - Configuration options
149
149
  * @returns Promise resolving to array of discovered handler constructors
150
150
  *
151
151
  * @example
152
152
  * ```typescript
153
153
  * // Discover all handlers in events directory
154
- * const handlers = await discoverHandlers('events/*.event.ts')
154
+ * const handlers = await discoverHandlers('events/*.handler.ts')
155
155
  *
156
156
  * // With filtering and logging
157
- * const handlers = await discoverHandlers('events/*.event.ts', {
157
+ * const handlers = await discoverHandlers('events/*.handler.ts', {
158
158
  * filter: (name, handler) => name.includes('Customer'),
159
159
  * log: true
160
160
  * })
@@ -1,6 +1,6 @@
1
- export { discoverHandlers } from './discovery';
2
1
  export { createContract } from './contract-helper';
2
+ export { discoverHandlers } from './discovery';
3
3
  export { eventSchema, handleEvent } from './handler-factory';
4
- export type { EnrichedEvent, EventHandler, HandleEventOptions, HandlerConstructor, HandlerMetadata, IdempotencyStore, InferEventData, MatchFn, RoutingConfig, } from './types';
4
+ export type { ChannelConfig, ChannelMetadata, EnrichedEvent, EventHandler, HandleEventOptions, HandlerConstructor, HandlerMetadata, IdempotencyStore, InferEventData, MatchFn, RoutingConfig, } from './types';
5
5
  export type { HandlerValidationError, ValidationErrorDetail } from './validation';
6
6
  export { extractTypeFromSchema, isValidHandler } from './validation';
@@ -1,4 +1,4 @@
1
- export { discoverHandlers } from './discovery';
2
1
  export { createContract } from './contract-helper';
2
+ export { discoverHandlers } from './discovery';
3
3
  export { eventSchema, handleEvent } from './handler-factory';
4
4
  export { extractTypeFromSchema, isValidHandler } from './validation';
@@ -43,6 +43,24 @@ export type HandlerMetadata<S extends ZodTypeAny> = {
43
43
  match?: MatchFn<unknown>;
44
44
  safeParse: boolean;
45
45
  };
46
+ /**
47
+ * Channel metadata for NATS JetStream routing
48
+ */
49
+ export interface ChannelMetadata {
50
+ /** JetStream stream name (e.g., 'ORDERS') */
51
+ stream: string;
52
+ /** NATS subject (defaults to event type if not specified) */
53
+ subject: string;
54
+ }
55
+ /**
56
+ * Channel configuration input (subject is optional, defaults to type)
57
+ */
58
+ export interface ChannelConfig {
59
+ /** JetStream stream name (e.g., 'ORDERS') */
60
+ stream: string;
61
+ /** NATS subject (optional, defaults to event type) */
62
+ subject?: string;
63
+ }
46
64
  /**
47
65
  * Options for creating event handlers
48
66
  *
@@ -56,6 +74,8 @@ export interface HandleEventOptions<S = ZodTypeAny> {
56
74
  safeParse?: boolean;
57
75
  /** Filter events by tenant ID(s). If set, only events matching these tenant(s) are processed. */
58
76
  tenantId?: string | string[];
77
+ /** Channel metadata for NATS JetStream routing (optional) */
78
+ channel?: ChannelConfig;
59
79
  }
60
80
  /**
61
81
  * Routing configuration for type → subject → stream mapping
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { EventContext } from './adapters/cloudevents';
2
2
  export { parseEventFromContext } from './adapters/cloudevents';
3
- export type { EnrichedEvent, HandleEventOptions, IdempotencyStore, InferEventData, RoutingConfig } from './domain';
3
+ export type { ChannelConfig, ChannelMetadata, EnrichedEvent, HandleEventOptions, IdempotencyStore, InferEventData, RoutingConfig, } from './domain';
4
4
  export { createContract, eventSchema, extractTypeFromSchema, handleEvent } from './domain';
5
5
  export { clearHandlerCache, cloudEvents } from './middlewares';
6
6
  export { checkAndMarkProcessed, createInMemoryIdempotencyStore, getDefaultIdempotencyStore, resetDefaultIdempotencyStore, } from './processing/idempotency';
@@ -28,7 +28,7 @@ export interface InMemoryIdempotencyStoreOptions {
28
28
  * stream: 'ORDERS',
29
29
  * subjects: ['orders.>'],
30
30
  * consumer: 'notifications',
31
- * discover: './src/handlers/**\/*.event.ts',
31
+ * discover: `./src/events/**\/*.handler.ts`,
32
32
  * idempotencyStore: store,
33
33
  * })
34
34
  * ```
@@ -18,7 +18,7 @@
18
18
  * stream: 'ORDERS',
19
19
  * subjects: ['orders.>'],
20
20
  * consumer: 'notifications',
21
- * discover: './src/handlers/**\/*.event.ts',
21
+ * discover: `./src/events/**\/*.handler.ts`,
22
22
  * idempotencyStore: store,
23
23
  * })
24
24
  * ```
@@ -157,7 +157,7 @@ export declare function ensureJetStreamStreams(options: JetStreamStreamsOptions)
157
157
  * stream: 'ORDERS',
158
158
  * subjects: ['orders.>'],
159
159
  * consumer: 'notifications',
160
- * discover: './src/handlers/**\/*.event.ts',
160
+ * discover: `./src/events/**\/*.handler.ts`,
161
161
  * })
162
162
  * ```
163
163
  */
@@ -200,7 +200,7 @@ export interface JetStreamStreamsConsumerOptions extends Pick<CloudEventsOptions
200
200
  * await consumeJetStreamStreams({
201
201
  * streams: ['ORDERS', 'CUSTOMERS'],
202
202
  * consumer: 'notifications',
203
- * discover: './src/events/**\/*.event.ts',
203
+ * discover: `./src/events/**\/*.handler.ts`,
204
204
  * })
205
205
  * ```
206
206
  */
@@ -167,7 +167,7 @@ async function ensureConsumer(jsm, streamName, consumerName, options) {
167
167
  * stream: 'ORDERS',
168
168
  * subjects: ['orders.>'],
169
169
  * consumer: 'notifications',
170
- * discover: './src/handlers/**\/*.event.ts',
170
+ * discover: `./src/events/**\/*.handler.ts`,
171
171
  * })
172
172
  * ```
173
173
  */
@@ -264,7 +264,7 @@ export async function consumeJetStreamEvents(options) {
264
264
  * await consumeJetStreamStreams({
265
265
  * streams: ['ORDERS', 'CUSTOMERS'],
266
266
  * consumer: 'notifications',
267
- * discover: './src/events/**\/*.event.ts',
267
+ * discover: `./src/events/**\/*.handler.ts`,
268
268
  * })
269
269
  * ```
270
270
  */
@@ -44,7 +44,7 @@ export async function consumeNatsEvents(options) {
44
44
  const pass = options.pass ?? process.env.NATS_PASSWORD;
45
45
  // Cleanup existing consumer with same name (handles hot-reload)
46
46
  await cleanupConsumer(name);
47
- // 1) Discover handler classes from *.event.ts files
47
+ // 1) Discover handler classes from *.handler.ts files
48
48
  const handlerConstructors = await discoverHandlers(options.discover);
49
49
  const processedHandlers = handlerConstructors
50
50
  .map(processHandler)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/cloudevents",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "CloudEvents toolkit for TypeScript - Zod validation, handler discovery, NATS JetStream",
5
5
  "author": "crossdelta",
6
6
  "license": "MIT",
@@ -23,15 +23,15 @@
23
23
  ],
24
24
  "exports": {
25
25
  ".": {
26
+ "types": "./dist/index.d.ts",
26
27
  "import": "./dist/index.js",
27
28
  "require": "./dist/index.js",
28
- "types": "./dist/index.d.ts",
29
29
  "default": "./dist/index.js"
30
30
  },
31
31
  "./transports/nats": {
32
+ "types": "./dist/src/transports/nats/index.d.ts",
32
33
  "import": "./dist/src/transports/nats/index.js",
33
34
  "require": "./dist/src/transports/nats/index.js",
34
- "types": "./dist/src/transports/nats/index.d.ts",
35
35
  "default": "./dist/src/transports/nats/index.js"
36
36
  }
37
37
  },