autotel-subscribers 4.0.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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +669 -0
  3. package/dist/amplitude.cjs +2486 -0
  4. package/dist/amplitude.cjs.map +1 -0
  5. package/dist/amplitude.d.cts +49 -0
  6. package/dist/amplitude.d.ts +49 -0
  7. package/dist/amplitude.js +2463 -0
  8. package/dist/amplitude.js.map +1 -0
  9. package/dist/event-subscriber-base-CnF3V56W.d.cts +182 -0
  10. package/dist/event-subscriber-base-CnF3V56W.d.ts +182 -0
  11. package/dist/factories.cjs +16660 -0
  12. package/dist/factories.cjs.map +1 -0
  13. package/dist/factories.d.cts +304 -0
  14. package/dist/factories.d.ts +304 -0
  15. package/dist/factories.js +16624 -0
  16. package/dist/factories.js.map +1 -0
  17. package/dist/index.cjs +16575 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +179 -0
  20. package/dist/index.d.ts +179 -0
  21. package/dist/index.js +16539 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/middleware.cjs +220 -0
  24. package/dist/middleware.cjs.map +1 -0
  25. package/dist/middleware.d.cts +227 -0
  26. package/dist/middleware.d.ts +227 -0
  27. package/dist/middleware.js +208 -0
  28. package/dist/middleware.js.map +1 -0
  29. package/dist/mixpanel.cjs +2940 -0
  30. package/dist/mixpanel.cjs.map +1 -0
  31. package/dist/mixpanel.d.cts +47 -0
  32. package/dist/mixpanel.d.ts +47 -0
  33. package/dist/mixpanel.js +2932 -0
  34. package/dist/mixpanel.js.map +1 -0
  35. package/dist/posthog.cjs +4115 -0
  36. package/dist/posthog.cjs.map +1 -0
  37. package/dist/posthog.d.cts +299 -0
  38. package/dist/posthog.d.ts +299 -0
  39. package/dist/posthog.js +4113 -0
  40. package/dist/posthog.js.map +1 -0
  41. package/dist/segment.cjs +6822 -0
  42. package/dist/segment.cjs.map +1 -0
  43. package/dist/segment.d.cts +49 -0
  44. package/dist/segment.d.ts +49 -0
  45. package/dist/segment.js +6794 -0
  46. package/dist/segment.js.map +1 -0
  47. package/dist/slack.cjs +368 -0
  48. package/dist/slack.cjs.map +1 -0
  49. package/dist/slack.d.cts +126 -0
  50. package/dist/slack.d.ts +126 -0
  51. package/dist/slack.js +366 -0
  52. package/dist/slack.js.map +1 -0
  53. package/dist/webhook.cjs +100 -0
  54. package/dist/webhook.cjs.map +1 -0
  55. package/dist/webhook.d.cts +53 -0
  56. package/dist/webhook.d.ts +53 -0
  57. package/dist/webhook.js +98 -0
  58. package/dist/webhook.js.map +1 -0
  59. package/examples/quickstart-custom-subscriber.ts +144 -0
  60. package/examples/subscriber-bigquery.ts +219 -0
  61. package/examples/subscriber-databricks.ts +280 -0
  62. package/examples/subscriber-kafka.ts +326 -0
  63. package/examples/subscriber-kinesis.ts +307 -0
  64. package/examples/subscriber-posthog.ts +421 -0
  65. package/examples/subscriber-pubsub.ts +336 -0
  66. package/examples/subscriber-snowflake.ts +232 -0
  67. package/package.json +141 -0
  68. package/src/amplitude.test.ts +231 -0
  69. package/src/amplitude.ts +148 -0
  70. package/src/event-subscriber-base.ts +325 -0
  71. package/src/factories.ts +197 -0
  72. package/src/index.ts +50 -0
  73. package/src/middleware.ts +489 -0
  74. package/src/mixpanel.test.ts +194 -0
  75. package/src/mixpanel.ts +134 -0
  76. package/src/mock-event-subscriber.ts +333 -0
  77. package/src/posthog.test.ts +629 -0
  78. package/src/posthog.ts +530 -0
  79. package/src/segment.test.ts +228 -0
  80. package/src/segment.ts +148 -0
  81. package/src/slack.ts +383 -0
  82. package/src/streaming-event-subscriber.ts +323 -0
  83. package/src/testing/index.ts +37 -0
  84. package/src/testing/mock-webhook-server.ts +242 -0
  85. package/src/testing/subscriber-test-harness.ts +365 -0
  86. package/src/webhook.test.ts +264 -0
  87. package/src/webhook.ts +158 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Jag Reehal 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,669 @@
