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
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { EventSubscriber, EventAttributes, FunnelStatus, OutcomeStatus } from 'autotel/event-subscriber';
|
|
2
|
+
export { PostHogConfig } from './posthog.cjs';
|
|
3
|
+
export { MixpanelConfig } from './mixpanel.cjs';
|
|
4
|
+
export { AmplitudeConfig } from './amplitude.cjs';
|
|
5
|
+
export { SegmentConfig } from './segment.cjs';
|
|
6
|
+
export { WebhookConfig } from './webhook.cjs';
|
|
7
|
+
export { SlackSubscriberConfig } from './slack.cjs';
|
|
8
|
+
import './event-subscriber-base-CnF3V56W.cjs';
|
|
9
|
+
import 'posthog-node';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Mock Events Subscriber for Testing
|
|
13
|
+
*
|
|
14
|
+
* In-memory subscriber that captures all events events for testing assertions.
|
|
15
|
+
* Useful for unit testing code that uses Events without making real API calls.
|
|
16
|
+
*
|
|
17
|
+
* @example Basic testing
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { Events } from 'autotel/events';
|
|
20
|
+
* import { MockEventSubscriber } from 'autotel-subscribers/mock-event-subscriber';
|
|
21
|
+
* import { describe, it, expect, beforeEach } from 'vitest';
|
|
22
|
+
*
|
|
23
|
+
* describe('CheckoutService', () => {
|
|
24
|
+
* let mockSubscriber: MockEventSubscriber;
|
|
25
|
+
* let events: Events;
|
|
26
|
+
*
|
|
27
|
+
* beforeEach(() => {
|
|
28
|
+
* mockSubscriber = new MockEventSubscriber();
|
|
29
|
+
* events = new Events('checkout', { subscribers: [mockSubscriber] });
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* it('should track order completion', async () => {
|
|
33
|
+
* const service = new CheckoutService(events);
|
|
34
|
+
* await service.completeOrder('ord_123', 99.99);
|
|
35
|
+
*
|
|
36
|
+
* expect(mockSubscriber.events).toHaveLength(1);
|
|
37
|
+
* expect(mockSubscriber.events[0]).toMatchObject({
|
|
38
|
+
* type: 'event',
|
|
39
|
+
* name: 'order.completed',
|
|
40
|
+
* attributes: { orderId: 'ord_123', amount: 99.99 }
|
|
41
|
+
* });
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* it('should track checkout funnel', () => {
|
|
45
|
+
* events.trackFunnelStep('checkout', 'started', { cartValue: 99.99 });
|
|
46
|
+
* events.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 });
|
|
47
|
+
*
|
|
48
|
+
* const funnelEvents = mockSubscriber.getFunnelEvents('checkout');
|
|
49
|
+
* expect(funnelEvents).toHaveLength(2);
|
|
50
|
+
* expect(funnelEvents[0].step).toBe('started');
|
|
51
|
+
* expect(funnelEvents[1].step).toBe('completed');
|
|
52
|
+
* });
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example With Outbox Pattern
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { createPublisher } from 'autotel-outbox';
|
|
59
|
+
* import { MockOutboxStorage } from 'autotel-outbox/testing';
|
|
60
|
+
*
|
|
61
|
+
* it('should broadcast events to all subscribers', async () => {
|
|
62
|
+
* const mockOutbox = new MockOutboxStorage();
|
|
63
|
+
* const mockSubscriber = new MockEventSubscriber();
|
|
64
|
+
*
|
|
65
|
+
* // Add events to outbox
|
|
66
|
+
* await mockOutbox.writeEvent({
|
|
67
|
+
* id: '1',
|
|
68
|
+
* type: 'order.completed',
|
|
69
|
+
* aggregateId: 'ord_123',
|
|
70
|
+
* aggregateType: 'Order',
|
|
71
|
+
* payload: { amount: 99.99 },
|
|
72
|
+
* createdAt: new Date().toISOString(),
|
|
73
|
+
* publishedAt: null
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* // Run publisher
|
|
77
|
+
* const publisher = createPublisher(mockOutbox, [mockSubscriber]);
|
|
78
|
+
* await publisher();
|
|
79
|
+
*
|
|
80
|
+
* // Assert event was broadcast
|
|
81
|
+
* expect(mockSubscriber.events).toHaveLength(1);
|
|
82
|
+
* expect(mockSubscriber.events[0].name).toBe('order.completed');
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Captured event data
|
|
89
|
+
*/
|
|
90
|
+
interface CapturedEvent {
|
|
91
|
+
type: 'event' | 'funnel' | 'outcome' | 'value';
|
|
92
|
+
name: string;
|
|
93
|
+
attributes?: EventAttributes;
|
|
94
|
+
funnel?: string;
|
|
95
|
+
step?: FunnelStatus;
|
|
96
|
+
operation?: string;
|
|
97
|
+
outcome?: OutcomeStatus;
|
|
98
|
+
value?: number;
|
|
99
|
+
timestamp: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Mock subscriber for testing
|
|
103
|
+
*
|
|
104
|
+
* Captures all events calls in memory for test assertions.
|
|
105
|
+
* Does not make any real API calls.
|
|
106
|
+
*/
|
|
107
|
+
declare class MockEventSubscriber implements EventSubscriber {
|
|
108
|
+
readonly name = "MockEventSubscriber";
|
|
109
|
+
readonly version = "1.0.0";
|
|
110
|
+
/**
|
|
111
|
+
* All captured events
|
|
112
|
+
*/
|
|
113
|
+
events: CapturedEvent[];
|
|
114
|
+
/**
|
|
115
|
+
* Track if shutdown was called
|
|
116
|
+
*/
|
|
117
|
+
shutdownCalled: boolean;
|
|
118
|
+
trackEvent(name: string, attributes?: EventAttributes): Promise<void>;
|
|
119
|
+
trackFunnelStep(funnelName: string, step: FunnelStatus, attributes?: EventAttributes): Promise<void>;
|
|
120
|
+
trackOutcome(operationName: string, outcome: OutcomeStatus, attributes?: EventAttributes): Promise<void>;
|
|
121
|
+
trackValue(name: string, value: number, attributes?: EventAttributes): Promise<void>;
|
|
122
|
+
shutdown(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Reset captured events (useful between tests)
|
|
125
|
+
*/
|
|
126
|
+
reset(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Get events by type
|
|
129
|
+
*/
|
|
130
|
+
getEventsByType(type: 'event' | 'funnel' | 'outcome' | 'value'): CapturedEvent[];
|
|
131
|
+
/**
|
|
132
|
+
* Get events by name
|
|
133
|
+
*/
|
|
134
|
+
getEventsByName(name: string): CapturedEvent[];
|
|
135
|
+
/**
|
|
136
|
+
* Get funnel events for a specific funnel
|
|
137
|
+
*/
|
|
138
|
+
getFunnelEvents(funnelName: string): CapturedEvent[];
|
|
139
|
+
/**
|
|
140
|
+
* Get outcome events for a specific operation
|
|
141
|
+
*/
|
|
142
|
+
getOutcomeEvents(operationName: string): CapturedEvent[];
|
|
143
|
+
/**
|
|
144
|
+
* Get value events by metric name
|
|
145
|
+
*/
|
|
146
|
+
getValueEvents(metricName: string): CapturedEvent[];
|
|
147
|
+
/**
|
|
148
|
+
* Assert that an event was tracked
|
|
149
|
+
*/
|
|
150
|
+
assertEventTracked(name: string, attributes?: Partial<EventAttributes>): void;
|
|
151
|
+
/**
|
|
152
|
+
* Assert that a funnel step was tracked
|
|
153
|
+
*/
|
|
154
|
+
assertFunnelStepTracked(funnelName: string, step: FunnelStatus, attributes?: Partial<EventAttributes>): void;
|
|
155
|
+
/**
|
|
156
|
+
* Assert that an outcome was tracked
|
|
157
|
+
*/
|
|
158
|
+
assertOutcomeTracked(operationName: string, outcome: OutcomeStatus, attributes?: Partial<EventAttributes>): void;
|
|
159
|
+
/**
|
|
160
|
+
* Pretty print all captured events (useful for debugging)
|
|
161
|
+
*/
|
|
162
|
+
printEvents(): void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Factory functions for creating events subscribers
|
|
167
|
+
*
|
|
168
|
+
* Function-based alternatives to `new SubscriberClass()` pattern.
|
|
169
|
+
* Provides a consistent API and better tree-shaking.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* import { createPostHogSubscriber, createWebhookSubscriber } from 'autotel-subscribers/factories'
|
|
174
|
+
*
|
|
175
|
+
* const events = new Events('my-service', {
|
|
176
|
+
* subscribers: [
|
|
177
|
+
* createPostHogSubscriber({ apiKey: 'phc_...' }),
|
|
178
|
+
* createWebhookSubscriber({ url: 'https://...' })
|
|
179
|
+
* ]
|
|
180
|
+
* })
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create a PostHog events subscriber
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const posthog = createPostHogSubscriber({
|
|
190
|
+
* apiKey: 'phc_...',
|
|
191
|
+
* host: 'https://app.posthog.com' // optional
|
|
192
|
+
* })
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare function createPostHogSubscriber(config: {
|
|
196
|
+
apiKey: string;
|
|
197
|
+
host?: string;
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
}): EventSubscriber;
|
|
200
|
+
/**
|
|
201
|
+
* Create a Mixpanel events subscriber
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const mixpanel = createMixpanelSubscriber({
|
|
206
|
+
* token: 'YOUR_TOKEN'
|
|
207
|
+
* })
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
declare function createMixpanelSubscriber(config: {
|
|
211
|
+
token: string;
|
|
212
|
+
enabled?: boolean;
|
|
213
|
+
}): EventSubscriber;
|
|
214
|
+
/**
|
|
215
|
+
* Create an Amplitude events subscriber
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* const amplitude = createAmplitudeSubscriber({
|
|
220
|
+
* apiKey: 'YOUR_API_KEY'
|
|
221
|
+
* })
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
declare function createAmplitudeSubscriber(config: {
|
|
225
|
+
apiKey: string;
|
|
226
|
+
enabled?: boolean;
|
|
227
|
+
}): EventSubscriber;
|
|
228
|
+
/**
|
|
229
|
+
* Create a Segment events subscriber
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* const segment = createSegmentSubscriber({
|
|
234
|
+
* writeKey: 'YOUR_WRITE_KEY'
|
|
235
|
+
* })
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
declare function createSegmentSubscriber(config: {
|
|
239
|
+
writeKey: string;
|
|
240
|
+
enabled?: boolean;
|
|
241
|
+
}): EventSubscriber;
|
|
242
|
+
/**
|
|
243
|
+
* Create a Webhook events subscriber
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const webhook = createWebhookSubscriber({
|
|
248
|
+
* url: 'https://your-webhook-endpoint.com/events',
|
|
249
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
250
|
+
* })
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
declare function createWebhookSubscriber(config: {
|
|
254
|
+
url: string;
|
|
255
|
+
method?: 'POST' | 'PUT';
|
|
256
|
+
headers?: Record<string, string>;
|
|
257
|
+
enabled?: boolean;
|
|
258
|
+
}): EventSubscriber;
|
|
259
|
+
/**
|
|
260
|
+
* Create a Slack events subscriber
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* const slack = createSlackSubscriber({
|
|
265
|
+
* webhookUrl: 'https://hooks.slack.com/services/...',
|
|
266
|
+
* channel: '#events'
|
|
267
|
+
* })
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
declare function createSlackSubscriber(config: {
|
|
271
|
+
webhookUrl: string;
|
|
272
|
+
channel?: string;
|
|
273
|
+
enabled?: boolean;
|
|
274
|
+
}): EventSubscriber;
|
|
275
|
+
/**
|
|
276
|
+
* Create a mock events subscriber (for testing)
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const mock = createMockSubscriber()
|
|
281
|
+
*
|
|
282
|
+
* // Capture events
|
|
283
|
+
* events.trackEvent('test.event', { foo: 'bar' })
|
|
284
|
+
*
|
|
285
|
+
* // Assert
|
|
286
|
+
* expect(mock.events).toHaveLength(1)
|
|
287
|
+
* expect(mock.events[0].name).toBe('test.event')
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare function createMockSubscriber(): MockEventSubscriber;
|
|
291
|
+
/**
|
|
292
|
+
* Compose multiple subscribers into one
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* const multiSubscriber = composeSubscribers([
|
|
297
|
+
* createPostHogSubscriber({ apiKey: '...' }),
|
|
298
|
+
* createWebhookSubscriber({ url: '...' })
|
|
299
|
+
* ])
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare function composeSubscribers(adapters: EventSubscriber[]): EventSubscriber;
|
|
303
|
+
|
|
304
|
+
export { composeSubscribers, createAmplitudeSubscriber, createMixpanelSubscriber, createMockSubscriber, createPostHogSubscriber, createSegmentSubscriber, createSlackSubscriber, createWebhookSubscriber };
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { EventSubscriber, EventAttributes, FunnelStatus, OutcomeStatus } from 'autotel/event-subscriber';
|
|
2
|
+
export { PostHogConfig } from './posthog.js';
|
|
3
|
+
export { MixpanelConfig } from './mixpanel.js';
|
|
4
|
+
export { AmplitudeConfig } from './amplitude.js';
|
|
5
|
+
export { SegmentConfig } from './segment.js';
|
|
6
|
+
export { WebhookConfig } from './webhook.js';
|
|
7
|
+
export { SlackSubscriberConfig } from './slack.js';
|
|
8
|
+
import './event-subscriber-base-CnF3V56W.js';
|
|
9
|
+
import 'posthog-node';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Mock Events Subscriber for Testing
|
|
13
|
+
*
|
|
14
|
+
* In-memory subscriber that captures all events events for testing assertions.
|
|
15
|
+
* Useful for unit testing code that uses Events without making real API calls.
|
|
16
|
+
*
|
|
17
|
+
* @example Basic testing
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { Events } from 'autotel/events';
|
|
20
|
+
* import { MockEventSubscriber } from 'autotel-subscribers/mock-event-subscriber';
|
|
21
|
+
* import { describe, it, expect, beforeEach } from 'vitest';
|
|
22
|
+
*
|
|
23
|
+
* describe('CheckoutService', () => {
|
|
24
|
+
* let mockSubscriber: MockEventSubscriber;
|
|
25
|
+
* let events: Events;
|
|
26
|
+
*
|
|
27
|
+
* beforeEach(() => {
|
|
28
|
+
* mockSubscriber = new MockEventSubscriber();
|
|
29
|
+
* events = new Events('checkout', { subscribers: [mockSubscriber] });
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* it('should track order completion', async () => {
|
|
33
|
+
* const service = new CheckoutService(events);
|
|
34
|
+
* await service.completeOrder('ord_123', 99.99);
|
|
35
|
+
*
|
|
36
|
+
* expect(mockSubscriber.events).toHaveLength(1);
|
|
37
|
+
* expect(mockSubscriber.events[0]).toMatchObject({
|
|
38
|
+
* type: 'event',
|
|
39
|
+
* name: 'order.completed',
|
|
40
|
+
* attributes: { orderId: 'ord_123', amount: 99.99 }
|
|
41
|
+
* });
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* it('should track checkout funnel', () => {
|
|
45
|
+
* events.trackFunnelStep('checkout', 'started', { cartValue: 99.99 });
|
|
46
|
+
* events.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 });
|
|
47
|
+
*
|
|
48
|
+
* const funnelEvents = mockSubscriber.getFunnelEvents('checkout');
|
|
49
|
+
* expect(funnelEvents).toHaveLength(2);
|
|
50
|
+
* expect(funnelEvents[0].step).toBe('started');
|
|
51
|
+
* expect(funnelEvents[1].step).toBe('completed');
|
|
52
|
+
* });
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example With Outbox Pattern
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { createPublisher } from 'autotel-outbox';
|
|
59
|
+
* import { MockOutboxStorage } from 'autotel-outbox/testing';
|
|
60
|
+
*
|
|
61
|
+
* it('should broadcast events to all subscribers', async () => {
|
|
62
|
+
* const mockOutbox = new MockOutboxStorage();
|
|
63
|
+
* const mockSubscriber = new MockEventSubscriber();
|
|
64
|
+
*
|
|
65
|
+
* // Add events to outbox
|
|
66
|
+
* await mockOutbox.writeEvent({
|
|
67
|
+
* id: '1',
|
|
68
|
+
* type: 'order.completed',
|
|
69
|
+
* aggregateId: 'ord_123',
|
|
70
|
+
* aggregateType: 'Order',
|
|
71
|
+
* payload: { amount: 99.99 },
|
|
72
|
+
* createdAt: new Date().toISOString(),
|
|
73
|
+
* publishedAt: null
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* // Run publisher
|
|
77
|
+
* const publisher = createPublisher(mockOutbox, [mockSubscriber]);
|
|
78
|
+
* await publisher();
|
|
79
|
+
*
|
|
80
|
+
* // Assert event was broadcast
|
|
81
|
+
* expect(mockSubscriber.events).toHaveLength(1);
|
|
82
|
+
* expect(mockSubscriber.events[0].name).toBe('order.completed');
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Captured event data
|
|
89
|
+
*/
|
|
90
|
+
interface CapturedEvent {
|
|
91
|
+
type: 'event' | 'funnel' | 'outcome' | 'value';
|
|
92
|
+
name: string;
|
|
93
|
+
attributes?: EventAttributes;
|
|
94
|
+
funnel?: string;
|
|
95
|
+
step?: FunnelStatus;
|
|
96
|
+
operation?: string;
|
|
97
|
+
outcome?: OutcomeStatus;
|
|
98
|
+
value?: number;
|
|
99
|
+
timestamp: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Mock subscriber for testing
|
|
103
|
+
*
|
|
104
|
+
* Captures all events calls in memory for test assertions.
|
|
105
|
+
* Does not make any real API calls.
|
|
106
|
+
*/
|
|
107
|
+
declare class MockEventSubscriber implements EventSubscriber {
|
|
108
|
+
readonly name = "MockEventSubscriber";
|
|
109
|
+
readonly version = "1.0.0";
|
|
110
|
+
/**
|
|
111
|
+
* All captured events
|
|
112
|
+
*/
|
|
113
|
+
events: CapturedEvent[];
|
|
114
|
+
/**
|
|
115
|
+
* Track if shutdown was called
|
|
116
|
+
*/
|
|
117
|
+
shutdownCalled: boolean;
|
|
118
|
+
trackEvent(name: string, attributes?: EventAttributes): Promise<void>;
|
|
119
|
+
trackFunnelStep(funnelName: string, step: FunnelStatus, attributes?: EventAttributes): Promise<void>;
|
|
120
|
+
trackOutcome(operationName: string, outcome: OutcomeStatus, attributes?: EventAttributes): Promise<void>;
|
|
121
|
+
trackValue(name: string, value: number, attributes?: EventAttributes): Promise<void>;
|
|
122
|
+
shutdown(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Reset captured events (useful between tests)
|
|
125
|
+
*/
|
|
126
|
+
reset(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Get events by type
|
|
129
|
+
*/
|
|
130
|
+
getEventsByType(type: 'event' | 'funnel' | 'outcome' | 'value'): CapturedEvent[];
|
|
131
|
+
/**
|
|
132
|
+
* Get events by name
|
|
133
|
+
*/
|
|
134
|
+
getEventsByName(name: string): CapturedEvent[];
|
|
135
|
+
/**
|
|
136
|
+
* Get funnel events for a specific funnel
|
|
137
|
+
*/
|
|
138
|
+
getFunnelEvents(funnelName: string): CapturedEvent[];
|
|
139
|
+
/**
|
|
140
|
+
* Get outcome events for a specific operation
|
|
141
|
+
*/
|
|
142
|
+
getOutcomeEvents(operationName: string): CapturedEvent[];
|
|
143
|
+
/**
|
|
144
|
+
* Get value events by metric name
|
|
145
|
+
*/
|
|
146
|
+
getValueEvents(metricName: string): CapturedEvent[];
|
|
147
|
+
/**
|
|
148
|
+
* Assert that an event was tracked
|
|
149
|
+
*/
|
|
150
|
+
assertEventTracked(name: string, attributes?: Partial<EventAttributes>): void;
|
|
151
|
+
/**
|
|
152
|
+
* Assert that a funnel step was tracked
|
|
153
|
+
*/
|
|
154
|
+
assertFunnelStepTracked(funnelName: string, step: FunnelStatus, attributes?: Partial<EventAttributes>): void;
|
|
155
|
+
/**
|
|
156
|
+
* Assert that an outcome was tracked
|
|
157
|
+
*/
|
|
158
|
+
assertOutcomeTracked(operationName: string, outcome: OutcomeStatus, attributes?: Partial<EventAttributes>): void;
|
|
159
|
+
/**
|
|
160
|
+
* Pretty print all captured events (useful for debugging)
|
|
161
|
+
*/
|
|
162
|
+
printEvents(): void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Factory functions for creating events subscribers
|
|
167
|
+
*
|
|
168
|
+
* Function-based alternatives to `new SubscriberClass()` pattern.
|
|
169
|
+
* Provides a consistent API and better tree-shaking.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* import { createPostHogSubscriber, createWebhookSubscriber } from 'autotel-subscribers/factories'
|
|
174
|
+
*
|
|
175
|
+
* const events = new Events('my-service', {
|
|
176
|
+
* subscribers: [
|
|
177
|
+
* createPostHogSubscriber({ apiKey: 'phc_...' }),
|
|
178
|
+
* createWebhookSubscriber({ url: 'https://...' })
|
|
179
|
+
* ]
|
|
180
|
+
* })
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create a PostHog events subscriber
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const posthog = createPostHogSubscriber({
|
|
190
|
+
* apiKey: 'phc_...',
|
|
191
|
+
* host: 'https://app.posthog.com' // optional
|
|
192
|
+
* })
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare function createPostHogSubscriber(config: {
|
|
196
|
+
apiKey: string;
|
|
197
|
+
host?: string;
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
}): EventSubscriber;
|
|
200
|
+
/**
|
|
201
|
+
* Create a Mixpanel events subscriber
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* const mixpanel = createMixpanelSubscriber({
|
|
206
|
+
* token: 'YOUR_TOKEN'
|
|
207
|
+
* })
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
declare function createMixpanelSubscriber(config: {
|
|
211
|
+
token: string;
|
|
212
|
+
enabled?: boolean;
|
|
213
|
+
}): EventSubscriber;
|
|
214
|
+
/**
|
|
215
|
+
* Create an Amplitude events subscriber
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* const amplitude = createAmplitudeSubscriber({
|
|
220
|
+
* apiKey: 'YOUR_API_KEY'
|
|
221
|
+
* })
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
declare function createAmplitudeSubscriber(config: {
|
|
225
|
+
apiKey: string;
|
|
226
|
+
enabled?: boolean;
|
|
227
|
+
}): EventSubscriber;
|
|
228
|
+
/**
|
|
229
|
+
* Create a Segment events subscriber
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* const segment = createSegmentSubscriber({
|
|
234
|
+
* writeKey: 'YOUR_WRITE_KEY'
|
|
235
|
+
* })
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
declare function createSegmentSubscriber(config: {
|
|
239
|
+
writeKey: string;
|
|
240
|
+
enabled?: boolean;
|
|
241
|
+
}): EventSubscriber;
|
|
242
|
+
/**
|
|
243
|
+
* Create a Webhook events subscriber
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const webhook = createWebhookSubscriber({
|
|
248
|
+
* url: 'https://your-webhook-endpoint.com/events',
|
|
249
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
250
|
+
* })
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
declare function createWebhookSubscriber(config: {
|
|
254
|
+
url: string;
|
|
255
|
+
method?: 'POST' | 'PUT';
|
|
256
|
+
headers?: Record<string, string>;
|
|
257
|
+
enabled?: boolean;
|
|
258
|
+
}): EventSubscriber;
|
|
259
|
+
/**
|
|
260
|
+
* Create a Slack events subscriber
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* const slack = createSlackSubscriber({
|
|
265
|
+
* webhookUrl: 'https://hooks.slack.com/services/...',
|
|
266
|
+
* channel: '#events'
|
|
267
|
+
* })
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
declare function createSlackSubscriber(config: {
|
|
271
|
+
webhookUrl: string;
|
|
272
|
+
channel?: string;
|
|
273
|
+
enabled?: boolean;
|
|
274
|
+
}): EventSubscriber;
|
|
275
|
+
/**
|
|
276
|
+
* Create a mock events subscriber (for testing)
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const mock = createMockSubscriber()
|
|
281
|
+
*
|
|
282
|
+
* // Capture events
|
|
283
|
+
* events.trackEvent('test.event', { foo: 'bar' })
|
|
284
|
+
*
|
|
285
|
+
* // Assert
|
|
286
|
+
* expect(mock.events).toHaveLength(1)
|
|
287
|
+
* expect(mock.events[0].name).toBe('test.event')
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare function createMockSubscriber(): MockEventSubscriber;
|
|
291
|
+
/**
|
|
292
|
+
* Compose multiple subscribers into one
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* const multiSubscriber = composeSubscribers([
|
|
297
|
+
* createPostHogSubscriber({ apiKey: '...' }),
|
|
298
|
+
* createWebhookSubscriber({ url: '...' })
|
|
299
|
+
* ])
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare function composeSubscribers(adapters: EventSubscriber[]): EventSubscriber;
|
|
303
|
+
|
|
304
|
+
export { composeSubscribers, createAmplitudeSubscriber, createMixpanelSubscriber, createMockSubscriber, createPostHogSubscriber, createSegmentSubscriber, createSlackSubscriber, createWebhookSubscriber };
|