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.
- package/LICENSE +21 -0
- package/README.md +669 -0
- package/dist/amplitude.cjs +2486 -0
- package/dist/amplitude.cjs.map +1 -0
- package/dist/amplitude.d.cts +49 -0
- package/dist/amplitude.d.ts +49 -0
- package/dist/amplitude.js +2463 -0
- package/dist/amplitude.js.map +1 -0
- package/dist/event-subscriber-base-CnF3V56W.d.cts +182 -0
- package/dist/event-subscriber-base-CnF3V56W.d.ts +182 -0
- package/dist/factories.cjs +16660 -0
- package/dist/factories.cjs.map +1 -0
- package/dist/factories.d.cts +304 -0
- package/dist/factories.d.ts +304 -0
- package/dist/factories.js +16624 -0
- package/dist/factories.js.map +1 -0
- package/dist/index.cjs +16575 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +179 -0
- package/dist/index.d.ts +179 -0
- package/dist/index.js +16539 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.cjs +220 -0
- package/dist/middleware.cjs.map +1 -0
- package/dist/middleware.d.cts +227 -0
- package/dist/middleware.d.ts +227 -0
- package/dist/middleware.js +208 -0
- package/dist/middleware.js.map +1 -0
- package/dist/mixpanel.cjs +2940 -0
- package/dist/mixpanel.cjs.map +1 -0
- package/dist/mixpanel.d.cts +47 -0
- package/dist/mixpanel.d.ts +47 -0
- package/dist/mixpanel.js +2932 -0
- package/dist/mixpanel.js.map +1 -0
- package/dist/posthog.cjs +4115 -0
- package/dist/posthog.cjs.map +1 -0
- package/dist/posthog.d.cts +299 -0
- package/dist/posthog.d.ts +299 -0
- package/dist/posthog.js +4113 -0
- package/dist/posthog.js.map +1 -0
- package/dist/segment.cjs +6822 -0
- package/dist/segment.cjs.map +1 -0
- package/dist/segment.d.cts +49 -0
- package/dist/segment.d.ts +49 -0
- package/dist/segment.js +6794 -0
- package/dist/segment.js.map +1 -0
- package/dist/slack.cjs +368 -0
- package/dist/slack.cjs.map +1 -0
- package/dist/slack.d.cts +126 -0
- package/dist/slack.d.ts +126 -0
- package/dist/slack.js +366 -0
- package/dist/slack.js.map +1 -0
- package/dist/webhook.cjs +100 -0
- package/dist/webhook.cjs.map +1 -0
- package/dist/webhook.d.cts +53 -0
- package/dist/webhook.d.ts +53 -0
- package/dist/webhook.js +98 -0
- package/dist/webhook.js.map +1 -0
- package/examples/quickstart-custom-subscriber.ts +144 -0
- package/examples/subscriber-bigquery.ts +219 -0
- package/examples/subscriber-databricks.ts +280 -0
- package/examples/subscriber-kafka.ts +326 -0
- package/examples/subscriber-kinesis.ts +307 -0
- package/examples/subscriber-posthog.ts +421 -0
- package/examples/subscriber-pubsub.ts +336 -0
- package/examples/subscriber-snowflake.ts +232 -0
- package/package.json +141 -0
- package/src/amplitude.test.ts +231 -0
- package/src/amplitude.ts +148 -0
- package/src/event-subscriber-base.ts +325 -0
- package/src/factories.ts +197 -0
- package/src/index.ts +50 -0
- package/src/middleware.ts +489 -0
- package/src/mixpanel.test.ts +194 -0
- package/src/mixpanel.ts +134 -0
- package/src/mock-event-subscriber.ts +333 -0
- package/src/posthog.test.ts +629 -0
- package/src/posthog.ts +530 -0
- package/src/segment.test.ts +228 -0
- package/src/segment.ts +148 -0
- package/src/slack.ts +383 -0
- package/src/streaming-event-subscriber.ts +323 -0
- package/src/testing/index.ts +37 -0
- package/src/testing/mock-webhook-server.ts +242 -0
- package/src/testing/subscriber-test-harness.ts +365 -0
- package/src/webhook.test.ts +264 -0
- 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
|
+
|