@kar-mi/spirit-vale-tools-metrics 0.2.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/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # @kar-mi/spirit-vale-tools-metrics
2
+
3
+ Shared rate-estimation primitives for Spirit Vale tools.
4
+
5
+ > **Internal package.** This package is published only because the domain
6
+ > packages (`combat`, `rewards`) depend on it at runtime; it is installed
7
+ > automatically alongside them and is not a supported public API.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ bun add @kar-mi/spirit-vale-tools-metrics
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ `RateTracker` accumulates one metric — experience, coins, or anything else countable — into a
18
+ running total, an exponentially-weighted per-second rate, and a bucketed timeline for graphing. It
19
+ is value-agnostic, so a second instance fed a different metric behaves identically:
20
+
21
+ ```ts
22
+ import { RateTracker } from "@kar-mi/spirit-vale-tools-metrics";
23
+
24
+ const experience = new RateTracker();
25
+ const coins = new RateTracker();
26
+
27
+ experience.record(kill.experience, recordedAtMs);
28
+ coins.record(kill.coins, recordedAtMs);
29
+
30
+ const { total, perSecond, perHour, timeline } = experience.snapshot(Date.now());
31
+ ```
32
+
33
+ `ewmaSeries` replays a snapshot's `timeline` to reconstruct `perSecond` over time, so a chart and
34
+ the single number beside it stay in step:
35
+
36
+ ```ts
37
+ import { ewmaSeries } from "@kar-mi/spirit-vale-tools-metrics";
38
+
39
+ const points = ewmaSeries(timeline, { start: Date.now() - 600_000, end: Date.now() });
40
+ ```
41
+
42
+ `EwmaRate` is the underlying O(1) estimator, used directly where the caller keeps its own timeline
43
+ (the combat meter's `currentDps`). Recording a value of `v` adds `v / tau` to the rate, which then
44
+ fades as `e^{-elapsed / tau}` — no window edge, and no discontinuity when an old event ages out.
45
+
46
+ ### Choosing a time constant
47
+
48
+ A flat rolling window of width `W` and an EWMA of time constant `tau` have equal estimator variance
49
+ when `W = 2 * tau`, and equal mean lag. A 5-second window is therefore reproduced by
50
+ `tauSeconds: 2.5`. Sparse metrics want a much longer constant — `RateTracker` defaults to 20
51
+ seconds, which keeps a single kill visible for about a minute.
52
+
53
+ `rateAt(nowMs, rampFromMs)` corrects the cold start: an estimator rising from zero under-reads a
54
+ steady stream by `1 - e^{-elapsed / tau}` (63% low at one tau). Pass the moment observation began
55
+ for dense streams read from the first instant; omit it for sparse gains, where the ramp is
56
+ indistinguishable from genuinely having earned nothing yet.
57
+
58
+ ## License
59
+
60
+ See [LICENSE.txt](../../LICENSE.txt).
@@ -0,0 +1,24 @@
1
+ export interface BucketTimelineOptions {
2
+ /** Width of each bucket. Defaults to one second. */
3
+ bucketMs?: number;
4
+ /** How much trailing history to retain. Defaults to one hour. */
5
+ retentionMs?: number;
6
+ }
7
+ export interface TimelineBucket {
8
+ atMs: number;
9
+ value: number;
10
+ }
11
+ /** A trailing FIFO of fixed-width buckets: the graphable history behind a rate, and its window sum. */
12
+ export declare class BucketTimeline {
13
+ private readonly bucketMs;
14
+ private readonly retentionMs;
15
+ private readonly buckets;
16
+ constructor(options?: BucketTimelineOptions);
17
+ record(value: number, atMs: number): void;
18
+ /** Total of every retained bucket — i.e. the sum over the retention window as of `nowMs`. */
19
+ windowSum(nowMs: number): number;
20
+ points(nowMs: number): TimelineBucket[];
21
+ reset(): void;
22
+ private prune;
23
+ }
24
+ //# sourceMappingURL=bucket-timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-timeline.d.ts","sourceRoot":"","sources":["../src/bucket-timeline.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAKD,uGAAuG;AACvG,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD,YAAY,OAAO,GAAE,qBAA0B,EAG9C;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAMxC;IAED,6FAA6F;IAC7F,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/B;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAGtC;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,OAAO,CAAC,KAAK;CAId"}
@@ -0,0 +1,37 @@
1
+ /** Continuous-time exponentially-weighted rate estimator (a "leaky bucket"). */
2
+ export interface EwmaRateOptions {
3
+ /** Decay time constant, in seconds. */
4
+ tauSeconds: number;
5
+ }
6
+ /** Cold-start correction for a `rateAt` read. See {@link EwmaRate.rateAt}. */
7
+ export interface EwmaRamp {
8
+ /** The moment observation of this stream began. */
9
+ fromMs: number;
10
+ /** Floor on the elapsed time, so the first moments do not divide by a near-zero factor. Defaults to 1000ms. */
11
+ minimumMs?: number;
12
+ }
13
+ /** Durable state of an estimator, safe to persist and later restore. */
14
+ export interface EwmaRateState {
15
+ rate: number;
16
+ updatedAtMs: number;
17
+ tauSeconds: number;
18
+ }
19
+ export declare class EwmaRate {
20
+ readonly tauSeconds: number;
21
+ private rate;
22
+ private updatedAtMs;
23
+ constructor(options: EwmaRateOptions);
24
+ /** A fresh estimator with the same time constant — for building a merge target from its sources. */
25
+ emptyLike(): EwmaRate;
26
+ record(value: number, atMs: number): void;
27
+ /** The estimated rate per second as of `nowMs`, decayed lazily without mutating state. */
28
+ rateAt(nowMs: number, ramp?: EwmaRamp): number;
29
+ reset(atMs: number): void;
30
+ state(): EwmaRateState;
31
+ /** Restores a persisted rate. The state's own `tauSeconds` must match, since the two only mean anything together. */
32
+ restore(state: EwmaRateState): void;
33
+ /** Folds `other` into this estimator. */
34
+ add(other: EwmaRate): void;
35
+ private decayTo;
36
+ }
37
+ //# sourceMappingURL=ewma-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ewma-rate.d.ts","sourceRoot":"","sources":["../src/ewma-rate.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,+GAA+G;IAC/G,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,qBAAa,QAAQ;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,WAAW,CAAK;IAExB,YAAY,OAAO,EAAE,eAAe,EAKnC;IAED,oGAAoG;IACpG,SAAS,IAAI,QAAQ,CAEpB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAIxC;IAED,0FAA0F;IAC1F,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAM7C;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGxB;IAED,KAAK,IAAI,aAAa,CAErB;IAED,qHAAqH;IACrH,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAIlC;IAED,yCAAyC;IACzC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAKzB;IAED,OAAO,CAAC,OAAO;CAKhB"}
@@ -0,0 +1,16 @@
1
+ import type { TimelineBucket } from "./bucket-timeline.ts";
2
+ export interface EwmaSeriesPoint {
3
+ time: number;
4
+ value: number;
5
+ }
6
+ export interface EwmaSeriesOptions {
7
+ /** Must match the tracker whose timeline is being replayed, or the chart will diverge from its `perSecond`. Defaults to 20 seconds. */
8
+ tauSeconds?: number;
9
+ /** Spacing between plotted points. Defaults to one second. */
10
+ stepMs?: number;
11
+ }
12
+ export declare function ewmaSeries(buckets: readonly TimelineBucket[], range: {
13
+ start: number;
14
+ end: number;
15
+ }, options?: EwmaSeriesOptions): EwmaSeriesPoint[];
16
+ //# sourceMappingURL=ewma-series.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ewma-series.d.ts","sourceRoot":"","sources":["../src/ewma-series.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,uIAAuI;IACvI,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACrC,OAAO,GAAE,iBAAsB,GAC9B,eAAe,EAAE,CAyBnB"}
@@ -0,0 +1,9 @@
1
+ export { BucketTimeline } from "./bucket-timeline.ts";
2
+ export type { BucketTimelineOptions, TimelineBucket } from "./bucket-timeline.ts";
3
+ export { EwmaRate } from "./ewma-rate.ts";
4
+ export type { EwmaRamp, EwmaRateOptions, EwmaRateState } from "./ewma-rate.ts";
5
+ export { ewmaSeries } from "./ewma-series.ts";
6
+ export type { EwmaSeriesOptions, EwmaSeriesPoint } from "./ewma-series.ts";
7
+ export { DEFAULT_RATE_TAU_SECONDS, RateTracker } from "./rate-tracker.ts";
8
+ export type { RateCheckpoint, RateSnapshot, RateTrackerOptions } from "./rate-tracker.ts";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1E,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,203 @@
1
+ // @bun
2
+ // src/bucket-timeline.ts
3
+ var DEFAULT_BUCKET_MS = 1000;
4
+ var DEFAULT_RETENTION_MS = 60 * 60 * 1000;
5
+
6
+ class BucketTimeline {
7
+ bucketMs;
8
+ retentionMs;
9
+ buckets = [];
10
+ constructor(options = {}) {
11
+ this.bucketMs = positiveFinite(options.bucketMs ?? DEFAULT_BUCKET_MS, "bucketMs");
12
+ this.retentionMs = positiveFinite(options.retentionMs ?? DEFAULT_RETENTION_MS, "retentionMs");
13
+ }
14
+ record(value, atMs) {
15
+ const bucketAtMs = Math.floor(atMs / this.bucketMs) * this.bucketMs;
16
+ const last = this.buckets.at(-1);
17
+ if (last && last.atMs === bucketAtMs)
18
+ last.value += value;
19
+ else
20
+ this.buckets.push({ atMs: bucketAtMs, value });
21
+ this.prune(atMs);
22
+ }
23
+ windowSum(nowMs) {
24
+ this.prune(nowMs);
25
+ let sum = 0;
26
+ for (const bucket of this.buckets)
27
+ sum += bucket.value;
28
+ return sum;
29
+ }
30
+ points(nowMs) {
31
+ this.prune(nowMs);
32
+ return this.buckets.map((bucket) => ({ ...bucket }));
33
+ }
34
+ reset() {
35
+ this.buckets.length = 0;
36
+ }
37
+ prune(nowMs) {
38
+ const cutoff = nowMs - this.retentionMs;
39
+ while (this.buckets.length > 0 && this.buckets[0].atMs < cutoff)
40
+ this.buckets.shift();
41
+ }
42
+ }
43
+ function positiveFinite(value, label) {
44
+ if (!Number.isFinite(value) || value <= 0)
45
+ throw new Error(`${label} must be a positive finite number`);
46
+ return value;
47
+ }
48
+ // src/ewma-rate.ts
49
+ var DEFAULT_MINIMUM_RAMP_MS = 1000;
50
+
51
+ class EwmaRate {
52
+ tauSeconds;
53
+ rate = 0;
54
+ updatedAtMs = 0;
55
+ constructor(options) {
56
+ if (!Number.isFinite(options.tauSeconds) || options.tauSeconds <= 0) {
57
+ throw new Error("tauSeconds must be a positive finite number");
58
+ }
59
+ this.tauSeconds = options.tauSeconds;
60
+ }
61
+ emptyLike() {
62
+ return new EwmaRate({ tauSeconds: this.tauSeconds });
63
+ }
64
+ record(value, atMs) {
65
+ if (!(value > 0))
66
+ return;
67
+ this.decayTo(atMs);
68
+ this.rate += value / this.tauSeconds;
69
+ }
70
+ rateAt(nowMs, ramp) {
71
+ const decaySeconds = Math.max(0, nowMs - this.updatedAtMs) / 1000;
72
+ const decayed = this.rate * Math.exp(-decaySeconds / this.tauSeconds);
73
+ if (ramp === undefined)
74
+ return decayed;
75
+ const elapsedMs = Math.max(ramp.minimumMs ?? DEFAULT_MINIMUM_RAMP_MS, nowMs - ramp.fromMs);
76
+ return decayed / (1 - Math.exp(-elapsedMs / 1000 / this.tauSeconds));
77
+ }
78
+ reset(atMs) {
79
+ this.rate = 0;
80
+ this.updatedAtMs = atMs;
81
+ }
82
+ state() {
83
+ return { rate: this.rate, updatedAtMs: this.updatedAtMs, tauSeconds: this.tauSeconds };
84
+ }
85
+ restore(state) {
86
+ if (state.tauSeconds !== this.tauSeconds)
87
+ throw new Error("cannot restore state accumulated with a different tauSeconds");
88
+ this.rate = state.rate;
89
+ this.updatedAtMs = state.updatedAtMs;
90
+ }
91
+ add(other) {
92
+ if (other.tauSeconds !== this.tauSeconds)
93
+ throw new Error("cannot merge estimators with different tauSeconds");
94
+ const atMs = Math.max(this.updatedAtMs, other.updatedAtMs);
95
+ this.decayTo(atMs);
96
+ this.rate += other.rate * Math.exp(-Math.max(0, atMs - other.updatedAtMs) / 1000 / this.tauSeconds);
97
+ }
98
+ decayTo(atMs) {
99
+ const dtSeconds = Math.max(0, atMs - this.updatedAtMs) / 1000;
100
+ this.rate *= Math.exp(-dtSeconds / this.tauSeconds);
101
+ this.updatedAtMs = Math.max(this.updatedAtMs, atMs);
102
+ }
103
+ }
104
+ // src/rate-tracker.ts
105
+ var DEFAULT_RATE_TAU_SECONDS = 20;
106
+
107
+ class RateTracker {
108
+ total = 0;
109
+ watermarkMs = 0;
110
+ watermarkOccurrences = 0;
111
+ replayedAtWatermark = 0;
112
+ rate;
113
+ timeline;
114
+ constructor(options = {}) {
115
+ this.rate = new EwmaRate({ tauSeconds: options.tauSeconds ?? DEFAULT_RATE_TAU_SECONDS });
116
+ this.timeline = new BucketTimeline({
117
+ ...options.bucketMs === undefined ? {} : { bucketMs: options.bucketMs },
118
+ ...options.retentionMs === undefined ? {} : { retentionMs: options.retentionMs }
119
+ });
120
+ }
121
+ record(value, atMs) {
122
+ if (value <= 0 || atMs < this.watermarkMs)
123
+ return;
124
+ if (atMs > this.watermarkMs) {
125
+ this.watermarkMs = atMs;
126
+ this.watermarkOccurrences = 0;
127
+ this.replayedAtWatermark = 0;
128
+ }
129
+ this.replayedAtWatermark += 1;
130
+ if (this.replayedAtWatermark <= this.watermarkOccurrences)
131
+ return;
132
+ this.watermarkOccurrences = this.replayedAtWatermark;
133
+ this.total += value;
134
+ this.rate.record(value, atMs);
135
+ this.timeline.record(value, atMs);
136
+ }
137
+ reset(atMs) {
138
+ this.total = 0;
139
+ this.timeline.reset();
140
+ this.rate.reset(atMs);
141
+ if (atMs >= this.watermarkMs) {
142
+ this.watermarkMs = atMs;
143
+ this.watermarkOccurrences = 0;
144
+ }
145
+ this.replayedAtWatermark = 0;
146
+ }
147
+ restoreCheckpoint(checkpoint) {
148
+ this.total = Math.max(0, checkpoint.total);
149
+ this.watermarkMs = Math.max(0, checkpoint.watermarkMs);
150
+ this.watermarkOccurrences = Math.max(0, checkpoint.watermarkOccurrences);
151
+ this.replayedAtWatermark = 0;
152
+ }
153
+ currentTotal() {
154
+ return this.total;
155
+ }
156
+ currentCheckpoint() {
157
+ return { total: this.total, watermarkMs: this.watermarkMs, watermarkOccurrences: this.watermarkOccurrences };
158
+ }
159
+ snapshot(nowMs) {
160
+ return {
161
+ total: this.total,
162
+ perSecond: this.rate.rateAt(nowMs),
163
+ perHour: this.timeline.windowSum(nowMs),
164
+ timeline: this.timeline.points(nowMs)
165
+ };
166
+ }
167
+ }
168
+
169
+ // src/ewma-series.ts
170
+ function ewmaSeries(buckets, range, options = {}) {
171
+ if (range.end <= range.start)
172
+ return [];
173
+ const stepMs = options.stepMs ?? 1000;
174
+ const rate = new EwmaRate({ tauSeconds: options.tauSeconds ?? DEFAULT_RATE_TAU_SECONDS });
175
+ rate.reset(buckets[0]?.atMs ?? range.start);
176
+ let bucketIndex = 0;
177
+ const consumeThrough = (toMs) => {
178
+ while (bucketIndex < buckets.length) {
179
+ const bucket = buckets[bucketIndex];
180
+ if (!bucket || bucket.atMs > toMs)
181
+ break;
182
+ rate.record(bucket.value, bucket.atMs);
183
+ bucketIndex += 1;
184
+ }
185
+ return rate.rateAt(toMs);
186
+ };
187
+ const points = [{ time: range.start, value: consumeThrough(range.start) }];
188
+ let next = Math.ceil(range.start / stepMs) * stepMs;
189
+ if (next <= range.start)
190
+ next += stepMs;
191
+ for (;next < range.end; next += stepMs) {
192
+ points.push({ time: next, value: consumeThrough(next) });
193
+ }
194
+ points.push({ time: range.end, value: consumeThrough(range.end) });
195
+ return points;
196
+ }
197
+ export {
198
+ BucketTimeline,
199
+ DEFAULT_RATE_TAU_SECONDS,
200
+ EwmaRate,
201
+ RateTracker,
202
+ ewmaSeries
203
+ };
@@ -0,0 +1,45 @@
1
+ import type { TimelineBucket } from "./bucket-timeline.ts";
2
+ /** Sparse gains (XP, coins) arrive seconds or minutes apart, so the rate is read far more often than it is updated and a short time constant would read as noise. */
3
+ export declare const DEFAULT_RATE_TAU_SECONDS = 20;
4
+ export interface RateTrackerOptions {
5
+ /** Decay constant for `perSecond`. Defaults to 20 seconds. */
6
+ tauSeconds?: number;
7
+ /** Width of the timeline buckets. Defaults to one second. */
8
+ bucketMs?: number;
9
+ /** Trailing history retained for `timeline` and summed into `perHour`. Defaults to one hour. */
10
+ retentionMs?: number;
11
+ }
12
+ export interface RateSnapshot {
13
+ /** Everything recorded since construction or the last `reset`. */
14
+ total: number;
15
+ /** Exponentially-weighted rate per second. */
16
+ perSecond: number;
17
+ /** Flat sum of the retention window — with the default hour of retention, the gain in the last hour. */
18
+ perHour: number;
19
+ timeline: TimelineBucket[];
20
+ }
21
+ /** Durable checkpoint of a tracker's progress, safe to persist and later restore with `restoreCheckpoint`. */
22
+ export interface RateCheckpoint {
23
+ total: number;
24
+ watermarkMs: number;
25
+ /** How many gains were already counted at exactly `watermarkMs` — disambiguates gains that share a timestamp from a duplicate replay of the same gain. */
26
+ watermarkOccurrences: number;
27
+ }
28
+ export declare class RateTracker {
29
+ private total;
30
+ private watermarkMs;
31
+ private watermarkOccurrences;
32
+ private replayedAtWatermark;
33
+ private readonly rate;
34
+ private readonly timeline;
35
+ constructor(options?: RateTrackerOptions);
36
+ record(value: number, atMs: number): void;
37
+ /** `atMs` should be "now" — everything up to this moment is treated as already accounted for. */
38
+ reset(atMs: number): void;
39
+ /** Seeds the running total and watermark from a durable checkpoint without affecting the (in-memory-only) rate/timeline. */
40
+ restoreCheckpoint(checkpoint: RateCheckpoint): void;
41
+ currentTotal(): number;
42
+ currentCheckpoint(): RateCheckpoint;
43
+ snapshot(nowMs: number): RateSnapshot;
44
+ }
45
+ //# sourceMappingURL=rate-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-tracker.d.ts","sourceRoot":"","sources":["../src/rate-tracker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,qKAAqK;AACrK,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gGAAgG;IAChG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,wGAAwG;IACxG,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED,8GAA8G;AAC9G,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,0JAA0J;IAC1J,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAW;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAE1C,YAAY,OAAO,GAAE,kBAAuB,EAM3C;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAaxC;IAED,iGAAiG;IACjG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CASxB;IAED,4HAA4H;IAC5H,iBAAiB,CAAC,UAAU,EAAE,cAAc,GAAG,IAAI,CAKlD;IAED,YAAY,IAAI,MAAM,CAErB;IAED,iBAAiB,IAAI,cAAc,CAElC;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAOpC;CACF"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@kar-mi/spirit-vale-tools-metrics",
3
+ "version": "0.2.0",
4
+ "description": "Shared rate-estimation primitives for Spirit Vale tools.",
5
+ "license": "AGPL-3.0-only",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/kar-mi/spirit-vale-tools.git",
9
+ "directory": "packages/metrics"
10
+ },
11
+ "type": "module",
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm && bunx tsc --project ./tsconfig.build.json"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
24
+ }
25
+ }