@wardx/core 0.1.2 → 0.1.4
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 +14 -8
- package/package.json +8 -3
- package/src/WardxCore.js +27 -8
- package/src/index.d.ts +410 -0
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ import { WardxCore, assignVariant, loadSdkDefaults } from '@wardx/core';
|
|
|
27
27
|
- A measure call does not wait for a Promise.
|
|
28
28
|
- If a buffer is full, the engine discards data. The engine does not block the application.
|
|
29
29
|
- Counters in a frame are window deltas. Counters are not lifetime totals.
|
|
30
|
+
- `identify(subjectId)` sets the default subject for this instance. A per-call `{ subjectId }` overrides it.
|
|
30
31
|
|
|
31
32
|
## Settings
|
|
32
33
|
|
|
@@ -125,7 +126,7 @@ A dimension value must be a string, a number, or a boolean.
|
|
|
125
126
|
```js
|
|
126
127
|
core.event('purchase', { product: 'premium' });
|
|
127
128
|
core.log.info('match_started', { mode: 'ranked', players: 4 });
|
|
128
|
-
core.log.error('payment_failed', { code: 'timeout' });
|
|
129
|
+
core.log.error('payment_failed', { code: 'timeout', stack: 'PaymentError: timeout' });
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
Log levels: `debug`, `info`, `warn`, `error`.
|
|
@@ -138,7 +139,7 @@ Log levels: `debug`, `info`, `warn`, `error`.
|
|
|
138
139
|
|
|
139
140
|
Dropped items increment `wardx.internal.events_dropped` or `wardx.internal.logs_dropped`.
|
|
140
141
|
|
|
141
|
-
Use counters and histograms for rates and latency. Use events for rare product facts: a purchase, an experiment exposure or goal, a named screen. Use logs for failures. Do not put user ids on metric dimensions.
|
|
142
|
+
Use counters and histograms for rates and latency. Use events for rare product facts: a purchase, an experiment exposure or goal, a named screen. Use logs for failures. Put a clipped `stack` or a provider `code` on the log attrs so MCP `get_recent_logs` can show an agent where to look. The engine does not edit source. Do not put user ids on metric dimensions.
|
|
142
143
|
|
|
143
144
|
A volume funnel is one event name and one counter per step. The engine does not join events by subject. See `docs/ARCHITECTURE.md`.
|
|
144
145
|
|
|
@@ -148,6 +149,8 @@ A volume funnel is one event name and one counter per step. The engine does not
|
|
|
148
149
|
|
|
149
150
|
**Objective:** Get a config value. If an experiment applies, get the variant value.
|
|
150
151
|
|
|
152
|
+
There is a default subject after `identify(subjectId)`. Pass `{ subjectId }` on a call to override it, or when one process serves many users. Use a stable account id, not `sessionId`. With no subject, `configGet` returns the Remote Config value and that call is not in the A/B test.
|
|
153
|
+
|
|
151
154
|
```js
|
|
152
155
|
core.applyConfig(13, {
|
|
153
156
|
values: {
|
|
@@ -170,23 +173,26 @@ core.applyConfig(13, {
|
|
|
170
173
|
});
|
|
171
174
|
|
|
172
175
|
const fallback = 1000;
|
|
173
|
-
const
|
|
174
|
-
core.
|
|
176
|
+
const shared = core.configGet('message.delayMs', fallback);
|
|
177
|
+
core.identify('user-1');
|
|
178
|
+
const delay = core.configGet('message.delayMs', fallback);
|
|
179
|
+
core.experimentGoal('message.sent', { value: 1 });
|
|
180
|
+
const other = core.configGet('message.delayMs', fallback, { subjectId: 'user-2' });
|
|
175
181
|
```
|
|
176
182
|
|
|
183
|
+
`shared` is always the snapshot value (`1000`). `delay` is `1000` or `400` for `user-1`. `other` is the variant for `user-2`. The same `subjectId`, experiment `id`, and `salt` always map to the same variant. You do not persist the group. Changing `salt` redistributes the population. `identify(null)` clears the default.
|
|
184
|
+
|
|
177
185
|
### Resolution order
|
|
178
186
|
|
|
179
187
|
1. If the key is not in the snapshot, return `fallback`.
|
|
180
|
-
2. If `
|
|
188
|
+
2. If there is no subject (`identify` unset and no `{ subjectId }`), return the Remote Config value.
|
|
181
189
|
3. If no enabled experiment contains the key, return the Remote Config value.
|
|
182
190
|
4. If the subject is not in the allocation, return the Remote Config value.
|
|
183
191
|
5. If the subject is in the allocation, return the variant value.
|
|
184
192
|
|
|
185
|
-
The assignment is deterministic. The same `experimentId`, `subjectId`, and `salt` always give the same variant.
|
|
186
|
-
|
|
187
193
|
The first resolve for a subject in a session emits event `experiment.exposure`. The payload contains a hashed subject. The payload does not contain the raw `subjectId`.
|
|
188
194
|
|
|
189
|
-
`experimentGoal` emits event `experiment.goal`.
|
|
195
|
+
`experimentGoal` emits event `experiment.goal`. The subject comes from `identify()` or from `{ subjectId }` on that call. You can supply `value`. Use milliseconds for a session-duration goal. The server stores that number as `goalSum` and `goalMean` per variant. Without a subject, the call throws. One experiment should have one quantitative goal name.
|
|
190
196
|
|
|
191
197
|
## Use case 4: Assign a variant without WardxCore
|
|
192
198
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wardx/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Runtime-agnostic Wardx engine for metrics, events, logs, Remote Config, and experiments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wardx",
|
|
@@ -27,8 +27,13 @@
|
|
|
27
27
|
"node": ">=20"
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
|
-
".":
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./src/index.d.ts",
|
|
32
|
+
"import": "./src/index.js",
|
|
33
|
+
"default": "./src/index.js"
|
|
34
|
+
}
|
|
31
35
|
},
|
|
36
|
+
"types": "./src/index.d.ts",
|
|
32
37
|
"files": [
|
|
33
38
|
"src",
|
|
34
39
|
"defaults.json"
|
|
@@ -36,4 +41,4 @@
|
|
|
36
41
|
"publishConfig": {
|
|
37
42
|
"access": "public"
|
|
38
43
|
}
|
|
39
|
-
}
|
|
44
|
+
}
|
package/src/WardxCore.js
CHANGED
|
@@ -40,6 +40,7 @@ export class WardxCore {
|
|
|
40
40
|
this.seq = 0;
|
|
41
41
|
this.pendingFrames = [];
|
|
42
42
|
this.windowStart = Date.now();
|
|
43
|
+
this._subjectId = null;
|
|
43
44
|
this.log = {
|
|
44
45
|
debug: (message, attrs) => this._log('debug', message, attrs),
|
|
45
46
|
info: (message, attrs) => this._log('info', message, attrs),
|
|
@@ -96,30 +97,48 @@ export class WardxCore {
|
|
|
96
97
|
return wrapped;
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
identify(subjectId) {
|
|
101
|
+
if (subjectId === undefined || subjectId === null) {
|
|
102
|
+
this._subjectId = null;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (typeof subjectId !== 'string' || subjectId.length === 0) {
|
|
106
|
+
throw new Error('identify requires a non-empty subjectId');
|
|
107
|
+
}
|
|
108
|
+
this._subjectId = subjectId;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
_subjectIdFrom(context) {
|
|
112
|
+
if (context && context.subjectId !== undefined && context.subjectId !== null) {
|
|
113
|
+
return context.subjectId;
|
|
114
|
+
}
|
|
115
|
+
return this._subjectId;
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
configGet(key, fallback, context) {
|
|
100
119
|
if (!this.configStore.has(key)) return fallback;
|
|
101
120
|
const remote = this.configStore.getRaw(key);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
121
|
+
const subjectId = this._subjectIdFrom(context);
|
|
122
|
+
if (subjectId === undefined || subjectId === null) return remote;
|
|
105
123
|
return this.experiments.resolve(
|
|
106
124
|
key,
|
|
107
125
|
remote,
|
|
108
|
-
|
|
126
|
+
subjectId,
|
|
109
127
|
this.configStore.experimentsByKey
|
|
110
128
|
);
|
|
111
129
|
}
|
|
112
130
|
|
|
113
131
|
experimentGoal(name, context) {
|
|
114
|
-
|
|
132
|
+
const subjectId = this._subjectIdFrom(context);
|
|
133
|
+
if (subjectId === undefined || subjectId === null) {
|
|
115
134
|
throw new Error('experiment.goal requires subjectId');
|
|
116
135
|
}
|
|
117
136
|
if (typeof name !== 'string' || name.length === 0) {
|
|
118
137
|
throw new Error('experiment.goal requires a metric name');
|
|
119
138
|
}
|
|
120
|
-
const subject = this.experiments.hashSubject(
|
|
139
|
+
const subject = this.experiments.hashSubject(subjectId);
|
|
121
140
|
const experiments = this.experiments.relevantExperiments(
|
|
122
|
-
|
|
141
|
+
subjectId,
|
|
123
142
|
this.configStore.experiments
|
|
124
143
|
);
|
|
125
144
|
const payload = {
|
|
@@ -127,7 +146,7 @@ export class WardxCore {
|
|
|
127
146
|
subject,
|
|
128
147
|
experiments
|
|
129
148
|
};
|
|
130
|
-
if (context.value !== undefined) payload.value = context.value;
|
|
149
|
+
if (context && context.value !== undefined) payload.value = context.value;
|
|
131
150
|
this.event('experiment.goal', payload);
|
|
132
151
|
}
|
|
133
152
|
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
export const PROTOCOL_VERSION: 1;
|
|
2
|
+
export const SDK_NAME: 'wardx-node';
|
|
3
|
+
export const PLATFORM: 'node';
|
|
4
|
+
|
|
5
|
+
export const INTERNAL: {
|
|
6
|
+
readonly eventsBuffered: 'wardx.internal.events_buffered';
|
|
7
|
+
readonly logsBuffered: 'wardx.internal.logs_buffered';
|
|
8
|
+
readonly eventsDropped: 'wardx.internal.events_dropped';
|
|
9
|
+
readonly logsDropped: 'wardx.internal.logs_dropped';
|
|
10
|
+
readonly cardinalityDropped: 'wardx.internal.cardinality_dropped';
|
|
11
|
+
readonly framesSent: 'wardx.internal.frames_sent';
|
|
12
|
+
readonly framesFailed: 'wardx.internal.frames_failed';
|
|
13
|
+
readonly bytesUncompressed: 'wardx.internal.bytes_uncompressed';
|
|
14
|
+
readonly bytesCompressed: 'wardx.internal.bytes_compressed';
|
|
15
|
+
readonly lastSyncMs: 'wardx.internal.last_sync_ms';
|
|
16
|
+
readonly configVersion: 'wardx.internal.config_version';
|
|
17
|
+
readonly processRssBytes: 'wardx.internal.process_rss_bytes';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
21
|
+
export type DimensionValue = string | number | boolean;
|
|
22
|
+
export interface Dimensions {
|
|
23
|
+
[name: string]: DimensionValue;
|
|
24
|
+
}
|
|
25
|
+
export type Attrs = Record<string, unknown>;
|
|
26
|
+
export type ConfigValue = string | number | boolean;
|
|
27
|
+
|
|
28
|
+
export interface HistogramOptions {
|
|
29
|
+
buckets?: number[];
|
|
30
|
+
[dimension: string]: DimensionValue | number[] | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CounterHandle {
|
|
34
|
+
inc(): void;
|
|
35
|
+
add(n: number): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface GaugeHandle {
|
|
39
|
+
set(value: number): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface HistogramHandle {
|
|
43
|
+
observe(value: number, attrs?: Dimensions | null): void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type StopTimer = (dims?: Dimensions | null) => void;
|
|
47
|
+
|
|
48
|
+
export interface SdkDefaults {
|
|
49
|
+
aggregateIntervalMs: number;
|
|
50
|
+
syncIntervalMs: number;
|
|
51
|
+
syncJitterMin: number;
|
|
52
|
+
syncJitterMax: number;
|
|
53
|
+
maxBufferedEvents: number;
|
|
54
|
+
maxBufferedLogs: number;
|
|
55
|
+
maxFrameBytes: number;
|
|
56
|
+
maxSeriesPerMetric: number;
|
|
57
|
+
maxDimensionKeys: number;
|
|
58
|
+
maxDimensionValueLength: number;
|
|
59
|
+
httpTimeoutMs: number;
|
|
60
|
+
histogramBuckets: number[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface CreateWardxOptions extends Partial<SdkDefaults> {
|
|
64
|
+
endpoint: string;
|
|
65
|
+
projectKey: string;
|
|
66
|
+
project: string;
|
|
67
|
+
role: string;
|
|
68
|
+
appVersion: string;
|
|
69
|
+
environment: string;
|
|
70
|
+
privacySalt?: string;
|
|
71
|
+
tracer?: Tracer | null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface CoreSettings extends SdkDefaults {
|
|
75
|
+
privacySalt: string;
|
|
76
|
+
endpoint?: string;
|
|
77
|
+
projectKey?: string;
|
|
78
|
+
project?: string;
|
|
79
|
+
role?: string;
|
|
80
|
+
appVersion?: string;
|
|
81
|
+
environment?: string;
|
|
82
|
+
tracer?: Tracer | null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ResolvedSettings extends SdkDefaults {
|
|
86
|
+
endpoint: string;
|
|
87
|
+
projectKey: string;
|
|
88
|
+
project: string;
|
|
89
|
+
role: string;
|
|
90
|
+
appVersion: string;
|
|
91
|
+
environment: string;
|
|
92
|
+
privacySalt: string;
|
|
93
|
+
tracer?: Tracer | null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface SubjectContext {
|
|
97
|
+
subjectId?: string | null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ExperimentGoalContext extends SubjectContext {
|
|
101
|
+
value?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ExperimentVariant {
|
|
105
|
+
key: string;
|
|
106
|
+
weight: number;
|
|
107
|
+
values: Record<string, ConfigValue>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface Experiment {
|
|
111
|
+
id: string;
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
allocation: number;
|
|
114
|
+
salt: string;
|
|
115
|
+
primaryMetric?: string;
|
|
116
|
+
variants: ExperimentVariant[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ConfigSnapshot {
|
|
120
|
+
values?: Record<string, ConfigValue>;
|
|
121
|
+
experiments?: Experiment[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface Assignment {
|
|
125
|
+
experiment: string;
|
|
126
|
+
variant: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface ExposurePayload {
|
|
130
|
+
experiment: string;
|
|
131
|
+
variant: string;
|
|
132
|
+
subject: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export type EventRow = [timestamp: number, name: string, attrs: Attrs | null];
|
|
136
|
+
export type LogRow = [timestamp: number, level: LogLevel, message: string, attrs: Attrs | null];
|
|
137
|
+
export type CounterRow = [name: string, dims: Dimensions | null, value: number];
|
|
138
|
+
export type GaugeRow = [name: string, dims: Dimensions | null, value: number, timestamp: number];
|
|
139
|
+
export type HistogramRow = [name: string, dims: Dimensions | null, snapshot: HistogramSnapshot];
|
|
140
|
+
|
|
141
|
+
export interface HistogramExemplar {
|
|
142
|
+
value: number;
|
|
143
|
+
attrs: Dimensions;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface HistogramSnapshot {
|
|
147
|
+
count: number;
|
|
148
|
+
sum: number;
|
|
149
|
+
min: number;
|
|
150
|
+
max: number;
|
|
151
|
+
buckets: Array<[bound: number, count: number]>;
|
|
152
|
+
exemplar?: HistogramExemplar;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface MetricsSnapshot {
|
|
156
|
+
counters: CounterRow[];
|
|
157
|
+
gauges: GaugeRow[];
|
|
158
|
+
histograms: HistogramRow[];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface Frame {
|
|
162
|
+
seq: number;
|
|
163
|
+
from: number;
|
|
164
|
+
to: number;
|
|
165
|
+
metrics: MetricsSnapshot;
|
|
166
|
+
events: EventRow[];
|
|
167
|
+
logs: LogRow[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface FittedFrame {
|
|
171
|
+
frame: Frame;
|
|
172
|
+
json: string;
|
|
173
|
+
droppedLogs: number;
|
|
174
|
+
droppedEvents: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface InternalSnapshot {
|
|
178
|
+
eventsDropped: number;
|
|
179
|
+
logsDropped: number;
|
|
180
|
+
cardinalityDropped: number;
|
|
181
|
+
framesSent: number;
|
|
182
|
+
framesFailed: number;
|
|
183
|
+
bytesUncompressed: number;
|
|
184
|
+
bytesCompressed: number;
|
|
185
|
+
eventsBuffered: number;
|
|
186
|
+
logsBuffered: number;
|
|
187
|
+
lastSyncMs: number;
|
|
188
|
+
configVersion: number;
|
|
189
|
+
processRssBytes: number;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface MeasureTraceRecord {
|
|
193
|
+
type: 'counter' | 'gauge' | 'histogram';
|
|
194
|
+
name: string;
|
|
195
|
+
dims: Dimensions | null;
|
|
196
|
+
op: 'inc' | 'add' | 'set' | 'observe';
|
|
197
|
+
value: number;
|
|
198
|
+
attrs?: Dimensions | null;
|
|
199
|
+
noop: boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface EventTraceRecord {
|
|
203
|
+
name: string;
|
|
204
|
+
attrs: Attrs | null;
|
|
205
|
+
dropped: boolean;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface LogTraceRecord {
|
|
209
|
+
level: LogLevel;
|
|
210
|
+
message: string;
|
|
211
|
+
attrs: Attrs | null;
|
|
212
|
+
dropped: boolean;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface FrameTraceRecord {
|
|
216
|
+
seq: number;
|
|
217
|
+
from: number;
|
|
218
|
+
to: number;
|
|
219
|
+
counters: number;
|
|
220
|
+
gauges: number;
|
|
221
|
+
histograms: number;
|
|
222
|
+
events: number;
|
|
223
|
+
logs: number;
|
|
224
|
+
droppedLogs: number;
|
|
225
|
+
droppedEvents: number;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface SyncTraceRecord {
|
|
229
|
+
phase: 'bootstrap' | 'flush' | 'tick';
|
|
230
|
+
frames: number;
|
|
231
|
+
bytesUncompressed: number;
|
|
232
|
+
bytesCompressed: number;
|
|
233
|
+
ms: number;
|
|
234
|
+
ok: boolean;
|
|
235
|
+
status?: number;
|
|
236
|
+
configVersion?: number;
|
|
237
|
+
appliedConfig?: boolean;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface Tracer {
|
|
241
|
+
measure?(record: MeasureTraceRecord): void;
|
|
242
|
+
event?(record: EventTraceRecord): void;
|
|
243
|
+
log?(record: LogTraceRecord): void;
|
|
244
|
+
frame?(record: FrameTraceRecord): void;
|
|
245
|
+
sync?(record: SyncTraceRecord): void;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface LogApi {
|
|
249
|
+
debug(message: string, attrs?: Attrs | null): void;
|
|
250
|
+
info(message: string, attrs?: Attrs | null): void;
|
|
251
|
+
warn(message: string, attrs?: Attrs | null): void;
|
|
252
|
+
error(message: string, attrs?: Attrs | null): void;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface InternalMetricsState extends InternalSnapshot {
|
|
256
|
+
hasCounterActivity(): boolean;
|
|
257
|
+
snapshotAndReset(): InternalSnapshot;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export class Counter implements CounterHandle {
|
|
261
|
+
name: string;
|
|
262
|
+
dims: Dimensions | null;
|
|
263
|
+
value: number;
|
|
264
|
+
constructor(name: string, dims?: Dimensions | null);
|
|
265
|
+
inc(): void;
|
|
266
|
+
add(n: number): void;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export class Gauge implements GaugeHandle {
|
|
270
|
+
name: string;
|
|
271
|
+
dims: Dimensions | null;
|
|
272
|
+
value: number;
|
|
273
|
+
timestamp: number;
|
|
274
|
+
dirty: boolean;
|
|
275
|
+
constructor(name: string, dims?: Dimensions | null);
|
|
276
|
+
set(value: number): void;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export class Histogram implements HistogramHandle {
|
|
280
|
+
name: string;
|
|
281
|
+
dims: Dimensions | null;
|
|
282
|
+
bounds: number[];
|
|
283
|
+
maxDimensionKeys: number | null;
|
|
284
|
+
maxDimensionValueLength: number | null;
|
|
285
|
+
counts: number[];
|
|
286
|
+
count: number;
|
|
287
|
+
sum: number;
|
|
288
|
+
min: number;
|
|
289
|
+
max: number;
|
|
290
|
+
exemplar: HistogramExemplar | null;
|
|
291
|
+
constructor(
|
|
292
|
+
name: string,
|
|
293
|
+
dims: Dimensions | null,
|
|
294
|
+
bounds: number[],
|
|
295
|
+
limits?: { maxDimensionKeys: number; maxDimensionValueLength: number } | null
|
|
296
|
+
);
|
|
297
|
+
observe(value: number, attrs?: Dimensions | null): void;
|
|
298
|
+
snapshot(): HistogramSnapshot;
|
|
299
|
+
reset(): void;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface MetricsRegistryOptions {
|
|
303
|
+
maxSeriesPerMetric: number;
|
|
304
|
+
maxDimensionKeys: number;
|
|
305
|
+
maxDimensionValueLength: number;
|
|
306
|
+
defaultHistogramBuckets: number[];
|
|
307
|
+
onCardinalityDropped: () => void;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export class MetricsRegistry {
|
|
311
|
+
constructor(options: MetricsRegistryOptions);
|
|
312
|
+
counter(name: string, dims?: Dimensions | null): CounterHandle;
|
|
313
|
+
gauge(name: string, dims?: Dimensions | null): GaugeHandle;
|
|
314
|
+
histogram(name: string, a?: HistogramOptions | null, b?: HistogramOptions | null): HistogramHandle;
|
|
315
|
+
timer(name: string, dims?: Dimensions | null): StopTimer;
|
|
316
|
+
snapshotAndReset(): MetricsSnapshot;
|
|
317
|
+
isDirty(): boolean;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export class EventBuffer {
|
|
321
|
+
max: number;
|
|
322
|
+
constructor(maxBufferedEvents: number);
|
|
323
|
+
get length(): number;
|
|
324
|
+
push(name: string, attrs?: Attrs | null): boolean;
|
|
325
|
+
swap(): EventRow[];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export class LogBuffer {
|
|
329
|
+
max: number;
|
|
330
|
+
constructor(maxBufferedLogs: number);
|
|
331
|
+
get length(): number;
|
|
332
|
+
push(level: LogLevel, message: string, attrs?: Attrs | null): boolean;
|
|
333
|
+
swap(): LogRow[];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export class ConfigStore {
|
|
337
|
+
version: number;
|
|
338
|
+
values: Record<string, ConfigValue>;
|
|
339
|
+
experiments: Experiment[];
|
|
340
|
+
experimentsByKey: Map<string, Experiment[]>;
|
|
341
|
+
applySnapshot(snapshot: { version: number } & ConfigSnapshot): void;
|
|
342
|
+
has(key: string): boolean;
|
|
343
|
+
getRaw(key: string): ConfigValue | undefined;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export class ExperimentResolver {
|
|
347
|
+
constructor(options: { privacySalt: string; onExposure: (payload: ExposurePayload) => void });
|
|
348
|
+
hashSubject(subjectId: string): string;
|
|
349
|
+
recordAssignment(subjectId: string, experiment: Experiment, variant: ExperimentVariant): void;
|
|
350
|
+
assignmentsFor(subjectId: string): Assignment[];
|
|
351
|
+
resolve(
|
|
352
|
+
key: string,
|
|
353
|
+
remoteValue: ConfigValue,
|
|
354
|
+
subjectId: string | null | undefined,
|
|
355
|
+
experimentsByKey: Map<string, Experiment[]>
|
|
356
|
+
): ConfigValue;
|
|
357
|
+
relevantExperiments(subjectId: string, experiments: Experiment[]): Assignment[];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export class FrameBuilder {
|
|
361
|
+
static build(input: {
|
|
362
|
+
seq: number;
|
|
363
|
+
from: number;
|
|
364
|
+
to: number;
|
|
365
|
+
metrics: MetricsSnapshot;
|
|
366
|
+
events: EventRow[];
|
|
367
|
+
logs: LogRow[];
|
|
368
|
+
internal: InternalSnapshot;
|
|
369
|
+
}): Frame;
|
|
370
|
+
static mergeInternal(counters: CounterRow[], gauges: GaugeRow[], internal: InternalSnapshot): void;
|
|
371
|
+
static fitToMaxBytes(frame: Frame, maxFrameBytes: number): FittedFrame;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export class WardxCore {
|
|
375
|
+
settings: CoreSettings;
|
|
376
|
+
stopped: boolean;
|
|
377
|
+
internal: InternalMetricsState;
|
|
378
|
+
metrics: MetricsRegistry;
|
|
379
|
+
events: EventBuffer;
|
|
380
|
+
logs: LogBuffer;
|
|
381
|
+
configStore: ConfigStore;
|
|
382
|
+
experiments: ExperimentResolver;
|
|
383
|
+
seq: number;
|
|
384
|
+
pendingFrames: Frame[];
|
|
385
|
+
windowStart: number;
|
|
386
|
+
log: LogApi;
|
|
387
|
+
constructor(settings: CoreSettings);
|
|
388
|
+
counter(name: string, dims?: Dimensions | null): CounterHandle;
|
|
389
|
+
gauge(name: string, dims?: Dimensions | null): GaugeHandle;
|
|
390
|
+
histogram(name: string, a?: HistogramOptions | null, b?: HistogramOptions | null): HistogramHandle;
|
|
391
|
+
timer(name: string, dims?: Dimensions | null): StopTimer;
|
|
392
|
+
event(name: string, attrs?: Attrs | null): void;
|
|
393
|
+
identify(subjectId: string | null | undefined): void;
|
|
394
|
+
configGet<T>(key: string, fallback: T, context?: SubjectContext): T;
|
|
395
|
+
experimentGoal(name: string, context?: ExperimentGoalContext): void;
|
|
396
|
+
applyConfig(version: number, config: ConfigSnapshot): void;
|
|
397
|
+
snapshotIfDirty(): FittedFrame | null;
|
|
398
|
+
snapshotFrame(): FittedFrame;
|
|
399
|
+
takePendingFrames(): Frame[];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function fnv1a32(input: string | Uint8Array): number;
|
|
403
|
+
export function assignmentHash(experimentId: string, subjectId: string, salt: string): number;
|
|
404
|
+
export function hashToUnitInterval(hash: number): number;
|
|
405
|
+
export function subjectHash(projectSalt: string, subjectId: string): string;
|
|
406
|
+
export function assignVariant(experiment: Experiment, subjectId: string): ExperimentVariant | null;
|
|
407
|
+
export function resolveSettings(options: CreateWardxOptions): ResolvedSettings;
|
|
408
|
+
export function loadSdkDefaults(): SdkDefaults;
|
|
409
|
+
export function nextSyncDelayMs(settings: Pick<ResolvedSettings, 'syncIntervalMs' | 'syncJitterMin' | 'syncJitterMax'>): number;
|
|
410
|
+
export function ulid(now?: number): string;
|