1
+ # autotel-subscribers
2
+
3
+ **Send events to multiple platforms**
4
+
5
+ Subscribers for [autotel](https://github.com/jagreehal/autotel) to send events to PostHog, Mixpanel, Amplitude, Segment, and custom webhooks.
6
+
7
+ ## Why Use This?
8
+
9
+ **Track once, send everywhere:**
10
+ - Primary metrics → **OpenTelemetry** (infrastructure monitoring)
11
+ - Product events → **PostHog / Mixpanel / Amplitude**
12
+ - Customer data → **Segment**
13
+ - Custom integrations → **Webhooks** (Zapier, Make.com, etc.)
14
+
15
+ **Zero overhead when not used:**
16
+ Adapters are optional. If you don't use them, they're tree-shaken out (0 bytes).
17
+
18
+ ---
19
+
20
+ ## Building Custom Subscribers
21
+
22
+ Two base classes available:
23
+
24
+ ### `EventSubscriber` - Standard Base Class
25
+
26
+ Use for most custom subscribers. Provides production-ready features:
27
+
28
+ - Error handling (automatic catching + custom handlers)
29
+ - Pending request tracking (ensures delivery during shutdown)
30
+ - Graceful shutdown (drains pending requests)
31
+ - Enable/disable control (runtime toggle)
32
+
33
+ **When to use:** Any custom adapter (HTTP APIs, databases, webhooks, etc.)
34
+
35
+ ```typescript
36
+ import { EventSubscriber, EventPayload } from 'autotel-subscribers';
37
+
38
+ class MySubscriber extends EventSubscriber {
39
+ readonly name = 'MySubscriber';
40
+
41
+ protected async sendToDestination(payload: EventPayload): Promise<void> {
42
+ // Send to your platform
43
+ await fetch('https://api.example.com/events', {
44
+ method: 'POST',
45
+ body: JSON.stringify(payload)
46
+ });
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### `StreamingEventSubscriber` - For High-Throughput Streams
52
+
53
+ Extends `EventSubscriber` with batching and partitioning for streaming platforms.
54
+
55
+ **When to use:** Kafka, Kinesis, Pub/Sub, event streams, high-volume data pipelines
56
+
57
+ ```typescript
58
+ import { StreamingEventSubscriber } from 'autotel-subscribers';
59
+
60
+ class KafkaSubscriber extends StreamingEventSubscriber {
61
+ readonly name = 'KafkaSubscriber';
62
+
63
+ protected async sendBatch(events: EventPayload[]): Promise<void> {
64
+ await this.producer.send({
65
+ topic: 'events',
66
+ messages: events.map(e => ({ value: JSON.stringify(e) }))
67
+ });
68
+ }
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Installation
75
+
76
+ ```bash
77
+ # Core package (required)
78
+ pnpm add autotel
79
+
80
+ # Subscribers package (optional)
81
+ pnpm add autotel-subscribers
82
+
83
+ # Install the events SDKs you need
84
+ pnpm add posthog-node # For PostHog
85
+ pnpm add mixpanel # For Mixpanel
86
+ pnpm add @segment/events-node # For Segment
87
+ pnpm add @amplitude/events-node # For Amplitude
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Quick Start
93
+
94
+ ### Using Built-in Subscribers (Easiest)
95
+
96
+ Import subscribers directly from their entry points:
97
+
98
+ ```typescript
99
+ import { Events } from 'autotel/events';
100
+ import { PostHogSubscriber } from 'autotel-subscribers/posthog';
101
+ import { WebhookSubscriber } from 'autotel-subscribers/webhook';
102
+
103
+ const events = new Event('checkout', {
104
+ subscribers: [
105
+ new PostHogSubscriber({ apiKey: process.env.POSTHOG_API_KEY! }),
106
+ new WebhookSubscriber({ url: 'https://your-webhook.com' })
107
+ ]
108
+ });
109
+
110
+ // Sent to: OpenTelemetry + PostHog + Webhook
111
+ events.trackEvent('order.completed', { userId: '123', amount: 99.99 });
112
+ ```
113
+
114
+ ### Your First Custom Subscriber (5 Minutes)
115
+
116
+ Create an adapter in 25 lines:
117
+
118
+ ```typescript
119
+ import { EventSubscriber, EventPayload } from 'autotel-subscribers';
120
+
121
+ class MySubscriber extends EventSubscriber {
122
+ readonly name = 'MySubscriber';
123
+
124
+ constructor(private apiKey: string) {
125
+ super();
126
+ }
127
+
128
+ protected async sendToDestination(payload: EventPayload): Promise<void> {
129
+ await fetch('https://your-api.com/events', {
130
+ method: 'POST',
131
+ headers: {
132
+ 'Authorization': `Bearer ${this.apiKey}`,
133
+ 'Content-Type': 'application/json'
134
+ },
135
+ body: JSON.stringify(payload)
136
+ });
137
+ }
138
+ }
139
+
140
+ // Use it!
141
+ const events = new Event('my-app', {
142
+ subscribers: [new MySubscriber('your-api-key')]
143
+ });
144
+ ```
145
+
146
+ **That's it!** Extend `EventSubscriber` and implement `sendToDestination()`. You get error handling, graceful shutdown, and pending request tracking automatically. See [Your First Adapter Guide](./docs/your-first-subscriber.md) for details.
147
+
148
+ ### Test Your Adapter
149
+
150
+ ```typescript
151
+ import { AdapterTestHarness } from 'autotel-subscribers/testing';
152
+
153
+ const harness = new AdapterTestHarness(new MySubscriber('test-key'));
154
+ const results = await harness.runAll();
155
+
156
+ AdapterTestHarness.printResults(results);
157
+ // All tests passed! Your adapter is ready to use.
158
+ ```
159
+
160
+ ### Add Middleware (Retry, Sampling, etc.)
161
+
162
+ ```typescript
163
+ import { applyMiddleware, retryMiddleware, samplingMiddleware } from 'autotel-subscribers/middleware';
164
+
165
+ const subscriber = applyMiddleware(
166
+ new MySubscriber('api-key'),
167
+ [
168
+ retryMiddleware({ maxRetries: 3 }), // Retry failed requests
169
+ samplingMiddleware(0.1) // Only send 10% of events
170
+ ]
171
+ );
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Built-in Adapters
177
+
178
+ ### PostHog
179
+
180
+ ```typescript
181
+ import { Events } from 'autotel/events';
182
+ import { PostHogSubscriber } from 'autotel-subscribers/posthog';
183
+
184
+ const events = new Event('checkout', {
185
+ subscribers: [
186
+ new PostHogSubscriber({
187
+ apiKey: process.env.POSTHOG_API_KEY!,
188
+ host: 'https://us.i.posthog.com' // optional
189
+ })
190
+ ]
191
+ });
192
+
193
+ // Sent to: OpenTelemetry + PostHog
194
+ events.trackEvent('order.completed', {
195
+ userId: '123',
196
+ amount: 99.99
197
+ });
198
+ ```
199
+
200
+ ### Mixpanel
201
+
202
+ ```typescript
203
+ import { MixpanelSubscriber } from 'autotel-subscribers/mixpanel';
204
+
205
+ const events = new Event('checkout', {
206
+ subscribers: [
207
+ new MixpanelSubscriber({
208
+ token: process.env.MIXPANEL_TOKEN!
209
+ })
210
+ ]
211
+ });
212
+ ```
213
+
214
+ ### Segment
215
+
216
+ ```typescript
217
+ import { SegmentSubscriber } from 'autotel-subscribers/segment';
218
+
219
+ const events = new Event('checkout', {
220
+ subscribers: [
221
+ new SegmentSubscriber({
222
+ writeKey: process.env.SEGMENT_WRITE_KEY!
223
+ })
224
+ ]
225
+ });
226
+ ```
227
+
228
+ ### Amplitude
229
+
230
+ ```typescript
231
+ import { AmplitudeSubscriber } from 'autotel-subscribers/amplitude';
232
+
233
+ const events = new Event('checkout', {
234
+ subscribers: [
235
+ new AmplitudeSubscriber({
236
+ apiKey: process.env.AMPLITUDE_API_KEY!
237
+ })
238
+ ]
239
+ });
240
+ ```
241
+
242
+ ### Webhook (Custom Integrations)
243
+
244
+ ```typescript
245
+ import { WebhookSubscriber } from 'autotel-subscribers/webhook';
246
+
247
+ const events = new Event('checkout', {
248
+ subscribers: [
249
+ new WebhookSubscriber({
250
+ url: 'https://hooks.zapier.com/hooks/catch/...',
251
+ headers: { 'X-API-Key': 'secret' },
252
+ maxRetries: 3
253
+ })
254
+ ]
255
+ });
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Multi-Platform Tracking
261
+
262
+ Send to **multiple platforms simultaneously**:
263
+
264
+ ```typescript
265
+ import { Events } from 'autotel/events';
266
+ import { PostHogSubscriber } from 'autotel-subscribers/posthog';
267
+ import { MixpanelSubscriber } from 'autotel-subscribers/mixpanel';
268
+ import { SegmentSubscriber } from 'autotel-subscribers/segment';
269
+
270
+ const events = new Event('checkout', {
271
+ subscribers: [
272
+ new PostHogSubscriber({ apiKey: 'phc_...' }),
273
+ new MixpanelSubscriber({ token: '...' }),
274
+ new SegmentSubscriber({ writeKey: '...' })
275
+ ]
276
+ });
277
+
278
+ // Sent to: OpenTelemetry + PostHog + Mixpanel + Segment
279
+ events.trackEvent('order.completed', {
280
+ userId: '123',
281
+ amount: 99.99,
282
+ currency: 'USD'
283
+ });
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Delivery Patterns
289
+
290
+ Autotel-subscribers provides **direct subscribers** (fire-and-forget) - events are sent immediately to events platforms.
291
+
292
+ ### Direct Subscribers (Default)
293
+
294
+ **Simple, fire-and-forget tracking** - Events sent immediately to events platforms:
295
+
296
+ ```typescript
297
+ const events = new Event('app', {
298
+ subscribers: [new PostHogSubscriber({ apiKey: '...' })]
299
+ })
300
+
301
+ // Events sent immediately, real-time
302
+ events.trackEvent('user.signup', { userId: '123' })
303
+ events.trackEvent('page.viewed', { path: '/checkout' })
304
+ ```
305
+
306
+ **Use for:**
307
+ - Page views, button clicks, feature usage
308
+ - User behavior tracking
309
+ - High-volume, non-critical events
310
+ - Real-time events dashboards
311
+
312
+ **Benefits:**
313
+ - Simple, zero infrastructure
314
+ - Real-time delivery
315
+ - No database overhead
316
+ - Fire-and-forget
317
+
318
+ **Trade-offs:**
319
+ - Events can be lost if adapter/network fails
320
+ - No atomicity with database transactions
321
+
322
+ ### Transactional Outbox Pattern
323
+
324
+ **For guaranteed delivery with atomicity**, use the separate [`autotel-outbox`](https://github.com/jagreehal/autotel/tree/main/packages/autotel-outbox) package.
325
+
326
+ This provides:
327
+ - Guaranteed delivery (retries on failure)
328
+ - Atomicity with database state changes
329
+ - Fan-out to multiple destinations
330
+ - Requires database table + publisher worker
331
+ - Adds latency (1+ minute delay)
332
+
333
+ **Install:**
334
+ ```bash
335
+ npm install autotel-outbox
336
+ ```
337
+
338
+ **Usage:**
339
+ ```typescript
340
+ import { OutboxEventSubscriber } from 'autotel-outbox';
341
+ import { PostHogSubscriber } from 'autotel-subscribers/posthog';
342
+
343
+ const outbox = new DrizzleD1OutboxStorage(env.DB);
344
+ const events = new Event('checkout', {
345
+ subscribers: [
346
+ new OutboxEventSubscriber(outbox, { aggregateType: 'Order' })
347
+ ]
348
+ });
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Adapter Methods
354
+
355
+ All subscribers implement these methods:
356
+
357
+ ```typescript
358
+ interface EventSubscriber {
359
+ // Track events
360
+ trackEvent(name: string, attributes?: Record<string, any>): void;
361
+
362
+ // Track conversion funnels
363
+ trackFunnelStep(
364
+ funnelName: string,
365
+ step: 'started' | 'completed' | 'abandoned' | 'failed',
366
+ attributes?: Record<string, any>
367
+ ): void;
368
+
369
+ // Track business outcomes
370
+ trackOutcome(
371
+ operationName: string,
372
+ outcome: 'success' | 'failure' | 'partial',
373
+ attributes?: Record<string, any>
374
+ ): void;
375
+
376
+ // Track business values (revenue, counts, etc.)
377
+ trackValue(
378
+ name: string,
379
+ value: number,
380
+ attributes?: Record<string, any>
381
+ ): void;
382
+ }
383
+ ```
384
+
385
+ ---
386
+
387
+ ## Custom Subscriber
388
+
389
+ Create your own adapter for any platform:
390
+
391
+ ```typescript
392
+ import { EventSubscriber } from 'autotel/events-adapter';
393
+
394
+ class MyCustomSubscriber implements EventSubscriber {
395
+ trackEvent(name: string, attributes?: Record<string, any>): void {
396
+ // Send to your platform
397
+ fetch('https://api.myplatform.com/events', {
398
+ method: 'POST',
399
+ body: JSON.stringify({ event: name, ...attributes })
400
+ });
401
+ }
402
+
403
+ trackFunnelStep(funnel: string, step: string, attributes?: any): void {
404
+ // Implement funnel tracking
405
+ }
406
+
407
+ trackOutcome(operation: string, outcome: string, attributes?: any): void {
408
+ // Implement outcome tracking
409
+ }
410
+
411
+ trackValue(name: string, value: number, attributes?: any): void {
412
+ // Implement value tracking
413
+ }
414
+ }
415
+
416
+ // Use it
417
+ const events = new Event('app', {
418
+ subscribers: [new MyCustomSubscriber()]
419
+ });
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Configuration
425
+
426
+ ### Enable/Disable Adapters
427
+
428
+ ```typescript
429
+ const events = new Event('checkout', {
430
+ subscribers: [
431
+ new PostHogSubscriber({
432
+ apiKey: 'phc_...',
433
+ enabled: process.env.NODE_ENV === 'production' // Only in prod
434
+ }),
435
+ new MixpanelSubscriber({
436
+ token: '...',
437
+ enabled: false // Temporarily disabled
438
+ })
439
+ ]
440
+ });
441
+ ```
442
+
443
+ ### Shutdown Gracefully
444
+
445
+ ```typescript
446
+ const posthog = new PostHogSubscriber({ apiKey: 'phc_...' });
447
+ const segment = new SegmentSubscriber({ writeKey: '...' });
448
+
449
+ // Before app shutdown
450
+ await posthog.shutdown();
451
+ await segment.shutdown();
452
+ ```
453
+
454
+ ---
455
+
456
+ ## Tree-Shaking
457
+
458
+ Adapters are **fully tree-shakeable**:
459
+
460
+ ```typescript
461
+ // Only PostHog code is bundled (not Mixpanel, Segment, etc.)
462
+ import { PostHogSubscriber } from 'autotel-subscribers/posthog';
463
+ ```
464
+
465
+ Bundle sizes (gzipped):
466
+ - PostHog: ~8KB
467
+ - Mixpanel: ~6KB
468
+ - Segment: ~12KB
469
+ - Amplitude: ~10KB
470
+ - Webhook: ~2KB
471
+
472
+ ---
473
+
474
+ ## Performance
475
+
476
+ **Zero overhead when not used:**
477
+ - If `subscribers: []` (empty), no adapter code runs
478
+ - Tree-shaken out in production builds
479
+
480
+ **Minimal overhead when used:**
481
+ - Adapters only fire if added to the array
482
+ - Non-blocking (fire-and-forget)
483
+ - No impact on primary OpenTelemetry metrics
484
+
485
+ ---
486
+
487
+ ## Middleware (Composition Patterns)
488
+
489
+ Add behaviors without modifying adapter code:
490
+
491
+ ### Available Middleware
492
+
493
+ ```typescript
494
+ import {
495
+ applyMiddleware,
496
+ retryMiddleware, // Exponential backoff retry
497
+ samplingMiddleware, // Send only X% of events
498
+ enrichmentMiddleware, // Add fields to events
499
+ loggingMiddleware, // Debug events
500
+ filterMiddleware, // Only send matching events
501
+ transformMiddleware, // Transform events
502
+ batchingMiddleware, // Batch for efficiency
503
+ rateLimitMiddleware, // Throttle requests
504
+ circuitBreakerMiddleware, // Prevent cascading failures
505
+ timeoutMiddleware // Add timeouts
506
+ } from 'autotel-subscribers/middleware';
507
+ ```
508
+
509
+ ### Examples
510
+
511
+ **Retry with Circuit Breaker:**
512
+ ```typescript
513
+ const subscriber = applyMiddleware(
514
+ new PostHogSubscriber({ apiKey: '...' }),
515
+ [
516
+ retryMiddleware({ maxRetries: 3, delayMs: 1000 }),
517
+ circuitBreakerMiddleware({ failureThreshold: 5, timeout: 60000 })
518
+ ]
519
+ );
520
+ ```
521
+
522
+ **Sample Events (Reduce Costs):**
523
+ ```typescript
524
+ // Only send 10% of events
525
+ const subscriber = applyMiddleware(
526
+ new WebhookSubscriber({ url: '...' }),
527
+ [samplingMiddleware(0.1)]
528
+ );
529
+ ```
530
+
531
+ **Enrich Events:**
532
+ ```typescript
533
+ const subscriber = applyMiddleware(
534
+ adapter,
535
+ [
536
+ enrichmentMiddleware((event) => ({
537
+ ...event,
538
+ attributes: {
539
+ ...event.attributes,
540
+ environment: process.env.NODE_ENV,
541
+ timestamp: Date.now()
542
+ }
543
+ }))
544
+ ]
545
+ );
546
+ ```
547
+
548
+ **Batch Events:**
549
+ ```typescript
550
+ const subscriber = applyMiddleware(
551
+ adapter,
552
+ [batchingMiddleware({ batchSize: 100, flushInterval: 5000 })]
553
+ );
554
+ ```
555
+
556
+ ---
557
+
558
+ ## Testing Custom Subscribers
559
+
560
+ ### AdapterTestHarness
561
+
562
+ Validate your adapter works correctly:
563
+
564
+ ```typescript
565
+ import { AdapterTestHarness } from 'autotel-subscribers/testing';
566
+
567
+ const harness = new AdapterTestHarness(new MySubscriber());
568
+ const results = await harness.runAll();
569
+
570
+ if (results.passed) {
571
+ console.log('All tests passed!');
572
+ } else {
573
+ console.error('Tests failed:', results.failures);
574
+ }
575
+
576
+ // Or use the built-in printer
577
+ AdapterTestHarness.printResults(results);
578
+ ```
579
+
580
+ Tests include:
581
+ - Basic event tracking
582
+ - Funnel tracking
583
+ - Outcome tracking
584
+ - Value tracking
585
+ - Concurrent requests (50 events)
586
+ - Error handling
587
+ - Graceful shutdown
588
+
589
+ ### MockWebhookServer
590
+
591
+ Test webhook subscribers without real HTTP calls:
592
+
593
+ ```typescript
594
+ import { MockWebhookServer } from 'autotel-subscribers/testing';
595
+
596
+ const server = new MockWebhookServer();
597
+ const url = await server.start();
598
+
599
+ const subscriber = new WebhookSubscriber({ url });
600
+ await subscriber.trackEvent('test', { foo: 'bar' });
601
+
602
+ // Assert
603
+ const requests = server.getRequests();
604
+ expect(requests).toHaveLength(1);
605
+ expect(requests[0].body.event).toBe('test');
606
+
607
+ await server.stop();
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Package Exports
613
+
614
+ All exports available:
615
+
616
+ ```typescript
617
+ // Import subscribers from their specific entry points
618
+ import { PostHogSubscriber } from 'autotel-subscribers/posthog';
619
+ import { MixpanelSubscriber } from 'autotel-subscribers/mixpanel';
620
+ import { SegmentSubscriber } from 'autotel-subscribers/segment';
621
+ import { AmplitudeSubscriber } from 'autotel-subscribers/amplitude';
622
+ import { WebhookSubscriber } from 'autotel-subscribers/webhook';
623
+ import { SlackSubscriber } from 'autotel-subscribers/slack';
624
+
625
+ // Base classes for building custom subscribers
626
+ import { EventSubscriber, EventPayload } from 'autotel-subscribers';
627
+ import { StreamingEventSubscriber } from 'autotel-subscribers';
628
+
629
+ // Middleware (composition)
630
+ import {
631
+ applyMiddleware,
632
+ retryMiddleware,
633
+ samplingMiddleware,
634
+ /* ... 8 more middleware functions */
635
+ } from 'autotel-subscribers/middleware';
636
+
637
+ // Testing utilities
638
+ import {
639
+ AdapterTestHarness,
640
+ MockWebhookServer,
641
+ MockEventSubscriber
642
+ } from 'autotel-subscribers/testing';
643
+
644
+ // For outbox pattern, see autotel-outbox package
645
+ ```
646
+
647
+ ---
648
+
649
+ ## Resources
650
+
651
+ - [Your First Adapter Guide](./docs/your-first-subscriber.md) - Create a custom adapter in 5 minutes
652
+ - [Quickstart Template](./examples/quickstart-custom-subscriber.ts) - Copy-paste 20-line template
653
+ - [Testing Guide](./docs/your-first-subscriber.md#test-your-adapter) - Validate your adapter works
654
+ - [Middleware Guide](./docs/your-first-subscriber.md#add-superpowers-with-middleware) - Add retry, sampling, etc.
655
+ - [Outbox Pattern](/packages/autotel-outbox/) - For transactional outbox pattern
656
+
657
+ ---
658
+
659
+ ## Examples
660
+
661
+ See the [autotel-examples](https://github.com/jagreehal/autotel/tree/main/packages/autotel-examples) package for complete examples.
662
+
663
+ ---
664
+
665
+ ## License
666
+
667
+ MIT © [Jag Reehal](https://jagreehal.com)
668
+
669
+