wolves-js-client 1.0.4 → 1.0.5
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/Store.d.ts +28 -2
- package/dist/Store.js +65 -9
- package/dist/WolvesClient.d.ts +1 -5
- package/dist/WolvesClient.js +12 -12
- package/dist/WolvesMetadata.d.ts +1 -1
- package/dist/WolvesMetadata.js +1 -1
- package/package.json +1 -1
package/dist/Store.d.ts
CHANGED
|
@@ -9,7 +9,16 @@ export type InitializeResponse = {
|
|
|
9
9
|
has_updates: boolean;
|
|
10
10
|
time: number;
|
|
11
11
|
};
|
|
12
|
-
export type DataSource = 'Network' | 'Cache' | 'NetworkNotModified' | 'NoValues';
|
|
12
|
+
export type DataSource = 'Network' | 'Cache' | 'NetworkNotModified' | 'NoValues' | 'Uninitialized';
|
|
13
|
+
export type EvaluationDetails = {
|
|
14
|
+
source: DataSource;
|
|
15
|
+
reason: string;
|
|
16
|
+
isRecognized: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type ExperimentEvaluation = {
|
|
19
|
+
config: ExperimentConfig | null;
|
|
20
|
+
details: EvaluationDetails;
|
|
21
|
+
};
|
|
13
22
|
export type DataAdapterResult = {
|
|
14
23
|
source: DataSource;
|
|
15
24
|
data: string;
|
|
@@ -23,6 +32,7 @@ export type DataAdapterResult = {
|
|
|
23
32
|
export declare class Store {
|
|
24
33
|
private values;
|
|
25
34
|
private source;
|
|
35
|
+
private isInitialized;
|
|
26
36
|
private sdkKey;
|
|
27
37
|
private cacheLimit;
|
|
28
38
|
private inMemoryCache;
|
|
@@ -60,7 +70,23 @@ export declare class Store {
|
|
|
60
70
|
* Reset the store for a new user.
|
|
61
71
|
*/
|
|
62
72
|
reset(): void;
|
|
63
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Mark the store as initialized.
|
|
75
|
+
*/
|
|
76
|
+
finalize(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Get experiment with evaluation details including reason.
|
|
79
|
+
* Following Statsig SDK pattern for exposure reasons:
|
|
80
|
+
* - Uninitialized: API called before initialize
|
|
81
|
+
* - NoValues: No data available
|
|
82
|
+
* - Network:Recognized / Network:Unrecognized
|
|
83
|
+
* - Cache:Recognized / Cache:Unrecognized
|
|
84
|
+
*/
|
|
85
|
+
getExperiment(name: string): ExperimentEvaluation;
|
|
86
|
+
/**
|
|
87
|
+
* Get the base source for building reason string.
|
|
88
|
+
*/
|
|
89
|
+
private _getBaseSource;
|
|
64
90
|
getValues(): InitializeResponse | null;
|
|
65
91
|
getSource(): DataSource;
|
|
66
92
|
getLastUpdateTime(): number | undefined;
|
package/dist/Store.js
CHANGED
|
@@ -8,13 +8,13 @@ const CACHE_LIMIT = 10;
|
|
|
8
8
|
const MAX_CACHE_WRITE_ATTEMPTS = 8;
|
|
9
9
|
const LAST_MODIFIED_KEY = 'wolves.last_modified_time';
|
|
10
10
|
/**
|
|
11
|
-
* Generate a
|
|
11
|
+
* Generate a user identifier for cache keys.
|
|
12
|
+
* Only uses userID as the unique identifier.
|
|
12
13
|
*/
|
|
13
|
-
function
|
|
14
|
-
if (!user)
|
|
14
|
+
function _getUserIdentifier(user) {
|
|
15
|
+
if (!user || !user.userID)
|
|
15
16
|
return 'anonymous';
|
|
16
|
-
|
|
17
|
-
return parts.join('|');
|
|
17
|
+
return user.userID;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Store class that manages experiment configurations with caching support.
|
|
@@ -24,6 +24,7 @@ class Store {
|
|
|
24
24
|
constructor(sdkKey = '') {
|
|
25
25
|
this.values = null;
|
|
26
26
|
this.source = 'NoValues';
|
|
27
|
+
this.isInitialized = false;
|
|
27
28
|
this.cacheLimit = CACHE_LIMIT;
|
|
28
29
|
this.inMemoryCache = new Map();
|
|
29
30
|
this.sdkKey = sdkKey;
|
|
@@ -32,7 +33,7 @@ class Store {
|
|
|
32
33
|
* Get the cache key for a specific user.
|
|
33
34
|
*/
|
|
34
35
|
getCacheKey(user) {
|
|
35
|
-
const userHash =
|
|
36
|
+
const userHash = _getUserIdentifier(user);
|
|
36
37
|
return `${CACHE_KEY_PREFIX}.${this.sdkKey}.${userHash}`;
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
@@ -109,7 +110,7 @@ class Store {
|
|
|
109
110
|
source: 'Network',
|
|
110
111
|
data: JSON.stringify(values),
|
|
111
112
|
receivedAt: Date.now(),
|
|
112
|
-
fullUserHash:
|
|
113
|
+
fullUserHash: _getUserIdentifier(user),
|
|
113
114
|
};
|
|
114
115
|
const cacheKey = this.getCacheKey(user);
|
|
115
116
|
this.inMemoryCache.set(cacheKey, result);
|
|
@@ -164,12 +165,67 @@ class Store {
|
|
|
164
165
|
reset() {
|
|
165
166
|
this.values = null;
|
|
166
167
|
this.source = 'NoValues';
|
|
168
|
+
this.isInitialized = false;
|
|
167
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Mark the store as initialized.
|
|
172
|
+
*/
|
|
173
|
+
finalize() {
|
|
174
|
+
this.isInitialized = true;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get experiment with evaluation details including reason.
|
|
178
|
+
* Following Statsig SDK pattern for exposure reasons:
|
|
179
|
+
* - Uninitialized: API called before initialize
|
|
180
|
+
* - NoValues: No data available
|
|
181
|
+
* - Network:Recognized / Network:Unrecognized
|
|
182
|
+
* - Cache:Recognized / Cache:Unrecognized
|
|
183
|
+
*/
|
|
168
184
|
getExperiment(name) {
|
|
185
|
+
// Check if uninitialized
|
|
186
|
+
if (!this.isInitialized) {
|
|
187
|
+
return {
|
|
188
|
+
config: null,
|
|
189
|
+
details: {
|
|
190
|
+
source: 'Uninitialized',
|
|
191
|
+
reason: 'Uninitialized',
|
|
192
|
+
isRecognized: false,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Check if no values
|
|
169
197
|
if (!this.values || !this.values.dynamic_configs) {
|
|
170
|
-
return
|
|
198
|
+
return {
|
|
199
|
+
config: null,
|
|
200
|
+
details: {
|
|
201
|
+
source: this.source,
|
|
202
|
+
reason: 'NoValues',
|
|
203
|
+
isRecognized: false,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
171
206
|
}
|
|
172
|
-
|
|
207
|
+
const config = this.values.dynamic_configs[name] || null;
|
|
208
|
+
const isRecognized = config !== null;
|
|
209
|
+
const baseSource = this._getBaseSource();
|
|
210
|
+
const subreason = isRecognized ? 'Recognized' : 'Unrecognized';
|
|
211
|
+
const reason = `${baseSource}:${subreason}`;
|
|
212
|
+
return {
|
|
213
|
+
config,
|
|
214
|
+
details: {
|
|
215
|
+
source: this.source,
|
|
216
|
+
reason,
|
|
217
|
+
isRecognized,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get the base source for building reason string.
|
|
223
|
+
*/
|
|
224
|
+
_getBaseSource() {
|
|
225
|
+
if (this.source === 'NetworkNotModified') {
|
|
226
|
+
return 'Network';
|
|
227
|
+
}
|
|
228
|
+
return this.source;
|
|
173
229
|
}
|
|
174
230
|
getValues() {
|
|
175
231
|
return this.values;
|
package/dist/WolvesClient.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WolvesUser } from './WolvesUser';
|
|
2
|
-
import {
|
|
2
|
+
import { DataSource } from './Store';
|
|
3
3
|
import { Experiment } from './Types';
|
|
4
4
|
export type LoadingStatus = 'Uninitialized' | 'Loading' | 'Ready';
|
|
5
5
|
export type WolvesUpdateDetails = {
|
|
@@ -107,8 +107,4 @@ export declare class WolvesClient {
|
|
|
107
107
|
logEvent(eventName: string, value?: string | number, metadata?: Record<string, string>): void;
|
|
108
108
|
shutdown(): Promise<void>;
|
|
109
109
|
private logExposure;
|
|
110
|
-
/**
|
|
111
|
-
* Get the underlying store (for testing purposes).
|
|
112
|
-
*/
|
|
113
|
-
getStore(): Store;
|
|
114
110
|
}
|
package/dist/WolvesClient.js
CHANGED
|
@@ -147,6 +147,7 @@ class WolvesClient {
|
|
|
147
147
|
}
|
|
148
148
|
// Timeout case - return with cached values
|
|
149
149
|
this.loadingStatus = 'Ready';
|
|
150
|
+
this.store.finalize();
|
|
150
151
|
return createUpdateDetails(cachedResult != null, this.store.getSource(), performance.now() - startTime, cachedResult ? null : new Error('Timeout waiting for network'), warnings);
|
|
151
152
|
});
|
|
152
153
|
}
|
|
@@ -172,14 +173,17 @@ class WolvesClient {
|
|
|
172
173
|
this.store.setValuesFromResult(notModifiedResult, user);
|
|
173
174
|
}
|
|
174
175
|
this.loadingStatus = 'Ready';
|
|
176
|
+
this.store.finalize();
|
|
175
177
|
return createUpdateDetails(true, this.store.getSource(), performance.now() - startTime, null, warnings);
|
|
176
178
|
}
|
|
177
179
|
// Network fetch failed
|
|
178
180
|
this.loadingStatus = 'Ready';
|
|
181
|
+
this.store.finalize();
|
|
179
182
|
return createUpdateDetails(cachedResult != null, this.store.getSource(), performance.now() - startTime, cachedResult ? null : new Error('Failed to fetch config from network'), [...warnings, 'NetworkFetchFailed']);
|
|
180
183
|
}
|
|
181
184
|
catch (e) {
|
|
182
185
|
this.loadingStatus = 'Ready';
|
|
186
|
+
this.store.finalize();
|
|
183
187
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
184
188
|
return createUpdateDetails(cachedResult != null, this.store.getSource(), performance.now() - startTime, cachedResult ? null : err, [...warnings, 'NetworkFetchFailed']);
|
|
185
189
|
}
|
|
@@ -221,6 +225,7 @@ class WolvesClient {
|
|
|
221
225
|
}
|
|
222
226
|
this.store.setValuesFromResult(cachedResult, user);
|
|
223
227
|
this.loadingStatus = 'Ready';
|
|
228
|
+
this.store.finalize();
|
|
224
229
|
// Trigger background refresh unless disabled
|
|
225
230
|
if (options === null || options === void 0 ? void 0 : options.disableBackgroundCacheRefresh) {
|
|
226
231
|
return createUpdateDetails(true, this.store.getSource(), performance.now() - startTime, null, warnings);
|
|
@@ -263,7 +268,7 @@ class WolvesClient {
|
|
|
263
268
|
}
|
|
264
269
|
getExperiment(experimentName) {
|
|
265
270
|
var _a, _b, _c;
|
|
266
|
-
const config = this.store.getExperiment(experimentName);
|
|
271
|
+
const { config, details } = this.store.getExperiment(experimentName);
|
|
267
272
|
const experiment = {
|
|
268
273
|
name: experimentName,
|
|
269
274
|
value: (_a = config === null || config === void 0 ? void 0 : config.value) !== null && _a !== void 0 ? _a : {},
|
|
@@ -278,8 +283,8 @@ class WolvesClient {
|
|
|
278
283
|
return val;
|
|
279
284
|
}
|
|
280
285
|
};
|
|
281
|
-
// Log exposure
|
|
282
|
-
this.logExposure(experimentName, config);
|
|
286
|
+
// Log exposure with reason
|
|
287
|
+
this.logExposure(experimentName, config, details);
|
|
283
288
|
return experiment;
|
|
284
289
|
}
|
|
285
290
|
/**
|
|
@@ -289,7 +294,7 @@ class WolvesClient {
|
|
|
289
294
|
*/
|
|
290
295
|
getExperimentForTest(experimentName, groupName) {
|
|
291
296
|
var _a;
|
|
292
|
-
const config = this.store.getExperiment(experimentName);
|
|
297
|
+
const { config, details } = this.store.getExperiment(experimentName);
|
|
293
298
|
const experimentId = (_a = config === null || config === void 0 ? void 0 : config.experiment_id) !== null && _a !== void 0 ? _a : '';
|
|
294
299
|
const experiment = {
|
|
295
300
|
name: experimentName,
|
|
@@ -301,7 +306,7 @@ class WolvesClient {
|
|
|
301
306
|
}
|
|
302
307
|
};
|
|
303
308
|
// Log exposure with the specified group and experiment ID from store
|
|
304
|
-
this.logExposure(experimentName, { group: groupName, experiment_id: experimentId, value: {} });
|
|
309
|
+
this.logExposure(experimentName, { group: groupName, experiment_id: experimentId, value: {} }, details);
|
|
305
310
|
return experiment;
|
|
306
311
|
}
|
|
307
312
|
logEvent(eventName, value, metadata) {
|
|
@@ -319,7 +324,7 @@ class WolvesClient {
|
|
|
319
324
|
yield this.logger.stop();
|
|
320
325
|
});
|
|
321
326
|
}
|
|
322
|
-
logExposure(experimentName, experiment) {
|
|
327
|
+
logExposure(experimentName, experiment, details) {
|
|
323
328
|
var _a, _b, _c;
|
|
324
329
|
const exposureEvent = {
|
|
325
330
|
eventName: 'exposure',
|
|
@@ -330,15 +335,10 @@ class WolvesClient {
|
|
|
330
335
|
experiment_id: (_a = experiment === null || experiment === void 0 ? void 0 : experiment.experiment_id) !== null && _a !== void 0 ? _a : '',
|
|
331
336
|
group: (_b = experiment === null || experiment === void 0 ? void 0 : experiment.group) !== null && _b !== void 0 ? _b : '',
|
|
332
337
|
value: JSON.stringify((_c = experiment === null || experiment === void 0 ? void 0 : experiment.value) !== null && _c !== void 0 ? _c : {}),
|
|
338
|
+
reason: details.reason,
|
|
333
339
|
},
|
|
334
340
|
};
|
|
335
341
|
this.logger.enqueue(exposureEvent);
|
|
336
342
|
}
|
|
337
|
-
/**
|
|
338
|
-
* Get the underlying store (for testing purposes).
|
|
339
|
-
*/
|
|
340
|
-
getStore() {
|
|
341
|
-
return this.store;
|
|
342
|
-
}
|
|
343
343
|
}
|
|
344
344
|
exports.WolvesClient = WolvesClient;
|
package/dist/WolvesMetadata.d.ts
CHANGED
package/dist/WolvesMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WolvesMetadataProvider = exports.SDK_TYPE = exports.SDK_VERSION = void 0;
|
|
4
|
-
exports.SDK_VERSION = '1.0.
|
|
4
|
+
exports.SDK_VERSION = '1.0.5';
|
|
5
5
|
exports.SDK_TYPE = 'wolves-js-client';
|
|
6
6
|
let metadata = {
|
|
7
7
|
sdk_version: exports.SDK_VERSION,
|