@contractspec/lib.analytics 1.57.0 → 1.58.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/dist/browser/churn/index.js +77 -0
- package/dist/browser/churn/predictor.js +77 -0
- package/dist/browser/cohort/index.js +117 -0
- package/dist/browser/cohort/tracker.js +117 -0
- package/dist/browser/funnel/analyzer.js +68 -0
- package/dist/browser/funnel/index.js +68 -0
- package/dist/browser/growth/hypothesis-generator.js +46 -0
- package/dist/browser/growth/index.js +46 -0
- package/dist/browser/index.js +723 -0
- package/dist/browser/lifecycle/index.js +287 -0
- package/dist/browser/lifecycle/metric-collectors.js +58 -0
- package/dist/browser/lifecycle/posthog-bridge.js +79 -0
- package/dist/browser/lifecycle/posthog-metric-source.js +205 -0
- package/dist/browser/posthog/event-source.js +138 -0
- package/dist/browser/posthog/index.js +138 -0
- package/dist/browser/types.js +0 -0
- package/dist/churn/index.d.ts +2 -2
- package/dist/churn/index.d.ts.map +1 -0
- package/dist/churn/index.js +77 -2
- package/dist/churn/predictor.d.ts +15 -19
- package/dist/churn/predictor.d.ts.map +1 -1
- package/dist/churn/predictor.js +72 -68
- package/dist/cohort/index.d.ts +2 -2
- package/dist/cohort/index.d.ts.map +1 -0
- package/dist/cohort/index.js +117 -2
- package/dist/cohort/tracker.d.ts +3 -7
- package/dist/cohort/tracker.d.ts.map +1 -1
- package/dist/cohort/tracker.js +106 -87
- package/dist/funnel/analyzer.d.ts +4 -8
- package/dist/funnel/analyzer.d.ts.map +1 -1
- package/dist/funnel/analyzer.js +67 -62
- package/dist/funnel/index.d.ts +2 -2
- package/dist/funnel/index.d.ts.map +1 -0
- package/dist/funnel/index.js +69 -3
- package/dist/growth/hypothesis-generator.d.ts +11 -15
- package/dist/growth/hypothesis-generator.d.ts.map +1 -1
- package/dist/growth/hypothesis-generator.js +46 -39
- package/dist/growth/index.d.ts +2 -2
- package/dist/growth/index.d.ts.map +1 -0
- package/dist/growth/index.js +47 -3
- package/dist/index.d.ts +8 -10
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +724 -12
- package/dist/lifecycle/index.d.ts +4 -4
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +287 -4
- package/dist/lifecycle/metric-collectors.d.ts +22 -26
- package/dist/lifecycle/metric-collectors.d.ts.map +1 -1
- package/dist/lifecycle/metric-collectors.js +55 -44
- package/dist/lifecycle/posthog-bridge.d.ts +9 -13
- package/dist/lifecycle/posthog-bridge.d.ts.map +1 -1
- package/dist/lifecycle/posthog-bridge.js +77 -25
- package/dist/lifecycle/posthog-metric-source.d.ts +40 -44
- package/dist/lifecycle/posthog-metric-source.d.ts.map +1 -1
- package/dist/lifecycle/posthog-metric-source.js +200 -180
- package/dist/node/churn/index.js +77 -0
- package/dist/node/churn/predictor.js +77 -0
- package/dist/node/cohort/index.js +117 -0
- package/dist/node/cohort/tracker.js +117 -0
- package/dist/node/funnel/analyzer.js +68 -0
- package/dist/node/funnel/index.js +68 -0
- package/dist/node/growth/hypothesis-generator.js +46 -0
- package/dist/node/growth/index.js +46 -0
- package/dist/node/index.js +723 -0
- package/dist/node/lifecycle/index.js +287 -0
- package/dist/node/lifecycle/metric-collectors.js +58 -0
- package/dist/node/lifecycle/posthog-bridge.js +79 -0
- package/dist/node/lifecycle/posthog-metric-source.js +205 -0
- package/dist/node/posthog/event-source.js +138 -0
- package/dist/node/posthog/index.js +138 -0
- package/dist/node/types.js +0 -0
- package/dist/posthog/event-source.d.ts +18 -22
- package/dist/posthog/event-source.d.ts.map +1 -1
- package/dist/posthog/event-source.js +131 -111
- package/dist/posthog/index.d.ts +2 -2
- package/dist/posthog/index.d.ts.map +1 -0
- package/dist/posthog/index.js +139 -3
- package/dist/types.d.ts +52 -55
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/package.json +189 -46
- package/dist/churn/predictor.js.map +0 -1
- package/dist/cohort/tracker.js.map +0 -1
- package/dist/funnel/analyzer.js.map +0 -1
- package/dist/growth/hypothesis-generator.js.map +0 -1
- package/dist/lifecycle/metric-collectors.js.map +0 -1
- package/dist/lifecycle/posthog-bridge.js.map +0 -1
- package/dist/lifecycle/posthog-metric-source.js.map +0 -1
- package/dist/posthog/event-source.js.map +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/posthog/event-source.ts
|
|
2
|
+
class PosthogAnalyticsEventSource {
|
|
3
|
+
reader;
|
|
4
|
+
limitPerEvent;
|
|
5
|
+
tenantPropertyKey;
|
|
6
|
+
defaultRangeDays;
|
|
7
|
+
constructor(reader, options = {}) {
|
|
8
|
+
this.reader = reader;
|
|
9
|
+
this.limitPerEvent = options.limitPerEvent ?? 1000;
|
|
10
|
+
this.tenantPropertyKey = options.tenantPropertyKey ?? "tenantId";
|
|
11
|
+
this.defaultRangeDays = options.defaultRangeDays ?? 30;
|
|
12
|
+
}
|
|
13
|
+
async getEventsForFunnel(definition, dateRange) {
|
|
14
|
+
const eventNames = definition.steps.map((step) => step.eventName);
|
|
15
|
+
return this.getEventsByNames(eventNames, dateRange);
|
|
16
|
+
}
|
|
17
|
+
async getEventsForCohort(definition, dateRange, eventNames) {
|
|
18
|
+
const events = eventNames && eventNames.length > 0 ? eventNames : ["*"];
|
|
19
|
+
if (events[0] === "*") {
|
|
20
|
+
return this.getEventsByNames([], dateRange, definition.periods * 500);
|
|
21
|
+
}
|
|
22
|
+
return this.getEventsByNames(events, dateRange, definition.periods * 500);
|
|
23
|
+
}
|
|
24
|
+
async getUserActivity(userId, dateRange, limit = 1000) {
|
|
25
|
+
if (!this.reader.getEvents) {
|
|
26
|
+
throw new Error("Analytics reader does not support event queries.");
|
|
27
|
+
}
|
|
28
|
+
const response = await this.reader.getEvents({
|
|
29
|
+
distinctId: userId,
|
|
30
|
+
dateRange,
|
|
31
|
+
limit
|
|
32
|
+
});
|
|
33
|
+
return response.results.map((event) => toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
34
|
+
}
|
|
35
|
+
async getGrowthMetrics(metricNames, dateRange) {
|
|
36
|
+
const range = resolveRange(dateRange, this.defaultRangeDays);
|
|
37
|
+
const previous = shiftRange(range);
|
|
38
|
+
const results = [];
|
|
39
|
+
for (const metricName of metricNames) {
|
|
40
|
+
const current = await this.countEvents(metricName, range);
|
|
41
|
+
const previousCount = await this.countEvents(metricName, previous);
|
|
42
|
+
results.push({
|
|
43
|
+
name: metricName,
|
|
44
|
+
current,
|
|
45
|
+
previous: previousCount
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
async getEventsByNames(eventNames, dateRange, limitPerEvent = this.limitPerEvent) {
|
|
51
|
+
if (!this.reader.getEvents) {
|
|
52
|
+
throw new Error("Analytics reader does not support event queries.");
|
|
53
|
+
}
|
|
54
|
+
if (eventNames.length === 0) {
|
|
55
|
+
const response = await this.reader.getEvents({
|
|
56
|
+
dateRange,
|
|
57
|
+
limit: limitPerEvent
|
|
58
|
+
});
|
|
59
|
+
return response.results.map((event) => toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
60
|
+
}
|
|
61
|
+
const events = [];
|
|
62
|
+
for (const eventName of eventNames) {
|
|
63
|
+
const response = await this.reader.getEvents({
|
|
64
|
+
event: eventName,
|
|
65
|
+
dateRange,
|
|
66
|
+
limit: limitPerEvent
|
|
67
|
+
});
|
|
68
|
+
response.results.forEach((event) => {
|
|
69
|
+
events.push(toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return events;
|
|
73
|
+
}
|
|
74
|
+
async countEvents(eventName, range) {
|
|
75
|
+
if (!this.reader.queryHogQL) {
|
|
76
|
+
throw new Error("Analytics reader does not support HogQL queries.");
|
|
77
|
+
}
|
|
78
|
+
const result = await this.reader.queryHogQL({
|
|
79
|
+
query: [
|
|
80
|
+
"select",
|
|
81
|
+
" count() as total",
|
|
82
|
+
"from events",
|
|
83
|
+
"where event = {eventName}",
|
|
84
|
+
"and timestamp >= {dateFrom}",
|
|
85
|
+
"and timestamp < {dateTo}"
|
|
86
|
+
].join(`
|
|
87
|
+
`),
|
|
88
|
+
values: {
|
|
89
|
+
eventName,
|
|
90
|
+
dateFrom: range.from.toISOString(),
|
|
91
|
+
dateTo: range.to.toISOString()
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return readSingleNumber(result) ?? 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function toAnalyticsEvent(event, tenantPropertyKey) {
|
|
98
|
+
const tenantIdValue = event.properties?.[tenantPropertyKey] ?? event.properties?.tenantId;
|
|
99
|
+
return {
|
|
100
|
+
name: event.event,
|
|
101
|
+
userId: event.distinctId,
|
|
102
|
+
tenantId: typeof tenantIdValue === "string" ? tenantIdValue : undefined,
|
|
103
|
+
timestamp: event.timestamp,
|
|
104
|
+
properties: event.properties
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function resolveRange(dateRange, defaultDays) {
|
|
108
|
+
const to = dateRange?.to instanceof Date ? dateRange.to : dateRange?.to ? new Date(dateRange.to) : new Date;
|
|
109
|
+
const from = dateRange?.from instanceof Date ? dateRange.from : dateRange?.from ? new Date(dateRange.from) : new Date(to.getTime() - defaultDays * 24 * 60 * 60 * 1000);
|
|
110
|
+
return { from, to };
|
|
111
|
+
}
|
|
112
|
+
function shiftRange(range) {
|
|
113
|
+
const duration = range.to.getTime() - range.from.getTime();
|
|
114
|
+
return {
|
|
115
|
+
from: new Date(range.from.getTime() - duration),
|
|
116
|
+
to: new Date(range.to.getTime() - duration)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function readSingleNumber(result) {
|
|
120
|
+
if (!Array.isArray(result.results) || result.results.length === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const firstRow = result.results[0];
|
|
124
|
+
if (Array.isArray(firstRow) && firstRow.length > 0) {
|
|
125
|
+
const value = firstRow[0];
|
|
126
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
127
|
+
return value;
|
|
128
|
+
if (typeof value === "string" && value.trim()) {
|
|
129
|
+
const parsed = Number(value);
|
|
130
|
+
if (Number.isFinite(parsed))
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
PosthogAnalyticsEventSource
|
|
138
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/posthog/event-source.ts
|
|
2
|
+
class PosthogAnalyticsEventSource {
|
|
3
|
+
reader;
|
|
4
|
+
limitPerEvent;
|
|
5
|
+
tenantPropertyKey;
|
|
6
|
+
defaultRangeDays;
|
|
7
|
+
constructor(reader, options = {}) {
|
|
8
|
+
this.reader = reader;
|
|
9
|
+
this.limitPerEvent = options.limitPerEvent ?? 1000;
|
|
10
|
+
this.tenantPropertyKey = options.tenantPropertyKey ?? "tenantId";
|
|
11
|
+
this.defaultRangeDays = options.defaultRangeDays ?? 30;
|
|
12
|
+
}
|
|
13
|
+
async getEventsForFunnel(definition, dateRange) {
|
|
14
|
+
const eventNames = definition.steps.map((step) => step.eventName);
|
|
15
|
+
return this.getEventsByNames(eventNames, dateRange);
|
|
16
|
+
}
|
|
17
|
+
async getEventsForCohort(definition, dateRange, eventNames) {
|
|
18
|
+
const events = eventNames && eventNames.length > 0 ? eventNames : ["*"];
|
|
19
|
+
if (events[0] === "*") {
|
|
20
|
+
return this.getEventsByNames([], dateRange, definition.periods * 500);
|
|
21
|
+
}
|
|
22
|
+
return this.getEventsByNames(events, dateRange, definition.periods * 500);
|
|
23
|
+
}
|
|
24
|
+
async getUserActivity(userId, dateRange, limit = 1000) {
|
|
25
|
+
if (!this.reader.getEvents) {
|
|
26
|
+
throw new Error("Analytics reader does not support event queries.");
|
|
27
|
+
}
|
|
28
|
+
const response = await this.reader.getEvents({
|
|
29
|
+
distinctId: userId,
|
|
30
|
+
dateRange,
|
|
31
|
+
limit
|
|
32
|
+
});
|
|
33
|
+
return response.results.map((event) => toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
34
|
+
}
|
|
35
|
+
async getGrowthMetrics(metricNames, dateRange) {
|
|
36
|
+
const range = resolveRange(dateRange, this.defaultRangeDays);
|
|
37
|
+
const previous = shiftRange(range);
|
|
38
|
+
const results = [];
|
|
39
|
+
for (const metricName of metricNames) {
|
|
40
|
+
const current = await this.countEvents(metricName, range);
|
|
41
|
+
const previousCount = await this.countEvents(metricName, previous);
|
|
42
|
+
results.push({
|
|
43
|
+
name: metricName,
|
|
44
|
+
current,
|
|
45
|
+
previous: previousCount
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
async getEventsByNames(eventNames, dateRange, limitPerEvent = this.limitPerEvent) {
|
|
51
|
+
if (!this.reader.getEvents) {
|
|
52
|
+
throw new Error("Analytics reader does not support event queries.");
|
|
53
|
+
}
|
|
54
|
+
if (eventNames.length === 0) {
|
|
55
|
+
const response = await this.reader.getEvents({
|
|
56
|
+
dateRange,
|
|
57
|
+
limit: limitPerEvent
|
|
58
|
+
});
|
|
59
|
+
return response.results.map((event) => toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
60
|
+
}
|
|
61
|
+
const events = [];
|
|
62
|
+
for (const eventName of eventNames) {
|
|
63
|
+
const response = await this.reader.getEvents({
|
|
64
|
+
event: eventName,
|
|
65
|
+
dateRange,
|
|
66
|
+
limit: limitPerEvent
|
|
67
|
+
});
|
|
68
|
+
response.results.forEach((event) => {
|
|
69
|
+
events.push(toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return events;
|
|
73
|
+
}
|
|
74
|
+
async countEvents(eventName, range) {
|
|
75
|
+
if (!this.reader.queryHogQL) {
|
|
76
|
+
throw new Error("Analytics reader does not support HogQL queries.");
|
|
77
|
+
}
|
|
78
|
+
const result = await this.reader.queryHogQL({
|
|
79
|
+
query: [
|
|
80
|
+
"select",
|
|
81
|
+
" count() as total",
|
|
82
|
+
"from events",
|
|
83
|
+
"where event = {eventName}",
|
|
84
|
+
"and timestamp >= {dateFrom}",
|
|
85
|
+
"and timestamp < {dateTo}"
|
|
86
|
+
].join(`
|
|
87
|
+
`),
|
|
88
|
+
values: {
|
|
89
|
+
eventName,
|
|
90
|
+
dateFrom: range.from.toISOString(),
|
|
91
|
+
dateTo: range.to.toISOString()
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return readSingleNumber(result) ?? 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function toAnalyticsEvent(event, tenantPropertyKey) {
|
|
98
|
+
const tenantIdValue = event.properties?.[tenantPropertyKey] ?? event.properties?.tenantId;
|
|
99
|
+
return {
|
|
100
|
+
name: event.event,
|
|
101
|
+
userId: event.distinctId,
|
|
102
|
+
tenantId: typeof tenantIdValue === "string" ? tenantIdValue : undefined,
|
|
103
|
+
timestamp: event.timestamp,
|
|
104
|
+
properties: event.properties
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function resolveRange(dateRange, defaultDays) {
|
|
108
|
+
const to = dateRange?.to instanceof Date ? dateRange.to : dateRange?.to ? new Date(dateRange.to) : new Date;
|
|
109
|
+
const from = dateRange?.from instanceof Date ? dateRange.from : dateRange?.from ? new Date(dateRange.from) : new Date(to.getTime() - defaultDays * 24 * 60 * 60 * 1000);
|
|
110
|
+
return { from, to };
|
|
111
|
+
}
|
|
112
|
+
function shiftRange(range) {
|
|
113
|
+
const duration = range.to.getTime() - range.from.getTime();
|
|
114
|
+
return {
|
|
115
|
+
from: new Date(range.from.getTime() - duration),
|
|
116
|
+
to: new Date(range.to.getTime() - duration)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function readSingleNumber(result) {
|
|
120
|
+
if (!Array.isArray(result.results) || result.results.length === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const firstRow = result.results[0];
|
|
124
|
+
if (Array.isArray(firstRow) && firstRow.length > 0) {
|
|
125
|
+
const value = firstRow[0];
|
|
126
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
127
|
+
return value;
|
|
128
|
+
if (typeof value === "string" && value.trim()) {
|
|
129
|
+
const parsed = Number(value);
|
|
130
|
+
if (Number.isFinite(parsed))
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
PosthogAnalyticsEventSource
|
|
138
|
+
};
|
|
File without changes
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
tenantPropertyKey?: string;
|
|
8
|
-
defaultRangeDays?: number;
|
|
1
|
+
import type { AnalyticsReader, DateRangeInput } from '@contractspec/lib.contracts/integrations/providers/analytics';
|
|
2
|
+
import type { AnalyticsEvent, CohortDefinition, FunnelDefinition, GrowthMetric } from '../types';
|
|
3
|
+
export interface PosthogAnalyticsEventSourceOptions {
|
|
4
|
+
limitPerEvent?: number;
|
|
5
|
+
tenantPropertyKey?: string;
|
|
6
|
+
defaultRangeDays?: number;
|
|
9
7
|
}
|
|
10
|
-
declare class PosthogAnalyticsEventSource {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
export declare class PosthogAnalyticsEventSource {
|
|
9
|
+
private readonly reader;
|
|
10
|
+
private readonly limitPerEvent;
|
|
11
|
+
private readonly tenantPropertyKey;
|
|
12
|
+
private readonly defaultRangeDays;
|
|
13
|
+
constructor(reader: AnalyticsReader, options?: PosthogAnalyticsEventSourceOptions);
|
|
14
|
+
getEventsForFunnel(definition: FunnelDefinition, dateRange?: DateRangeInput): Promise<AnalyticsEvent[]>;
|
|
15
|
+
getEventsForCohort(definition: CohortDefinition, dateRange?: DateRangeInput, eventNames?: string[]): Promise<AnalyticsEvent[]>;
|
|
16
|
+
getUserActivity(userId: string, dateRange?: DateRangeInput, limit?: number): Promise<AnalyticsEvent[]>;
|
|
17
|
+
getGrowthMetrics(metricNames: string[], dateRange?: DateRangeInput): Promise<GrowthMetric[]>;
|
|
18
|
+
private getEventsByNames;
|
|
19
|
+
private countEvents;
|
|
22
20
|
}
|
|
23
|
-
//#endregion
|
|
24
|
-
export { PosthogAnalyticsEventSource, PosthogAnalyticsEventSourceOptions };
|
|
25
21
|
//# sourceMappingURL=event-source.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-source.d.ts","
|
|
1
|
+
{"version":3,"file":"event-source.d.ts","sourceRoot":"","sources":["../../src/posthog/event-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACf,MAAM,8DAA8D,CAAC;AACtE,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,kCAAkC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAGxC,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,kCAAuC;IAQ5C,kBAAkB,CACtB,UAAU,EAAE,gBAAgB,EAC5B,SAAS,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC;IAKtB,kBAAkB,CACtB,UAAU,EAAE,gBAAgB,EAC5B,SAAS,CAAC,EAAE,cAAc,EAC1B,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,cAAc,EAAE,CAAC;IAQtB,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,cAAc,EAC1B,KAAK,SAAO,GACX,OAAO,CAAC,cAAc,EAAE,CAAC;IActB,gBAAgB,CACpB,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,YAAY,EAAE,CAAC;YAgBZ,gBAAgB;YA+BhB,WAAW;CAwB1B"}
|
|
@@ -1,119 +1,139 @@
|
|
|
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
|
-
|
|
1
|
+
// @bun
|
|
2
|
+
// src/posthog/event-source.ts
|
|
3
|
+
class PosthogAnalyticsEventSource {
|
|
4
|
+
reader;
|
|
5
|
+
limitPerEvent;
|
|
6
|
+
tenantPropertyKey;
|
|
7
|
+
defaultRangeDays;
|
|
8
|
+
constructor(reader, options = {}) {
|
|
9
|
+
this.reader = reader;
|
|
10
|
+
this.limitPerEvent = options.limitPerEvent ?? 1000;
|
|
11
|
+
this.tenantPropertyKey = options.tenantPropertyKey ?? "tenantId";
|
|
12
|
+
this.defaultRangeDays = options.defaultRangeDays ?? 30;
|
|
13
|
+
}
|
|
14
|
+
async getEventsForFunnel(definition, dateRange) {
|
|
15
|
+
const eventNames = definition.steps.map((step) => step.eventName);
|
|
16
|
+
return this.getEventsByNames(eventNames, dateRange);
|
|
17
|
+
}
|
|
18
|
+
async getEventsForCohort(definition, dateRange, eventNames) {
|
|
19
|
+
const events = eventNames && eventNames.length > 0 ? eventNames : ["*"];
|
|
20
|
+
if (events[0] === "*") {
|
|
21
|
+
return this.getEventsByNames([], dateRange, definition.periods * 500);
|
|
22
|
+
}
|
|
23
|
+
return this.getEventsByNames(events, dateRange, definition.periods * 500);
|
|
24
|
+
}
|
|
25
|
+
async getUserActivity(userId, dateRange, limit = 1000) {
|
|
26
|
+
if (!this.reader.getEvents) {
|
|
27
|
+
throw new Error("Analytics reader does not support event queries.");
|
|
28
|
+
}
|
|
29
|
+
const response = await this.reader.getEvents({
|
|
30
|
+
distinctId: userId,
|
|
31
|
+
dateRange,
|
|
32
|
+
limit
|
|
33
|
+
});
|
|
34
|
+
return response.results.map((event) => toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
35
|
+
}
|
|
36
|
+
async getGrowthMetrics(metricNames, dateRange) {
|
|
37
|
+
const range = resolveRange(dateRange, this.defaultRangeDays);
|
|
38
|
+
const previous = shiftRange(range);
|
|
39
|
+
const results = [];
|
|
40
|
+
for (const metricName of metricNames) {
|
|
41
|
+
const current = await this.countEvents(metricName, range);
|
|
42
|
+
const previousCount = await this.countEvents(metricName, previous);
|
|
43
|
+
results.push({
|
|
44
|
+
name: metricName,
|
|
45
|
+
current,
|
|
46
|
+
previous: previousCount
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
async getEventsByNames(eventNames, dateRange, limitPerEvent = this.limitPerEvent) {
|
|
52
|
+
if (!this.reader.getEvents) {
|
|
53
|
+
throw new Error("Analytics reader does not support event queries.");
|
|
54
|
+
}
|
|
55
|
+
if (eventNames.length === 0) {
|
|
56
|
+
const response = await this.reader.getEvents({
|
|
57
|
+
dateRange,
|
|
58
|
+
limit: limitPerEvent
|
|
59
|
+
});
|
|
60
|
+
return response.results.map((event) => toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
61
|
+
}
|
|
62
|
+
const events = [];
|
|
63
|
+
for (const eventName of eventNames) {
|
|
64
|
+
const response = await this.reader.getEvents({
|
|
65
|
+
event: eventName,
|
|
66
|
+
dateRange,
|
|
67
|
+
limit: limitPerEvent
|
|
68
|
+
});
|
|
69
|
+
response.results.forEach((event) => {
|
|
70
|
+
events.push(toAnalyticsEvent(event, this.tenantPropertyKey));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return events;
|
|
74
|
+
}
|
|
75
|
+
async countEvents(eventName, range) {
|
|
76
|
+
if (!this.reader.queryHogQL) {
|
|
77
|
+
throw new Error("Analytics reader does not support HogQL queries.");
|
|
78
|
+
}
|
|
79
|
+
const result = await this.reader.queryHogQL({
|
|
80
|
+
query: [
|
|
81
|
+
"select",
|
|
82
|
+
" count() as total",
|
|
83
|
+
"from events",
|
|
84
|
+
"where event = {eventName}",
|
|
85
|
+
"and timestamp >= {dateFrom}",
|
|
86
|
+
"and timestamp < {dateTo}"
|
|
87
|
+
].join(`
|
|
88
|
+
`),
|
|
89
|
+
values: {
|
|
90
|
+
eventName,
|
|
91
|
+
dateFrom: range.from.toISOString(),
|
|
92
|
+
dateTo: range.to.toISOString()
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return readSingleNumber(result) ?? 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
80
98
|
function toAnalyticsEvent(event, tenantPropertyKey) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
const tenantIdValue = event.properties?.[tenantPropertyKey] ?? event.properties?.tenantId;
|
|
100
|
+
return {
|
|
101
|
+
name: event.event,
|
|
102
|
+
userId: event.distinctId,
|
|
103
|
+
tenantId: typeof tenantIdValue === "string" ? tenantIdValue : undefined,
|
|
104
|
+
timestamp: event.timestamp,
|
|
105
|
+
properties: event.properties
|
|
106
|
+
};
|
|
89
107
|
}
|
|
90
108
|
function resolveRange(dateRange, defaultDays) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
to
|
|
95
|
-
};
|
|
109
|
+
const to = dateRange?.to instanceof Date ? dateRange.to : dateRange?.to ? new Date(dateRange.to) : new Date;
|
|
110
|
+
const from = dateRange?.from instanceof Date ? dateRange.from : dateRange?.from ? new Date(dateRange.from) : new Date(to.getTime() - defaultDays * 24 * 60 * 60 * 1000);
|
|
111
|
+
return { from, to };
|
|
96
112
|
}
|
|
97
113
|
function shiftRange(range) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
const duration = range.to.getTime() - range.from.getTime();
|
|
115
|
+
return {
|
|
116
|
+
from: new Date(range.from.getTime() - duration),
|
|
117
|
+
to: new Date(range.to.getTime() - duration)
|
|
118
|
+
};
|
|
103
119
|
}
|
|
104
120
|
function readSingleNumber(result) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
if (!Array.isArray(result.results) || result.results.length === 0) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const firstRow = result.results[0];
|
|
125
|
+
if (Array.isArray(firstRow) && firstRow.length > 0) {
|
|
126
|
+
const value = firstRow[0];
|
|
127
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
128
|
+
return value;
|
|
129
|
+
if (typeof value === "string" && value.trim()) {
|
|
130
|
+
const parsed = Number(value);
|
|
131
|
+
if (Number.isFinite(parsed))
|
|
132
|
+
return parsed;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
115
136
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//# sourceMappingURL=event-source.js.map
|
|
137
|
+
export {
|
|
138
|
+
PosthogAnalyticsEventSource
|
|
139
|
+
};
|
package/dist/posthog/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export * from './event-source';
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/posthog/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|