autotel-subscribers 40.0.0 → 41.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/package.json +3 -4
- package/src/amplitude.test.ts +0 -229
- package/src/amplitude.ts +0 -150
- package/src/architecture-snapshot.test.ts +0 -220
- package/src/architecture-snapshot.ts +0 -416
- package/src/event-subscriber-base.ts +0 -433
- package/src/factories.compose.test.ts +0 -107
- package/src/factories.ts +0 -342
- package/src/file.test.ts +0 -87
- package/src/file.ts +0 -97
- package/src/http-client.ts +0 -127
- package/src/index.ts +0 -65
- package/src/middleware.test.ts +0 -115
- package/src/middleware.ts +0 -636
- package/src/mixpanel.test.ts +0 -194
- package/src/mixpanel.ts +0 -205
- package/src/mock-event-subscriber.ts +0 -333
- package/src/posthog-capture-exception.test.ts +0 -146
- package/src/posthog-error-formatter.test.ts +0 -152
- package/src/posthog-error-formatter.ts +0 -113
- package/src/posthog-redaction.test.ts +0 -143
- package/src/posthog.test.ts +0 -629
- package/src/posthog.ts +0 -874
- package/src/retry-classification.ts +0 -78
- package/src/security.test.ts +0 -251
- package/src/security.ts +0 -200
- package/src/segment.test.ts +0 -223
- package/src/segment.ts +0 -148
- package/src/slack.ts +0 -383
- package/src/streaming-event-subscriber.ts +0 -323
- package/src/testing/index.ts +0 -37
- package/src/testing/mock-webhook-server.ts +0 -242
- package/src/testing/subscriber-test-harness.ts +0 -365
- package/src/webhook-delivery.ts +0 -88
- package/src/webhook.test.ts +0 -147
- package/src/webhook.ts +0 -134
package/src/mixpanel.test.ts
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { MixpanelSubscriber } from './mixpanel';
|
|
3
|
-
|
|
4
|
-
// Mock the mixpanel module
|
|
5
|
-
vi.mock('mixpanel', () => ({
|
|
6
|
-
default: {
|
|
7
|
-
init: vi.fn().mockReturnValue({
|
|
8
|
-
track: vi.fn(),
|
|
9
|
-
}),
|
|
10
|
-
},
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
describe('MixpanelSubscriber', () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
vi.clearAllMocks();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('initialization', () => {
|
|
19
|
-
it('should initialize with valid config', async () => {
|
|
20
|
-
const adapter = new MixpanelSubscriber({
|
|
21
|
-
token: 'test_token',
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
25
|
-
|
|
26
|
-
expect(adapter).toBeDefined();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should not initialize when disabled', () => {
|
|
30
|
-
const adapter = new MixpanelSubscriber({
|
|
31
|
-
token: 'test_token',
|
|
32
|
-
enabled: false,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
expect(adapter).toBeDefined();
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('trackEvent', () => {
|
|
40
|
-
it('should track event with attributes', async () => {
|
|
41
|
-
const Mixpanel = await import('mixpanel');
|
|
42
|
-
const adapter = new MixpanelSubscriber({
|
|
43
|
-
token: 'test_token',
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
47
|
-
|
|
48
|
-
adapter.trackEvent('order.completed', {
|
|
49
|
-
userId: 'user-123',
|
|
50
|
-
amount: 99.99,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
54
|
-
|
|
55
|
-
const mockInstance = (Mixpanel.default.init as any).mock.results[0].value;
|
|
56
|
-
expect(mockInstance.track).toHaveBeenCalledWith('order.completed', {
|
|
57
|
-
distinct_id: 'user-123',
|
|
58
|
-
userId: 'user-123',
|
|
59
|
-
amount: 99.99,
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should use user_id if userId is not present', async () => {
|
|
64
|
-
const Mixpanel = await import('mixpanel');
|
|
65
|
-
const adapter = new MixpanelSubscriber({
|
|
66
|
-
token: 'test_token',
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
70
|
-
|
|
71
|
-
adapter.trackEvent('order.completed', {
|
|
72
|
-
user_id: 'user-456',
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
76
|
-
|
|
77
|
-
const mockInstance = (Mixpanel.default.init as any).mock.results[0].value;
|
|
78
|
-
expect(mockInstance.track).toHaveBeenCalledWith('order.completed', {
|
|
79
|
-
distinct_id: 'user-456',
|
|
80
|
-
user_id: 'user-456',
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should use anonymous if no userId is present', async () => {
|
|
85
|
-
const Mixpanel = await import('mixpanel');
|
|
86
|
-
const adapter = new MixpanelSubscriber({
|
|
87
|
-
token: 'test_token',
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
91
|
-
|
|
92
|
-
adapter.trackEvent('page.viewed');
|
|
93
|
-
|
|
94
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
95
|
-
|
|
96
|
-
const mockInstance = (Mixpanel.default.init as any).mock.results[0].value;
|
|
97
|
-
expect(mockInstance.track).toHaveBeenCalledWith('page.viewed', {
|
|
98
|
-
distinct_id: 'anonymous',
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should not track when disabled', () => {
|
|
103
|
-
const adapter = new MixpanelSubscriber({
|
|
104
|
-
token: 'test_token',
|
|
105
|
-
enabled: false,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
adapter.trackEvent('order.completed', { userId: 'user-123' });
|
|
109
|
-
|
|
110
|
-
// Should not throw
|
|
111
|
-
expect(true).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe('trackFunnelStep', () => {
|
|
116
|
-
it('should track funnel step', async () => {
|
|
117
|
-
const Mixpanel = await import('mixpanel');
|
|
118
|
-
const adapter = new MixpanelSubscriber({
|
|
119
|
-
token: 'test_token',
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
123
|
-
|
|
124
|
-
adapter.trackFunnelStep('checkout', 'started', {
|
|
125
|
-
userId: 'user-123',
|
|
126
|
-
cartValue: 150,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
130
|
-
|
|
131
|
-
const mockInstance = (Mixpanel.default.init as any).mock.results[0].value;
|
|
132
|
-
expect(mockInstance.track).toHaveBeenCalledWith('checkout.started', {
|
|
133
|
-
distinct_id: 'user-123',
|
|
134
|
-
funnel: 'checkout',
|
|
135
|
-
step: 'started',
|
|
136
|
-
userId: 'user-123',
|
|
137
|
-
cartValue: 150,
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('trackOutcome', () => {
|
|
143
|
-
it('should track outcome', async () => {
|
|
144
|
-
const Mixpanel = await import('mixpanel');
|
|
145
|
-
const adapter = new MixpanelSubscriber({
|
|
146
|
-
token: 'test_token',
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
150
|
-
|
|
151
|
-
adapter.trackOutcome('payment.processing', 'success', {
|
|
152
|
-
userId: 'user-123',
|
|
153
|
-
transactionId: 'txn-789',
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
157
|
-
|
|
158
|
-
const mockInstance = (Mixpanel.default.init as any).mock.results[0].value;
|
|
159
|
-
expect(mockInstance.track).toHaveBeenCalledWith('payment.processing.success', {
|
|
160
|
-
distinct_id: 'user-123',
|
|
161
|
-
operation: 'payment.processing',
|
|
162
|
-
outcome: 'success',
|
|
163
|
-
userId: 'user-123',
|
|
164
|
-
transactionId: 'txn-789',
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('trackValue', () => {
|
|
170
|
-
it('should track value', async () => {
|
|
171
|
-
const Mixpanel = await import('mixpanel');
|
|
172
|
-
const adapter = new MixpanelSubscriber({
|
|
173
|
-
token: 'test_token',
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
177
|
-
|
|
178
|
-
adapter.trackValue('revenue', 99.99, {
|
|
179
|
-
userId: 'user-123',
|
|
180
|
-
currency: 'USD',
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
184
|
-
|
|
185
|
-
const mockInstance = (Mixpanel.default.init as any).mock.results[0].value;
|
|
186
|
-
expect(mockInstance.track).toHaveBeenCalledWith('revenue', {
|
|
187
|
-
distinct_id: 'user-123',
|
|
188
|
-
value: 99.99,
|
|
189
|
-
userId: 'user-123',
|
|
190
|
-
currency: 'USD',
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
});
|
package/src/mixpanel.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mixpanel Subscriber for autotel
|
|
3
|
-
*
|
|
4
|
-
* Send events to Mixpanel for product events.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import { Events } from 'autotel/events';
|
|
9
|
-
* import { MixpanelSubscriber } from 'autotel-subscribers/mixpanel';
|
|
10
|
-
*
|
|
11
|
-
* const events = new Events('checkout', {
|
|
12
|
-
* subscribers: [
|
|
13
|
-
* new MixpanelSubscriber({
|
|
14
|
-
* token: process.env.MIXPANEL_TOKEN!
|
|
15
|
-
* })
|
|
16
|
-
* ]
|
|
17
|
-
* });
|
|
18
|
-
*
|
|
19
|
-
* events.trackEvent('order.completed', { userId: '123', amount: 99.99 });
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import type {
|
|
24
|
-
EventSubscriber,
|
|
25
|
-
EventAttributes,
|
|
26
|
-
FunnelStatus,
|
|
27
|
-
OutcomeStatus,
|
|
28
|
-
EventTrackingOptions,
|
|
29
|
-
AutotelEventContext,
|
|
30
|
-
} from 'autotel/event-subscriber';
|
|
31
|
-
|
|
32
|
-
export interface MixpanelConfig {
|
|
33
|
-
/** Mixpanel project token */
|
|
34
|
-
token: string;
|
|
35
|
-
/** Enable/disable the subscriber */
|
|
36
|
-
enabled?: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class MixpanelSubscriber implements EventSubscriber {
|
|
40
|
-
readonly name = 'MixpanelSubscriber';
|
|
41
|
-
readonly version = '1.0.0';
|
|
42
|
-
|
|
43
|
-
private mixpanel: any;
|
|
44
|
-
private enabled: boolean;
|
|
45
|
-
private config: MixpanelConfig;
|
|
46
|
-
private initPromise: Promise<void> | null = null;
|
|
47
|
-
|
|
48
|
-
constructor(config: MixpanelConfig) {
|
|
49
|
-
this.enabled = config.enabled ?? true;
|
|
50
|
-
this.config = config;
|
|
51
|
-
|
|
52
|
-
if (this.enabled) {
|
|
53
|
-
// Start initialization immediately but don't block constructor
|
|
54
|
-
this.initPromise = this.initialize();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private async initialize(): Promise<void> {
|
|
59
|
-
try {
|
|
60
|
-
// Dynamic import to avoid adding mixpanel as a hard dependency
|
|
61
|
-
const Mixpanel = await import('mixpanel');
|
|
62
|
-
this.mixpanel = Mixpanel.default.init(this.config.token);
|
|
63
|
-
} catch (error) {
|
|
64
|
-
console.error(
|
|
65
|
-
'Mixpanel subscriber failed to initialize. Install mixpanel: pnpm add mixpanel',
|
|
66
|
-
error,
|
|
67
|
-
);
|
|
68
|
-
this.enabled = false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private async ensureInitialized(): Promise<void> {
|
|
73
|
-
if (this.initPromise) {
|
|
74
|
-
await this.initPromise;
|
|
75
|
-
this.initPromise = null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Map autotel context to Mixpanel properties
|
|
81
|
-
*
|
|
82
|
-
* Mixpanel uses standard snake_case field names:
|
|
83
|
-
* - autotel.trace_id → trace_id
|
|
84
|
-
* - autotel.span_id → span_id
|
|
85
|
-
* - autotel.correlation_id → correlation_id
|
|
86
|
-
*/
|
|
87
|
-
private mapAutotelContext(
|
|
88
|
-
autotel?: AutotelEventContext,
|
|
89
|
-
): Record<string, unknown> {
|
|
90
|
-
if (!autotel) return {};
|
|
91
|
-
|
|
92
|
-
const mapped: Record<string, unknown> = {};
|
|
93
|
-
|
|
94
|
-
if (autotel.trace_id) {
|
|
95
|
-
mapped.trace_id = autotel.trace_id;
|
|
96
|
-
}
|
|
97
|
-
if (autotel.span_id) {
|
|
98
|
-
mapped.span_id = autotel.span_id;
|
|
99
|
-
}
|
|
100
|
-
if (autotel.correlation_id) {
|
|
101
|
-
mapped.correlation_id = autotel.correlation_id;
|
|
102
|
-
}
|
|
103
|
-
if (autotel.trace_flags) {
|
|
104
|
-
mapped.trace_flags = autotel.trace_flags;
|
|
105
|
-
}
|
|
106
|
-
if (autotel.trace_state) {
|
|
107
|
-
mapped.trace_state = autotel.trace_state;
|
|
108
|
-
}
|
|
109
|
-
if (autotel.trace_url) {
|
|
110
|
-
mapped.trace_url = autotel.trace_url;
|
|
111
|
-
}
|
|
112
|
-
// Batch/fan-in context
|
|
113
|
-
if (autotel.linked_trace_id_count !== undefined) {
|
|
114
|
-
mapped.linked_trace_id_count = autotel.linked_trace_id_count;
|
|
115
|
-
}
|
|
116
|
-
if (autotel.linked_trace_id_hash) {
|
|
117
|
-
mapped.linked_trace_id_hash = autotel.linked_trace_id_hash;
|
|
118
|
-
}
|
|
119
|
-
if (autotel.linked_trace_ids) {
|
|
120
|
-
mapped.linked_trace_ids = autotel.linked_trace_ids;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return mapped;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async trackEvent(
|
|
127
|
-
name: string,
|
|
128
|
-
attributes?: EventAttributes,
|
|
129
|
-
options?: EventTrackingOptions,
|
|
130
|
-
): Promise<void> {
|
|
131
|
-
if (!this.enabled) return;
|
|
132
|
-
|
|
133
|
-
await this.ensureInitialized();
|
|
134
|
-
const distinctId = attributes?.userId || attributes?.user_id || 'anonymous';
|
|
135
|
-
const autotelProps = this.mapAutotelContext(options?.autotel);
|
|
136
|
-
|
|
137
|
-
this.mixpanel?.track(name, {
|
|
138
|
-
distinct_id: distinctId,
|
|
139
|
-
...attributes,
|
|
140
|
-
...autotelProps,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async trackFunnelStep(
|
|
145
|
-
funnelName: string,
|
|
146
|
-
step: FunnelStatus,
|
|
147
|
-
attributes?: EventAttributes,
|
|
148
|
-
options?: EventTrackingOptions,
|
|
149
|
-
): Promise<void> {
|
|
150
|
-
if (!this.enabled) return;
|
|
151
|
-
|
|
152
|
-
await this.ensureInitialized();
|
|
153
|
-
const distinctId = attributes?.userId || attributes?.user_id || 'anonymous';
|
|
154
|
-
const autotelProps = this.mapAutotelContext(options?.autotel);
|
|
155
|
-
|
|
156
|
-
this.mixpanel?.track(`${funnelName}.${step}`, {
|
|
157
|
-
distinct_id: distinctId,
|
|
158
|
-
funnel: funnelName,
|
|
159
|
-
step,
|
|
160
|
-
...attributes,
|
|
161
|
-
...autotelProps,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async trackOutcome(
|
|
166
|
-
operationName: string,
|
|
167
|
-
outcome: OutcomeStatus,
|
|
168
|
-
attributes?: EventAttributes,
|
|
169
|
-
options?: EventTrackingOptions,
|
|
170
|
-
): Promise<void> {
|
|
171
|
-
if (!this.enabled) return;
|
|
172
|
-
|
|
173
|
-
await this.ensureInitialized();
|
|
174
|
-
const distinctId = attributes?.userId || attributes?.user_id || 'anonymous';
|
|
175
|
-
const autotelProps = this.mapAutotelContext(options?.autotel);
|
|
176
|
-
|
|
177
|
-
this.mixpanel?.track(`${operationName}.${outcome}`, {
|
|
178
|
-
distinct_id: distinctId,
|
|
179
|
-
operation: operationName,
|
|
180
|
-
outcome,
|
|
181
|
-
...attributes,
|
|
182
|
-
...autotelProps,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async trackValue(
|
|
187
|
-
name: string,
|
|
188
|
-
value: number,
|
|
189
|
-
attributes?: EventAttributes,
|
|
190
|
-
options?: EventTrackingOptions,
|
|
191
|
-
): Promise<void> {
|
|
192
|
-
if (!this.enabled) return;
|
|
193
|
-
|
|
194
|
-
await this.ensureInitialized();
|
|
195
|
-
const distinctId = attributes?.userId || attributes?.user_id || 'anonymous';
|
|
196
|
-
const autotelProps = this.mapAutotelContext(options?.autotel);
|
|
197
|
-
|
|
198
|
-
this.mixpanel?.track(name, {
|
|
199
|
-
distinct_id: distinctId,
|
|
200
|
-
value,
|
|
201
|
-
...attributes,
|
|
202
|
-
...autotelProps,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}
|