@contractspec/lib.metering 3.7.17 → 3.7.18
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/aggregation/index.js +1 -265
- package/dist/analytics/posthog-metering-reader.js +4 -266
- package/dist/analytics/posthog-metering.js +1 -45
- package/dist/browser/aggregation/index.js +1 -265
- package/dist/browser/analytics/posthog-metering-reader.js +4 -266
- package/dist/browser/analytics/posthog-metering.js +1 -45
- package/dist/browser/contracts/index.js +1 -617
- package/dist/browser/docs/index.js +4 -18
- package/dist/browser/docs/metering.docblock.js +4 -18
- package/dist/browser/entities/index.js +1 -350
- package/dist/browser/events.js +1 -269
- package/dist/browser/index.js +7 -1877
- package/dist/browser/metering.capability.js +1 -31
- package/dist/browser/metering.feature.js +1 -53
- package/dist/contracts/index.js +1 -617
- package/dist/docs/index.js +4 -18
- package/dist/docs/metering.docblock.js +4 -18
- package/dist/entities/index.js +1 -350
- package/dist/events.js +1 -269
- package/dist/index.js +7 -1877
- package/dist/metering.capability.js +1 -31
- package/dist/metering.feature.js +1 -53
- package/dist/node/aggregation/index.js +1 -265
- package/dist/node/analytics/posthog-metering-reader.js +4 -266
- package/dist/node/analytics/posthog-metering.js +1 -45
- package/dist/node/contracts/index.js +1 -617
- package/dist/node/docs/index.js +4 -18
- package/dist/node/docs/metering.docblock.js +4 -18
- package/dist/node/entities/index.js +1 -350
- package/dist/node/events.js +1 -269
- package/dist/node/index.js +7 -1877
- package/dist/node/metering.capability.js +1 -31
- package/dist/node/metering.feature.js +1 -53
- package/package.json +6 -6
|
@@ -1,32 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
defineCapability,
|
|
5
|
-
StabilityEnum
|
|
6
|
-
} from "@contractspec/lib.contracts-spec";
|
|
7
|
-
var MeteringCapability = defineCapability({
|
|
8
|
-
meta: {
|
|
9
|
-
key: "metering",
|
|
10
|
-
version: "1.0.0",
|
|
11
|
-
kind: "api",
|
|
12
|
-
stability: StabilityEnum.Experimental,
|
|
13
|
-
description: "Usage metering and tracking",
|
|
14
|
-
owners: ["@platform.finance"],
|
|
15
|
-
tags: ["metering", "usage", "billing"]
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
var ThresholdsCapability = defineCapability({
|
|
19
|
-
meta: {
|
|
20
|
-
key: "thresholds",
|
|
21
|
-
version: "1.0.0",
|
|
22
|
-
kind: "api",
|
|
23
|
-
stability: StabilityEnum.Experimental,
|
|
24
|
-
description: "Usage threshold alerts and limits",
|
|
25
|
-
owners: ["@platform.finance"],
|
|
26
|
-
tags: ["thresholds", "limits", "metering"]
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
export {
|
|
30
|
-
ThresholdsCapability,
|
|
31
|
-
MeteringCapability
|
|
32
|
-
};
|
|
2
|
+
import{defineCapability as g,StabilityEnum as h}from"@contractspec/lib.contracts-spec";var k=g({meta:{key:"metering",version:"1.0.0",kind:"api",stability:h.Experimental,description:"Usage metering and tracking",owners:["@platform.finance"],tags:["metering","usage","billing"]}}),q=g({meta:{key:"thresholds",version:"1.0.0",kind:"api",stability:h.Experimental,description:"Usage threshold alerts and limits",owners:["@platform.finance"],tags:["thresholds","limits","metering"]}});export{q as ThresholdsCapability,k as MeteringCapability};
|
package/dist/metering.feature.js
CHANGED
|
@@ -1,54 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
import { defineFeature } from "@contractspec/lib.contracts-spec";
|
|
4
|
-
var MeteringFeature = defineFeature({
|
|
5
|
-
meta: {
|
|
6
|
-
key: "metrics",
|
|
7
|
-
version: "1.0.0",
|
|
8
|
-
title: "Usage Metering",
|
|
9
|
-
description: "Usage metering, metric definitions, and threshold alerting",
|
|
10
|
-
domain: "platform",
|
|
11
|
-
owners: ["@platform.metering"],
|
|
12
|
-
tags: ["metering", "usage", "billing", "thresholds"],
|
|
13
|
-
stability: "stable"
|
|
14
|
-
},
|
|
15
|
-
operations: [
|
|
16
|
-
{ key: "metric.define", version: "1.0.0" },
|
|
17
|
-
{ key: "metric.update", version: "1.0.0" },
|
|
18
|
-
{ key: "metric.delete", version: "1.0.0" },
|
|
19
|
-
{ key: "metric.get", version: "1.0.0" },
|
|
20
|
-
{ key: "metric.list", version: "1.0.0" },
|
|
21
|
-
{ key: "usage.record", version: "1.0.0" },
|
|
22
|
-
{ key: "usage.recordBatch", version: "1.0.0" },
|
|
23
|
-
{ key: "usage.get", version: "1.0.0" },
|
|
24
|
-
{ key: "usage.getSummary", version: "1.0.0" },
|
|
25
|
-
{ key: "threshold.create", version: "1.0.0" },
|
|
26
|
-
{ key: "threshold.update", version: "1.0.0" },
|
|
27
|
-
{ key: "threshold.delete", version: "1.0.0" },
|
|
28
|
-
{ key: "threshold.list", version: "1.0.0" }
|
|
29
|
-
],
|
|
30
|
-
events: [
|
|
31
|
-
{ key: "metric.defined", version: "1.0.0" },
|
|
32
|
-
{ key: "metric.updated", version: "1.0.0" },
|
|
33
|
-
{ key: "usage.recorded", version: "1.0.0" },
|
|
34
|
-
{ key: "usage.batch_recorded", version: "1.0.0" },
|
|
35
|
-
{ key: "usage.aggregated", version: "1.0.0" },
|
|
36
|
-
{ key: "threshold.created", version: "1.0.0" },
|
|
37
|
-
{ key: "threshold.exceeded", version: "1.0.0" },
|
|
38
|
-
{ key: "threshold.approaching", version: "1.0.0" },
|
|
39
|
-
{ key: "model.selected", version: "1.0.0" }
|
|
40
|
-
],
|
|
41
|
-
presentations: [],
|
|
42
|
-
opToPresentation: [],
|
|
43
|
-
presentationsTargets: [],
|
|
44
|
-
capabilities: {
|
|
45
|
-
provides: [
|
|
46
|
-
{ key: "metering", version: "1.0.0" },
|
|
47
|
-
{ key: "thresholds", version: "1.0.0" }
|
|
48
|
-
],
|
|
49
|
-
requires: []
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
export {
|
|
53
|
-
MeteringFeature
|
|
54
|
-
};
|
|
2
|
+
import{defineFeature as g}from"@contractspec/lib.contracts-spec";var j=g({meta:{key:"metrics",version:"1.0.0",title:"Usage Metering",description:"Usage metering, metric definitions, and threshold alerting",domain:"platform",owners:["@platform.metering"],tags:["metering","usage","billing","thresholds"],stability:"stable"},operations:[{key:"metric.define",version:"1.0.0"},{key:"metric.update",version:"1.0.0"},{key:"metric.delete",version:"1.0.0"},{key:"metric.get",version:"1.0.0"},{key:"metric.list",version:"1.0.0"},{key:"usage.record",version:"1.0.0"},{key:"usage.recordBatch",version:"1.0.0"},{key:"usage.get",version:"1.0.0"},{key:"usage.getSummary",version:"1.0.0"},{key:"threshold.create",version:"1.0.0"},{key:"threshold.update",version:"1.0.0"},{key:"threshold.delete",version:"1.0.0"},{key:"threshold.list",version:"1.0.0"}],events:[{key:"metric.defined",version:"1.0.0"},{key:"metric.updated",version:"1.0.0"},{key:"usage.recorded",version:"1.0.0"},{key:"usage.batch_recorded",version:"1.0.0"},{key:"usage.aggregated",version:"1.0.0"},{key:"threshold.created",version:"1.0.0"},{key:"threshold.exceeded",version:"1.0.0"},{key:"threshold.approaching",version:"1.0.0"},{key:"model.selected",version:"1.0.0"}],presentations:[],opToPresentation:[],presentationsTargets:[],capabilities:{provides:[{key:"metering",version:"1.0.0"},{key:"thresholds",version:"1.0.0"}],requires:[]}});export{j as MeteringFeature};
|
|
@@ -1,265 +1 @@
|
|
|
1
|
-
|
|
2
|
-
function getPeriodStart(date, periodType) {
|
|
3
|
-
const d = new Date(date);
|
|
4
|
-
switch (periodType) {
|
|
5
|
-
case "HOURLY":
|
|
6
|
-
d.setMinutes(0, 0, 0);
|
|
7
|
-
return d;
|
|
8
|
-
case "DAILY":
|
|
9
|
-
d.setHours(0, 0, 0, 0);
|
|
10
|
-
return d;
|
|
11
|
-
case "WEEKLY": {
|
|
12
|
-
d.setHours(0, 0, 0, 0);
|
|
13
|
-
const day = d.getDay();
|
|
14
|
-
d.setDate(d.getDate() - day);
|
|
15
|
-
return d;
|
|
16
|
-
}
|
|
17
|
-
case "MONTHLY":
|
|
18
|
-
d.setHours(0, 0, 0, 0);
|
|
19
|
-
d.setDate(1);
|
|
20
|
-
return d;
|
|
21
|
-
case "YEARLY":
|
|
22
|
-
d.setHours(0, 0, 0, 0);
|
|
23
|
-
d.setMonth(0, 1);
|
|
24
|
-
return d;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function getPeriodEnd(date, periodType) {
|
|
28
|
-
const start = getPeriodStart(date, periodType);
|
|
29
|
-
switch (periodType) {
|
|
30
|
-
case "HOURLY":
|
|
31
|
-
return new Date(start.getTime() + 60 * 60 * 1000);
|
|
32
|
-
case "DAILY":
|
|
33
|
-
return new Date(start.getTime() + 24 * 60 * 60 * 1000);
|
|
34
|
-
case "WEEKLY":
|
|
35
|
-
return new Date(start.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
36
|
-
case "MONTHLY": {
|
|
37
|
-
const end = new Date(start);
|
|
38
|
-
end.setMonth(end.getMonth() + 1);
|
|
39
|
-
return end;
|
|
40
|
-
}
|
|
41
|
-
case "YEARLY": {
|
|
42
|
-
const end = new Date(start);
|
|
43
|
-
end.setFullYear(end.getFullYear() + 1);
|
|
44
|
-
return end;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function formatPeriodKey(date, periodType) {
|
|
49
|
-
const start = getPeriodStart(date, periodType);
|
|
50
|
-
const year = start.getFullYear();
|
|
51
|
-
const month = String(start.getMonth() + 1).padStart(2, "0");
|
|
52
|
-
const day = String(start.getDate()).padStart(2, "0");
|
|
53
|
-
const hour = String(start.getHours()).padStart(2, "0");
|
|
54
|
-
switch (periodType) {
|
|
55
|
-
case "HOURLY":
|
|
56
|
-
return `${year}-${month}-${day}T${hour}`;
|
|
57
|
-
case "DAILY":
|
|
58
|
-
return `${year}-${month}-${day}`;
|
|
59
|
-
case "WEEKLY":
|
|
60
|
-
return `${year}-W${getWeekNumber(start)}`;
|
|
61
|
-
case "MONTHLY":
|
|
62
|
-
return `${year}-${month}`;
|
|
63
|
-
case "YEARLY":
|
|
64
|
-
return `${year}`;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function getWeekNumber(date) {
|
|
68
|
-
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
69
|
-
const dayNum = d.getUTCDay() || 7;
|
|
70
|
-
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
71
|
-
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
72
|
-
const weekNum = Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
|
|
73
|
-
return String(weekNum).padStart(2, "0");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
class UsageAggregator {
|
|
77
|
-
storage;
|
|
78
|
-
batchSize;
|
|
79
|
-
constructor(options) {
|
|
80
|
-
this.storage = options.storage;
|
|
81
|
-
this.batchSize = options.batchSize || 1000;
|
|
82
|
-
}
|
|
83
|
-
async aggregate(params) {
|
|
84
|
-
const { periodType, periodStart, metricKey } = params;
|
|
85
|
-
const periodEnd = params.periodEnd || getPeriodEnd(periodStart, periodType);
|
|
86
|
-
const result = {
|
|
87
|
-
periodType,
|
|
88
|
-
periodStart,
|
|
89
|
-
periodEnd,
|
|
90
|
-
recordsProcessed: 0,
|
|
91
|
-
summariesCreated: 0,
|
|
92
|
-
summariesUpdated: 0,
|
|
93
|
-
errors: []
|
|
94
|
-
};
|
|
95
|
-
const records = await this.storage.getUnaggregatedRecords({
|
|
96
|
-
metricKey,
|
|
97
|
-
periodStart,
|
|
98
|
-
periodEnd,
|
|
99
|
-
limit: this.batchSize
|
|
100
|
-
});
|
|
101
|
-
if (records.length === 0) {
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
const groups = this.groupRecords(records, periodType);
|
|
105
|
-
for (const [groupKey, groupRecords] of groups.entries()) {
|
|
106
|
-
try {
|
|
107
|
-
await this.aggregateGroup(groupKey, groupRecords, periodType, result);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
const [metricKey2, subjectType, subjectId] = groupKey.split("::");
|
|
110
|
-
result.errors.push({
|
|
111
|
-
metricKey: metricKey2 ?? "unknown",
|
|
112
|
-
subjectType: subjectType ?? "unknown",
|
|
113
|
-
subjectId: subjectId ?? "unknown",
|
|
114
|
-
error: error instanceof Error ? error.message : String(error)
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const recordIds = records.map((r) => r.id);
|
|
119
|
-
await this.storage.markRecordsAggregated(recordIds, new Date);
|
|
120
|
-
result.recordsProcessed = records.length;
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
groupRecords(records, periodType) {
|
|
124
|
-
const groups = new Map;
|
|
125
|
-
for (const record of records) {
|
|
126
|
-
const periodKey = formatPeriodKey(record.timestamp, periodType);
|
|
127
|
-
const groupKey = `${record.metricKey}::${record.subjectType}::${record.subjectId}::${periodKey}`;
|
|
128
|
-
const existing = groups.get(groupKey) || [];
|
|
129
|
-
existing.push(record);
|
|
130
|
-
groups.set(groupKey, existing);
|
|
131
|
-
}
|
|
132
|
-
return groups;
|
|
133
|
-
}
|
|
134
|
-
async aggregateGroup(groupKey, records, periodType, result) {
|
|
135
|
-
const [metricKey, subjectType, subjectId] = groupKey.split("::");
|
|
136
|
-
if (!metricKey || !subjectType || !subjectId || records.length === 0) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const firstRecord = records[0];
|
|
140
|
-
if (!firstRecord)
|
|
141
|
-
return;
|
|
142
|
-
const periodStart = getPeriodStart(firstRecord.timestamp, periodType);
|
|
143
|
-
const periodEnd = getPeriodEnd(firstRecord.timestamp, periodType);
|
|
144
|
-
const metric = await this.storage.getMetric(metricKey);
|
|
145
|
-
const aggregationType = metric?.aggregationType || "SUM";
|
|
146
|
-
const quantities = records.map((r) => r.quantity);
|
|
147
|
-
const aggregated = this.calculateAggregation(quantities, aggregationType);
|
|
148
|
-
await this.storage.upsertSummary({
|
|
149
|
-
metricKey,
|
|
150
|
-
subjectType,
|
|
151
|
-
subjectId,
|
|
152
|
-
periodType,
|
|
153
|
-
periodStart,
|
|
154
|
-
periodEnd,
|
|
155
|
-
totalQuantity: aggregated.total,
|
|
156
|
-
recordCount: records.length,
|
|
157
|
-
minQuantity: aggregated.min,
|
|
158
|
-
maxQuantity: aggregated.max,
|
|
159
|
-
avgQuantity: aggregated.avg
|
|
160
|
-
});
|
|
161
|
-
result.summariesCreated++;
|
|
162
|
-
}
|
|
163
|
-
calculateAggregation(quantities, aggregationType) {
|
|
164
|
-
if (quantities.length === 0) {
|
|
165
|
-
return { total: 0, min: 0, max: 0, avg: 0 };
|
|
166
|
-
}
|
|
167
|
-
const min = Math.min(...quantities);
|
|
168
|
-
const max = Math.max(...quantities);
|
|
169
|
-
const sum = quantities.reduce((a, b) => a + b, 0);
|
|
170
|
-
const avg = sum / quantities.length;
|
|
171
|
-
const count = quantities.length;
|
|
172
|
-
let total;
|
|
173
|
-
switch (aggregationType) {
|
|
174
|
-
case "COUNT":
|
|
175
|
-
total = count;
|
|
176
|
-
break;
|
|
177
|
-
case "SUM":
|
|
178
|
-
total = sum;
|
|
179
|
-
break;
|
|
180
|
-
case "AVG":
|
|
181
|
-
total = avg;
|
|
182
|
-
break;
|
|
183
|
-
case "MAX":
|
|
184
|
-
total = max;
|
|
185
|
-
break;
|
|
186
|
-
case "MIN":
|
|
187
|
-
total = min;
|
|
188
|
-
break;
|
|
189
|
-
case "LAST":
|
|
190
|
-
total = quantities[quantities.length - 1] ?? 0;
|
|
191
|
-
break;
|
|
192
|
-
default:
|
|
193
|
-
total = sum;
|
|
194
|
-
}
|
|
195
|
-
return { total, min, max, avg };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
class InMemoryUsageStorage {
|
|
200
|
-
records = [];
|
|
201
|
-
summaries = new Map;
|
|
202
|
-
metrics = new Map;
|
|
203
|
-
addRecord(record) {
|
|
204
|
-
this.records.push(record);
|
|
205
|
-
}
|
|
206
|
-
addMetric(metric) {
|
|
207
|
-
this.metrics.set(metric.key, metric);
|
|
208
|
-
}
|
|
209
|
-
async getUnaggregatedRecords(options) {
|
|
210
|
-
let records = this.records.filter((r) => {
|
|
211
|
-
const inPeriod = r.timestamp >= options.periodStart && r.timestamp < options.periodEnd;
|
|
212
|
-
const matchesMetric = !options.metricKey || r.metricKey === options.metricKey;
|
|
213
|
-
return inPeriod && matchesMetric;
|
|
214
|
-
});
|
|
215
|
-
if (options.limit) {
|
|
216
|
-
records = records.slice(0, options.limit);
|
|
217
|
-
}
|
|
218
|
-
return records;
|
|
219
|
-
}
|
|
220
|
-
async markRecordsAggregated(recordIds) {
|
|
221
|
-
this.records = this.records.filter((r) => !recordIds.includes(r.id));
|
|
222
|
-
}
|
|
223
|
-
async upsertSummary(summary) {
|
|
224
|
-
const key = `${summary.metricKey}::${summary.subjectType}::${summary.subjectId}::${summary.periodType}::${summary.periodStart.toISOString()}`;
|
|
225
|
-
const existing = this.summaries.get(key);
|
|
226
|
-
if (existing) {
|
|
227
|
-
existing.totalQuantity += summary.totalQuantity;
|
|
228
|
-
existing.recordCount += summary.recordCount;
|
|
229
|
-
if (summary.minQuantity !== undefined) {
|
|
230
|
-
existing.minQuantity = Math.min(existing.minQuantity ?? Infinity, summary.minQuantity);
|
|
231
|
-
}
|
|
232
|
-
if (summary.maxQuantity !== undefined) {
|
|
233
|
-
existing.maxQuantity = Math.max(existing.maxQuantity ?? -Infinity, summary.maxQuantity);
|
|
234
|
-
}
|
|
235
|
-
return existing;
|
|
236
|
-
}
|
|
237
|
-
const newSummary = {
|
|
238
|
-
id: `summary-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
239
|
-
...summary
|
|
240
|
-
};
|
|
241
|
-
this.summaries.set(key, newSummary);
|
|
242
|
-
return newSummary;
|
|
243
|
-
}
|
|
244
|
-
async getMetric(key) {
|
|
245
|
-
return this.metrics.get(key) || null;
|
|
246
|
-
}
|
|
247
|
-
async listMetrics() {
|
|
248
|
-
return Array.from(this.metrics.values());
|
|
249
|
-
}
|
|
250
|
-
getSummaries() {
|
|
251
|
-
return Array.from(this.summaries.values());
|
|
252
|
-
}
|
|
253
|
-
clear() {
|
|
254
|
-
this.records = [];
|
|
255
|
-
this.summaries.clear();
|
|
256
|
-
this.metrics.clear();
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
export {
|
|
260
|
-
getPeriodStart,
|
|
261
|
-
getPeriodEnd,
|
|
262
|
-
formatPeriodKey,
|
|
263
|
-
UsageAggregator,
|
|
264
|
-
InMemoryUsageStorage
|
|
265
|
-
};
|
|
1
|
+
function Z(z,B){let A=new Date(z);switch(B){case"HOURLY":return A.setMinutes(0,0,0),A;case"DAILY":return A.setHours(0,0,0,0),A;case"WEEKLY":{A.setHours(0,0,0,0);let D=A.getDay();return A.setDate(A.getDate()-D),A}case"MONTHLY":return A.setHours(0,0,0,0),A.setDate(1),A;case"YEARLY":return A.setHours(0,0,0,0),A.setMonth(0,1),A}}function $(z,B){let A=Z(z,B);switch(B){case"HOURLY":return new Date(A.getTime()+3600000);case"DAILY":return new Date(A.getTime()+86400000);case"WEEKLY":return new Date(A.getTime()+604800000);case"MONTHLY":{let D=new Date(A);return D.setMonth(D.getMonth()+1),D}case"YEARLY":{let D=new Date(A);return D.setFullYear(D.getFullYear()+1),D}}}function C(z,B){let A=Z(z,B),D=A.getFullYear(),F=String(A.getMonth()+1).padStart(2,"0"),G=String(A.getDate()).padStart(2,"0"),H=String(A.getHours()).padStart(2,"0");switch(B){case"HOURLY":return`${D}-${F}-${G}T${H}`;case"DAILY":return`${D}-${F}-${G}`;case"WEEKLY":return`${D}-W${M(A)}`;case"MONTHLY":return`${D}-${F}`;case"YEARLY":return`${D}`}}function M(z){let B=new Date(Date.UTC(z.getFullYear(),z.getMonth(),z.getDate())),A=B.getUTCDay()||7;B.setUTCDate(B.getUTCDate()+4-A);let D=new Date(Date.UTC(B.getUTCFullYear(),0,1)),F=Math.ceil(((B.getTime()-D.getTime())/86400000+1)/7);return String(F).padStart(2,"0")}class U{storage;batchSize;constructor(z){this.storage=z.storage,this.batchSize=z.batchSize||1000}async aggregate(z){let{periodType:B,periodStart:A,metricKey:D}=z,F=z.periodEnd||$(A,B),G={periodType:B,periodStart:A,periodEnd:F,recordsProcessed:0,summariesCreated:0,summariesUpdated:0,errors:[]},H=await this.storage.getUnaggregatedRecords({metricKey:D,periodStart:A,periodEnd:F,limit:this.batchSize});if(H.length===0)return G;let J=this.groupRecords(H,B);for(let[L,_]of J.entries())try{await this.aggregateGroup(L,_,B,G)}catch(V){let[X,O,Y]=L.split("::");G.errors.push({metricKey:X??"unknown",subjectType:O??"unknown",subjectId:Y??"unknown",error:V instanceof Error?V.message:String(V)})}let Q=H.map((L)=>L.id);return await this.storage.markRecordsAggregated(Q,new Date),G.recordsProcessed=H.length,G}groupRecords(z,B){let A=new Map;for(let D of z){let F=C(D.timestamp,B),G=`${D.metricKey}::${D.subjectType}::${D.subjectId}::${F}`,H=A.get(G)||[];H.push(D),A.set(G,H)}return A}async aggregateGroup(z,B,A,D){let[F,G,H]=z.split("::");if(!F||!G||!H||B.length===0)return;let J=B[0];if(!J)return;let Q=Z(J.timestamp,A),L=$(J.timestamp,A),V=(await this.storage.getMetric(F))?.aggregationType||"SUM",X=B.map((Y)=>Y.quantity),O=this.calculateAggregation(X,V);await this.storage.upsertSummary({metricKey:F,subjectType:G,subjectId:H,periodType:A,periodStart:Q,periodEnd:L,totalQuantity:O.total,recordCount:B.length,minQuantity:O.min,maxQuantity:O.max,avgQuantity:O.avg}),D.summariesCreated++}calculateAggregation(z,B){if(z.length===0)return{total:0,min:0,max:0,avg:0};let A=Math.min(...z),D=Math.max(...z),F=z.reduce((Q,L)=>Q+L,0),G=F/z.length,H=z.length,J;switch(B){case"COUNT":J=H;break;case"SUM":J=F;break;case"AVG":J=G;break;case"MAX":J=D;break;case"MIN":J=A;break;case"LAST":J=z[z.length-1]??0;break;default:J=F}return{total:J,min:A,max:D,avg:G}}}class W{records=[];summaries=new Map;metrics=new Map;addRecord(z){this.records.push(z)}addMetric(z){this.metrics.set(z.key,z)}async getUnaggregatedRecords(z){let B=this.records.filter((A)=>{let D=A.timestamp>=z.periodStart&&A.timestamp<z.periodEnd,F=!z.metricKey||A.metricKey===z.metricKey;return D&&F});if(z.limit)B=B.slice(0,z.limit);return B}async markRecordsAggregated(z){this.records=this.records.filter((B)=>!z.includes(B.id))}async upsertSummary(z){let B=`${z.metricKey}::${z.subjectType}::${z.subjectId}::${z.periodType}::${z.periodStart.toISOString()}`,A=this.summaries.get(B);if(A){if(A.totalQuantity+=z.totalQuantity,A.recordCount+=z.recordCount,z.minQuantity!==void 0)A.minQuantity=Math.min(A.minQuantity??1/0,z.minQuantity);if(z.maxQuantity!==void 0)A.maxQuantity=Math.max(A.maxQuantity??-1/0,z.maxQuantity);return A}let D={id:`summary-${Date.now()}-${Math.random().toString(36).slice(2)}`,...z};return this.summaries.set(B,D),D}async getMetric(z){return this.metrics.get(z)||null}async listMetrics(){return Array.from(this.metrics.values())}getSummaries(){return Array.from(this.summaries.values())}clear(){this.records=[],this.summaries.clear(),this.metrics.clear()}}export{Z as getPeriodStart,$ as getPeriodEnd,C as formatPeriodKey,U as UsageAggregator,W as InMemoryUsageStorage};
|
|
@@ -1,266 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
constructor(reader, options = {}) {
|
|
6
|
-
this.reader = reader;
|
|
7
|
-
this.eventPrefix = options.eventPrefix ?? "metering";
|
|
8
|
-
}
|
|
9
|
-
async getUsageByMetric(input) {
|
|
10
|
-
const result = await this.queryHogQL({
|
|
11
|
-
query: [
|
|
12
|
-
"select",
|
|
13
|
-
" properties.recordId as recordId,",
|
|
14
|
-
" properties.metricKey as metricKey,",
|
|
15
|
-
" properties.subjectType as subjectType,",
|
|
16
|
-
" distinct_id as subjectId,",
|
|
17
|
-
" properties.quantity as quantity,",
|
|
18
|
-
" properties.source as source,",
|
|
19
|
-
" timestamp as timestamp",
|
|
20
|
-
"from events",
|
|
21
|
-
`where ${buildUsageWhereClause(this.eventPrefix, input)}`,
|
|
22
|
-
"order by timestamp desc",
|
|
23
|
-
`limit ${input.limit ?? 200}`
|
|
24
|
-
].join(`
|
|
25
|
-
`),
|
|
26
|
-
values: buildUsageValues(input)
|
|
27
|
-
});
|
|
28
|
-
return mapUsageRecords(result);
|
|
29
|
-
}
|
|
30
|
-
async getUsageSummary(input) {
|
|
31
|
-
const result = await this.queryHogQL({
|
|
32
|
-
query: [
|
|
33
|
-
"select",
|
|
34
|
-
" properties.summaryId as summaryId,",
|
|
35
|
-
" properties.metricKey as metricKey,",
|
|
36
|
-
" properties.subjectType as subjectType,",
|
|
37
|
-
" distinct_id as subjectId,",
|
|
38
|
-
" properties.periodType as periodType,",
|
|
39
|
-
" properties.periodStart as periodStart,",
|
|
40
|
-
" properties.periodEnd as periodEnd,",
|
|
41
|
-
" properties.totalQuantity as totalQuantity,",
|
|
42
|
-
" properties.recordCount as recordCount,",
|
|
43
|
-
" timestamp as aggregatedAt",
|
|
44
|
-
"from events",
|
|
45
|
-
`where ${buildSummaryWhereClause(this.eventPrefix, input)}`,
|
|
46
|
-
"order by timestamp desc",
|
|
47
|
-
`limit ${input.limit ?? 200}`
|
|
48
|
-
].join(`
|
|
49
|
-
`),
|
|
50
|
-
values: buildSummaryValues(input)
|
|
51
|
-
});
|
|
52
|
-
return mapUsageSummaries(result);
|
|
53
|
-
}
|
|
54
|
-
async getUsageTrend(input) {
|
|
55
|
-
const result = await this.queryHogQL({
|
|
56
|
-
query: [
|
|
57
|
-
"select",
|
|
58
|
-
` ${bucketExpression(input.bucket)} as bucketStart,`,
|
|
59
|
-
" sum(properties.quantity) as totalQuantity,",
|
|
60
|
-
" count() as recordCount",
|
|
61
|
-
"from events",
|
|
62
|
-
`where ${buildUsageWhereClause(this.eventPrefix, input)}`,
|
|
63
|
-
"group by bucketStart",
|
|
64
|
-
"order by bucketStart asc"
|
|
65
|
-
].join(`
|
|
66
|
-
`),
|
|
67
|
-
values: buildUsageValues(input)
|
|
68
|
-
});
|
|
69
|
-
return mapUsageTrend(result);
|
|
70
|
-
}
|
|
71
|
-
async queryHogQL(input) {
|
|
72
|
-
if (!this.reader.queryHogQL) {
|
|
73
|
-
throw new Error("Analytics reader does not support HogQL queries.");
|
|
74
|
-
}
|
|
75
|
-
return this.reader.queryHogQL(input);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function buildUsageWhereClause(eventPrefix, input) {
|
|
79
|
-
const clauses = [
|
|
80
|
-
`event = '${eventPrefix}.usage_recorded'`,
|
|
81
|
-
`properties.metricKey = {metricKey}`
|
|
82
|
-
];
|
|
83
|
-
if (input.subjectId) {
|
|
84
|
-
clauses.push("distinct_id = {subjectId}");
|
|
85
|
-
}
|
|
86
|
-
if (input.dateRange?.from) {
|
|
87
|
-
clauses.push("timestamp >= {dateFrom}");
|
|
88
|
-
}
|
|
89
|
-
if (input.dateRange?.to) {
|
|
90
|
-
clauses.push("timestamp < {dateTo}");
|
|
91
|
-
}
|
|
92
|
-
return clauses.join(" and ");
|
|
93
|
-
}
|
|
94
|
-
function buildSummaryWhereClause(eventPrefix, input) {
|
|
95
|
-
const clauses = [
|
|
96
|
-
`event = '${eventPrefix}.usage_aggregated'`,
|
|
97
|
-
`properties.metricKey = {metricKey}`
|
|
98
|
-
];
|
|
99
|
-
if (input.subjectId) {
|
|
100
|
-
clauses.push("distinct_id = {subjectId}");
|
|
101
|
-
}
|
|
102
|
-
if (input.periodType) {
|
|
103
|
-
clauses.push("properties.periodType = {periodType}");
|
|
104
|
-
}
|
|
105
|
-
if (input.dateRange?.from) {
|
|
106
|
-
clauses.push("timestamp >= {dateFrom}");
|
|
107
|
-
}
|
|
108
|
-
if (input.dateRange?.to) {
|
|
109
|
-
clauses.push("timestamp < {dateTo}");
|
|
110
|
-
}
|
|
111
|
-
return clauses.join(" and ");
|
|
112
|
-
}
|
|
113
|
-
function buildUsageValues(input) {
|
|
114
|
-
return {
|
|
115
|
-
metricKey: input.metricKey,
|
|
116
|
-
subjectId: input.subjectId,
|
|
117
|
-
dateFrom: toIsoString(input.dateRange?.from),
|
|
118
|
-
dateTo: toIsoString(input.dateRange?.to)
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function buildSummaryValues(input) {
|
|
122
|
-
return {
|
|
123
|
-
metricKey: input.metricKey,
|
|
124
|
-
subjectId: input.subjectId,
|
|
125
|
-
periodType: input.periodType,
|
|
126
|
-
dateFrom: toIsoString(input.dateRange?.from),
|
|
127
|
-
dateTo: toIsoString(input.dateRange?.to)
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function bucketExpression(bucket) {
|
|
131
|
-
switch (bucket) {
|
|
132
|
-
case "hour":
|
|
133
|
-
return "toStartOfHour(timestamp)";
|
|
134
|
-
case "week":
|
|
135
|
-
return "toStartOfWeek(timestamp)";
|
|
136
|
-
case "month":
|
|
137
|
-
return "toStartOfMonth(timestamp)";
|
|
138
|
-
case "day":
|
|
139
|
-
default:
|
|
140
|
-
return "toStartOfDay(timestamp)";
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function mapUsageRecords(result) {
|
|
144
|
-
const rows = mapRows(result);
|
|
145
|
-
return rows.flatMap((row) => {
|
|
146
|
-
const metricKey = asString(row.metricKey);
|
|
147
|
-
const subjectType = asString(row.subjectType);
|
|
148
|
-
const subjectId = asString(row.subjectId);
|
|
149
|
-
const timestamp = asDate(row.timestamp);
|
|
150
|
-
if (!metricKey || !subjectType || !subjectId || !timestamp) {
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
return [
|
|
154
|
-
{
|
|
155
|
-
recordId: asOptionalString(row.recordId),
|
|
156
|
-
metricKey,
|
|
157
|
-
subjectType,
|
|
158
|
-
subjectId,
|
|
159
|
-
quantity: asNumber(row.quantity),
|
|
160
|
-
source: asOptionalString(row.source),
|
|
161
|
-
timestamp
|
|
162
|
-
}
|
|
163
|
-
];
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
function mapUsageSummaries(result) {
|
|
167
|
-
const rows = mapRows(result);
|
|
168
|
-
return rows.flatMap((row) => {
|
|
169
|
-
const metricKey = asString(row.metricKey);
|
|
170
|
-
const subjectType = asString(row.subjectType);
|
|
171
|
-
const subjectId = asString(row.subjectId);
|
|
172
|
-
const periodType = asString(row.periodType);
|
|
173
|
-
const periodStart = asDate(row.periodStart);
|
|
174
|
-
const periodEnd = asDate(row.periodEnd);
|
|
175
|
-
const aggregatedAt = asDate(row.aggregatedAt);
|
|
176
|
-
if (!metricKey || !subjectType || !subjectId || !periodType || !periodStart || !periodEnd || !aggregatedAt) {
|
|
177
|
-
return [];
|
|
178
|
-
}
|
|
179
|
-
return [
|
|
180
|
-
{
|
|
181
|
-
summaryId: asOptionalString(row.summaryId),
|
|
182
|
-
metricKey,
|
|
183
|
-
subjectType,
|
|
184
|
-
subjectId,
|
|
185
|
-
periodType,
|
|
186
|
-
periodStart,
|
|
187
|
-
periodEnd,
|
|
188
|
-
totalQuantity: asNumber(row.totalQuantity),
|
|
189
|
-
recordCount: asNumber(row.recordCount),
|
|
190
|
-
aggregatedAt
|
|
191
|
-
}
|
|
192
|
-
];
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
function mapUsageTrend(result) {
|
|
196
|
-
const rows = mapRows(result);
|
|
197
|
-
return rows.flatMap((row) => {
|
|
198
|
-
const bucketStart = asString(row.bucketStart);
|
|
199
|
-
if (!bucketStart)
|
|
200
|
-
return [];
|
|
201
|
-
return [
|
|
202
|
-
{
|
|
203
|
-
bucketStart,
|
|
204
|
-
totalQuantity: asNumber(row.totalQuantity),
|
|
205
|
-
recordCount: asNumber(row.recordCount)
|
|
206
|
-
}
|
|
207
|
-
];
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
function mapRows(result) {
|
|
211
|
-
if (!Array.isArray(result.results) || !Array.isArray(result.columns)) {
|
|
212
|
-
return [];
|
|
213
|
-
}
|
|
214
|
-
const columns = result.columns;
|
|
215
|
-
return result.results.flatMap((row) => {
|
|
216
|
-
if (!Array.isArray(row))
|
|
217
|
-
return [];
|
|
218
|
-
const record = {};
|
|
219
|
-
columns.forEach((column, index) => {
|
|
220
|
-
record[column] = row[index];
|
|
221
|
-
});
|
|
222
|
-
return [record];
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
function asString(value) {
|
|
226
|
-
if (typeof value === "string" && value.trim())
|
|
227
|
-
return value;
|
|
228
|
-
if (typeof value === "number")
|
|
229
|
-
return String(value);
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
function asOptionalString(value) {
|
|
233
|
-
if (typeof value === "string")
|
|
234
|
-
return value;
|
|
235
|
-
if (typeof value === "number")
|
|
236
|
-
return String(value);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
function asNumber(value) {
|
|
240
|
-
if (typeof value === "number" && Number.isFinite(value))
|
|
241
|
-
return value;
|
|
242
|
-
if (typeof value === "string" && value.trim()) {
|
|
243
|
-
const parsed = Number(value);
|
|
244
|
-
if (Number.isFinite(parsed))
|
|
245
|
-
return parsed;
|
|
246
|
-
}
|
|
247
|
-
return 0;
|
|
248
|
-
}
|
|
249
|
-
function asDate(value) {
|
|
250
|
-
if (value instanceof Date)
|
|
251
|
-
return value;
|
|
252
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
253
|
-
const date = new Date(value);
|
|
254
|
-
if (!Number.isNaN(date.getTime()))
|
|
255
|
-
return date;
|
|
256
|
-
}
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
function toIsoString(value) {
|
|
260
|
-
if (!value)
|
|
261
|
-
return;
|
|
262
|
-
return typeof value === "string" ? value : value.toISOString();
|
|
263
|
-
}
|
|
264
|
-
export {
|
|
265
|
-
PosthogMeteringReader
|
|
266
|
-
};
|
|
1
|
+
class R{reader;eventPrefix;constructor(e,r={}){this.reader=e,this.eventPrefix=r.eventPrefix??"metering"}async getUsageByMetric(e){let r=await this.queryHogQL({query:["select"," properties.recordId as recordId,"," properties.metricKey as metricKey,"," properties.subjectType as subjectType,"," distinct_id as subjectId,"," properties.quantity as quantity,"," properties.source as source,"," timestamp as timestamp","from events",`where ${b(this.eventPrefix,e)}`,"order by timestamp desc",`limit ${e.limit??200}`].join(`
|
|
2
|
+
`),values:l(e)});return j(r)}async getUsageSummary(e){let r=await this.queryHogQL({query:["select"," properties.summaryId as summaryId,"," properties.metricKey as metricKey,"," properties.subjectType as subjectType,"," distinct_id as subjectId,"," properties.periodType as periodType,"," properties.periodStart as periodStart,"," properties.periodEnd as periodEnd,"," properties.totalQuantity as totalQuantity,"," properties.recordCount as recordCount,"," timestamp as aggregatedAt","from events",`where ${I(this.eventPrefix,e)}`,"order by timestamp desc",`limit ${e.limit??200}`].join(`
|
|
3
|
+
`),values:T(e)});return S(r)}async getUsageTrend(e){let r=await this.queryHogQL({query:["select",` ${h(e.bucket)} as bucketStart,`," sum(properties.quantity) as totalQuantity,"," count() as recordCount","from events",`where ${b(this.eventPrefix,e)}`,"group by bucketStart","order by bucketStart asc"].join(`
|
|
4
|
+
`),values:l(e)});return U(r)}async queryHogQL(e){if(!this.reader.queryHogQL)throw Error("Analytics reader does not support HogQL queries.");return this.reader.queryHogQL(e)}}function b(e,r){let t=[`event = '${e}.usage_recorded'`,"properties.metricKey = {metricKey}"];if(r.subjectId)t.push("distinct_id = {subjectId}");if(r.dateRange?.from)t.push("timestamp >= {dateFrom}");if(r.dateRange?.to)t.push("timestamp < {dateTo}");return t.join(" and ")}function I(e,r){let t=[`event = '${e}.usage_aggregated'`,"properties.metricKey = {metricKey}"];if(r.subjectId)t.push("distinct_id = {subjectId}");if(r.periodType)t.push("properties.periodType = {periodType}");if(r.dateRange?.from)t.push("timestamp >= {dateFrom}");if(r.dateRange?.to)t.push("timestamp < {dateTo}");return t.join(" and ")}function l(e){return{metricKey:e.metricKey,subjectId:e.subjectId,dateFrom:d(e.dateRange?.from),dateTo:d(e.dateRange?.to)}}function T(e){return{metricKey:e.metricKey,subjectId:e.subjectId,periodType:e.periodType,dateFrom:d(e.dateRange?.from),dateTo:d(e.dateRange?.to)}}function h(e){switch(e){case"hour":return"toStartOfHour(timestamp)";case"week":return"toStartOfWeek(timestamp)";case"month":return"toStartOfMonth(timestamp)";case"day":default:return"toStartOfDay(timestamp)"}}function j(e){return p(e).flatMap((t)=>{let n=s(t.metricKey),i=s(t.subjectType),a=s(t.subjectId),o=c(t.timestamp);if(!n||!i||!a||!o)return[];return[{recordId:g(t.recordId),metricKey:n,subjectType:i,subjectId:a,quantity:u(t.quantity),source:g(t.source),timestamp:o}]})}function S(e){return p(e).flatMap((t)=>{let n=s(t.metricKey),i=s(t.subjectType),a=s(t.subjectId),o=s(t.periodType),m=c(t.periodStart),y=c(t.periodEnd),f=c(t.aggregatedAt);if(!n||!i||!a||!o||!m||!y||!f)return[];return[{summaryId:g(t.summaryId),metricKey:n,subjectType:i,subjectId:a,periodType:o,periodStart:m,periodEnd:y,totalQuantity:u(t.totalQuantity),recordCount:u(t.recordCount),aggregatedAt:f}]})}function U(e){return p(e).flatMap((t)=>{let n=s(t.bucketStart);if(!n)return[];return[{bucketStart:n,totalQuantity:u(t.totalQuantity),recordCount:u(t.recordCount)}]})}function p(e){if(!Array.isArray(e.results)||!Array.isArray(e.columns))return[];let r=e.columns;return e.results.flatMap((t)=>{if(!Array.isArray(t))return[];let n={};return r.forEach((i,a)=>{n[i]=t[a]}),[n]})}function s(e){if(typeof e==="string"&&e.trim())return e;if(typeof e==="number")return String(e);return null}function g(e){if(typeof e==="string")return e;if(typeof e==="number")return String(e);return}function u(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim()){let r=Number(e);if(Number.isFinite(r))return r}return 0}function c(e){if(e instanceof Date)return e;if(typeof e==="string"||typeof e==="number"){let r=new Date(e);if(!Number.isNaN(r.getTime()))return r}return null}function d(e){if(!e)return;return typeof e==="string"?e:e.toISOString()}export{R as PosthogMeteringReader};
|
|
@@ -1,45 +1 @@
|
|
|
1
|
-
|
|
2
|
-
class PosthogMeteringReporter {
|
|
3
|
-
provider;
|
|
4
|
-
eventPrefix;
|
|
5
|
-
includeSource;
|
|
6
|
-
constructor(provider, options = {}) {
|
|
7
|
-
this.provider = provider;
|
|
8
|
-
this.eventPrefix = options.eventPrefix ?? "metering";
|
|
9
|
-
this.includeSource = options.includeSource ?? true;
|
|
10
|
-
}
|
|
11
|
-
async captureUsageRecorded(record) {
|
|
12
|
-
await this.provider.capture({
|
|
13
|
-
distinctId: record.subjectId,
|
|
14
|
-
event: `${this.eventPrefix}.usage_recorded`,
|
|
15
|
-
timestamp: record.timestamp,
|
|
16
|
-
properties: {
|
|
17
|
-
recordId: record.recordId ?? null,
|
|
18
|
-
metricKey: record.metricKey,
|
|
19
|
-
subjectType: record.subjectType,
|
|
20
|
-
quantity: record.quantity,
|
|
21
|
-
...this.includeSource && record.source ? { source: record.source } : {}
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
async captureUsageAggregated(summary) {
|
|
26
|
-
await this.provider.capture({
|
|
27
|
-
distinctId: summary.subjectId,
|
|
28
|
-
event: `${this.eventPrefix}.usage_aggregated`,
|
|
29
|
-
timestamp: summary.aggregatedAt,
|
|
30
|
-
properties: {
|
|
31
|
-
summaryId: summary.summaryId ?? null,
|
|
32
|
-
metricKey: summary.metricKey,
|
|
33
|
-
subjectType: summary.subjectType,
|
|
34
|
-
periodType: summary.periodType,
|
|
35
|
-
periodStart: summary.periodStart.toISOString(),
|
|
36
|
-
periodEnd: summary.periodEnd.toISOString(),
|
|
37
|
-
totalQuantity: summary.totalQuantity,
|
|
38
|
-
recordCount: summary.recordCount
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
export {
|
|
44
|
-
PosthogMeteringReporter
|
|
45
|
-
};
|
|
1
|
+
class r{provider;eventPrefix;includeSource;constructor(e,t={}){this.provider=e,this.eventPrefix=t.eventPrefix??"metering",this.includeSource=t.includeSource??!0}async captureUsageRecorded(e){await this.provider.capture({distinctId:e.subjectId,event:`${this.eventPrefix}.usage_recorded`,timestamp:e.timestamp,properties:{recordId:e.recordId??null,metricKey:e.metricKey,subjectType:e.subjectType,quantity:e.quantity,...this.includeSource&&e.source?{source:e.source}:{}}})}async captureUsageAggregated(e){await this.provider.capture({distinctId:e.subjectId,event:`${this.eventPrefix}.usage_aggregated`,timestamp:e.aggregatedAt,properties:{summaryId:e.summaryId??null,metricKey:e.metricKey,subjectType:e.subjectType,periodType:e.periodType,periodStart:e.periodStart.toISOString(),periodEnd:e.periodEnd.toISOString(),totalQuantity:e.totalQuantity,recordCount:e.recordCount}})}}export{r as PosthogMeteringReporter};
|