@contractspec/lib.metering 1.56.1 → 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/aggregation/index.d.ts +119 -122
- package/dist/aggregation/index.d.ts.map +1 -1
- package/dist/aggregation/index.js +257 -265
- package/dist/analytics/posthog-metering-reader.d.ts +39 -0
- package/dist/analytics/posthog-metering-reader.d.ts.map +1 -0
- package/dist/analytics/posthog-metering-reader.js +267 -0
- package/dist/analytics/posthog-metering.d.ts +35 -0
- package/dist/analytics/posthog-metering.d.ts.map +1 -0
- package/dist/analytics/posthog-metering.js +46 -0
- package/dist/browser/aggregation/index.js +265 -0
- package/dist/browser/analytics/posthog-metering-reader.js +266 -0
- package/dist/browser/analytics/posthog-metering.js +45 -0
- package/dist/browser/contracts/index.js +617 -0
- package/dist/browser/docs/index.js +62 -0
- package/dist/browser/docs/metering.docblock.js +62 -0
- package/dist/browser/entities/index.js +350 -0
- package/dist/browser/events.js +228 -0
- package/dist/browser/index.js +1879 -0
- package/dist/browser/metering.capability.js +28 -0
- package/dist/browser/metering.feature.js +52 -0
- package/dist/contracts/index.d.ts +1076 -1082
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +616 -1030
- package/dist/docs/index.d.ts +2 -1
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +63 -1
- package/dist/docs/metering.docblock.d.ts +2 -1
- package/dist/docs/metering.docblock.d.ts.map +1 -0
- package/dist/docs/metering.docblock.js +18 -22
- package/dist/entities/index.d.ts +186 -191
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +340 -407
- package/dist/events.d.ts +411 -417
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +226 -414
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1880 -8
- package/dist/metering.capability.d.ts +2 -7
- package/dist/metering.capability.d.ts.map +1 -1
- package/dist/metering.capability.js +29 -33
- package/dist/metering.feature.d.ts +1 -7
- package/dist/metering.feature.d.ts.map +1 -1
- package/dist/metering.feature.js +51 -132
- package/dist/node/aggregation/index.js +265 -0
- package/dist/node/analytics/posthog-metering-reader.js +266 -0
- package/dist/node/analytics/posthog-metering.js +45 -0
- package/dist/node/contracts/index.js +617 -0
- package/dist/node/docs/index.js +62 -0
- package/dist/node/docs/metering.docblock.js +62 -0
- package/dist/node/entities/index.js +350 -0
- package/dist/node/events.js +228 -0
- package/dist/node/index.js +1879 -0
- package/dist/node/metering.capability.js +28 -0
- package/dist/node/metering.feature.js +52 -0
- package/package.json +133 -30
- package/dist/aggregation/index.js.map +0 -1
- package/dist/contracts/index.js.map +0 -1
- package/dist/docs/metering.docblock.js.map +0 -1
- package/dist/entities/index.js.map +0 -1
- package/dist/events.js.map +0 -1
- package/dist/metering.capability.js.map +0 -1
- package/dist/metering.feature.js.map +0 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/docs/metering.docblock.ts
|
|
2
|
+
import { registerDocBlocks } from "@contractspec/lib.contracts/docs";
|
|
3
|
+
var meteringDocBlocks = [
|
|
4
|
+
{
|
|
5
|
+
id: "docs.metering.usage",
|
|
6
|
+
title: "Usage Metering & Billing Core",
|
|
7
|
+
summary: "Reusable usage/metering layer with metric definitions, usage ingestion, aggregation, thresholds, and alerts for billing or quotas.",
|
|
8
|
+
kind: "reference",
|
|
9
|
+
visibility: "public",
|
|
10
|
+
route: "/docs/metering/usage",
|
|
11
|
+
tags: ["metering", "usage", "billing", "quotas"],
|
|
12
|
+
body: `## Capabilities
|
|
13
|
+
|
|
14
|
+
- **Entities**: MetricDefinition, UsageRecord, UsageSummary, UsageThreshold, UsageAlert.
|
|
15
|
+
- **Contracts**: define/list metrics; record usage (batch + idempotent); retrieve usage by subject; manage thresholds and alerts.
|
|
16
|
+
- **Aggregation**: hourly/daily/weekly/monthly rollups with SUM/COUNT/AVG/MIN/MAX/LAST strategies.
|
|
17
|
+
- **Events**: usage.recorded, usage.aggregated, threshold.exceeded (see events export).
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
1) Compose schema
|
|
22
|
+
- Add \`meteringSchemaContribution\` to your schema composition.
|
|
23
|
+
|
|
24
|
+
2) Register contracts/events
|
|
25
|
+
- Import from \`@contractspec/lib.metering\` into your spec registry.
|
|
26
|
+
|
|
27
|
+
3) Ingest usage
|
|
28
|
+
- Use \`recordUsage\` contract (see contracts export) from application services whenever a billable/important action happens (e.g., agent run, API call).
|
|
29
|
+
|
|
30
|
+
4) Aggregate + thresholds
|
|
31
|
+
- Run scheduled jobs using \`aggregateUsage\` helper to roll up summaries.
|
|
32
|
+
- Configure \`UsageThreshold\` to emit alerts or blocking actions; pipe alerts into Notifications/Audit.
|
|
33
|
+
- Use \`PosthogMeteringReader\` to backfill or validate usage from PostHog events.
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
|
|
37
|
+
${"```"}ts
|
|
38
|
+
import { meteringSchemaContribution } from '@contractspec/lib.metering';
|
|
39
|
+
import { aggregateUsage } from '@contractspec/lib.metering/aggregation';
|
|
40
|
+
|
|
41
|
+
// schema composition
|
|
42
|
+
const schema = {
|
|
43
|
+
modules: [meteringSchemaContribution],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// aggregation job
|
|
47
|
+
await aggregateUsage({
|
|
48
|
+
metrics: metricRepository,
|
|
49
|
+
usage: usageRepository,
|
|
50
|
+
period: 'DAILY',
|
|
51
|
+
});
|
|
52
|
+
${"```"},
|
|
53
|
+
|
|
54
|
+
## Guardrails
|
|
55
|
+
|
|
56
|
+
- Keep metric keys stable; store quantities as decimals for currency/units.
|
|
57
|
+
- Use idempotency keys for external ingestion; avoid PII in metric metadata.
|
|
58
|
+
- Scope by org/user for multi-tenant isolation; emit audit + analytics events on changes.
|
|
59
|
+
`
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
registerDocBlocks(meteringDocBlocks);
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// src/entities/index.ts
|
|
2
|
+
import {
|
|
3
|
+
defineEntity,
|
|
4
|
+
defineEntityEnum,
|
|
5
|
+
field,
|
|
6
|
+
index
|
|
7
|
+
} from "@contractspec/lib.schema";
|
|
8
|
+
var AggregationTypeEnum = defineEntityEnum({
|
|
9
|
+
name: "AggregationType",
|
|
10
|
+
values: ["COUNT", "SUM", "AVG", "MAX", "MIN", "LAST"],
|
|
11
|
+
schema: "lssm_metering",
|
|
12
|
+
description: "How to aggregate metric values."
|
|
13
|
+
});
|
|
14
|
+
var ResetPeriodEnum = defineEntityEnum({
|
|
15
|
+
name: "ResetPeriod",
|
|
16
|
+
values: ["NEVER", "HOURLY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY"],
|
|
17
|
+
schema: "lssm_metering",
|
|
18
|
+
description: "When to reset metric counters."
|
|
19
|
+
});
|
|
20
|
+
var PeriodTypeEnum = defineEntityEnum({
|
|
21
|
+
name: "PeriodType",
|
|
22
|
+
values: ["HOURLY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY"],
|
|
23
|
+
schema: "lssm_metering",
|
|
24
|
+
description: "Time period for aggregation."
|
|
25
|
+
});
|
|
26
|
+
var ThresholdActionEnum = defineEntityEnum({
|
|
27
|
+
name: "ThresholdAction",
|
|
28
|
+
values: ["NONE", "ALERT", "WARN", "BLOCK", "DOWNGRADE"],
|
|
29
|
+
schema: "lssm_metering",
|
|
30
|
+
description: "Action to take when threshold is exceeded."
|
|
31
|
+
});
|
|
32
|
+
var MetricDefinitionEntity = defineEntity({
|
|
33
|
+
name: "MetricDefinition",
|
|
34
|
+
description: "Definition of a usage metric.",
|
|
35
|
+
schema: "lssm_metering",
|
|
36
|
+
map: "metric_definition",
|
|
37
|
+
fields: {
|
|
38
|
+
id: field.id({ description: "Unique identifier" }),
|
|
39
|
+
key: field.string({
|
|
40
|
+
isUnique: true,
|
|
41
|
+
description: "Metric key (e.g., api_calls, storage_gb)"
|
|
42
|
+
}),
|
|
43
|
+
name: field.string({ description: "Human-readable name" }),
|
|
44
|
+
description: field.string({
|
|
45
|
+
isOptional: true,
|
|
46
|
+
description: "Metric description"
|
|
47
|
+
}),
|
|
48
|
+
unit: field.string({
|
|
49
|
+
description: "Unit of measurement (calls, bytes, etc.)"
|
|
50
|
+
}),
|
|
51
|
+
aggregationType: field.enum("AggregationType", {
|
|
52
|
+
default: "SUM",
|
|
53
|
+
description: "How to aggregate values"
|
|
54
|
+
}),
|
|
55
|
+
resetPeriod: field.enum("ResetPeriod", {
|
|
56
|
+
default: "MONTHLY",
|
|
57
|
+
description: "When to reset counters"
|
|
58
|
+
}),
|
|
59
|
+
precision: field.int({ default: 2, description: "Decimal precision" }),
|
|
60
|
+
orgId: field.string({
|
|
61
|
+
isOptional: true,
|
|
62
|
+
description: "Organization scope (null = global metric)"
|
|
63
|
+
}),
|
|
64
|
+
category: field.string({
|
|
65
|
+
isOptional: true,
|
|
66
|
+
description: "Category for grouping"
|
|
67
|
+
}),
|
|
68
|
+
displayOrder: field.int({ default: 0, description: "Order for display" }),
|
|
69
|
+
metadata: field.json({
|
|
70
|
+
isOptional: true,
|
|
71
|
+
description: "Additional metadata"
|
|
72
|
+
}),
|
|
73
|
+
isActive: field.boolean({
|
|
74
|
+
default: true,
|
|
75
|
+
description: "Whether metric is active"
|
|
76
|
+
}),
|
|
77
|
+
createdAt: field.createdAt(),
|
|
78
|
+
updatedAt: field.updatedAt(),
|
|
79
|
+
usageRecords: field.hasMany("UsageRecord"),
|
|
80
|
+
usageSummaries: field.hasMany("UsageSummary"),
|
|
81
|
+
thresholds: field.hasMany("UsageThreshold")
|
|
82
|
+
},
|
|
83
|
+
indexes: [
|
|
84
|
+
index.on(["orgId", "key"]),
|
|
85
|
+
index.on(["category"]),
|
|
86
|
+
index.on(["isActive"])
|
|
87
|
+
],
|
|
88
|
+
enums: [AggregationTypeEnum, ResetPeriodEnum]
|
|
89
|
+
});
|
|
90
|
+
var UsageRecordEntity = defineEntity({
|
|
91
|
+
name: "UsageRecord",
|
|
92
|
+
description: "A single usage event.",
|
|
93
|
+
schema: "lssm_metering",
|
|
94
|
+
map: "usage_record",
|
|
95
|
+
fields: {
|
|
96
|
+
id: field.id({ description: "Unique identifier" }),
|
|
97
|
+
metricKey: field.string({ description: "Metric being recorded" }),
|
|
98
|
+
metricId: field.string({
|
|
99
|
+
isOptional: true,
|
|
100
|
+
description: "Metric ID (for FK)"
|
|
101
|
+
}),
|
|
102
|
+
subjectType: field.string({
|
|
103
|
+
description: "Subject type (org, user, project)"
|
|
104
|
+
}),
|
|
105
|
+
subjectId: field.string({ description: "Subject identifier" }),
|
|
106
|
+
quantity: field.decimal({ description: "Usage quantity" }),
|
|
107
|
+
source: field.string({
|
|
108
|
+
isOptional: true,
|
|
109
|
+
description: "Source of usage (endpoint, feature, etc.)"
|
|
110
|
+
}),
|
|
111
|
+
resourceId: field.string({
|
|
112
|
+
isOptional: true,
|
|
113
|
+
description: "Related resource ID"
|
|
114
|
+
}),
|
|
115
|
+
resourceType: field.string({
|
|
116
|
+
isOptional: true,
|
|
117
|
+
description: "Related resource type"
|
|
118
|
+
}),
|
|
119
|
+
metadata: field.json({
|
|
120
|
+
isOptional: true,
|
|
121
|
+
description: "Additional context"
|
|
122
|
+
}),
|
|
123
|
+
idempotencyKey: field.string({
|
|
124
|
+
isOptional: true,
|
|
125
|
+
description: "Idempotency key for deduplication"
|
|
126
|
+
}),
|
|
127
|
+
timestamp: field.dateTime({ description: "When usage occurred" }),
|
|
128
|
+
createdAt: field.createdAt(),
|
|
129
|
+
aggregated: field.boolean({
|
|
130
|
+
default: false,
|
|
131
|
+
description: "Whether included in summary"
|
|
132
|
+
}),
|
|
133
|
+
aggregatedAt: field.dateTime({
|
|
134
|
+
isOptional: true,
|
|
135
|
+
description: "When aggregated"
|
|
136
|
+
})
|
|
137
|
+
},
|
|
138
|
+
indexes: [
|
|
139
|
+
index.on(["metricKey", "subjectType", "subjectId", "timestamp"]),
|
|
140
|
+
index.on(["subjectType", "subjectId", "timestamp"]),
|
|
141
|
+
index.on(["timestamp"]),
|
|
142
|
+
index.on(["aggregated", "timestamp"]),
|
|
143
|
+
index.unique(["idempotencyKey"], { name: "usage_record_idempotency" })
|
|
144
|
+
]
|
|
145
|
+
});
|
|
146
|
+
var UsageSummaryEntity = defineEntity({
|
|
147
|
+
name: "UsageSummary",
|
|
148
|
+
description: "Pre-aggregated usage summary.",
|
|
149
|
+
schema: "lssm_metering",
|
|
150
|
+
map: "usage_summary",
|
|
151
|
+
fields: {
|
|
152
|
+
id: field.id({ description: "Unique identifier" }),
|
|
153
|
+
metricKey: field.string({ description: "Metric key" }),
|
|
154
|
+
metricId: field.string({
|
|
155
|
+
isOptional: true,
|
|
156
|
+
description: "Metric ID (for FK)"
|
|
157
|
+
}),
|
|
158
|
+
subjectType: field.string({ description: "Subject type" }),
|
|
159
|
+
subjectId: field.string({ description: "Subject identifier" }),
|
|
160
|
+
periodType: field.enum("PeriodType", { description: "Period type" }),
|
|
161
|
+
periodStart: field.dateTime({ description: "Period start time" }),
|
|
162
|
+
periodEnd: field.dateTime({ description: "Period end time" }),
|
|
163
|
+
totalQuantity: field.decimal({ description: "Total/aggregated quantity" }),
|
|
164
|
+
recordCount: field.int({
|
|
165
|
+
default: 0,
|
|
166
|
+
description: "Number of records aggregated"
|
|
167
|
+
}),
|
|
168
|
+
minQuantity: field.decimal({
|
|
169
|
+
isOptional: true,
|
|
170
|
+
description: "Minimum value"
|
|
171
|
+
}),
|
|
172
|
+
maxQuantity: field.decimal({
|
|
173
|
+
isOptional: true,
|
|
174
|
+
description: "Maximum value"
|
|
175
|
+
}),
|
|
176
|
+
avgQuantity: field.decimal({
|
|
177
|
+
isOptional: true,
|
|
178
|
+
description: "Average value"
|
|
179
|
+
}),
|
|
180
|
+
metadata: field.json({
|
|
181
|
+
isOptional: true,
|
|
182
|
+
description: "Additional metadata"
|
|
183
|
+
}),
|
|
184
|
+
createdAt: field.createdAt(),
|
|
185
|
+
updatedAt: field.updatedAt()
|
|
186
|
+
},
|
|
187
|
+
indexes: [
|
|
188
|
+
index.unique(["metricKey", "subjectType", "subjectId", "periodType", "periodStart"], { name: "usage_summary_unique" }),
|
|
189
|
+
index.on(["subjectType", "subjectId", "periodType", "periodStart"]),
|
|
190
|
+
index.on(["metricKey", "periodType", "periodStart"])
|
|
191
|
+
],
|
|
192
|
+
enums: [PeriodTypeEnum]
|
|
193
|
+
});
|
|
194
|
+
var UsageThresholdEntity = defineEntity({
|
|
195
|
+
name: "UsageThreshold",
|
|
196
|
+
description: "Usage threshold configuration.",
|
|
197
|
+
schema: "lssm_metering",
|
|
198
|
+
map: "usage_threshold",
|
|
199
|
+
fields: {
|
|
200
|
+
id: field.id({ description: "Unique identifier" }),
|
|
201
|
+
metricKey: field.string({ description: "Metric to monitor" }),
|
|
202
|
+
metricId: field.string({
|
|
203
|
+
isOptional: true,
|
|
204
|
+
description: "Metric ID (for FK)"
|
|
205
|
+
}),
|
|
206
|
+
subjectType: field.string({
|
|
207
|
+
isOptional: true,
|
|
208
|
+
description: "Subject type"
|
|
209
|
+
}),
|
|
210
|
+
subjectId: field.string({
|
|
211
|
+
isOptional: true,
|
|
212
|
+
description: "Subject identifier"
|
|
213
|
+
}),
|
|
214
|
+
name: field.string({ description: "Threshold name" }),
|
|
215
|
+
threshold: field.decimal({ description: "Threshold value" }),
|
|
216
|
+
warnThreshold: field.decimal({
|
|
217
|
+
isOptional: true,
|
|
218
|
+
description: "Warning threshold (e.g., 80%)"
|
|
219
|
+
}),
|
|
220
|
+
periodType: field.enum("PeriodType", {
|
|
221
|
+
default: "MONTHLY",
|
|
222
|
+
description: "Period to evaluate"
|
|
223
|
+
}),
|
|
224
|
+
action: field.enum("ThresholdAction", {
|
|
225
|
+
default: "ALERT",
|
|
226
|
+
description: "Action when exceeded"
|
|
227
|
+
}),
|
|
228
|
+
notifyEmails: field.json({
|
|
229
|
+
isOptional: true,
|
|
230
|
+
description: "Email addresses to notify"
|
|
231
|
+
}),
|
|
232
|
+
notifyWebhook: field.string({
|
|
233
|
+
isOptional: true,
|
|
234
|
+
description: "Webhook URL to call"
|
|
235
|
+
}),
|
|
236
|
+
currentValue: field.decimal({
|
|
237
|
+
default: 0,
|
|
238
|
+
description: "Current usage value"
|
|
239
|
+
}),
|
|
240
|
+
lastCheckedAt: field.dateTime({
|
|
241
|
+
isOptional: true,
|
|
242
|
+
description: "Last threshold check"
|
|
243
|
+
}),
|
|
244
|
+
lastExceededAt: field.dateTime({
|
|
245
|
+
isOptional: true,
|
|
246
|
+
description: "Last time threshold was exceeded"
|
|
247
|
+
}),
|
|
248
|
+
isActive: field.boolean({
|
|
249
|
+
default: true,
|
|
250
|
+
description: "Whether threshold is active"
|
|
251
|
+
}),
|
|
252
|
+
metadata: field.json({
|
|
253
|
+
isOptional: true,
|
|
254
|
+
description: "Additional metadata"
|
|
255
|
+
}),
|
|
256
|
+
createdAt: field.createdAt(),
|
|
257
|
+
updatedAt: field.updatedAt()
|
|
258
|
+
},
|
|
259
|
+
indexes: [
|
|
260
|
+
index.on(["metricKey"]),
|
|
261
|
+
index.on(["subjectType", "subjectId"]),
|
|
262
|
+
index.on(["isActive", "metricKey"])
|
|
263
|
+
],
|
|
264
|
+
enums: [ThresholdActionEnum]
|
|
265
|
+
});
|
|
266
|
+
var UsageAlertEntity = defineEntity({
|
|
267
|
+
name: "UsageAlert",
|
|
268
|
+
description: "Alert generated when threshold is exceeded.",
|
|
269
|
+
schema: "lssm_metering",
|
|
270
|
+
map: "usage_alert",
|
|
271
|
+
fields: {
|
|
272
|
+
id: field.id({ description: "Unique identifier" }),
|
|
273
|
+
thresholdId: field.foreignKey({
|
|
274
|
+
description: "Threshold that triggered alert"
|
|
275
|
+
}),
|
|
276
|
+
metricKey: field.string({ description: "Metric key" }),
|
|
277
|
+
subjectType: field.string({
|
|
278
|
+
isOptional: true,
|
|
279
|
+
description: "Subject type"
|
|
280
|
+
}),
|
|
281
|
+
subjectId: field.string({
|
|
282
|
+
isOptional: true,
|
|
283
|
+
description: "Subject identifier"
|
|
284
|
+
}),
|
|
285
|
+
alertType: field.string({ description: "Alert type (warn, exceed, etc.)" }),
|
|
286
|
+
threshold: field.decimal({ description: "Threshold value" }),
|
|
287
|
+
actualValue: field.decimal({ description: "Actual usage value" }),
|
|
288
|
+
percentageUsed: field.decimal({
|
|
289
|
+
description: "Percentage of threshold used"
|
|
290
|
+
}),
|
|
291
|
+
status: field.string({
|
|
292
|
+
default: '"pending"',
|
|
293
|
+
description: "Alert status (pending, acknowledged, resolved)"
|
|
294
|
+
}),
|
|
295
|
+
acknowledgedBy: field.string({
|
|
296
|
+
isOptional: true,
|
|
297
|
+
description: "User who acknowledged"
|
|
298
|
+
}),
|
|
299
|
+
acknowledgedAt: field.dateTime({
|
|
300
|
+
isOptional: true,
|
|
301
|
+
description: "When acknowledged"
|
|
302
|
+
}),
|
|
303
|
+
resolvedAt: field.dateTime({
|
|
304
|
+
isOptional: true,
|
|
305
|
+
description: "When resolved"
|
|
306
|
+
}),
|
|
307
|
+
notificationsSent: field.json({
|
|
308
|
+
isOptional: true,
|
|
309
|
+
description: "Notifications sent"
|
|
310
|
+
}),
|
|
311
|
+
triggeredAt: field.dateTime({ description: "When alert was triggered" }),
|
|
312
|
+
createdAt: field.createdAt(),
|
|
313
|
+
thresholdRelation: field.belongsTo("UsageThreshold", ["thresholdId"], ["id"], { onDelete: "Cascade" })
|
|
314
|
+
},
|
|
315
|
+
indexes: [
|
|
316
|
+
index.on(["thresholdId", "status"]),
|
|
317
|
+
index.on(["metricKey", "triggeredAt"]),
|
|
318
|
+
index.on(["status", "triggeredAt"])
|
|
319
|
+
]
|
|
320
|
+
});
|
|
321
|
+
var meteringEntities = [
|
|
322
|
+
MetricDefinitionEntity,
|
|
323
|
+
UsageRecordEntity,
|
|
324
|
+
UsageSummaryEntity,
|
|
325
|
+
UsageThresholdEntity,
|
|
326
|
+
UsageAlertEntity
|
|
327
|
+
];
|
|
328
|
+
var meteringSchemaContribution = {
|
|
329
|
+
moduleId: "@contractspec/lib.metering",
|
|
330
|
+
entities: meteringEntities,
|
|
331
|
+
enums: [
|
|
332
|
+
AggregationTypeEnum,
|
|
333
|
+
ResetPeriodEnum,
|
|
334
|
+
PeriodTypeEnum,
|
|
335
|
+
ThresholdActionEnum
|
|
336
|
+
]
|
|
337
|
+
};
|
|
338
|
+
export {
|
|
339
|
+
meteringSchemaContribution,
|
|
340
|
+
meteringEntities,
|
|
341
|
+
UsageThresholdEntity,
|
|
342
|
+
UsageSummaryEntity,
|
|
343
|
+
UsageRecordEntity,
|
|
344
|
+
UsageAlertEntity,
|
|
345
|
+
ThresholdActionEnum,
|
|
346
|
+
ResetPeriodEnum,
|
|
347
|
+
PeriodTypeEnum,
|
|
348
|
+
MetricDefinitionEntity,
|
|
349
|
+
AggregationTypeEnum
|
|
350
|
+
};
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// src/events.ts
|
|
2
|
+
import { ScalarTypeEnum, defineSchemaModel } from "@contractspec/lib.schema";
|
|
3
|
+
import { defineEvent } from "@contractspec/lib.contracts";
|
|
4
|
+
var MetricDefinedPayload = defineSchemaModel({
|
|
5
|
+
name: "MetricDefinedEventPayload",
|
|
6
|
+
description: "Payload when a metric is defined",
|
|
7
|
+
fields: {
|
|
8
|
+
metricId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
9
|
+
key: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
10
|
+
name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
11
|
+
unit: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
12
|
+
aggregationType: {
|
|
13
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
14
|
+
isOptional: false
|
|
15
|
+
},
|
|
16
|
+
orgId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
17
|
+
createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
18
|
+
createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
var MetricUpdatedPayload = defineSchemaModel({
|
|
22
|
+
name: "MetricUpdatedEventPayload",
|
|
23
|
+
description: "Payload when a metric is updated",
|
|
24
|
+
fields: {
|
|
25
|
+
metricId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
26
|
+
key: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
27
|
+
changes: { type: ScalarTypeEnum.JSON(), isOptional: false },
|
|
28
|
+
updatedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
29
|
+
updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
var UsageRecordedPayload = defineSchemaModel({
|
|
33
|
+
name: "UsageRecordedEventPayload",
|
|
34
|
+
description: "Payload when usage is recorded",
|
|
35
|
+
fields: {
|
|
36
|
+
recordId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
37
|
+
metricKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
38
|
+
subjectType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
39
|
+
subjectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
40
|
+
quantity: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
41
|
+
source: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
42
|
+
timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
var UsageBatchRecordedPayload = defineSchemaModel({
|
|
46
|
+
name: "UsageBatchRecordedEventPayload",
|
|
47
|
+
description: "Payload when a batch of usage is recorded",
|
|
48
|
+
fields: {
|
|
49
|
+
recordCount: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
|
|
50
|
+
metricKeys: { type: ScalarTypeEnum.JSON(), isOptional: false },
|
|
51
|
+
totalQuantity: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
52
|
+
timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
var UsageAggregatedPayload = defineSchemaModel({
|
|
56
|
+
name: "UsageAggregatedEventPayload",
|
|
57
|
+
description: "Payload when usage is aggregated",
|
|
58
|
+
fields: {
|
|
59
|
+
summaryId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
60
|
+
metricKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
61
|
+
subjectType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
62
|
+
subjectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
63
|
+
periodType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
64
|
+
periodStart: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
65
|
+
periodEnd: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
66
|
+
totalQuantity: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
67
|
+
recordCount: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
|
|
68
|
+
aggregatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
var ThresholdCreatedPayload = defineSchemaModel({
|
|
72
|
+
name: "ThresholdCreatedEventPayload",
|
|
73
|
+
description: "Payload when a threshold is created",
|
|
74
|
+
fields: {
|
|
75
|
+
thresholdId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
76
|
+
metricKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
77
|
+
subjectType: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
78
|
+
subjectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
79
|
+
threshold: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
80
|
+
action: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
81
|
+
createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
var ThresholdExceededPayload = defineSchemaModel({
|
|
85
|
+
name: "ThresholdExceededEventPayload",
|
|
86
|
+
description: "Payload when a threshold is exceeded",
|
|
87
|
+
fields: {
|
|
88
|
+
alertId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
89
|
+
thresholdId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
90
|
+
metricKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
91
|
+
subjectType: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
92
|
+
subjectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
93
|
+
threshold: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
94
|
+
actualValue: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
95
|
+
percentageUsed: {
|
|
96
|
+
type: ScalarTypeEnum.Float_unsecure(),
|
|
97
|
+
isOptional: false
|
|
98
|
+
},
|
|
99
|
+
action: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
100
|
+
triggeredAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
var ThresholdApproachingPayload = defineSchemaModel({
|
|
104
|
+
name: "ThresholdApproachingEventPayload",
|
|
105
|
+
description: "Payload when usage is approaching a threshold",
|
|
106
|
+
fields: {
|
|
107
|
+
thresholdId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
108
|
+
metricKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
109
|
+
subjectType: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
110
|
+
subjectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
111
|
+
threshold: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
112
|
+
currentValue: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
|
|
113
|
+
percentageUsed: {
|
|
114
|
+
type: ScalarTypeEnum.Float_unsecure(),
|
|
115
|
+
isOptional: false
|
|
116
|
+
},
|
|
117
|
+
triggeredAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
var MetricDefinedEvent = defineEvent({
|
|
121
|
+
meta: {
|
|
122
|
+
key: "metric.defined",
|
|
123
|
+
version: "1.0.0",
|
|
124
|
+
description: "A metric has been defined.",
|
|
125
|
+
stability: "stable",
|
|
126
|
+
owners: ["@platform.metering"],
|
|
127
|
+
tags: ["metering", "metric"]
|
|
128
|
+
},
|
|
129
|
+
payload: MetricDefinedPayload
|
|
130
|
+
});
|
|
131
|
+
var MetricUpdatedEvent = defineEvent({
|
|
132
|
+
meta: {
|
|
133
|
+
key: "metric.updated",
|
|
134
|
+
version: "1.0.0",
|
|
135
|
+
description: "A metric has been updated.",
|
|
136
|
+
stability: "stable",
|
|
137
|
+
owners: ["@platform.metering"],
|
|
138
|
+
tags: ["metering", "metric"]
|
|
139
|
+
},
|
|
140
|
+
payload: MetricUpdatedPayload
|
|
141
|
+
});
|
|
142
|
+
var UsageRecordedEvent = defineEvent({
|
|
143
|
+
meta: {
|
|
144
|
+
key: "usage.recorded",
|
|
145
|
+
version: "1.0.0",
|
|
146
|
+
description: "Usage has been recorded.",
|
|
147
|
+
stability: "stable",
|
|
148
|
+
owners: ["@platform.metering"],
|
|
149
|
+
tags: ["metering", "usage"]
|
|
150
|
+
},
|
|
151
|
+
payload: UsageRecordedPayload
|
|
152
|
+
});
|
|
153
|
+
var UsageBatchRecordedEvent = defineEvent({
|
|
154
|
+
meta: {
|
|
155
|
+
key: "usage.batch_recorded",
|
|
156
|
+
version: "1.0.0",
|
|
157
|
+
description: "A batch of usage has been recorded.",
|
|
158
|
+
stability: "stable",
|
|
159
|
+
owners: ["@platform.metering"],
|
|
160
|
+
tags: ["metering", "usage"]
|
|
161
|
+
},
|
|
162
|
+
payload: UsageBatchRecordedPayload
|
|
163
|
+
});
|
|
164
|
+
var UsageAggregatedEvent = defineEvent({
|
|
165
|
+
meta: {
|
|
166
|
+
key: "usage.aggregated",
|
|
167
|
+
version: "1.0.0",
|
|
168
|
+
description: "Usage has been aggregated into a summary.",
|
|
169
|
+
stability: "stable",
|
|
170
|
+
owners: ["@platform.metering"],
|
|
171
|
+
tags: ["metering", "usage"]
|
|
172
|
+
},
|
|
173
|
+
payload: UsageAggregatedPayload
|
|
174
|
+
});
|
|
175
|
+
var ThresholdCreatedEvent = defineEvent({
|
|
176
|
+
meta: {
|
|
177
|
+
key: "threshold.created",
|
|
178
|
+
version: "1.0.0",
|
|
179
|
+
description: "A usage threshold has been created.",
|
|
180
|
+
stability: "stable",
|
|
181
|
+
owners: ["@platform.metering"],
|
|
182
|
+
tags: ["metering", "threshold"]
|
|
183
|
+
},
|
|
184
|
+
payload: ThresholdCreatedPayload
|
|
185
|
+
});
|
|
186
|
+
var ThresholdExceededEvent = defineEvent({
|
|
187
|
+
meta: {
|
|
188
|
+
key: "threshold.exceeded",
|
|
189
|
+
version: "1.0.0",
|
|
190
|
+
description: "Usage has exceeded a threshold.",
|
|
191
|
+
stability: "stable",
|
|
192
|
+
owners: ["@platform.metering"],
|
|
193
|
+
tags: ["metering", "threshold"]
|
|
194
|
+
},
|
|
195
|
+
payload: ThresholdExceededPayload
|
|
196
|
+
});
|
|
197
|
+
var ThresholdApproachingEvent = defineEvent({
|
|
198
|
+
meta: {
|
|
199
|
+
key: "threshold.approaching",
|
|
200
|
+
version: "1.0.0",
|
|
201
|
+
description: "Usage is approaching a threshold.",
|
|
202
|
+
stability: "stable",
|
|
203
|
+
owners: ["@platform.metering"],
|
|
204
|
+
tags: ["metering", "threshold"]
|
|
205
|
+
},
|
|
206
|
+
payload: ThresholdApproachingPayload
|
|
207
|
+
});
|
|
208
|
+
var MeteringEvents = {
|
|
209
|
+
MetricDefinedEvent,
|
|
210
|
+
MetricUpdatedEvent,
|
|
211
|
+
UsageRecordedEvent,
|
|
212
|
+
UsageBatchRecordedEvent,
|
|
213
|
+
UsageAggregatedEvent,
|
|
214
|
+
ThresholdCreatedEvent,
|
|
215
|
+
ThresholdExceededEvent,
|
|
216
|
+
ThresholdApproachingEvent
|
|
217
|
+
};
|
|
218
|
+
export {
|
|
219
|
+
UsageRecordedEvent,
|
|
220
|
+
UsageBatchRecordedEvent,
|
|
221
|
+
UsageAggregatedEvent,
|
|
222
|
+
ThresholdExceededEvent,
|
|
223
|
+
ThresholdCreatedEvent,
|
|
224
|
+
ThresholdApproachingEvent,
|
|
225
|
+
MetricUpdatedEvent,
|
|
226
|
+
MetricDefinedEvent,
|
|
227
|
+
MeteringEvents
|
|
228
|
+
};
|