@substrat-run/engine-metering 0.0.0 → 0.1.1
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/index.d.ts +224 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +456 -0
- package/dist/index.js.map +1 -0
- package/package.json +12 -12
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type EntityRef } from '@substrat-run/contracts';
|
|
3
|
+
import { type ModuleRegistration, type OperationContext } from '@substrat-run/kernel';
|
|
4
|
+
export declare const PERM: {
|
|
5
|
+
read: string & z.$brand<"PermissionKey">;
|
|
6
|
+
record: string & z.$brand<"PermissionKey">;
|
|
7
|
+
configure: string & z.$brand<"PermissionKey">;
|
|
8
|
+
close: string & z.$brand<"PermissionKey">;
|
|
9
|
+
};
|
|
10
|
+
export declare const meteringManifest: {
|
|
11
|
+
id: string & z.$brand<"ModuleId">;
|
|
12
|
+
version: string;
|
|
13
|
+
kernelContract: string;
|
|
14
|
+
permissions: {
|
|
15
|
+
key: string & z.$brand<"PermissionKey">;
|
|
16
|
+
description: string;
|
|
17
|
+
}[];
|
|
18
|
+
events: {
|
|
19
|
+
emits: {
|
|
20
|
+
type: string;
|
|
21
|
+
schemaVersion: number;
|
|
22
|
+
}[];
|
|
23
|
+
consumes: {
|
|
24
|
+
type: string;
|
|
25
|
+
schemaVersion: number;
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
migrations: {
|
|
29
|
+
journalDir: string;
|
|
30
|
+
compatibleFrom: string;
|
|
31
|
+
};
|
|
32
|
+
attachmentTargets: {
|
|
33
|
+
entityType: string;
|
|
34
|
+
readPermission: string & z.$brand<"PermissionKey">;
|
|
35
|
+
writePermission?: (string & z.$brand<"PermissionKey">) | undefined;
|
|
36
|
+
}[];
|
|
37
|
+
entityRelations?: {
|
|
38
|
+
entityType: string;
|
|
39
|
+
parentType: string;
|
|
40
|
+
}[] | undefined;
|
|
41
|
+
guards?: {
|
|
42
|
+
before: string;
|
|
43
|
+
predicate: string;
|
|
44
|
+
config: Record<string, unknown>;
|
|
45
|
+
}[] | undefined;
|
|
46
|
+
schedules?: {
|
|
47
|
+
operation: string;
|
|
48
|
+
cadence: {
|
|
49
|
+
everyMinutes: number;
|
|
50
|
+
};
|
|
51
|
+
input?: Record<string, unknown> | undefined;
|
|
52
|
+
permissions: (string & z.$brand<"PermissionKey">)[];
|
|
53
|
+
}[] | undefined;
|
|
54
|
+
withdraws?: string[] | undefined;
|
|
55
|
+
entitlementKey: string;
|
|
56
|
+
envSpec?: {
|
|
57
|
+
key: string;
|
|
58
|
+
label?: string | undefined;
|
|
59
|
+
description: string;
|
|
60
|
+
placeholder?: string | undefined;
|
|
61
|
+
required: boolean;
|
|
62
|
+
secret: boolean;
|
|
63
|
+
default?: string | undefined;
|
|
64
|
+
group?: string | undefined;
|
|
65
|
+
}[] | undefined;
|
|
66
|
+
ownerGrants?: (string & z.$brand<"PermissionKey">)[] | undefined;
|
|
67
|
+
entitlements?: string[] | undefined;
|
|
68
|
+
provides?: string[] | undefined;
|
|
69
|
+
requires?: string[] | undefined;
|
|
70
|
+
api?: string | undefined;
|
|
71
|
+
searchables?: {
|
|
72
|
+
entityType: string;
|
|
73
|
+
fields: string[];
|
|
74
|
+
}[] | undefined;
|
|
75
|
+
ui?: {
|
|
76
|
+
routes?: {
|
|
77
|
+
path: string;
|
|
78
|
+
screen: string;
|
|
79
|
+
permission: string & z.$brand<"PermissionKey">;
|
|
80
|
+
}[] | undefined;
|
|
81
|
+
nav?: {
|
|
82
|
+
label: string;
|
|
83
|
+
icon?: string | undefined;
|
|
84
|
+
to: string;
|
|
85
|
+
permission: string & z.$brand<"PermissionKey">;
|
|
86
|
+
}[] | undefined;
|
|
87
|
+
entityViews?: {
|
|
88
|
+
entityType: string;
|
|
89
|
+
view: string;
|
|
90
|
+
}[] | undefined;
|
|
91
|
+
widgets?: {
|
|
92
|
+
slot: string;
|
|
93
|
+
component: string;
|
|
94
|
+
permission: string & z.$brand<"PermissionKey">;
|
|
95
|
+
}[] | undefined;
|
|
96
|
+
settingsPanels?: {
|
|
97
|
+
label: string;
|
|
98
|
+
component: string;
|
|
99
|
+
permission: string & z.$brand<"PermissionKey">;
|
|
100
|
+
}[] | undefined;
|
|
101
|
+
} | undefined;
|
|
102
|
+
};
|
|
103
|
+
export declare const meteringMigrations: {
|
|
104
|
+
version: string;
|
|
105
|
+
sql: string;
|
|
106
|
+
}[];
|
|
107
|
+
export type MeterKind = 'counter' | 'gauge';
|
|
108
|
+
export interface Meter {
|
|
109
|
+
key: string;
|
|
110
|
+
kind: MeterKind;
|
|
111
|
+
unit: string;
|
|
112
|
+
description: string | null;
|
|
113
|
+
active: boolean;
|
|
114
|
+
createdAt: string;
|
|
115
|
+
}
|
|
116
|
+
export interface UsageEntry {
|
|
117
|
+
id: string;
|
|
118
|
+
meterKey: string;
|
|
119
|
+
qty: string;
|
|
120
|
+
subject: EntityRef | null;
|
|
121
|
+
occurredAt: string;
|
|
122
|
+
dedupeKey: string;
|
|
123
|
+
note: string | null;
|
|
124
|
+
createdBy: string;
|
|
125
|
+
createdAt: string;
|
|
126
|
+
}
|
|
127
|
+
export interface MeteringPeriod {
|
|
128
|
+
id: string;
|
|
129
|
+
from: string;
|
|
130
|
+
to: string;
|
|
131
|
+
closedBy: string;
|
|
132
|
+
closedAt: string;
|
|
133
|
+
}
|
|
134
|
+
/** One meter's frozen aggregate for a closed period. Unpriced, by design (D-E). */
|
|
135
|
+
export interface PeriodLine {
|
|
136
|
+
meterKey: string;
|
|
137
|
+
kind: MeterKind;
|
|
138
|
+
unit: string;
|
|
139
|
+
qty: string;
|
|
140
|
+
entryCount: number;
|
|
141
|
+
}
|
|
142
|
+
export declare const configureMeterInput: z.ZodObject<{
|
|
143
|
+
key: z.ZodString;
|
|
144
|
+
kind: z.ZodEnum<{
|
|
145
|
+
counter: "counter";
|
|
146
|
+
gauge: "gauge";
|
|
147
|
+
}>;
|
|
148
|
+
unit: z.ZodString;
|
|
149
|
+
description: z.ZodOptional<z.ZodString>;
|
|
150
|
+
active: z.ZodOptional<z.ZodBoolean>;
|
|
151
|
+
}, z.core.$strip>;
|
|
152
|
+
export type ConfigureMeterInput = z.infer<typeof configureMeterInput>;
|
|
153
|
+
/**
|
|
154
|
+
* Register or update a meter. `kind` and `unit` are FROZEN after creation:
|
|
155
|
+
* changing a meter's unit mid-period corrupts every aggregate that spans the
|
|
156
|
+
* change — a new unit is a new meter key (D-B). `description`/`active` update.
|
|
157
|
+
*/
|
|
158
|
+
export declare function configureMeter(ctx: OperationContext, rawInput: ConfigureMeterInput): Meter;
|
|
159
|
+
export declare function listMeters(ctx: OperationContext): Meter[];
|
|
160
|
+
export declare const recordUsageInput: z.ZodObject<{
|
|
161
|
+
meter: z.ZodString;
|
|
162
|
+
qty: z.ZodString;
|
|
163
|
+
subject: z.ZodOptional<z.ZodObject<{
|
|
164
|
+
entityType: z.ZodString;
|
|
165
|
+
entityId: z.ZodString;
|
|
166
|
+
}, z.core.$strip>>;
|
|
167
|
+
occurredAt: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
168
|
+
dedupeKey: z.ZodString;
|
|
169
|
+
note: z.ZodOptional<z.ZodString>;
|
|
170
|
+
}, z.core.$strip>;
|
|
171
|
+
export type RecordUsageInput = z.infer<typeof recordUsageInput>;
|
|
172
|
+
/**
|
|
173
|
+
* The idempotent ingest (D-C): a replay with the same (meter, dedupeKey) and
|
|
174
|
+
* the same qty returns the existing entry — no second row, no second event, no
|
|
175
|
+
* double bill. The same key with a DIFFERENT qty throws: that is an upstream
|
|
176
|
+
* bug, and swallowing it would hide exactly the defect the key exists to catch.
|
|
177
|
+
*/
|
|
178
|
+
export declare function recordUsage(ctx: OperationContext, rawInput: RecordUsageInput): {
|
|
179
|
+
entry: UsageEntry;
|
|
180
|
+
deduped: boolean;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* A window aggregate over half-open [from, to) — the same code path a close
|
|
184
|
+
* would freeze, so a preview and the eventual line can never disagree. Null
|
|
185
|
+
* means the meter has nothing to say for the window (see aggregateMeter).
|
|
186
|
+
*/
|
|
187
|
+
export declare function usageTotal(ctx: OperationContext, input: {
|
|
188
|
+
meter: string;
|
|
189
|
+
from: string;
|
|
190
|
+
to: string;
|
|
191
|
+
}): {
|
|
192
|
+
qty: string;
|
|
193
|
+
entryCount: number;
|
|
194
|
+
} | null;
|
|
195
|
+
export declare function listEntries(ctx: OperationContext, input?: {
|
|
196
|
+
meter?: string;
|
|
197
|
+
subject?: EntityRef;
|
|
198
|
+
from?: string;
|
|
199
|
+
to?: string;
|
|
200
|
+
}): UsageEntry[];
|
|
201
|
+
export declare const closePeriodInput: z.ZodObject<{
|
|
202
|
+
from: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
203
|
+
to: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
204
|
+
}, z.core.$strip>;
|
|
205
|
+
export type ClosePeriodInput = z.infer<typeof closePeriodInput>;
|
|
206
|
+
/**
|
|
207
|
+
* Freeze [from, to): aggregate EVERY meter (inactive ones too — usage recorded
|
|
208
|
+
* while a meter lived still bills after deactivation), write immutable period +
|
|
209
|
+
* line rows, advance the close horizon, and emit ONE fat event carrying the
|
|
210
|
+
* unpriced lines — the vertical prices them and feeds invoicing (D-E), the
|
|
211
|
+
* same wiring as timesheet.period-closed. Closes are monotonic and
|
|
212
|
+
* non-overlapping; gaps are allowed (metering may start mid-life), rewinds are
|
|
213
|
+
* not (D-D).
|
|
214
|
+
*/
|
|
215
|
+
export declare function closePeriod(ctx: OperationContext, rawInput: ClosePeriodInput): {
|
|
216
|
+
period: MeteringPeriod;
|
|
217
|
+
lines: PeriodLine[];
|
|
218
|
+
};
|
|
219
|
+
export declare function listPeriods(ctx: OperationContext): MeteringPeriod[];
|
|
220
|
+
export declare function periodLines(ctx: OperationContext, input: {
|
|
221
|
+
periodId: string;
|
|
222
|
+
}): PeriodLine[];
|
|
223
|
+
export declare const meteringModule: ModuleRegistration;
|
|
224
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { addDecimal, compareDecimal, entityRef, moduleManifest, permissionKey, } from '@substrat-run/contracts';
|
|
3
|
+
import { assertAllowed, ulid, } from '@substrat-run/kernel';
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// The metering engine (docs/design/engine-metering.md, #646). Owns an
|
|
6
|
+
// APPEND-ONLY usage ledger over configured meters, idempotent ingest keyed by
|
|
7
|
+
// (meter, dedupe key), window aggregation (counters sum; gauges sample and
|
|
8
|
+
// carry forward), and an append-only period-close journal whose latest close is
|
|
9
|
+
// a hard horizon no new entry may land behind — a closed period's lines stay
|
|
10
|
+
// reproducible from its entries forever.
|
|
11
|
+
//
|
|
12
|
+
// It owns QUANTITIES, never prices: no currency, no rates, no plans (D-E) —
|
|
13
|
+
// pricing is vertical vocabulary, and the vertical feeds invoicing from the fat
|
|
14
|
+
// `metering.period-closed` event exactly like the timesheet hand-off. It is the
|
|
15
|
+
// BILLABLE plane; platform metering stays Analytics Engine (D-A).
|
|
16
|
+
// ============================================================================
|
|
17
|
+
export const PERM = {
|
|
18
|
+
read: permissionKey.parse('metering:read'),
|
|
19
|
+
record: permissionKey.parse('metering:record'),
|
|
20
|
+
configure: permissionKey.parse('metering:configure'),
|
|
21
|
+
close: permissionKey.parse('metering:close'),
|
|
22
|
+
};
|
|
23
|
+
export const meteringManifest = moduleManifest.parse({
|
|
24
|
+
id: '@substrat-run/engine-metering',
|
|
25
|
+
version: '0.0.1',
|
|
26
|
+
kernelContract: '^0.0.1',
|
|
27
|
+
permissions: [
|
|
28
|
+
{ key: 'metering:read', description: 'Read meters, usage entries, totals and closed periods' },
|
|
29
|
+
{ key: 'metering:record', description: 'Record usage entries against a meter' },
|
|
30
|
+
{ key: 'metering:configure', description: 'Configure meters (register keys; kind/unit are frozen after creation)' },
|
|
31
|
+
{ key: 'metering:close', description: 'Close a billing period, freezing its aggregates' },
|
|
32
|
+
],
|
|
33
|
+
events: {
|
|
34
|
+
emits: [
|
|
35
|
+
{ type: 'metering.meter-configured', schemaVersion: 1 },
|
|
36
|
+
{ type: 'metering.usage-recorded', schemaVersion: 1 },
|
|
37
|
+
{ type: 'metering.period-closed', schemaVersion: 1 },
|
|
38
|
+
],
|
|
39
|
+
consumes: [],
|
|
40
|
+
},
|
|
41
|
+
migrations: { journalDir: './migrations', compatibleFrom: '0.0.1' },
|
|
42
|
+
attachmentTargets: [],
|
|
43
|
+
entityRelations: [],
|
|
44
|
+
entitlementKey: 'metering',
|
|
45
|
+
// No schedules: WHEN to close a period is billing policy — vertical
|
|
46
|
+
// vocabulary. The vertical calls closePeriod from its own schedule.
|
|
47
|
+
schedules: [],
|
|
48
|
+
});
|
|
49
|
+
export const meteringMigrations = [
|
|
50
|
+
{
|
|
51
|
+
version: '0001-init',
|
|
52
|
+
sql: `
|
|
53
|
+
CREATE TABLE metering_meters (
|
|
54
|
+
key TEXT PRIMARY KEY,
|
|
55
|
+
kind TEXT NOT NULL CHECK (kind IN ('counter','gauge')),
|
|
56
|
+
unit TEXT NOT NULL,
|
|
57
|
+
description TEXT,
|
|
58
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
59
|
+
created_at TEXT NOT NULL
|
|
60
|
+
);
|
|
61
|
+
CREATE TABLE metering_entries (
|
|
62
|
+
id TEXT PRIMARY KEY,
|
|
63
|
+
meter_key TEXT NOT NULL,
|
|
64
|
+
qty TEXT NOT NULL,
|
|
65
|
+
subject_type TEXT,
|
|
66
|
+
subject_id TEXT,
|
|
67
|
+
occurred_at TEXT NOT NULL,
|
|
68
|
+
dedupe_key TEXT NOT NULL,
|
|
69
|
+
note TEXT,
|
|
70
|
+
created_by TEXT NOT NULL,
|
|
71
|
+
created_at TEXT NOT NULL,
|
|
72
|
+
UNIQUE (meter_key, dedupe_key)
|
|
73
|
+
);
|
|
74
|
+
CREATE INDEX metering_entries_by_meter_time
|
|
75
|
+
ON metering_entries (meter_key, occurred_at);
|
|
76
|
+
CREATE TABLE metering_periods (
|
|
77
|
+
id TEXT PRIMARY KEY,
|
|
78
|
+
from_at TEXT NOT NULL,
|
|
79
|
+
to_at TEXT NOT NULL,
|
|
80
|
+
closed_by TEXT NOT NULL,
|
|
81
|
+
closed_at TEXT NOT NULL
|
|
82
|
+
);
|
|
83
|
+
CREATE TABLE metering_period_lines (
|
|
84
|
+
id TEXT PRIMARY KEY,
|
|
85
|
+
period_id TEXT NOT NULL,
|
|
86
|
+
meter_key TEXT NOT NULL,
|
|
87
|
+
kind TEXT NOT NULL,
|
|
88
|
+
unit TEXT NOT NULL,
|
|
89
|
+
qty TEXT NOT NULL,
|
|
90
|
+
entry_count INTEGER NOT NULL
|
|
91
|
+
);
|
|
92
|
+
`,
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Schemas & shapes
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
/**
|
|
99
|
+
* Instants are UTC ISO-8601, normalized to millisecond precision so
|
|
100
|
+
* lexicographic order IS chronological order — mixed precision would break
|
|
101
|
+
* every string comparison below ('…T00:00:00Z' sorts AFTER '…T00:00:00.000Z').
|
|
102
|
+
*/
|
|
103
|
+
const isoInstant = z
|
|
104
|
+
.string()
|
|
105
|
+
.regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/, 'must be a UTC ISO-8601 instant (…Z)')
|
|
106
|
+
.transform((s) => new Date(s).toISOString());
|
|
107
|
+
const nonNegDecimal = z.string().regex(/^\d+(\.\d{1,6})?$/, 'must be a non-negative decimal');
|
|
108
|
+
const signedDecimal = z.string().regex(/^-?\d+(\.\d{1,6})?$/, 'must be a decimal');
|
|
109
|
+
const toMeter = (r) => ({
|
|
110
|
+
key: r.key,
|
|
111
|
+
kind: r.kind,
|
|
112
|
+
unit: r.unit,
|
|
113
|
+
description: r.description,
|
|
114
|
+
active: r.active === 1,
|
|
115
|
+
createdAt: r.created_at,
|
|
116
|
+
});
|
|
117
|
+
const toEntry = (r) => ({
|
|
118
|
+
id: r.id,
|
|
119
|
+
meterKey: r.meter_key,
|
|
120
|
+
qty: r.qty,
|
|
121
|
+
subject: r.subject_type !== null && r.subject_id !== null
|
|
122
|
+
? { entityType: r.subject_type, entityId: r.subject_id }
|
|
123
|
+
: null,
|
|
124
|
+
occurredAt: r.occurred_at,
|
|
125
|
+
dedupeKey: r.dedupe_key,
|
|
126
|
+
note: r.note,
|
|
127
|
+
createdBy: r.created_by,
|
|
128
|
+
createdAt: r.created_at,
|
|
129
|
+
});
|
|
130
|
+
const toPeriod = (r) => ({
|
|
131
|
+
id: r.id,
|
|
132
|
+
from: r.from_at,
|
|
133
|
+
to: r.to_at,
|
|
134
|
+
closedBy: r.closed_by,
|
|
135
|
+
closedAt: r.closed_at,
|
|
136
|
+
});
|
|
137
|
+
const toLine = (r) => ({
|
|
138
|
+
meterKey: r.meter_key,
|
|
139
|
+
kind: r.kind,
|
|
140
|
+
unit: r.unit,
|
|
141
|
+
qty: r.qty,
|
|
142
|
+
entryCount: r.entry_count,
|
|
143
|
+
});
|
|
144
|
+
function getMeterRow(ctx, key) {
|
|
145
|
+
const row = ctx.sql.query('SELECT * FROM metering_meters WHERE key = ?', [key])[0];
|
|
146
|
+
if (!row)
|
|
147
|
+
throw new Error(`meter not found: ${key}`);
|
|
148
|
+
return row;
|
|
149
|
+
}
|
|
150
|
+
/** The latest closed `to` — nothing may be recorded behind it (D-D). */
|
|
151
|
+
function closeHorizon(ctx) {
|
|
152
|
+
return (ctx.sql.query('SELECT MAX(to_at) AS horizon FROM metering_periods')[0]?.horizon ?? null);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* One meter's aggregate over half-open [from, to) — the SINGLE code path shared
|
|
156
|
+
* by `usageTotal` and `closePeriod`, so a preview can never disagree with the
|
|
157
|
+
* frozen line. Returns null when the meter has nothing to say for the window:
|
|
158
|
+
* a counter with no entries (a zero sum bills nothing), or a gauge never
|
|
159
|
+
* sampled at all. A gauge with no in-window samples carries the latest earlier
|
|
160
|
+
* sample forward with entryCount 0 — a level persists between observations.
|
|
161
|
+
*/
|
|
162
|
+
function aggregateMeter(ctx, meter, from, to) {
|
|
163
|
+
const rows = ctx.sql.query(`SELECT qty FROM metering_entries
|
|
164
|
+
WHERE meter_key = ? AND occurred_at >= ? AND occurred_at < ?
|
|
165
|
+
ORDER BY occurred_at, id`, [meter.key, from, to]);
|
|
166
|
+
if (meter.kind === 'counter') {
|
|
167
|
+
if (rows.length === 0)
|
|
168
|
+
return null;
|
|
169
|
+
return { qty: rows.reduce((sum, r) => addDecimal(sum, r.qty), '0'), entryCount: rows.length };
|
|
170
|
+
}
|
|
171
|
+
if (rows.length > 0) {
|
|
172
|
+
const max = rows.reduce((m, r) => (compareDecimal(r.qty, m) > 0 ? r.qty : m), rows[0].qty);
|
|
173
|
+
return { qty: max, entryCount: rows.length };
|
|
174
|
+
}
|
|
175
|
+
const carried = ctx.sql.query(`SELECT qty FROM metering_entries
|
|
176
|
+
WHERE meter_key = ? AND occurred_at < ?
|
|
177
|
+
ORDER BY occurred_at DESC, id DESC LIMIT 1`, [meter.key, from])[0];
|
|
178
|
+
return carried ? { qty: carried.qty, entryCount: 0 } : null;
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// In-scope functions (K-16) — composable from vertical operations, same
|
|
182
|
+
// transaction. The registered operations below are their default bindings.
|
|
183
|
+
// The CALLER is responsible for the permission check.
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
export const configureMeterInput = z.object({
|
|
186
|
+
key: z.string().min(1),
|
|
187
|
+
kind: z.enum(['counter', 'gauge']),
|
|
188
|
+
unit: z.string().min(1),
|
|
189
|
+
description: z.string().optional(),
|
|
190
|
+
active: z.boolean().optional(),
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* Register or update a meter. `kind` and `unit` are FROZEN after creation:
|
|
194
|
+
* changing a meter's unit mid-period corrupts every aggregate that spans the
|
|
195
|
+
* change — a new unit is a new meter key (D-B). `description`/`active` update.
|
|
196
|
+
*/
|
|
197
|
+
export function configureMeter(ctx, rawInput) {
|
|
198
|
+
const input = configureMeterInput.parse(rawInput);
|
|
199
|
+
const existing = ctx.sql.query('SELECT * FROM metering_meters WHERE key = ?', [
|
|
200
|
+
input.key,
|
|
201
|
+
])[0];
|
|
202
|
+
if (existing) {
|
|
203
|
+
if (existing.kind !== input.kind || existing.unit !== input.unit) {
|
|
204
|
+
throw new Error(`meter '${input.key}' is ${existing.kind}/${existing.unit} — kind and unit are frozen after creation; a new unit is a new meter key`);
|
|
205
|
+
}
|
|
206
|
+
ctx.sql.exec(`UPDATE metering_meters SET description = COALESCE(?, description), active = COALESCE(?, active) WHERE key = ?`, [
|
|
207
|
+
input.description ?? null,
|
|
208
|
+
input.active === undefined ? null : input.active ? 1 : 0,
|
|
209
|
+
input.key,
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
ctx.sql.exec(`INSERT INTO metering_meters (key, kind, unit, description, active, created_at)
|
|
214
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
215
|
+
input.key,
|
|
216
|
+
input.kind,
|
|
217
|
+
input.unit,
|
|
218
|
+
input.description ?? null,
|
|
219
|
+
input.active === false ? 0 : 1,
|
|
220
|
+
new Date().toISOString(),
|
|
221
|
+
]);
|
|
222
|
+
}
|
|
223
|
+
const row = getMeterRow(ctx, input.key);
|
|
224
|
+
ctx.emit({
|
|
225
|
+
type: 'metering.meter-configured',
|
|
226
|
+
schemaVersion: 1,
|
|
227
|
+
entity: { entityType: 'metering-meter', entityId: row.key },
|
|
228
|
+
piiClass: 'none',
|
|
229
|
+
payload: { key: row.key, kind: row.kind, unit: row.unit, active: row.active === 1 },
|
|
230
|
+
});
|
|
231
|
+
return toMeter(row);
|
|
232
|
+
}
|
|
233
|
+
export function listMeters(ctx) {
|
|
234
|
+
return ctx.sql.query('SELECT * FROM metering_meters ORDER BY key').map(toMeter);
|
|
235
|
+
}
|
|
236
|
+
export const recordUsageInput = z.object({
|
|
237
|
+
meter: z.string().min(1),
|
|
238
|
+
/** Counters take signed deltas (a correction is a compensating entry, never
|
|
239
|
+
* an edit); gauges take non-negative level samples — enforced per kind. */
|
|
240
|
+
qty: signedDecimal,
|
|
241
|
+
subject: entityRef.optional(),
|
|
242
|
+
/** Defaults to now. Must not fall behind the close horizon (D-D). */
|
|
243
|
+
occurredAt: isoInstant.optional(),
|
|
244
|
+
/** Caller-supplied idempotency key, unique per meter (D-C) — e.g. a turn id. */
|
|
245
|
+
dedupeKey: z.string().min(1),
|
|
246
|
+
note: z.string().optional(),
|
|
247
|
+
});
|
|
248
|
+
/**
|
|
249
|
+
* The idempotent ingest (D-C): a replay with the same (meter, dedupeKey) and
|
|
250
|
+
* the same qty returns the existing entry — no second row, no second event, no
|
|
251
|
+
* double bill. The same key with a DIFFERENT qty throws: that is an upstream
|
|
252
|
+
* bug, and swallowing it would hide exactly the defect the key exists to catch.
|
|
253
|
+
*/
|
|
254
|
+
export function recordUsage(ctx, rawInput) {
|
|
255
|
+
const input = recordUsageInput.parse(rawInput);
|
|
256
|
+
const meter = getMeterRow(ctx, input.meter);
|
|
257
|
+
if (meter.active !== 1)
|
|
258
|
+
throw new Error(`meter '${meter.key}' is inactive`);
|
|
259
|
+
if (meter.kind === 'gauge')
|
|
260
|
+
nonNegDecimal.parse(input.qty);
|
|
261
|
+
const existing = ctx.sql.query('SELECT * FROM metering_entries WHERE meter_key = ? AND dedupe_key = ?', [meter.key, input.dedupeKey])[0];
|
|
262
|
+
if (existing) {
|
|
263
|
+
if (compareDecimal(existing.qty, input.qty) !== 0) {
|
|
264
|
+
throw new Error(`dedupe key '${input.dedupeKey}' on meter '${meter.key}' was recorded with qty ${existing.qty}, now ${input.qty} — a dedupe key must name one observation`);
|
|
265
|
+
}
|
|
266
|
+
return { entry: toEntry(existing), deduped: true };
|
|
267
|
+
}
|
|
268
|
+
const occurredAt = input.occurredAt ?? new Date().toISOString();
|
|
269
|
+
const horizon = closeHorizon(ctx);
|
|
270
|
+
if (horizon !== null && occurredAt < horizon) {
|
|
271
|
+
throw new Error(`occurred_at ${occurredAt} is behind the close horizon ${horizon} — the period covering it is closed; record late usage at observation time`);
|
|
272
|
+
}
|
|
273
|
+
const id = ulid();
|
|
274
|
+
ctx.sql.exec(`INSERT INTO metering_entries
|
|
275
|
+
(id, meter_key, qty, subject_type, subject_id, occurred_at, dedupe_key, note, created_by, created_at)
|
|
276
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
277
|
+
id,
|
|
278
|
+
meter.key,
|
|
279
|
+
input.qty,
|
|
280
|
+
input.subject?.entityType ?? null,
|
|
281
|
+
input.subject?.entityId ?? null,
|
|
282
|
+
occurredAt,
|
|
283
|
+
input.dedupeKey,
|
|
284
|
+
input.note ?? null,
|
|
285
|
+
ctx.principal,
|
|
286
|
+
new Date().toISOString(),
|
|
287
|
+
]);
|
|
288
|
+
const entry = toEntry(ctx.sql.query('SELECT * FROM metering_entries WHERE id = ?', [id])[0]);
|
|
289
|
+
ctx.emit({
|
|
290
|
+
type: 'metering.usage-recorded',
|
|
291
|
+
schemaVersion: 1,
|
|
292
|
+
entity: { entityType: 'metering-entry', entityId: id },
|
|
293
|
+
piiClass: 'none',
|
|
294
|
+
payload: {
|
|
295
|
+
entryId: id,
|
|
296
|
+
meterKey: meter.key,
|
|
297
|
+
kind: meter.kind,
|
|
298
|
+
unit: meter.unit,
|
|
299
|
+
qty: input.qty,
|
|
300
|
+
subject: input.subject ?? null,
|
|
301
|
+
occurredAt,
|
|
302
|
+
dedupeKey: input.dedupeKey,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
return { entry, deduped: false };
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* A window aggregate over half-open [from, to) — the same code path a close
|
|
309
|
+
* would freeze, so a preview and the eventual line can never disagree. Null
|
|
310
|
+
* means the meter has nothing to say for the window (see aggregateMeter).
|
|
311
|
+
*/
|
|
312
|
+
export function usageTotal(ctx, input) {
|
|
313
|
+
const from = isoInstant.parse(input.from);
|
|
314
|
+
const to = isoInstant.parse(input.to);
|
|
315
|
+
if (to <= from)
|
|
316
|
+
throw new Error(`to ${to} must be after from ${from}`);
|
|
317
|
+
return aggregateMeter(ctx, getMeterRow(ctx, input.meter), from, to);
|
|
318
|
+
}
|
|
319
|
+
export function listEntries(ctx, input) {
|
|
320
|
+
const where = [];
|
|
321
|
+
const params = [];
|
|
322
|
+
if (input?.meter) {
|
|
323
|
+
where.push('meter_key = ?');
|
|
324
|
+
params.push(input.meter);
|
|
325
|
+
}
|
|
326
|
+
if (input?.subject) {
|
|
327
|
+
where.push('subject_type = ? AND subject_id = ?');
|
|
328
|
+
params.push(input.subject.entityType, input.subject.entityId);
|
|
329
|
+
}
|
|
330
|
+
if (input?.from) {
|
|
331
|
+
where.push('occurred_at >= ?');
|
|
332
|
+
params.push(isoInstant.parse(input.from));
|
|
333
|
+
}
|
|
334
|
+
if (input?.to) {
|
|
335
|
+
where.push('occurred_at < ?');
|
|
336
|
+
params.push(isoInstant.parse(input.to));
|
|
337
|
+
}
|
|
338
|
+
const sql = `SELECT * FROM metering_entries${where.length ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY occurred_at, id`;
|
|
339
|
+
return ctx.sql.query(sql, params).map(toEntry);
|
|
340
|
+
}
|
|
341
|
+
export const closePeriodInput = z.object({
|
|
342
|
+
from: isoInstant,
|
|
343
|
+
to: isoInstant,
|
|
344
|
+
});
|
|
345
|
+
/**
|
|
346
|
+
* Freeze [from, to): aggregate EVERY meter (inactive ones too — usage recorded
|
|
347
|
+
* while a meter lived still bills after deactivation), write immutable period +
|
|
348
|
+
* line rows, advance the close horizon, and emit ONE fat event carrying the
|
|
349
|
+
* unpriced lines — the vertical prices them and feeds invoicing (D-E), the
|
|
350
|
+
* same wiring as timesheet.period-closed. Closes are monotonic and
|
|
351
|
+
* non-overlapping; gaps are allowed (metering may start mid-life), rewinds are
|
|
352
|
+
* not (D-D).
|
|
353
|
+
*/
|
|
354
|
+
export function closePeriod(ctx, rawInput) {
|
|
355
|
+
const input = closePeriodInput.parse(rawInput);
|
|
356
|
+
if (input.to <= input.from)
|
|
357
|
+
throw new Error(`to ${input.to} must be after from ${input.from}`);
|
|
358
|
+
const horizon = closeHorizon(ctx);
|
|
359
|
+
if (horizon !== null && input.from < horizon) {
|
|
360
|
+
throw new Error(`period from ${input.from} overlaps the close horizon ${horizon} — closes are monotonic and non-overlapping`);
|
|
361
|
+
}
|
|
362
|
+
const id = ulid();
|
|
363
|
+
const now = new Date().toISOString();
|
|
364
|
+
ctx.sql.exec(`INSERT INTO metering_periods (id, from_at, to_at, closed_by, closed_at) VALUES (?, ?, ?, ?, ?)`, [id, input.from, input.to, ctx.principal, now]);
|
|
365
|
+
const lines = [];
|
|
366
|
+
for (const meter of ctx.sql.query('SELECT * FROM metering_meters ORDER BY key')) {
|
|
367
|
+
const agg = aggregateMeter(ctx, meter, input.from, input.to);
|
|
368
|
+
if (!agg)
|
|
369
|
+
continue;
|
|
370
|
+
ctx.sql.exec(`INSERT INTO metering_period_lines (id, period_id, meter_key, kind, unit, qty, entry_count)
|
|
371
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [ulid(), id, meter.key, meter.kind, meter.unit, agg.qty, agg.entryCount]);
|
|
372
|
+
lines.push({
|
|
373
|
+
meterKey: meter.key,
|
|
374
|
+
kind: meter.kind,
|
|
375
|
+
unit: meter.unit,
|
|
376
|
+
qty: agg.qty,
|
|
377
|
+
entryCount: agg.entryCount,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
ctx.emit({
|
|
381
|
+
type: 'metering.period-closed',
|
|
382
|
+
schemaVersion: 1,
|
|
383
|
+
entity: { entityType: 'metering-period', entityId: id },
|
|
384
|
+
piiClass: 'none',
|
|
385
|
+
payload: { periodId: id, from: input.from, to: input.to, lines },
|
|
386
|
+
});
|
|
387
|
+
return {
|
|
388
|
+
period: { id, from: input.from, to: input.to, closedBy: String(ctx.principal), closedAt: now },
|
|
389
|
+
lines,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
export function listPeriods(ctx) {
|
|
393
|
+
return ctx.sql
|
|
394
|
+
.query('SELECT * FROM metering_periods ORDER BY from_at, id')
|
|
395
|
+
.map(toPeriod);
|
|
396
|
+
}
|
|
397
|
+
export function periodLines(ctx, input) {
|
|
398
|
+
const period = ctx.sql.query('SELECT * FROM metering_periods WHERE id = ?', [
|
|
399
|
+
input.periodId,
|
|
400
|
+
])[0];
|
|
401
|
+
if (!period)
|
|
402
|
+
throw new Error(`metering period not found: ${input.periodId}`);
|
|
403
|
+
return ctx.sql
|
|
404
|
+
.query('SELECT * FROM metering_period_lines WHERE period_id = ? ORDER BY meter_key', [input.periodId])
|
|
405
|
+
.map(toLine);
|
|
406
|
+
}
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
// Default operation bindings — each starts with the permission check.
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
const configureMeterOp = async (ctx, input) => {
|
|
411
|
+
assertAllowed(await ctx.check(PERM.configure));
|
|
412
|
+
return configureMeter(ctx, input);
|
|
413
|
+
};
|
|
414
|
+
const listMetersOp = async (ctx) => {
|
|
415
|
+
assertAllowed(await ctx.check(PERM.read));
|
|
416
|
+
return listMeters(ctx);
|
|
417
|
+
};
|
|
418
|
+
const recordOp = async (ctx, input) => {
|
|
419
|
+
assertAllowed(await ctx.check(PERM.record));
|
|
420
|
+
return recordUsage(ctx, input);
|
|
421
|
+
};
|
|
422
|
+
const totalOp = async (ctx, input) => {
|
|
423
|
+
assertAllowed(await ctx.check(PERM.read));
|
|
424
|
+
return usageTotal(ctx, input);
|
|
425
|
+
};
|
|
426
|
+
const listEntriesOp = async (ctx, input) => {
|
|
427
|
+
assertAllowed(await ctx.check(PERM.read));
|
|
428
|
+
return listEntries(ctx, input);
|
|
429
|
+
};
|
|
430
|
+
const closePeriodOp = async (ctx, input) => {
|
|
431
|
+
assertAllowed(await ctx.check(PERM.close));
|
|
432
|
+
return closePeriod(ctx, input);
|
|
433
|
+
};
|
|
434
|
+
const listPeriodsOp = async (ctx) => {
|
|
435
|
+
assertAllowed(await ctx.check(PERM.read));
|
|
436
|
+
return listPeriods(ctx);
|
|
437
|
+
};
|
|
438
|
+
const periodLinesOp = async (ctx, input) => {
|
|
439
|
+
assertAllowed(await ctx.check(PERM.read));
|
|
440
|
+
return periodLines(ctx, input);
|
|
441
|
+
};
|
|
442
|
+
export const meteringModule = {
|
|
443
|
+
manifest: meteringManifest,
|
|
444
|
+
migrations: meteringMigrations,
|
|
445
|
+
operations: {
|
|
446
|
+
'metering/configure-meter': configureMeterOp,
|
|
447
|
+
'metering/list-meters': listMetersOp,
|
|
448
|
+
'metering/record': recordOp,
|
|
449
|
+
'metering/total': totalOp,
|
|
450
|
+
'metering/list-entries': listEntriesOp,
|
|
451
|
+
'metering/close-period': closePeriodOp,
|
|
452
|
+
'metering/list-periods': listPeriodsOp,
|
|
453
|
+
'metering/period-lines': periodLinesOp,
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/engine-metering",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Substrat engine: billable usage — an append-only, idempotent meter ledger (counters and gauges) whose closed periods are frozen billing evidence",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsc -p tsconfig.json",
|
|
29
|
-
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
30
|
-
"test": "vitest run"
|
|
31
|
-
},
|
|
32
27
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@substrat-run/kernel": "
|
|
35
|
-
"
|
|
28
|
+
"zod": "^4.4.3",
|
|
29
|
+
"@substrat-run/kernel": "^0.66.0",
|
|
30
|
+
"@substrat-run/contracts": "^0.66.0"
|
|
36
31
|
},
|
|
37
32
|
"devDependencies": {
|
|
38
33
|
"typescript": "^7.0.0",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
34
|
+
"vitest": "^3.0.0",
|
|
35
|
+
"@substrat-run/engine-test-kit": "^0.0.64"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -p tsconfig.json",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
40
|
+
"test": "vitest run"
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
}
|