-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathanalytics.ts
More file actions
125 lines (111 loc) · 3.45 KB
/
analytics.ts
File metadata and controls
125 lines (111 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { PostHog } from 'posthog-node';
import {
ANALYTICS_HOST_URL,
ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY,
ANALYTICS_TEAM_TAG,
} from '../lib/constants';
import { v4 as uuidv4 } from 'uuid';
import { debug } from './debug';
export class Analytics {
private client: PostHog;
private tags: Record<string, string | boolean | number | null | undefined> =
{};
private distinctId?: string;
private anonymousId: string;
private appName = 'wizard';
private activeFlags: Record<string, string> | null = null;
constructor() {
this.client = new PostHog(ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, {
host: ANALYTICS_HOST_URL,
flushAt: 1,
flushInterval: 0,
enableExceptionAutocapture: true,
});
this.tags = { $app_name: this.appName };
this.anonymousId = uuidv4();
this.distinctId = undefined;
}
setDistinctId(distinctId: string) {
this.distinctId = distinctId;
this.client.alias({
distinctId,
alias: this.anonymousId,
});
}
setTag(key: string, value: string | boolean | number | null | undefined) {
this.tags[key] = value;
}
captureException(error: Error, properties: Record<string, unknown> = {}) {
this.client.captureException(error, this.distinctId ?? this.anonymousId, {
team: ANALYTICS_TEAM_TAG,
...this.tags,
...properties,
});
}
capture(eventName: string, properties?: Record<string, unknown>) {
this.client.capture({
distinctId: this.distinctId ?? this.anonymousId,
event: eventName,
properties: {
...this.tags,
...properties,
},
});
}
async getFeatureFlag(flagKey: string): Promise<string | boolean | undefined> {
try {
const distinctId = this.distinctId ?? this.anonymousId;
return await this.client.getFeatureFlag(flagKey, distinctId, {
sendFeatureFlagEvents: true,
personProperties: {
$app_name: this.appName,
},
});
} catch (error) {
debug('Failed to get feature flag:', flagKey, error);
return undefined;
}
}
/**
* Evaluate all feature flags for the current user at the start of a run.
* Result is cached; subsequent calls in the same run return the same map.
* Returns flag key -> string value (booleans become 'true'/'false').
*/
async getAllFlagsForWizard(): Promise<Record<string, string>> {
if (this.activeFlags !== null) {
return this.activeFlags;
}
try {
const distinctId = this.distinctId ?? this.anonymousId;
const result = await this.client.getAllFlagsAndPayloads(distinctId, {
personProperties: { $app_name: this.appName },
});
const flags = result.featureFlags ?? {};
const out: Record<string, string> = {};
for (const [key, value] of Object.entries(flags)) {
if (value === undefined) continue;
out[key] = typeof value === 'boolean' ? String(value) : String(value);
}
this.activeFlags = out;
return out;
} catch (error) {
debug('Failed to get all feature flags:', error);
return {};
}
}
async shutdown(status: 'success' | 'error' | 'cancelled') {
if (Object.keys(this.tags).length === 0) {
return;
}
this.client.capture({
distinctId: this.distinctId ?? this.anonymousId,
event: 'setup wizard finished',
properties: {
status,
tags: this.tags,
},
});
await this.client.shutdown();
}
}
export const analytics = new Analytics();