wolves-js-client 1.0.4 → 1.0.6
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/DeviceInfo.d.ts +22 -0
- package/dist/DeviceInfo.js +98 -0
- package/dist/Network.d.ts +3 -1
- package/dist/Network.js +17 -4
- package/dist/StatsigClient.d.ts +15 -0
- package/dist/StatsigClient.js +92 -0
- package/dist/StatsigUser.d.ts +6 -0
- package/dist/StatsigUser.js +2 -0
- package/dist/Store.d.ts +28 -2
- package/dist/Store.js +65 -9
- package/dist/WolvesClient.d.ts +2 -6
- package/dist/WolvesClient.js +14 -14
- package/dist/WolvesMetadata.d.ts +1 -1
- package/dist/WolvesMetadata.js +1 -1
- package/dist/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeviceInfo - Collects device and environment information for event metadata
|
|
3
|
+
* Aligns with Statsig SDK's approach: sends raw user_agent for server-side parsing
|
|
4
|
+
*
|
|
5
|
+
* Note: Browser/OS parsing is done server-side from user_agent, not client-side
|
|
6
|
+
*/
|
|
7
|
+
export type DeviceInfoData = {
|
|
8
|
+
user_agent: string;
|
|
9
|
+
locale: string;
|
|
10
|
+
screen_width: number | null;
|
|
11
|
+
screen_height: number | null;
|
|
12
|
+
viewport_width: number | null;
|
|
13
|
+
viewport_height: number | null;
|
|
14
|
+
timezone: string;
|
|
15
|
+
timezone_offset: number | null;
|
|
16
|
+
};
|
|
17
|
+
export declare function getDeviceInfo(): DeviceInfoData;
|
|
18
|
+
/**
|
|
19
|
+
* Get device info as a flat record suitable for event metadata
|
|
20
|
+
* Only includes non-null values
|
|
21
|
+
*/
|
|
22
|
+
export declare function getDeviceInfoForMetadata(): Record<string, string | number>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DeviceInfo - Collects device and environment information for event metadata
|
|
4
|
+
* Aligns with Statsig SDK's approach: sends raw user_agent for server-side parsing
|
|
5
|
+
*
|
|
6
|
+
* Note: Browser/OS parsing is done server-side from user_agent, not client-side
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getDeviceInfo = getDeviceInfo;
|
|
10
|
+
exports.getDeviceInfoForMetadata = getDeviceInfoForMetadata;
|
|
11
|
+
function getWindowSafe() {
|
|
12
|
+
if (typeof window !== 'undefined') {
|
|
13
|
+
return window;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function getNavigatorSafe() {
|
|
18
|
+
var _a;
|
|
19
|
+
const win = getWindowSafe();
|
|
20
|
+
return (_a = win === null || win === void 0 ? void 0 : win.navigator) !== null && _a !== void 0 ? _a : null;
|
|
21
|
+
}
|
|
22
|
+
function getSafeTimezone() {
|
|
23
|
+
try {
|
|
24
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || '';
|
|
25
|
+
}
|
|
26
|
+
catch (_a) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getSafeTimezoneOffset() {
|
|
31
|
+
try {
|
|
32
|
+
return new Date().getTimezoneOffset();
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let cachedDeviceInfo = null;
|
|
39
|
+
function getDeviceInfo() {
|
|
40
|
+
var _a, _b, _c, _d, _e, _f;
|
|
41
|
+
// Return cached info if available
|
|
42
|
+
if (cachedDeviceInfo) {
|
|
43
|
+
return cachedDeviceInfo;
|
|
44
|
+
}
|
|
45
|
+
const win = getWindowSafe();
|
|
46
|
+
const nav = getNavigatorSafe();
|
|
47
|
+
if (!win || !nav) {
|
|
48
|
+
// Return empty values for server-side or non-browser environments
|
|
49
|
+
return {
|
|
50
|
+
user_agent: '',
|
|
51
|
+
locale: '',
|
|
52
|
+
screen_width: null,
|
|
53
|
+
screen_height: null,
|
|
54
|
+
viewport_width: null,
|
|
55
|
+
viewport_height: null,
|
|
56
|
+
timezone: '',
|
|
57
|
+
timezone_offset: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const userAgent = nav.userAgent || '';
|
|
61
|
+
cachedDeviceInfo = {
|
|
62
|
+
// Send raw user_agent - server will parse browser/os info
|
|
63
|
+
user_agent: userAgent.length > 500 ? userAgent.substring(0, 500) : userAgent,
|
|
64
|
+
locale: nav.language || '',
|
|
65
|
+
screen_width: (_b = (_a = win.screen) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : null,
|
|
66
|
+
screen_height: (_d = (_c = win.screen) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : null,
|
|
67
|
+
viewport_width: (_e = win.innerWidth) !== null && _e !== void 0 ? _e : null,
|
|
68
|
+
viewport_height: (_f = win.innerHeight) !== null && _f !== void 0 ? _f : null,
|
|
69
|
+
timezone: getSafeTimezone(),
|
|
70
|
+
timezone_offset: getSafeTimezoneOffset(),
|
|
71
|
+
};
|
|
72
|
+
return cachedDeviceInfo;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get device info as a flat record suitable for event metadata
|
|
76
|
+
* Only includes non-null values
|
|
77
|
+
*/
|
|
78
|
+
function getDeviceInfoForMetadata() {
|
|
79
|
+
const info = getDeviceInfo();
|
|
80
|
+
const result = {};
|
|
81
|
+
if (info.user_agent)
|
|
82
|
+
result['user_agent'] = info.user_agent;
|
|
83
|
+
if (info.locale)
|
|
84
|
+
result['locale'] = info.locale;
|
|
85
|
+
if (info.screen_width !== null)
|
|
86
|
+
result['screen_width'] = info.screen_width;
|
|
87
|
+
if (info.screen_height !== null)
|
|
88
|
+
result['screen_height'] = info.screen_height;
|
|
89
|
+
if (info.viewport_width !== null)
|
|
90
|
+
result['viewport_width'] = info.viewport_width;
|
|
91
|
+
if (info.viewport_height !== null)
|
|
92
|
+
result['viewport_height'] = info.viewport_height;
|
|
93
|
+
if (info.timezone)
|
|
94
|
+
result['timezone'] = info.timezone;
|
|
95
|
+
if (info.timezone_offset !== null)
|
|
96
|
+
result['timezone_offset'] = info.timezone_offset;
|
|
97
|
+
return result;
|
|
98
|
+
}
|
package/dist/Network.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { WolvesUser } from './WolvesUser';
|
|
2
2
|
export declare const API_LOCAL = "http://localhost:8000/api";
|
|
3
3
|
export declare const API_DEV = "https://wolves-nova-dev.azurewebsites.net/api";
|
|
4
|
+
export declare const API_PROD = "https://wolves-nova.azurewebsites.net/api";
|
|
5
|
+
export type ApiEnvironment = 'local' | 'dev' | 'prod';
|
|
4
6
|
export declare class Network {
|
|
5
7
|
private sdkKey;
|
|
6
8
|
private api;
|
|
7
|
-
constructor(sdkKey: string,
|
|
9
|
+
constructor(sdkKey: string, apiEnv?: ApiEnvironment);
|
|
8
10
|
fetchConfig(user: WolvesUser, sinceTime?: number, retries?: number, backoff?: number): Promise<any>;
|
|
9
11
|
sendEvents(events: any[], retries?: number, backoff?: number): Promise<void>;
|
|
10
12
|
}
|
package/dist/Network.js
CHANGED
|
@@ -9,17 +9,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.Network = exports.API_DEV = exports.API_LOCAL = void 0;
|
|
12
|
+
exports.Network = exports.API_PROD = exports.API_DEV = exports.API_LOCAL = void 0;
|
|
13
13
|
const Log_1 = require("./Log");
|
|
14
14
|
const WolvesMetadata_1 = require("./WolvesMetadata");
|
|
15
15
|
const RETRYABLE_CODES = [408, 500, 502, 503, 504, 522, 524, 599];
|
|
16
|
-
// API endpoints
|
|
16
|
+
// API endpoints
|
|
17
17
|
exports.API_LOCAL = 'http://localhost:8000/api';
|
|
18
18
|
exports.API_DEV = 'https://wolves-nova-dev.azurewebsites.net/api';
|
|
19
|
+
exports.API_PROD = 'https://wolves-nova.azurewebsites.net/api';
|
|
20
|
+
const API_ENDPOINTS = {
|
|
21
|
+
local: exports.API_LOCAL,
|
|
22
|
+
dev: exports.API_DEV,
|
|
23
|
+
prod: exports.API_PROD,
|
|
24
|
+
};
|
|
25
|
+
const VALID_ENVIRONMENTS = ['local', 'dev', 'prod'];
|
|
26
|
+
function validateAndGetEndpoint(apiEnv) {
|
|
27
|
+
if (!VALID_ENVIRONMENTS.includes(apiEnv)) {
|
|
28
|
+
throw new Error(`Invalid apiEnv: "${apiEnv}". Must be one of: ${VALID_ENVIRONMENTS.join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
return API_ENDPOINTS[apiEnv];
|
|
31
|
+
}
|
|
19
32
|
class Network {
|
|
20
|
-
constructor(sdkKey,
|
|
33
|
+
constructor(sdkKey, apiEnv = 'prod') {
|
|
21
34
|
this.sdkKey = sdkKey;
|
|
22
|
-
this.api =
|
|
35
|
+
this.api = validateAndGetEndpoint(apiEnv);
|
|
23
36
|
}
|
|
24
37
|
fetchConfig(user_1, sinceTime_1) {
|
|
25
38
|
return __awaiter(this, arguments, void 0, function* (user, sinceTime, retries = 2, backoff = 1000) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StatsigUser } from './StatsigUser';
|
|
2
|
+
import { Experiment, StatsigUpdateDetails } from './Types';
|
|
3
|
+
export declare class StatsigClient {
|
|
4
|
+
private sdkKey;
|
|
5
|
+
private user;
|
|
6
|
+
private network;
|
|
7
|
+
private store;
|
|
8
|
+
private initialized;
|
|
9
|
+
constructor(sdkKey: string, user: StatsigUser);
|
|
10
|
+
initializeAsync(): Promise<StatsigUpdateDetails>;
|
|
11
|
+
initializeSync(): StatsigUpdateDetails;
|
|
12
|
+
getExperiment(experimentName: string): Experiment;
|
|
13
|
+
logEvent(eventName: string, value?: string | number, metadata?: Record<string, string>): void;
|
|
14
|
+
private logExposure;
|
|
15
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.StatsigClient = void 0;
|
|
13
|
+
const Network_1 = require("./Network");
|
|
14
|
+
const Store_1 = require("./Store");
|
|
15
|
+
class StatsigClient {
|
|
16
|
+
constructor(sdkKey, user) {
|
|
17
|
+
this.initialized = false;
|
|
18
|
+
this.sdkKey = sdkKey;
|
|
19
|
+
this.user = user;
|
|
20
|
+
this.network = new Network_1.Network(sdkKey);
|
|
21
|
+
this.store = new Store_1.Store();
|
|
22
|
+
}
|
|
23
|
+
initializeAsync() {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const config = yield this.network.fetchConfig(this.user);
|
|
26
|
+
if (config) {
|
|
27
|
+
this.store.setValues(config);
|
|
28
|
+
this.initialized = true;
|
|
29
|
+
return { success: true };
|
|
30
|
+
}
|
|
31
|
+
return { success: false, errorMessage: 'Failed to fetch config' };
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
initializeSync() {
|
|
35
|
+
// In a real implementation, this might load from local storage or cache.
|
|
36
|
+
// For this minimal implementation, we trigger a background fetch.
|
|
37
|
+
this.network.fetchConfig(this.user).then((config) => {
|
|
38
|
+
if (config) {
|
|
39
|
+
this.store.setValues(config);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
this.initialized = true;
|
|
43
|
+
return { success: true };
|
|
44
|
+
}
|
|
45
|
+
getExperiment(experimentName) {
|
|
46
|
+
var _a, _b, _c;
|
|
47
|
+
const config = this.store.getExperiment(experimentName);
|
|
48
|
+
const experiment = {
|
|
49
|
+
name: experimentName,
|
|
50
|
+
value: (_a = config === null || config === void 0 ? void 0 : config.value) !== null && _a !== void 0 ? _a : {},
|
|
51
|
+
ruleID: (_b = config === null || config === void 0 ? void 0 : config.rule_id) !== null && _b !== void 0 ? _b : '',
|
|
52
|
+
groupName: (_c = config === null || config === void 0 ? void 0 : config.group) !== null && _c !== void 0 ? _c : null,
|
|
53
|
+
get: (key, defaultValue) => {
|
|
54
|
+
var _a;
|
|
55
|
+
const val = (_a = config === null || config === void 0 ? void 0 : config.value) === null || _a === void 0 ? void 0 : _a[key];
|
|
56
|
+
if (val === undefined || val === null) {
|
|
57
|
+
return defaultValue;
|
|
58
|
+
}
|
|
59
|
+
return val;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
// Log exposure
|
|
63
|
+
this.logExposure(experimentName, config);
|
|
64
|
+
return experiment;
|
|
65
|
+
}
|
|
66
|
+
logEvent(eventName, value, metadata) {
|
|
67
|
+
const event = {
|
|
68
|
+
eventName,
|
|
69
|
+
value,
|
|
70
|
+
metadata,
|
|
71
|
+
user: this.user,
|
|
72
|
+
time: Date.now(),
|
|
73
|
+
};
|
|
74
|
+
this.network.sendEvents([event]);
|
|
75
|
+
}
|
|
76
|
+
logExposure(experimentName, experiment) {
|
|
77
|
+
var _a, _b, _c;
|
|
78
|
+
const exposureEvent = {
|
|
79
|
+
eventName: 'statsig::exposure',
|
|
80
|
+
user: this.user,
|
|
81
|
+
time: Date.now(),
|
|
82
|
+
metadata: {
|
|
83
|
+
config: experimentName,
|
|
84
|
+
ruleID: (_a = experiment === null || experiment === void 0 ? void 0 : experiment.rule_id) !== null && _a !== void 0 ? _a : '',
|
|
85
|
+
group: (_b = experiment === null || experiment === void 0 ? void 0 : experiment.group) !== null && _b !== void 0 ? _b : '',
|
|
86
|
+
value: JSON.stringify((_c = experiment === null || experiment === void 0 ? void 0 : experiment.value) !== null && _c !== void 0 ? _c : {}),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
this.network.sendEvents([exposureEvent]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.StatsigClient = StatsigClient;
|
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 = {
|
|
@@ -23,7 +23,7 @@ export declare class WolvesClient {
|
|
|
23
23
|
private logger;
|
|
24
24
|
private loadingStatus;
|
|
25
25
|
private initializePromise;
|
|
26
|
-
constructor(sdkKey: string, user: WolvesUser);
|
|
26
|
+
constructor(sdkKey: string, user: WolvesUser, apiEnv?: string);
|
|
27
27
|
/**
|
|
28
28
|
* Returns the current loading status of the client.
|
|
29
29
|
*/
|
|
@@ -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
|
@@ -22,12 +22,12 @@ function createUpdateDetails(success, source, duration, error, warnings = []) {
|
|
|
22
22
|
return { success, source, duration, error, warnings };
|
|
23
23
|
}
|
|
24
24
|
class WolvesClient {
|
|
25
|
-
constructor(sdkKey, user) {
|
|
25
|
+
constructor(sdkKey, user, apiEnv = 'prod') {
|
|
26
26
|
this.loadingStatus = 'Uninitialized';
|
|
27
27
|
this.initializePromise = null;
|
|
28
28
|
this.sdkKey = sdkKey;
|
|
29
29
|
this.user = user;
|
|
30
|
-
this.network = new Network_1.Network(sdkKey);
|
|
30
|
+
this.network = new Network_1.Network(sdkKey, apiEnv);
|
|
31
31
|
this.store = new Store_1.Store(sdkKey);
|
|
32
32
|
this.logger = new EventLogger_1.EventLogger(this.network);
|
|
33
33
|
}
|
|
@@ -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.6';
|
|
5
5
|
exports.SDK_TYPE = 'wolves-js-client';
|
|
6
6
|
let metadata = {
|
|
7
7
|
sdk_version: exports.SDK_VERSION,
|
package/dist/index.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export type { WolvesMetadata } from './WolvesMetadata';
|
|
|
8
8
|
export { Storage } from './StorageProvider';
|
|
9
9
|
export type { StorageProvider } from './StorageProvider';
|
|
10
10
|
export type { DataSource, DataAdapterResult } from './Store';
|
|
11
|
+
export type { ApiEnvironment } from './Network';
|