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/src/posthog.ts
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostHog Subscriber for autotel
|
|
3
|
+
*
|
|
4
|
+
* Send events to PostHog for product events, feature flags, and A/B testing.
|
|
5
|
+
*
|
|
6
|
+
* @example Basic usage
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { Events } from 'autotel/events';
|
|
9
|
+
* import { PostHogSubscriber } from 'autotel-subscribers/posthog';
|
|
10
|
+
*
|
|
11
|
+
* const events = new Events('checkout', {
|
|
12
|
+
* subscribers: [
|
|
13
|
+
* new PostHogSubscriber({
|
|
14
|
+
* apiKey: process.env.POSTHOG_API_KEY!,
|
|
15
|
+
* host: 'https://us.i.posthog.com' // optional, defaults to US cloud
|
|
16
|
+
* })
|
|
17
|
+
* ]
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Events go to both OpenTelemetry AND PostHog
|
|
21
|
+
* events.trackEvent('order.completed', { userId: '123', amount: 99.99 });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example Feature flags
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const subscriber = new PostHogSubscriber({ apiKey: 'phc_...' });
|
|
27
|
+
*
|
|
28
|
+
* // Check if feature is enabled
|
|
29
|
+
* const isEnabled = await subscriber.isFeatureEnabled('new-checkout', 'user-123');
|
|
30
|
+
*
|
|
31
|
+
* // Get feature flag value (string, boolean, number)
|
|
32
|
+
* const variant = await subscriber.getFeatureFlag('experiment-variant', 'user-123');
|
|
33
|
+
*
|
|
34
|
+
* // Get all flags for a user
|
|
35
|
+
* const allFlags = await subscriber.getAllFlags('user-123');
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example Person and group events
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Identify user and set properties
|
|
41
|
+
* await subscriber.identify('user-123', {
|
|
42
|
+
* email: 'user@example.com',
|
|
43
|
+
* plan: 'premium'
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Identify a group (e.g., organization)
|
|
47
|
+
* await subscriber.groupIdentify('company', 'acme-corp', {
|
|
48
|
+
* industry: 'saas',
|
|
49
|
+
* employees: 500
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example Serverless configuration
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // Optimized for AWS Lambda / Vercel Functions
|
|
56
|
+
* const subscriber = new PostHogSubscriber({
|
|
57
|
+
* apiKey: 'phc_...',
|
|
58
|
+
* flushAt: 1, // Send immediately (don't batch)
|
|
59
|
+
* flushInterval: 0, // Disable interval-based flushing
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example Custom PostHog client
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { PostHog } from 'posthog-node';
|
|
66
|
+
*
|
|
67
|
+
* const customClient = new PostHog('phc_...', {
|
|
68
|
+
* host: 'https://eu.i.posthog.com',
|
|
69
|
+
* // ... other PostHog options
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* const subscriber = new PostHogSubscriber({
|
|
73
|
+
* client: customClient
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @example Error handling
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const subscriber = new PostHogSubscriber({
|
|
80
|
+
* apiKey: 'phc_...',
|
|
81
|
+
* onError: (error) => {
|
|
82
|
+
* console.error('PostHog error:', error);
|
|
83
|
+
* // Send to error tracking service
|
|
84
|
+
* }
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
import type { EventAttributes } from 'autotel/event-subscriber';
|
|
90
|
+
import { EventSubscriber, type EventPayload } from './event-subscriber-base';
|
|
91
|
+
|
|
92
|
+
// Type-only import to avoid runtime dependency
|
|
93
|
+
import type { PostHog } from 'posthog-node';
|
|
94
|
+
|
|
95
|
+
export interface PostHogConfig {
|
|
96
|
+
/** PostHog API key (starts with phc_) - required if not providing custom client */
|
|
97
|
+
apiKey?: string;
|
|
98
|
+
|
|
99
|
+
/** PostHog host (defaults to US cloud) */
|
|
100
|
+
host?: string;
|
|
101
|
+
|
|
102
|
+
/** Enable/disable the subscriber */
|
|
103
|
+
enabled?: boolean;
|
|
104
|
+
|
|
105
|
+
/** Custom PostHog client instance (bypasses apiKey/host) */
|
|
106
|
+
client?: PostHog;
|
|
107
|
+
|
|
108
|
+
// Serverless optimizations
|
|
109
|
+
/** Flush batch when it reaches this size (default: 20, set to 1 for immediate send) */
|
|
110
|
+
flushAt?: number;
|
|
111
|
+
|
|
112
|
+
/** Flush interval in milliseconds (default: 10000, set to 0 to disable) */
|
|
113
|
+
flushInterval?: number;
|
|
114
|
+
|
|
115
|
+
// Performance tuning
|
|
116
|
+
/** Disable geoip lookup to reduce request size (default: false) */
|
|
117
|
+
disableGeoip?: boolean;
|
|
118
|
+
|
|
119
|
+
/** Request timeout in milliseconds (default: 10000) */
|
|
120
|
+
requestTimeout?: number;
|
|
121
|
+
|
|
122
|
+
/** Send feature flag evaluation events (default: true) */
|
|
123
|
+
sendFeatureFlags?: boolean;
|
|
124
|
+
|
|
125
|
+
// Error handling
|
|
126
|
+
/** Error callback for debugging and monitoring */
|
|
127
|
+
onError?: (error: Error) => void;
|
|
128
|
+
|
|
129
|
+
/** Enable debug logging (default: false) */
|
|
130
|
+
debug?: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* PostHog feature flag options
|
|
135
|
+
*/
|
|
136
|
+
export interface FeatureFlagOptions {
|
|
137
|
+
/** Group context for group-based feature flags */
|
|
138
|
+
groups?: Record<string, string | number>;
|
|
139
|
+
|
|
140
|
+
/** Group properties for feature flag evaluation */
|
|
141
|
+
groupProperties?: Record<string, Record<string, any>>;
|
|
142
|
+
|
|
143
|
+
/** Person properties for feature flag evaluation */
|
|
144
|
+
personProperties?: Record<string, any>;
|
|
145
|
+
|
|
146
|
+
/** Only evaluate locally, don't send $feature_flag_called event */
|
|
147
|
+
onlyEvaluateLocally?: boolean;
|
|
148
|
+
|
|
149
|
+
/** Send feature flag events even if disabled globally */
|
|
150
|
+
sendFeatureFlagEvents?: boolean;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Person properties for identify calls
|
|
155
|
+
*/
|
|
156
|
+
export interface PersonProperties {
|
|
157
|
+
/** Set properties (will update existing values) */
|
|
158
|
+
$set?: Record<string, any>;
|
|
159
|
+
|
|
160
|
+
/** Set properties only if they don't exist */
|
|
161
|
+
$set_once?: Record<string, any>;
|
|
162
|
+
|
|
163
|
+
/** Any custom properties */
|
|
164
|
+
[key: string]: any;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export class PostHogSubscriber extends EventSubscriber {
|
|
168
|
+
readonly name = 'PostHogSubscriber';
|
|
169
|
+
readonly version = '2.0.0';
|
|
170
|
+
|
|
171
|
+
private posthog: PostHog | null = null;
|
|
172
|
+
private config: PostHogConfig;
|
|
173
|
+
private initPromise: Promise<void> | null = null;
|
|
174
|
+
|
|
175
|
+
constructor(config: PostHogConfig) {
|
|
176
|
+
super();
|
|
177
|
+
|
|
178
|
+
if (!config.apiKey && !config.client) {
|
|
179
|
+
throw new Error('PostHogSubscriber requires either apiKey or client to be provided');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.enabled = config.enabled ?? true;
|
|
183
|
+
this.config = config;
|
|
184
|
+
|
|
185
|
+
if (this.enabled) {
|
|
186
|
+
// Start initialization immediately but don't block constructor
|
|
187
|
+
this.initPromise = this.initialize();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private async initialize(): Promise<void> {
|
|
192
|
+
try {
|
|
193
|
+
// Use custom client if provided
|
|
194
|
+
if (this.config.client) {
|
|
195
|
+
this.posthog = this.config.client;
|
|
196
|
+
this.setupErrorHandling();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Dynamic import to avoid adding posthog-node as a hard dependency
|
|
201
|
+
const { PostHog } = await import('posthog-node');
|
|
202
|
+
|
|
203
|
+
this.posthog = new PostHog(this.config.apiKey!, {
|
|
204
|
+
host: this.config.host || 'https://us.i.posthog.com',
|
|
205
|
+
flushAt: this.config.flushAt,
|
|
206
|
+
flushInterval: this.config.flushInterval,
|
|
207
|
+
requestTimeout: this.config.requestTimeout,
|
|
208
|
+
disableGeoip: this.config.disableGeoip,
|
|
209
|
+
sendFeatureFlagEvent: this.config.sendFeatureFlags,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
this.setupErrorHandling();
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error(
|
|
215
|
+
'PostHog subscriber failed to initialize. Install posthog-node: pnpm add posthog-node',
|
|
216
|
+
error,
|
|
217
|
+
);
|
|
218
|
+
this.enabled = false;
|
|
219
|
+
this.config.onError?.(error as Error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private setupErrorHandling(): void {
|
|
224
|
+
if (this.config.debug) {
|
|
225
|
+
this.posthog?.debug();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (this.config.onError && this.posthog?.on) {
|
|
229
|
+
this.posthog.on('error', this.config.onError);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private async ensureInitialized(): Promise<void> {
|
|
234
|
+
if (this.initPromise) {
|
|
235
|
+
await this.initPromise;
|
|
236
|
+
this.initPromise = null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private extractDistinctId(attributes?: EventAttributes): string {
|
|
241
|
+
return (attributes?.userId || attributes?.user_id || 'anonymous') as string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Send payload to PostHog
|
|
246
|
+
*/
|
|
247
|
+
protected async sendToDestination(payload: EventPayload): Promise<void> {
|
|
248
|
+
await this.ensureInitialized();
|
|
249
|
+
|
|
250
|
+
// Build properties object, including value if present
|
|
251
|
+
let properties: any = payload.attributes;
|
|
252
|
+
if (payload.value !== undefined) {
|
|
253
|
+
properties = { ...payload.attributes, value: payload.value };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Build PostHog capture payload
|
|
257
|
+
const capturePayload: any = {
|
|
258
|
+
distinctId: this.extractDistinctId(payload.attributes),
|
|
259
|
+
event: payload.name,
|
|
260
|
+
properties,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Add groups if present in attributes
|
|
264
|
+
if (payload.attributes?.groups) {
|
|
265
|
+
capturePayload.groups = payload.attributes.groups;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this.posthog?.capture(capturePayload);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Feature Flag Methods
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check if a feature flag is enabled for a user
|
|
275
|
+
*
|
|
276
|
+
* @param flagKey - Feature flag key
|
|
277
|
+
* @param distinctId - User ID or anonymous ID
|
|
278
|
+
* @param options - Feature flag evaluation options
|
|
279
|
+
* @returns true if enabled, false otherwise
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const isEnabled = await subscriber.isFeatureEnabled('new-checkout', 'user-123');
|
|
284
|
+
*
|
|
285
|
+
* // With groups
|
|
286
|
+
* const isEnabled = await subscriber.isFeatureEnabled('beta-features', 'user-123', {
|
|
287
|
+
* groups: { company: 'acme-corp' }
|
|
288
|
+
* });
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
async isFeatureEnabled(
|
|
292
|
+
flagKey: string,
|
|
293
|
+
distinctId: string,
|
|
294
|
+
options?: FeatureFlagOptions,
|
|
295
|
+
): Promise<boolean> {
|
|
296
|
+
if (!this.enabled) return false;
|
|
297
|
+
await this.ensureInitialized();
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
return await this.posthog?.isFeatureEnabled(flagKey, distinctId, options as any) ?? false;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
this.config.onError?.(error as Error);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get feature flag value for a user
|
|
309
|
+
*
|
|
310
|
+
* @param flagKey - Feature flag key
|
|
311
|
+
* @param distinctId - User ID or anonymous ID
|
|
312
|
+
* @param options - Feature flag evaluation options
|
|
313
|
+
* @returns Flag value (string, boolean, or undefined)
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* const variant = await subscriber.getFeatureFlag('experiment-variant', 'user-123');
|
|
318
|
+
* // Returns: 'control' | 'test' | 'test-2' | undefined
|
|
319
|
+
*
|
|
320
|
+
* // With person properties
|
|
321
|
+
* const variant = await subscriber.getFeatureFlag('premium-feature', 'user-123', {
|
|
322
|
+
* personProperties: { plan: 'premium' }
|
|
323
|
+
* });
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
async getFeatureFlag(
|
|
327
|
+
flagKey: string,
|
|
328
|
+
distinctId: string,
|
|
329
|
+
options?: FeatureFlagOptions,
|
|
330
|
+
): Promise<string | boolean | undefined> {
|
|
331
|
+
if (!this.enabled) return undefined;
|
|
332
|
+
await this.ensureInitialized();
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
return await this.posthog?.getFeatureFlag(flagKey, distinctId, options as any);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
this.config.onError?.(error as Error);
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get all feature flags for a user
|
|
344
|
+
*
|
|
345
|
+
* @param distinctId - User ID or anonymous ID
|
|
346
|
+
* @param options - Feature flag evaluation options
|
|
347
|
+
* @returns Object mapping flag keys to their values
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```typescript
|
|
351
|
+
* const flags = await subscriber.getAllFlags('user-123');
|
|
352
|
+
* // Returns: { 'new-checkout': true, 'experiment-variant': 'test', ... }
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
async getAllFlags(
|
|
356
|
+
distinctId: string,
|
|
357
|
+
options?: FeatureFlagOptions,
|
|
358
|
+
): Promise<Record<string, string | number | boolean>> {
|
|
359
|
+
if (!this.enabled) return {};
|
|
360
|
+
await this.ensureInitialized();
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const flags = await this.posthog?.getAllFlags(distinctId, options as any);
|
|
364
|
+
return flags ?? {};
|
|
365
|
+
} catch (error) {
|
|
366
|
+
this.config.onError?.(error as Error);
|
|
367
|
+
return {};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Reload feature flags from PostHog server
|
|
373
|
+
*
|
|
374
|
+
* Call this to refresh feature flag definitions without restarting.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```typescript
|
|
378
|
+
* await subscriber.reloadFeatureFlags();
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
async reloadFeatureFlags(): Promise<void> {
|
|
382
|
+
if (!this.enabled) return;
|
|
383
|
+
await this.ensureInitialized();
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
await this.posthog?.reloadFeatureFlags();
|
|
387
|
+
} catch (error) {
|
|
388
|
+
this.config.onError?.(error as Error);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Person and Group Events
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Identify a user and set their properties
|
|
396
|
+
*
|
|
397
|
+
* @param distinctId - User ID
|
|
398
|
+
* @param properties - Person properties ($set, $set_once, or custom properties)
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* // Set properties (will update existing values)
|
|
403
|
+
* await subscriber.identify('user-123', {
|
|
404
|
+
* $set: {
|
|
405
|
+
* email: 'user@example.com',
|
|
406
|
+
* plan: 'premium'
|
|
407
|
+
* }
|
|
408
|
+
* });
|
|
409
|
+
*
|
|
410
|
+
* // Set properties only once (won't update if already exists)
|
|
411
|
+
* await subscriber.identify('user-123', {
|
|
412
|
+
* $set_once: {
|
|
413
|
+
* signup_date: '2025-01-17'
|
|
414
|
+
* }
|
|
415
|
+
* });
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
async identify(distinctId: string, properties?: PersonProperties): Promise<void> {
|
|
419
|
+
if (!this.enabled) return;
|
|
420
|
+
await this.ensureInitialized();
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
this.posthog?.identify({
|
|
424
|
+
distinctId,
|
|
425
|
+
properties,
|
|
426
|
+
});
|
|
427
|
+
} catch (error) {
|
|
428
|
+
this.config.onError?.(error as Error);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Identify a group and set its properties
|
|
434
|
+
*
|
|
435
|
+
* Groups are useful for B2B SaaS to track organizations, teams, or accounts.
|
|
436
|
+
*
|
|
437
|
+
* @param groupType - Type of group (e.g., 'company', 'organization', 'team')
|
|
438
|
+
* @param groupKey - Unique identifier for the group
|
|
439
|
+
* @param properties - Group properties
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```typescript
|
|
443
|
+
* await subscriber.groupIdentify('company', 'acme-corp', {
|
|
444
|
+
* $set: {
|
|
445
|
+
* name: 'Acme Corporation',
|
|
446
|
+
* industry: 'saas',
|
|
447
|
+
* employees: 500,
|
|
448
|
+
* plan: 'enterprise'
|
|
449
|
+
* }
|
|
450
|
+
* });
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
async groupIdentify(
|
|
454
|
+
groupType: string,
|
|
455
|
+
groupKey: string | number,
|
|
456
|
+
properties?: Record<string, any>,
|
|
457
|
+
): Promise<void> {
|
|
458
|
+
if (!this.enabled) return;
|
|
459
|
+
await this.ensureInitialized();
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
this.posthog?.groupIdentify({
|
|
463
|
+
groupType,
|
|
464
|
+
groupKey: String(groupKey), // Convert to string for PostHog SDK
|
|
465
|
+
properties,
|
|
466
|
+
});
|
|
467
|
+
} catch (error) {
|
|
468
|
+
this.config.onError?.(error as Error);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Track an event with group context
|
|
474
|
+
*
|
|
475
|
+
* Use this to associate events with groups (e.g., organizations).
|
|
476
|
+
*
|
|
477
|
+
* @param name - Event name
|
|
478
|
+
* @param attributes - Event attributes
|
|
479
|
+
* @param groups - Group context (e.g., { company: 'acme-corp' })
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```typescript
|
|
483
|
+
* await subscriber.trackEventWithGroups('feature.used', {
|
|
484
|
+
* userId: 'user-123',
|
|
485
|
+
* feature: 'advanced-events'
|
|
486
|
+
* }, {
|
|
487
|
+
* company: 'acme-corp'
|
|
488
|
+
* });
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
async trackEventWithGroups(
|
|
492
|
+
name: string,
|
|
493
|
+
attributes?: EventAttributes,
|
|
494
|
+
groups?: Record<string, string | number>,
|
|
495
|
+
): Promise<void> {
|
|
496
|
+
if (!this.enabled) return;
|
|
497
|
+
await this.ensureInitialized();
|
|
498
|
+
|
|
499
|
+
const eventAttributes: EventAttributes = { ...attributes } as EventAttributes;
|
|
500
|
+
if (groups) {
|
|
501
|
+
(eventAttributes as any).groups = groups;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
await this.trackEvent(name, eventAttributes);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Flush pending events and clean up resources
|
|
509
|
+
*/
|
|
510
|
+
async shutdown(): Promise<void> {
|
|
511
|
+
await super.shutdown(); // Drain pending requests first
|
|
512
|
+
await this.ensureInitialized();
|
|
513
|
+
|
|
514
|
+
if (this.posthog) {
|
|
515
|
+
try {
|
|
516
|
+
await this.posthog.shutdown();
|
|
517
|
+
} catch (error) {
|
|
518
|
+
this.config.onError?.(error as Error);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Handle errors with custom error handler
|
|
525
|
+
*/
|
|
526
|
+
protected handleError(error: Error, payload: EventPayload): void {
|
|
527
|
+
this.config.onError?.(error);
|
|
528
|
+
super.handleError(error, payload);
|
|
529
|
+
}
|
|
530
|
+
}
|