@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export * from './metric-collectors';
|
|
2
|
+
export * from './posthog-bridge';
|
|
3
|
+
export * from './posthog-metric-source';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
|
package/dist/lifecycle/index.js
CHANGED
|
@@ -1,5 +1,288 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// @bun
|
|
2
|
+
// src/lifecycle/metric-collectors.ts
|
|
3
|
+
var collectLifecycleMetrics = async (source) => {
|
|
4
|
+
const [
|
|
5
|
+
activeUsers,
|
|
6
|
+
weeklyActiveUsers,
|
|
7
|
+
retentionRate,
|
|
8
|
+
monthlyRecurringRevenue,
|
|
9
|
+
customerCount,
|
|
10
|
+
teamSize,
|
|
11
|
+
burnMultiple
|
|
12
|
+
] = await Promise.all([
|
|
13
|
+
source.getActiveUsers(),
|
|
14
|
+
source.getWeeklyActiveUsers?.(),
|
|
15
|
+
source.getRetentionRate?.(),
|
|
16
|
+
source.getMonthlyRecurringRevenue?.(),
|
|
17
|
+
source.getCustomerCount?.(),
|
|
18
|
+
source.getTeamSize?.(),
|
|
19
|
+
source.getBurnMultiple?.()
|
|
20
|
+
]);
|
|
21
|
+
return {
|
|
22
|
+
activeUsers,
|
|
23
|
+
weeklyActiveUsers,
|
|
24
|
+
retentionRate,
|
|
25
|
+
monthlyRecurringRevenue,
|
|
26
|
+
customerCount,
|
|
27
|
+
teamSize,
|
|
28
|
+
burnMultiple
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
var metricsToSignals = (metrics, tenantId) => Object.entries(metrics).filter(([, value]) => value !== undefined && value !== null).map(([metricKey, value]) => ({
|
|
32
|
+
id: `lifecycle-metric:${metricKey}`,
|
|
33
|
+
kind: "metric",
|
|
34
|
+
source: "analytics",
|
|
35
|
+
name: metricKey,
|
|
36
|
+
value,
|
|
37
|
+
weight: 1,
|
|
38
|
+
confidence: 0.8,
|
|
39
|
+
details: tenantId ? { tenantId } : undefined,
|
|
40
|
+
capturedAt: new Date().toISOString()
|
|
41
|
+
}));
|
|
42
|
+
var lifecycleEventNames = {
|
|
43
|
+
assessmentRun: "lifecycle_assessment_run",
|
|
44
|
+
stageChanged: "lifecycle_stage_changed",
|
|
45
|
+
guidanceConsumed: "lifecycle_guidance_consumed"
|
|
46
|
+
};
|
|
47
|
+
var createStageChangeEvent = (payload) => ({
|
|
48
|
+
name: lifecycleEventNames.stageChanged,
|
|
49
|
+
userId: "system",
|
|
50
|
+
tenantId: payload.tenantId,
|
|
51
|
+
timestamp: new Date,
|
|
52
|
+
properties: { ...payload }
|
|
53
|
+
});
|
|
4
54
|
|
|
5
|
-
|
|
55
|
+
// src/lifecycle/posthog-bridge.ts
|
|
56
|
+
var trackLifecycleAssessment = async (client, tenantId, assessment) => {
|
|
57
|
+
await client.capture({
|
|
58
|
+
distinctId: tenantId,
|
|
59
|
+
event: lifecycleEventNames.assessmentRun,
|
|
60
|
+
properties: {
|
|
61
|
+
stage: assessment.stage,
|
|
62
|
+
confidence: assessment.confidence,
|
|
63
|
+
axes: assessment.axes
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
var trackLifecycleStageChange = async (client, tenantId, previousStage, nextStage) => {
|
|
68
|
+
await client.capture({
|
|
69
|
+
distinctId: tenantId,
|
|
70
|
+
event: lifecycleEventNames.stageChanged,
|
|
71
|
+
properties: {
|
|
72
|
+
previousStage,
|
|
73
|
+
nextStage
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/lifecycle/posthog-metric-source.ts
|
|
79
|
+
class PosthogLifecycleMetricSource {
|
|
80
|
+
reader;
|
|
81
|
+
activityEvents;
|
|
82
|
+
retentionWindowDays;
|
|
83
|
+
revenueEvent;
|
|
84
|
+
revenueProperty;
|
|
85
|
+
customerEvent;
|
|
86
|
+
customerProperty;
|
|
87
|
+
teamSizeEvent;
|
|
88
|
+
teamSizeProperty;
|
|
89
|
+
burnMultipleEvent;
|
|
90
|
+
burnMultipleProperty;
|
|
91
|
+
constructor(reader, options = {}) {
|
|
92
|
+
this.reader = reader;
|
|
93
|
+
this.activityEvents = options.activityEvents;
|
|
94
|
+
this.retentionWindowDays = options.retentionWindowDays ?? 7;
|
|
95
|
+
this.revenueEvent = options.revenueEvent;
|
|
96
|
+
this.revenueProperty = options.revenueProperty ?? "amount";
|
|
97
|
+
this.customerEvent = options.customerEvent;
|
|
98
|
+
this.customerProperty = options.customerProperty ?? "is_customer";
|
|
99
|
+
this.teamSizeEvent = options.teamSizeEvent;
|
|
100
|
+
this.teamSizeProperty = options.teamSizeProperty ?? "team_size";
|
|
101
|
+
this.burnMultipleEvent = options.burnMultipleEvent;
|
|
102
|
+
this.burnMultipleProperty = options.burnMultipleProperty ?? "burn_multiple";
|
|
103
|
+
}
|
|
104
|
+
async getActiveUsers() {
|
|
105
|
+
return this.countDistinctUsers(1);
|
|
106
|
+
}
|
|
107
|
+
async getWeeklyActiveUsers() {
|
|
108
|
+
return this.countDistinctUsers(7);
|
|
109
|
+
}
|
|
110
|
+
async getRetentionRate() {
|
|
111
|
+
const windowDays = this.retentionWindowDays;
|
|
112
|
+
const now = new Date;
|
|
113
|
+
const prevEnd = new Date(now.getTime() - windowDays * 24 * 60 * 60 * 1000);
|
|
114
|
+
const prevStart = new Date(now.getTime() - windowDays * 2 * 24 * 60 * 60 * 1000);
|
|
115
|
+
const returningUsers = await this.countReturningUsers(prevStart, prevEnd, now);
|
|
116
|
+
const prevUsers = await this.countDistinctUsersBetween(prevStart, prevEnd);
|
|
117
|
+
if (prevUsers === undefined || prevUsers === 0)
|
|
118
|
+
return;
|
|
119
|
+
return returningUsers / prevUsers;
|
|
120
|
+
}
|
|
121
|
+
async getMonthlyRecurringRevenue() {
|
|
122
|
+
if (!this.revenueEvent || !this.revenueProperty)
|
|
123
|
+
return;
|
|
124
|
+
return this.sumMetric(this.revenueEvent, this.revenueProperty);
|
|
125
|
+
}
|
|
126
|
+
async getCustomerCount() {
|
|
127
|
+
if (!this.customerEvent || !this.customerProperty)
|
|
128
|
+
return;
|
|
129
|
+
return this.countDistinctUsersByProperty(this.customerEvent, this.customerProperty);
|
|
130
|
+
}
|
|
131
|
+
async getTeamSize() {
|
|
132
|
+
if (!this.teamSizeEvent || !this.teamSizeProperty)
|
|
133
|
+
return;
|
|
134
|
+
return this.latestMetric(this.teamSizeEvent, this.teamSizeProperty);
|
|
135
|
+
}
|
|
136
|
+
async getBurnMultiple() {
|
|
137
|
+
if (!this.burnMultipleEvent || !this.burnMultipleProperty)
|
|
138
|
+
return;
|
|
139
|
+
return this.latestMetric(this.burnMultipleEvent, this.burnMultipleProperty);
|
|
140
|
+
}
|
|
141
|
+
async countDistinctUsers(days) {
|
|
142
|
+
const range = buildDateRange(days);
|
|
143
|
+
return this.countDistinctUsersBetween(range.from, range.to);
|
|
144
|
+
}
|
|
145
|
+
async countDistinctUsersBetween(from, to) {
|
|
146
|
+
const eventFilter = buildEventFilter(this.activityEvents, "activityEvent");
|
|
147
|
+
const result = await this.queryHogQL({
|
|
148
|
+
query: [
|
|
149
|
+
"select",
|
|
150
|
+
" countDistinct(distinct_id) as total",
|
|
151
|
+
"from events",
|
|
152
|
+
`where timestamp >= {dateFrom} and timestamp < {dateTo}`,
|
|
153
|
+
eventFilter.clause ? `and ${eventFilter.clause}` : ""
|
|
154
|
+
].filter(Boolean).join(`
|
|
155
|
+
`),
|
|
156
|
+
values: {
|
|
157
|
+
dateFrom: from.toISOString(),
|
|
158
|
+
dateTo: to.toISOString(),
|
|
159
|
+
...eventFilter.values
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
return readSingleNumber(result);
|
|
163
|
+
}
|
|
164
|
+
async countReturningUsers(previousStart, previousEnd, currentEnd) {
|
|
165
|
+
const eventFilter = buildEventFilter(this.activityEvents, "activityEvent");
|
|
166
|
+
const result = await this.queryHogQL({
|
|
167
|
+
query: [
|
|
168
|
+
"select",
|
|
169
|
+
" countDistinct(distinct_id) as total",
|
|
170
|
+
"from events",
|
|
171
|
+
`where timestamp >= {currentFrom} and timestamp < {currentTo}`,
|
|
172
|
+
eventFilter.clause ? `and ${eventFilter.clause}` : "",
|
|
173
|
+
"and distinct_id in (",
|
|
174
|
+
" select distinct_id from events",
|
|
175
|
+
" where timestamp >= {previousFrom} and timestamp < {previousTo}",
|
|
176
|
+
eventFilter.clause ? `and ${eventFilter.clause}` : "",
|
|
177
|
+
")"
|
|
178
|
+
].join(`
|
|
179
|
+
`),
|
|
180
|
+
values: {
|
|
181
|
+
currentFrom: previousEnd.toISOString(),
|
|
182
|
+
currentTo: currentEnd.toISOString(),
|
|
183
|
+
previousFrom: previousStart.toISOString(),
|
|
184
|
+
previousTo: previousEnd.toISOString(),
|
|
185
|
+
...eventFilter.values
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return readSingleNumber(result) ?? 0;
|
|
189
|
+
}
|
|
190
|
+
async sumMetric(eventName, propertyKey) {
|
|
191
|
+
const result = await this.queryHogQL({
|
|
192
|
+
query: [
|
|
193
|
+
"select",
|
|
194
|
+
` sum(properties.${propertyKey}) as total`,
|
|
195
|
+
"from events",
|
|
196
|
+
"where event = {eventName}"
|
|
197
|
+
].join(`
|
|
198
|
+
`),
|
|
199
|
+
values: { eventName }
|
|
200
|
+
});
|
|
201
|
+
return readSingleNumber(result);
|
|
202
|
+
}
|
|
203
|
+
async countDistinctUsersByProperty(eventName, propertyKey) {
|
|
204
|
+
const result = await this.queryHogQL({
|
|
205
|
+
query: [
|
|
206
|
+
"select",
|
|
207
|
+
" countDistinct(distinct_id) as total",
|
|
208
|
+
"from events",
|
|
209
|
+
"where event = {eventName}",
|
|
210
|
+
`and properties.${propertyKey} = {propertyValue}`
|
|
211
|
+
].join(`
|
|
212
|
+
`),
|
|
213
|
+
values: { eventName, propertyValue: true }
|
|
214
|
+
});
|
|
215
|
+
return readSingleNumber(result);
|
|
216
|
+
}
|
|
217
|
+
async latestMetric(eventName, propertyKey) {
|
|
218
|
+
const result = await this.queryHogQL({
|
|
219
|
+
query: [
|
|
220
|
+
"select",
|
|
221
|
+
` properties.${propertyKey} as value`,
|
|
222
|
+
"from events",
|
|
223
|
+
"where event = {eventName}",
|
|
224
|
+
"order by timestamp desc",
|
|
225
|
+
"limit 1"
|
|
226
|
+
].join(`
|
|
227
|
+
`),
|
|
228
|
+
values: { eventName }
|
|
229
|
+
});
|
|
230
|
+
return readSingleNumber(result);
|
|
231
|
+
}
|
|
232
|
+
async queryHogQL(input) {
|
|
233
|
+
if (!this.reader.queryHogQL) {
|
|
234
|
+
throw new Error("Analytics reader does not support HogQL queries.");
|
|
235
|
+
}
|
|
236
|
+
return this.reader.queryHogQL(input);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function buildDateRange(days) {
|
|
240
|
+
const to = new Date;
|
|
241
|
+
const from = new Date(to.getTime() - days * 24 * 60 * 60 * 1000);
|
|
242
|
+
return { from, to };
|
|
243
|
+
}
|
|
244
|
+
function buildEventFilter(events, prefix) {
|
|
245
|
+
if (!events || events.length === 0)
|
|
246
|
+
return {};
|
|
247
|
+
if (events.length === 1) {
|
|
248
|
+
return {
|
|
249
|
+
clause: `event = {${prefix}0}`,
|
|
250
|
+
values: { [`${prefix}0`]: events[0] }
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const clauses = events.map((_event, index) => `event = {${prefix}${index}}`);
|
|
254
|
+
const values = {};
|
|
255
|
+
events.forEach((event, index) => {
|
|
256
|
+
values[`${prefix}${index}`] = event;
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
clause: `(${clauses.join(" or ")})`,
|
|
260
|
+
values
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function readSingleNumber(result) {
|
|
264
|
+
if (!Array.isArray(result.results) || result.results.length === 0) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const firstRow = result.results[0];
|
|
268
|
+
if (Array.isArray(firstRow) && firstRow.length > 0) {
|
|
269
|
+
const value = firstRow[0];
|
|
270
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
271
|
+
return value;
|
|
272
|
+
if (typeof value === "string" && value.trim()) {
|
|
273
|
+
const parsed = Number(value);
|
|
274
|
+
if (Number.isFinite(parsed))
|
|
275
|
+
return parsed;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
export {
|
|
281
|
+
trackLifecycleStageChange,
|
|
282
|
+
trackLifecycleAssessment,
|
|
283
|
+
metricsToSignals,
|
|
284
|
+
lifecycleEventNames,
|
|
285
|
+
createStageChangeEvent,
|
|
286
|
+
collectLifecycleMetrics,
|
|
287
|
+
PosthogLifecycleMetricSource
|
|
288
|
+
};
|
|
@@ -1,30 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
getTeamSize?(): Promise<number | undefined>;
|
|
12
|
-
getBurnMultiple?(): Promise<number | undefined>;
|
|
1
|
+
import type { LifecycleMetricSnapshot, LifecycleSignal, LifecycleStage } from '@contractspec/lib.lifecycle';
|
|
2
|
+
import type { AnalyticsEvent } from '../types';
|
|
3
|
+
export interface LifecycleMetricSource {
|
|
4
|
+
getActiveUsers(): Promise<number | undefined>;
|
|
5
|
+
getWeeklyActiveUsers?(): Promise<number | undefined>;
|
|
6
|
+
getRetentionRate?(): Promise<number | undefined>;
|
|
7
|
+
getMonthlyRecurringRevenue?(): Promise<number | undefined>;
|
|
8
|
+
getCustomerCount?(): Promise<number | undefined>;
|
|
9
|
+
getTeamSize?(): Promise<number | undefined>;
|
|
10
|
+
getBurnMultiple?(): Promise<number | undefined>;
|
|
13
11
|
}
|
|
14
|
-
declare const collectLifecycleMetrics: (source: LifecycleMetricSource) => Promise<LifecycleMetricSnapshot>;
|
|
15
|
-
declare const metricsToSignals: (metrics: LifecycleMetricSnapshot, tenantId?: string) => LifecycleSignal[];
|
|
16
|
-
declare const lifecycleEventNames: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
export declare const collectLifecycleMetrics: (source: LifecycleMetricSource) => Promise<LifecycleMetricSnapshot>;
|
|
13
|
+
export declare const metricsToSignals: (metrics: LifecycleMetricSnapshot, tenantId?: string) => LifecycleSignal[];
|
|
14
|
+
export declare const lifecycleEventNames: {
|
|
15
|
+
readonly assessmentRun: "lifecycle_assessment_run";
|
|
16
|
+
readonly stageChanged: "lifecycle_stage_changed";
|
|
17
|
+
readonly guidanceConsumed: "lifecycle_guidance_consumed";
|
|
20
18
|
};
|
|
21
|
-
interface LifecycleStageChangePayload {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
export interface LifecycleStageChangePayload {
|
|
20
|
+
tenantId?: string;
|
|
21
|
+
previousStage?: LifecycleStage;
|
|
22
|
+
nextStage: LifecycleStage;
|
|
23
|
+
confidence: number;
|
|
26
24
|
}
|
|
27
|
-
declare const createStageChangeEvent: (payload: LifecycleStageChangePayload) => AnalyticsEvent;
|
|
28
|
-
//#endregion
|
|
29
|
-
export { LifecycleMetricSource, LifecycleStageChangePayload, collectLifecycleMetrics, createStageChangeEvent, lifecycleEventNames, metricsToSignals };
|
|
25
|
+
export declare const createStageChangeEvent: (payload: LifecycleStageChangePayload) => AnalyticsEvent;
|
|
30
26
|
//# sourceMappingURL=metric-collectors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metric-collectors.d.ts","
|
|
1
|
+
{"version":3,"file":"metric-collectors.d.ts","sourceRoot":"","sources":["../../src/lifecycle/metric-collectors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,eAAe,EACf,cAAc,EACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9C,oBAAoB,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACrD,gBAAgB,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,0BAA0B,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3D,gBAAgB,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,WAAW,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,eAAe,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACjD;AAED,eAAO,MAAM,uBAAuB,GAClC,QAAQ,qBAAqB,KAC5B,OAAO,CAAC,uBAAuB,CA4BjC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,uBAAuB,EAChC,WAAW,MAAM,KAChB,eAAe,EAaX,CAAC;AAER,eAAO,MAAM,mBAAmB;;;;CAItB,CAAC;AAEX,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,cAAc,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,GACjC,SAAS,2BAA2B,KACnC,cAMD,CAAC"}
|
|
@@ -1,48 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
// @bun
|
|
2
|
+
// src/lifecycle/metric-collectors.ts
|
|
3
|
+
var collectLifecycleMetrics = async (source) => {
|
|
4
|
+
const [
|
|
5
|
+
activeUsers,
|
|
6
|
+
weeklyActiveUsers,
|
|
7
|
+
retentionRate,
|
|
8
|
+
monthlyRecurringRevenue,
|
|
9
|
+
customerCount,
|
|
10
|
+
teamSize,
|
|
11
|
+
burnMultiple
|
|
12
|
+
] = await Promise.all([
|
|
13
|
+
source.getActiveUsers(),
|
|
14
|
+
source.getWeeklyActiveUsers?.(),
|
|
15
|
+
source.getRetentionRate?.(),
|
|
16
|
+
source.getMonthlyRecurringRevenue?.(),
|
|
17
|
+
source.getCustomerCount?.(),
|
|
18
|
+
source.getTeamSize?.(),
|
|
19
|
+
source.getBurnMultiple?.()
|
|
20
|
+
]);
|
|
21
|
+
return {
|
|
22
|
+
activeUsers,
|
|
23
|
+
weeklyActiveUsers,
|
|
24
|
+
retentionRate,
|
|
25
|
+
monthlyRecurringRevenue,
|
|
26
|
+
customerCount,
|
|
27
|
+
teamSize,
|
|
28
|
+
burnMultiple
|
|
29
|
+
};
|
|
21
30
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
var metricsToSignals = (metrics, tenantId) => Object.entries(metrics).filter(([, value]) => value !== undefined && value !== null).map(([metricKey, value]) => ({
|
|
32
|
+
id: `lifecycle-metric:${metricKey}`,
|
|
33
|
+
kind: "metric",
|
|
34
|
+
source: "analytics",
|
|
35
|
+
name: metricKey,
|
|
36
|
+
value,
|
|
37
|
+
weight: 1,
|
|
38
|
+
confidence: 0.8,
|
|
39
|
+
details: tenantId ? { tenantId } : undefined,
|
|
40
|
+
capturedAt: new Date().toISOString()
|
|
32
41
|
}));
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
var lifecycleEventNames = {
|
|
43
|
+
assessmentRun: "lifecycle_assessment_run",
|
|
44
|
+
stageChanged: "lifecycle_stage_changed",
|
|
45
|
+
guidanceConsumed: "lifecycle_guidance_consumed"
|
|
37
46
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
var createStageChangeEvent = (payload) => ({
|
|
48
|
+
name: lifecycleEventNames.stageChanged,
|
|
49
|
+
userId: "system",
|
|
50
|
+
tenantId: payload.tenantId,
|
|
51
|
+
timestamp: new Date,
|
|
52
|
+
properties: { ...payload }
|
|
44
53
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
export {
|
|
55
|
+
metricsToSignals,
|
|
56
|
+
lifecycleEventNames,
|
|
57
|
+
createStageChangeEvent,
|
|
58
|
+
collectLifecycleMetrics
|
|
59
|
+
};
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import { LifecycleAssessment } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
properties?: Record<string, unknown>;
|
|
9
|
-
}) => Promise<void> | void;
|
|
1
|
+
import type { LifecycleAssessment } from '@contractspec/lib.lifecycle';
|
|
2
|
+
export interface PostHogLikeClient {
|
|
3
|
+
capture: (event: {
|
|
4
|
+
distinctId: string;
|
|
5
|
+
event: string;
|
|
6
|
+
properties?: Record<string, unknown>;
|
|
7
|
+
}) => Promise<void> | void;
|
|
10
8
|
}
|
|
11
|
-
declare const trackLifecycleAssessment: (client: PostHogLikeClient, tenantId: string, assessment: LifecycleAssessment) => Promise<void>;
|
|
12
|
-
declare const trackLifecycleStageChange: (client: PostHogLikeClient, tenantId: string, previousStage: number | undefined, nextStage: number) => Promise<void>;
|
|
13
|
-
//#endregion
|
|
14
|
-
export { PostHogLikeClient, trackLifecycleAssessment, trackLifecycleStageChange };
|
|
9
|
+
export declare const trackLifecycleAssessment: (client: PostHogLikeClient, tenantId: string, assessment: LifecycleAssessment) => Promise<void>;
|
|
10
|
+
export declare const trackLifecycleStageChange: (client: PostHogLikeClient, tenantId: string, previousStage: number | undefined, nextStage: number) => Promise<void>;
|
|
15
11
|
//# sourceMappingURL=posthog-bridge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"posthog-bridge.d.ts","
|
|
1
|
+
{"version":3,"file":"posthog-bridge.d.ts","sourceRoot":"","sources":["../../src/lifecycle/posthog-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,CAAC,KAAK,EAAE;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,wBAAwB,GACnC,QAAQ,iBAAiB,EACzB,UAAU,MAAM,EAChB,YAAY,mBAAmB,kBAWhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,QAAQ,iBAAiB,EACzB,UAAU,MAAM,EAChB,eAAe,MAAM,GAAG,SAAS,EACjC,WAAW,MAAM,kBAUlB,CAAC"}
|
|
@@ -1,28 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
// @bun
|
|
2
|
+
// src/lifecycle/metric-collectors.ts
|
|
3
|
+
var collectLifecycleMetrics = async (source) => {
|
|
4
|
+
const [
|
|
5
|
+
activeUsers,
|
|
6
|
+
weeklyActiveUsers,
|
|
7
|
+
retentionRate,
|
|
8
|
+
monthlyRecurringRevenue,
|
|
9
|
+
customerCount,
|
|
10
|
+
teamSize,
|
|
11
|
+
burnMultiple
|
|
12
|
+
] = await Promise.all([
|
|
13
|
+
source.getActiveUsers(),
|
|
14
|
+
source.getWeeklyActiveUsers?.(),
|
|
15
|
+
source.getRetentionRate?.(),
|
|
16
|
+
source.getMonthlyRecurringRevenue?.(),
|
|
17
|
+
source.getCustomerCount?.(),
|
|
18
|
+
source.getTeamSize?.(),
|
|
19
|
+
source.getBurnMultiple?.()
|
|
20
|
+
]);
|
|
21
|
+
return {
|
|
22
|
+
activeUsers,
|
|
23
|
+
weeklyActiveUsers,
|
|
24
|
+
retentionRate,
|
|
25
|
+
monthlyRecurringRevenue,
|
|
26
|
+
customerCount,
|
|
27
|
+
teamSize,
|
|
28
|
+
burnMultiple
|
|
29
|
+
};
|
|
14
30
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
var metricsToSignals = (metrics, tenantId) => Object.entries(metrics).filter(([, value]) => value !== undefined && value !== null).map(([metricKey, value]) => ({
|
|
32
|
+
id: `lifecycle-metric:${metricKey}`,
|
|
33
|
+
kind: "metric",
|
|
34
|
+
source: "analytics",
|
|
35
|
+
name: metricKey,
|
|
36
|
+
value,
|
|
37
|
+
weight: 1,
|
|
38
|
+
confidence: 0.8,
|
|
39
|
+
details: tenantId ? { tenantId } : undefined,
|
|
40
|
+
capturedAt: new Date().toISOString()
|
|
41
|
+
}));
|
|
42
|
+
var lifecycleEventNames = {
|
|
43
|
+
assessmentRun: "lifecycle_assessment_run",
|
|
44
|
+
stageChanged: "lifecycle_stage_changed",
|
|
45
|
+
guidanceConsumed: "lifecycle_guidance_consumed"
|
|
24
46
|
};
|
|
47
|
+
var createStageChangeEvent = (payload) => ({
|
|
48
|
+
name: lifecycleEventNames.stageChanged,
|
|
49
|
+
userId: "system",
|
|
50
|
+
tenantId: payload.tenantId,
|
|
51
|
+
timestamp: new Date,
|
|
52
|
+
properties: { ...payload }
|
|
53
|
+
});
|
|
25
54
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
55
|
+
// src/lifecycle/posthog-bridge.ts
|
|
56
|
+
var trackLifecycleAssessment = async (client, tenantId, assessment) => {
|
|
57
|
+
await client.capture({
|
|
58
|
+
distinctId: tenantId,
|
|
59
|
+
event: lifecycleEventNames.assessmentRun,
|
|
60
|
+
properties: {
|
|
61
|
+
stage: assessment.stage,
|
|
62
|
+
confidence: assessment.confidence,
|
|
63
|
+
axes: assessment.axes
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
var trackLifecycleStageChange = async (client, tenantId, previousStage, nextStage) => {
|
|
68
|
+
await client.capture({
|
|
69
|
+
distinctId: tenantId,
|
|
70
|
+
event: lifecycleEventNames.stageChanged,
|
|
71
|
+
properties: {
|
|
72
|
+
previousStage,
|
|
73
|
+
nextStage
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
export {
|
|
78
|
+
trackLifecycleStageChange,
|
|
79
|
+
trackLifecycleAssessment
|
|
80
|
+
};
|