wolves-js-client 1.0.3 → 1.0.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/dist/EventLogger.d.ts +2 -1
- package/dist/EventLogger.js +3 -0
- package/dist/StorageProvider.d.ts +35 -0
- package/dist/StorageProvider.js +131 -0
- package/dist/Store.d.ts +55 -2
- package/dist/Store.js +168 -2
- package/dist/Types.d.ts +0 -4
- package/dist/WolvesClient.d.ts +95 -6
- package/dist/WolvesClient.js +232 -33
- package/dist/WolvesMetadata.d.ts +1 -1
- package/dist/WolvesMetadata.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/DeviceInfo.d.ts +0 -22
- package/dist/DeviceInfo.js +0 -98
- package/dist/StatsigClient.d.ts +0 -15
- package/dist/StatsigClient.js +0 -92
- package/dist/StatsigUser.d.ts +0 -6
- package/dist/StatsigUser.js +0 -2
package/dist/EventLogger.d.ts
CHANGED
package/dist/EventLogger.js
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for storage providers that can persist data.
|
|
3
|
+
* Supports both synchronous and asynchronous storage implementations.
|
|
4
|
+
*/
|
|
5
|
+
export interface StorageProvider {
|
|
6
|
+
isReady: () => boolean;
|
|
7
|
+
isReadyResolver: () => Promise<void> | null;
|
|
8
|
+
getProviderName: () => string;
|
|
9
|
+
getItem: (key: string) => string | null;
|
|
10
|
+
setItem: (key: string, value: string) => void;
|
|
11
|
+
removeItem: (key: string) => void;
|
|
12
|
+
getAllKeys: () => readonly string[];
|
|
13
|
+
}
|
|
14
|
+
interface StorageProviderManagement {
|
|
15
|
+
_setProvider: (newProvider: StorageProvider) => void;
|
|
16
|
+
_setDisabled: (isDisabled: boolean) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Global storage singleton that automatically chooses between localStorage (browser)
|
|
20
|
+
* and in-memory storage (Node.js or when localStorage is unavailable).
|
|
21
|
+
*/
|
|
22
|
+
export declare const Storage: StorageProvider & StorageProviderManagement;
|
|
23
|
+
/**
|
|
24
|
+
* Helper to get a parsed object from storage.
|
|
25
|
+
*/
|
|
26
|
+
export declare function _getObjectFromStorage<T>(key: string): T | null;
|
|
27
|
+
/**
|
|
28
|
+
* Helper to set an object in storage as JSON string.
|
|
29
|
+
*/
|
|
30
|
+
export declare function _setObjectInStorage(key: string, obj: unknown): void;
|
|
31
|
+
/**
|
|
32
|
+
* Clear the in-memory store (useful for testing).
|
|
33
|
+
*/
|
|
34
|
+
export declare function _clearInMemoryStore(): void;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Storage = void 0;
|
|
4
|
+
exports._getObjectFromStorage = _getObjectFromStorage;
|
|
5
|
+
exports._setObjectInStorage = _setObjectInStorage;
|
|
6
|
+
exports._clearInMemoryStore = _clearInMemoryStore;
|
|
7
|
+
const Log_1 = require("./Log");
|
|
8
|
+
const inMemoryStore = {};
|
|
9
|
+
/**
|
|
10
|
+
* In-memory storage provider for non-browser environments or when localStorage is unavailable.
|
|
11
|
+
*/
|
|
12
|
+
const _inMemoryProvider = {
|
|
13
|
+
isReady: () => true,
|
|
14
|
+
isReadyResolver: () => null,
|
|
15
|
+
getProviderName: () => 'InMemory',
|
|
16
|
+
getItem: (key) => inMemoryStore[key] ? inMemoryStore[key] : null,
|
|
17
|
+
setItem: (key, value) => {
|
|
18
|
+
inMemoryStore[key] = value;
|
|
19
|
+
},
|
|
20
|
+
removeItem: (key) => {
|
|
21
|
+
delete inMemoryStore[key];
|
|
22
|
+
},
|
|
23
|
+
getAllKeys: () => Object.keys(inMemoryStore),
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Helper function to safely get window object.
|
|
27
|
+
*/
|
|
28
|
+
function _getWindowSafe() {
|
|
29
|
+
try {
|
|
30
|
+
if (typeof window !== 'undefined') {
|
|
31
|
+
return window;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
// window is not defined (Node.js environment)
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Try to create a localStorage provider if available.
|
|
41
|
+
*/
|
|
42
|
+
let _localStorageProvider = null;
|
|
43
|
+
try {
|
|
44
|
+
const win = _getWindowSafe();
|
|
45
|
+
if (win &&
|
|
46
|
+
win.localStorage &&
|
|
47
|
+
typeof win.localStorage.getItem === 'function') {
|
|
48
|
+
_localStorageProvider = {
|
|
49
|
+
isReady: () => true,
|
|
50
|
+
isReadyResolver: () => null,
|
|
51
|
+
getProviderName: () => 'LocalStorage',
|
|
52
|
+
getItem: (key) => win.localStorage.getItem(key),
|
|
53
|
+
setItem: (key, value) => win.localStorage.setItem(key, value),
|
|
54
|
+
removeItem: (key) => win.localStorage.removeItem(key),
|
|
55
|
+
getAllKeys: () => Object.keys(win.localStorage),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (_a) {
|
|
60
|
+
Log_1.Log.warn('Failed to setup localStorage provider');
|
|
61
|
+
}
|
|
62
|
+
let _main = _localStorageProvider !== null && _localStorageProvider !== void 0 ? _localStorageProvider : _inMemoryProvider;
|
|
63
|
+
let _current = _main;
|
|
64
|
+
/**
|
|
65
|
+
* Helper function that falls back to in-memory storage on SecurityError.
|
|
66
|
+
*/
|
|
67
|
+
function _inMemoryBreaker(action) {
|
|
68
|
+
try {
|
|
69
|
+
return action();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof Error && error.name === 'SecurityError') {
|
|
73
|
+
exports.Storage._setProvider(_inMemoryProvider);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Global storage singleton that automatically chooses between localStorage (browser)
|
|
81
|
+
* and in-memory storage (Node.js or when localStorage is unavailable).
|
|
82
|
+
*/
|
|
83
|
+
exports.Storage = {
|
|
84
|
+
isReady: () => _current.isReady(),
|
|
85
|
+
isReadyResolver: () => _current.isReadyResolver(),
|
|
86
|
+
getProviderName: () => _current.getProviderName(),
|
|
87
|
+
getItem: (key) => _inMemoryBreaker(() => _current.getItem(key)),
|
|
88
|
+
setItem: (key, value) => _inMemoryBreaker(() => _current.setItem(key, value)),
|
|
89
|
+
removeItem: (key) => _current.removeItem(key),
|
|
90
|
+
getAllKeys: () => _current.getAllKeys(),
|
|
91
|
+
// StorageProviderManagement
|
|
92
|
+
_setProvider: (newProvider) => {
|
|
93
|
+
_main = newProvider;
|
|
94
|
+
_current = newProvider;
|
|
95
|
+
},
|
|
96
|
+
_setDisabled: (isDisabled) => {
|
|
97
|
+
if (isDisabled) {
|
|
98
|
+
_current = _inMemoryProvider;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
_current = _main;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Helper to get a parsed object from storage.
|
|
107
|
+
*/
|
|
108
|
+
function _getObjectFromStorage(key) {
|
|
109
|
+
const value = exports.Storage.getItem(key);
|
|
110
|
+
try {
|
|
111
|
+
return JSON.parse(value !== null && value !== void 0 ? value : 'null');
|
|
112
|
+
}
|
|
113
|
+
catch (_a) {
|
|
114
|
+
Log_1.Log.error(`Failed to parse value for key "${key}"`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Helper to set an object in storage as JSON string.
|
|
120
|
+
*/
|
|
121
|
+
function _setObjectInStorage(key, obj) {
|
|
122
|
+
exports.Storage.setItem(key, JSON.stringify(obj));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Clear the in-memory store (useful for testing).
|
|
126
|
+
*/
|
|
127
|
+
function _clearInMemoryStore() {
|
|
128
|
+
for (const key of Object.keys(inMemoryStore)) {
|
|
129
|
+
delete inMemoryStore[key];
|
|
130
|
+
}
|
|
131
|
+
}
|
package/dist/Store.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WolvesUser } from './WolvesUser';
|
|
1
2
|
export type ExperimentConfig = {
|
|
2
3
|
value: Record<string, any>;
|
|
3
4
|
experiment_id?: string;
|
|
@@ -8,11 +9,63 @@ export type InitializeResponse = {
|
|
|
8
9
|
has_updates: boolean;
|
|
9
10
|
time: number;
|
|
10
11
|
};
|
|
12
|
+
export type DataSource = 'Network' | 'Cache' | 'NetworkNotModified' | 'NoValues';
|
|
13
|
+
export type DataAdapterResult = {
|
|
14
|
+
source: DataSource;
|
|
15
|
+
data: string;
|
|
16
|
+
receivedAt: number;
|
|
17
|
+
fullUserHash?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Store class that manages experiment configurations with caching support.
|
|
21
|
+
* Supports localStorage in browser environments and in-memory storage elsewhere.
|
|
22
|
+
*/
|
|
11
23
|
export declare class Store {
|
|
12
24
|
private values;
|
|
13
|
-
|
|
14
|
-
|
|
25
|
+
private source;
|
|
26
|
+
private sdkKey;
|
|
27
|
+
private cacheLimit;
|
|
28
|
+
private inMemoryCache;
|
|
29
|
+
constructor(sdkKey?: string);
|
|
30
|
+
/**
|
|
31
|
+
* Get the cache key for a specific user.
|
|
32
|
+
*/
|
|
33
|
+
getCacheKey(user?: WolvesUser): string;
|
|
34
|
+
/**
|
|
35
|
+
* Load cached data synchronously.
|
|
36
|
+
* First checks in-memory cache, then persistent storage.
|
|
37
|
+
*/
|
|
38
|
+
getDataSync(user?: WolvesUser): DataAdapterResult | null;
|
|
39
|
+
/**
|
|
40
|
+
* Load from persistent storage cache.
|
|
41
|
+
*/
|
|
42
|
+
private _loadFromCache;
|
|
43
|
+
/**
|
|
44
|
+
* Set values from a data adapter result.
|
|
45
|
+
*/
|
|
46
|
+
setValuesFromResult(result: DataAdapterResult | null, user?: WolvesUser): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Set values directly from an InitializeResponse (legacy method).
|
|
49
|
+
*/
|
|
50
|
+
setValues(values: InitializeResponse, user?: WolvesUser): void;
|
|
51
|
+
/**
|
|
52
|
+
* Write to persistent cache with eviction support.
|
|
53
|
+
*/
|
|
54
|
+
private _writeToCache;
|
|
55
|
+
/**
|
|
56
|
+
* Run cache eviction to stay within limits.
|
|
57
|
+
*/
|
|
58
|
+
private _runCacheEviction;
|
|
59
|
+
/**
|
|
60
|
+
* Reset the store for a new user.
|
|
61
|
+
*/
|
|
62
|
+
reset(): void;
|
|
15
63
|
getExperiment(name: string): ExperimentConfig | null;
|
|
16
64
|
getValues(): InitializeResponse | null;
|
|
65
|
+
getSource(): DataSource;
|
|
17
66
|
getLastUpdateTime(): number | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Clear in-memory cache (useful for testing).
|
|
69
|
+
*/
|
|
70
|
+
clearInMemoryCache(): void;
|
|
18
71
|
}
|
package/dist/Store.js
CHANGED
|
@@ -1,12 +1,169 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Store = void 0;
|
|
4
|
+
const StorageProvider_1 = require("./StorageProvider");
|
|
5
|
+
const Log_1 = require("./Log");
|
|
6
|
+
const CACHE_KEY_PREFIX = 'wolves.cache';
|
|
7
|
+
const CACHE_LIMIT = 10;
|
|
8
|
+
const MAX_CACHE_WRITE_ATTEMPTS = 8;
|
|
9
|
+
const LAST_MODIFIED_KEY = 'wolves.last_modified_time';
|
|
10
|
+
/**
|
|
11
|
+
* Generate a hash for user identity to create unique cache keys.
|
|
12
|
+
*/
|
|
13
|
+
function _getUserHash(user) {
|
|
14
|
+
if (!user)
|
|
15
|
+
return 'anonymous';
|
|
16
|
+
const parts = [user.userID || '', user.email || ''];
|
|
17
|
+
return parts.join('|');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Store class that manages experiment configurations with caching support.
|
|
21
|
+
* Supports localStorage in browser environments and in-memory storage elsewhere.
|
|
22
|
+
*/
|
|
4
23
|
class Store {
|
|
5
|
-
constructor() {
|
|
24
|
+
constructor(sdkKey = '') {
|
|
6
25
|
this.values = null;
|
|
26
|
+
this.source = 'NoValues';
|
|
27
|
+
this.cacheLimit = CACHE_LIMIT;
|
|
28
|
+
this.inMemoryCache = new Map();
|
|
29
|
+
this.sdkKey = sdkKey;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the cache key for a specific user.
|
|
33
|
+
*/
|
|
34
|
+
getCacheKey(user) {
|
|
35
|
+
const userHash = _getUserHash(user);
|
|
36
|
+
return `${CACHE_KEY_PREFIX}.${this.sdkKey}.${userHash}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Load cached data synchronously.
|
|
40
|
+
* First checks in-memory cache, then persistent storage.
|
|
41
|
+
*/
|
|
42
|
+
getDataSync(user) {
|
|
43
|
+
const cacheKey = this.getCacheKey(user);
|
|
44
|
+
// Check in-memory cache first
|
|
45
|
+
const inMemResult = this.inMemoryCache.get(cacheKey);
|
|
46
|
+
if (inMemResult) {
|
|
47
|
+
return inMemResult;
|
|
48
|
+
}
|
|
49
|
+
// Check persistent storage
|
|
50
|
+
const cached = this._loadFromCache(cacheKey);
|
|
51
|
+
if (cached) {
|
|
52
|
+
this.inMemoryCache.set(cacheKey, cached);
|
|
53
|
+
return cached;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Load from persistent storage cache.
|
|
59
|
+
*/
|
|
60
|
+
_loadFromCache(cacheKey) {
|
|
61
|
+
const cache = StorageProvider_1.Storage.getItem(cacheKey);
|
|
62
|
+
if (cache == null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const result = JSON.parse(cache);
|
|
67
|
+
if (result && result.source) {
|
|
68
|
+
return Object.assign(Object.assign({}, result), { source: 'Cache' });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (_a) {
|
|
72
|
+
Log_1.Log.error(`Failed to parse cached value for key "${cacheKey}"`);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
7
75
|
}
|
|
8
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Set values from a data adapter result.
|
|
78
|
+
*/
|
|
79
|
+
setValuesFromResult(result, user) {
|
|
80
|
+
if (!result || !result.data) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(result.data);
|
|
85
|
+
this.values = parsed;
|
|
86
|
+
this.source = result.source;
|
|
87
|
+
// Update caches
|
|
88
|
+
const cacheKey = this.getCacheKey(user);
|
|
89
|
+
this.inMemoryCache.set(cacheKey, result);
|
|
90
|
+
// Persist to storage if it's from network
|
|
91
|
+
if (result.source === 'Network' || result.source === 'NetworkNotModified') {
|
|
92
|
+
this._writeToCache(cacheKey, result);
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch (_a) {
|
|
97
|
+
Log_1.Log.error('Failed to parse data adapter result');
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Set values directly from an InitializeResponse (legacy method).
|
|
103
|
+
*/
|
|
104
|
+
setValues(values, user) {
|
|
9
105
|
this.values = values;
|
|
106
|
+
this.source = 'Network';
|
|
107
|
+
// Create and cache the result
|
|
108
|
+
const result = {
|
|
109
|
+
source: 'Network',
|
|
110
|
+
data: JSON.stringify(values),
|
|
111
|
+
receivedAt: Date.now(),
|
|
112
|
+
fullUserHash: _getUserHash(user),
|
|
113
|
+
};
|
|
114
|
+
const cacheKey = this.getCacheKey(user);
|
|
115
|
+
this.inMemoryCache.set(cacheKey, result);
|
|
116
|
+
this._writeToCache(cacheKey, result);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Write to persistent cache with eviction support.
|
|
120
|
+
*/
|
|
121
|
+
_writeToCache(cacheKey, result) {
|
|
122
|
+
const resultString = JSON.stringify(result);
|
|
123
|
+
for (let i = 0; i < MAX_CACHE_WRITE_ATTEMPTS; i++) {
|
|
124
|
+
try {
|
|
125
|
+
StorageProvider_1.Storage.setItem(cacheKey, resultString);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (!(error instanceof Error) ||
|
|
130
|
+
!(error.toString().includes('QuotaExceededError') ||
|
|
131
|
+
error.toString().includes('QUOTA_EXCEEDED_ERR')) ||
|
|
132
|
+
this.cacheLimit <= 1) {
|
|
133
|
+
Log_1.Log.warn('Failed to write to cache', error);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.cacheLimit = Math.ceil(this.cacheLimit / 2);
|
|
137
|
+
this._runCacheEviction(cacheKey, this.cacheLimit - 1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
this._runCacheEviction(cacheKey);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Run cache eviction to stay within limits.
|
|
144
|
+
*/
|
|
145
|
+
_runCacheEviction(cacheKey, limit = this.cacheLimit) {
|
|
146
|
+
var _a;
|
|
147
|
+
const lastModifiedTimeMap = (_a = (0, StorageProvider_1._getObjectFromStorage)(LAST_MODIFIED_KEY)) !== null && _a !== void 0 ? _a : {};
|
|
148
|
+
lastModifiedTimeMap[cacheKey] = Date.now();
|
|
149
|
+
const keys = Object.keys(lastModifiedTimeMap);
|
|
150
|
+
if (keys.length > limit) {
|
|
151
|
+
// Sort by time and remove oldest
|
|
152
|
+
const sortedKeys = keys.sort((a, b) => lastModifiedTimeMap[a] - lastModifiedTimeMap[b]);
|
|
153
|
+
const keysToEvict = sortedKeys.slice(0, keys.length - limit);
|
|
154
|
+
for (const evictKey of keysToEvict) {
|
|
155
|
+
delete lastModifiedTimeMap[evictKey];
|
|
156
|
+
StorageProvider_1.Storage.removeItem(evictKey);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
(0, StorageProvider_1._setObjectInStorage)(LAST_MODIFIED_KEY, lastModifiedTimeMap);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Reset the store for a new user.
|
|
163
|
+
*/
|
|
164
|
+
reset() {
|
|
165
|
+
this.values = null;
|
|
166
|
+
this.source = 'NoValues';
|
|
10
167
|
}
|
|
11
168
|
getExperiment(name) {
|
|
12
169
|
if (!this.values || !this.values.dynamic_configs) {
|
|
@@ -17,9 +174,18 @@ class Store {
|
|
|
17
174
|
getValues() {
|
|
18
175
|
return this.values;
|
|
19
176
|
}
|
|
177
|
+
getSource() {
|
|
178
|
+
return this.source;
|
|
179
|
+
}
|
|
20
180
|
getLastUpdateTime() {
|
|
21
181
|
var _a;
|
|
22
182
|
return (_a = this.values) === null || _a === void 0 ? void 0 : _a.time;
|
|
23
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Clear in-memory cache (useful for testing).
|
|
186
|
+
*/
|
|
187
|
+
clearInMemoryCache() {
|
|
188
|
+
this.inMemoryCache.clear();
|
|
189
|
+
}
|
|
24
190
|
}
|
|
25
191
|
exports.Store = Store;
|
package/dist/Types.d.ts
CHANGED
package/dist/WolvesClient.d.ts
CHANGED
|
@@ -1,17 +1,102 @@
|
|
|
1
1
|
import { WolvesUser } from './WolvesUser';
|
|
2
|
-
import {
|
|
2
|
+
import { Store, DataSource } from './Store';
|
|
3
|
+
import { Experiment } from './Types';
|
|
4
|
+
export type LoadingStatus = 'Uninitialized' | 'Loading' | 'Ready';
|
|
5
|
+
export type WolvesUpdateDetails = {
|
|
6
|
+
success: boolean;
|
|
7
|
+
source: DataSource;
|
|
8
|
+
duration: number;
|
|
9
|
+
error: Error | null;
|
|
10
|
+
warnings: string[];
|
|
11
|
+
};
|
|
12
|
+
export type SyncUpdateOptions = {
|
|
13
|
+
disableBackgroundCacheRefresh?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type AsyncUpdateOptions = {
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
};
|
|
3
18
|
export declare class WolvesClient {
|
|
4
19
|
private sdkKey;
|
|
5
20
|
private user;
|
|
6
21
|
private network;
|
|
7
22
|
private store;
|
|
8
23
|
private logger;
|
|
9
|
-
private
|
|
24
|
+
private loadingStatus;
|
|
25
|
+
private initializePromise;
|
|
10
26
|
constructor(sdkKey: string, user: WolvesUser);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Returns the current loading status of the client.
|
|
29
|
+
*/
|
|
30
|
+
getLoadingStatus(): LoadingStatus;
|
|
31
|
+
/**
|
|
32
|
+
* Initializes the WolvesClient asynchronously by first using cached values
|
|
33
|
+
* and then updating to the latest values from the network.
|
|
34
|
+
*
|
|
35
|
+
* @param options - Optional settings including timeout
|
|
36
|
+
* @returns Promise that resolves once initialized with latest values or timeout is hit
|
|
37
|
+
*/
|
|
38
|
+
initializeAsync(options?: AsyncUpdateOptions): Promise<WolvesUpdateDetails>;
|
|
39
|
+
/**
|
|
40
|
+
* Internal implementation of async initialization.
|
|
41
|
+
*/
|
|
42
|
+
private _initializeAsyncImpl;
|
|
43
|
+
/**
|
|
44
|
+
* Initializes the WolvesClient synchronously using cached values.
|
|
45
|
+
* After initialization, cache values are updated in the background for future use.
|
|
46
|
+
* This is useful for quickly starting with last-known-good configurations while
|
|
47
|
+
* refreshing data in the background.
|
|
48
|
+
*
|
|
49
|
+
* Aligned with Statsig SDK behavior:
|
|
50
|
+
* - If no cached values exist, returns with NoCachedValues warning
|
|
51
|
+
* - Triggers background network fetch to update cache for next session
|
|
52
|
+
*
|
|
53
|
+
* @param options - Optional settings
|
|
54
|
+
* @returns Update details including success status and any warnings
|
|
55
|
+
*/
|
|
56
|
+
initializeSync(options?: SyncUpdateOptions): WolvesUpdateDetails;
|
|
57
|
+
/**
|
|
58
|
+
* Asynchronously updates the user by first using cached values and then fetching
|
|
59
|
+
* the latest values from the network.
|
|
60
|
+
*
|
|
61
|
+
* @param user - The new user to switch to
|
|
62
|
+
* @param options - Optional settings including timeout
|
|
63
|
+
* @returns Promise that resolves once updated with latest values or timeout is hit
|
|
64
|
+
*/
|
|
65
|
+
updateUserAsync(user: WolvesUser, options?: AsyncUpdateOptions): Promise<WolvesUpdateDetails>;
|
|
66
|
+
/**
|
|
67
|
+
* Internal implementation of async user update.
|
|
68
|
+
*/
|
|
69
|
+
private _updateUserAsyncImpl;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch from network and update the store.
|
|
72
|
+
*/
|
|
73
|
+
private _fetchAndUpdateFromNetwork;
|
|
74
|
+
/**
|
|
75
|
+
* Synchronously updates the user in the client and switches to cached values.
|
|
76
|
+
* After the initial switch to cached values, updates values in the background.
|
|
77
|
+
*
|
|
78
|
+
* Aligned with Statsig SDK behavior:
|
|
79
|
+
* - Immediately returns with cached values if available
|
|
80
|
+
* - If no cached values exist, returns with NoCachedValues warning
|
|
81
|
+
* - Triggers background network fetch (unless disableBackgroundCacheRefresh is true)
|
|
82
|
+
*
|
|
83
|
+
* @param user - The new user to switch to
|
|
84
|
+
* @param options - Optional settings
|
|
85
|
+
* @returns Update details including success status and any warnings
|
|
86
|
+
*/
|
|
87
|
+
updateUserSync(user: WolvesUser, options?: SyncUpdateOptions): WolvesUpdateDetails;
|
|
88
|
+
/**
|
|
89
|
+
* Internal implementation of sync user update.
|
|
90
|
+
*/
|
|
91
|
+
private _updateUserSyncImpl;
|
|
92
|
+
/**
|
|
93
|
+
* Run a background update to refresh the cache.
|
|
94
|
+
*/
|
|
95
|
+
private _runBackgroundUpdate;
|
|
96
|
+
/**
|
|
97
|
+
* Reset the store and logger for a new user.
|
|
98
|
+
*/
|
|
99
|
+
private _resetForUser;
|
|
15
100
|
getExperiment(experimentName: string): Experiment;
|
|
16
101
|
/**
|
|
17
102
|
* Test-only method to manually trigger an exposure event with a specified group.
|
|
@@ -22,4 +107,8 @@ export declare class WolvesClient {
|
|
|
22
107
|
logEvent(eventName: string, value?: string | number, metadata?: Record<string, string>): void;
|
|
23
108
|
shutdown(): Promise<void>;
|
|
24
109
|
private logExposure;
|
|
110
|
+
/**
|
|
111
|
+
* Get the underlying store (for testing purposes).
|
|
112
|
+
*/
|
|
113
|
+
getStore(): Store;
|
|
25
114
|
}
|
package/dist/WolvesClient.js
CHANGED
|
@@ -13,60 +13,253 @@ exports.WolvesClient = void 0;
|
|
|
13
13
|
const Network_1 = require("./Network");
|
|
14
14
|
const Store_1 = require("./Store");
|
|
15
15
|
const EventLogger_1 = require("./EventLogger");
|
|
16
|
+
const StorageProvider_1 = require("./StorageProvider");
|
|
17
|
+
const Log_1 = require("./Log");
|
|
18
|
+
/**
|
|
19
|
+
* Creates a WolvesUpdateDetails result object.
|
|
20
|
+
*/
|
|
21
|
+
function createUpdateDetails(success, source, duration, error, warnings = []) {
|
|
22
|
+
return { success, source, duration, error, warnings };
|
|
23
|
+
}
|
|
16
24
|
class WolvesClient {
|
|
17
25
|
constructor(sdkKey, user) {
|
|
18
|
-
this.
|
|
26
|
+
this.loadingStatus = 'Uninitialized';
|
|
27
|
+
this.initializePromise = null;
|
|
19
28
|
this.sdkKey = sdkKey;
|
|
20
29
|
this.user = user;
|
|
21
30
|
this.network = new Network_1.Network(sdkKey);
|
|
22
|
-
this.store = new Store_1.Store();
|
|
31
|
+
this.store = new Store_1.Store(sdkKey);
|
|
23
32
|
this.logger = new EventLogger_1.EventLogger(this.network);
|
|
24
33
|
}
|
|
25
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Returns the current loading status of the client.
|
|
36
|
+
*/
|
|
37
|
+
getLoadingStatus() {
|
|
38
|
+
return this.loadingStatus;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Initializes the WolvesClient asynchronously by first using cached values
|
|
42
|
+
* and then updating to the latest values from the network.
|
|
43
|
+
*
|
|
44
|
+
* @param options - Optional settings including timeout
|
|
45
|
+
* @returns Promise that resolves once initialized with latest values or timeout is hit
|
|
46
|
+
*/
|
|
47
|
+
initializeAsync(options) {
|
|
26
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.store.setValues(config);
|
|
30
|
-
this.initialized = true;
|
|
31
|
-
return { success: true };
|
|
49
|
+
if (this.initializePromise) {
|
|
50
|
+
return this.initializePromise;
|
|
32
51
|
}
|
|
33
|
-
|
|
52
|
+
this.initializePromise = this._initializeAsyncImpl(options);
|
|
53
|
+
return this.initializePromise;
|
|
34
54
|
});
|
|
35
55
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Internal implementation of async initialization.
|
|
58
|
+
*/
|
|
59
|
+
_initializeAsyncImpl(options) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
if (!StorageProvider_1.Storage.isReady()) {
|
|
62
|
+
const resolver = StorageProvider_1.Storage.isReadyResolver();
|
|
63
|
+
if (resolver) {
|
|
64
|
+
yield resolver;
|
|
65
|
+
}
|
|
42
66
|
}
|
|
67
|
+
this.logger.start();
|
|
68
|
+
return this.updateUserAsync(this.user, options);
|
|
43
69
|
});
|
|
44
|
-
this.initialized = true;
|
|
45
|
-
return { success: true };
|
|
46
70
|
}
|
|
47
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Initializes the WolvesClient synchronously using cached values.
|
|
73
|
+
* After initialization, cache values are updated in the background for future use.
|
|
74
|
+
* This is useful for quickly starting with last-known-good configurations while
|
|
75
|
+
* refreshing data in the background.
|
|
76
|
+
*
|
|
77
|
+
* Aligned with Statsig SDK behavior:
|
|
78
|
+
* - If no cached values exist, returns with NoCachedValues warning
|
|
79
|
+
* - Triggers background network fetch to update cache for next session
|
|
80
|
+
*
|
|
81
|
+
* @param options - Optional settings
|
|
82
|
+
* @returns Update details including success status and any warnings
|
|
83
|
+
*/
|
|
84
|
+
initializeSync(options) {
|
|
85
|
+
if (this.loadingStatus !== 'Uninitialized') {
|
|
86
|
+
return createUpdateDetails(true, this.store.getSource(), -1, null, ['MultipleInitializations']);
|
|
87
|
+
}
|
|
88
|
+
this.logger.start();
|
|
89
|
+
return this.updateUserSync(this.user, options);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Asynchronously updates the user by first using cached values and then fetching
|
|
93
|
+
* the latest values from the network.
|
|
94
|
+
*
|
|
95
|
+
* @param user - The new user to switch to
|
|
96
|
+
* @param options - Optional settings including timeout
|
|
97
|
+
* @returns Promise that resolves once updated with latest values or timeout is hit
|
|
98
|
+
*/
|
|
99
|
+
updateUserAsync(user, options) {
|
|
48
100
|
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
101
|
+
const startTime = performance.now();
|
|
102
|
+
try {
|
|
103
|
+
return yield this._updateUserAsyncImpl(user, options, startTime);
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
107
|
+
return createUpdateDetails(false, this.store.getSource(), performance.now() - startTime, err);
|
|
55
108
|
}
|
|
56
|
-
return { success: false, errorMessage: 'Failed to fetch config' };
|
|
57
109
|
});
|
|
58
110
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Internal implementation of async user update.
|
|
113
|
+
*/
|
|
114
|
+
_updateUserAsyncImpl(user, options, startTime) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
this._resetForUser(user);
|
|
117
|
+
const initiator = this.user;
|
|
118
|
+
const warnings = [];
|
|
119
|
+
// First, try to load from cache
|
|
120
|
+
let cachedResult = this.store.getDataSync(user);
|
|
121
|
+
if (cachedResult) {
|
|
122
|
+
this.store.setValuesFromResult(cachedResult, user);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
warnings.push('NoCachedValues');
|
|
126
|
+
}
|
|
127
|
+
this.loadingStatus = 'Loading';
|
|
128
|
+
// Fetch from network
|
|
129
|
+
const ops = [
|
|
130
|
+
this._fetchAndUpdateFromNetwork(user, cachedResult, startTime, warnings)
|
|
131
|
+
];
|
|
132
|
+
if (options === null || options === void 0 ? void 0 : options.timeoutMs) {
|
|
133
|
+
ops.push(new Promise((resolve) => {
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
Log_1.Log.debug('Fetching latest value timed out');
|
|
136
|
+
resolve(null);
|
|
137
|
+
}, options.timeoutMs);
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
const result = yield Promise.race(ops);
|
|
141
|
+
// Check if user changed during update
|
|
142
|
+
if (initiator !== this.user) {
|
|
143
|
+
return createUpdateDetails(false, this.store.getSource(), performance.now() - startTime, new Error('User changed during update'), warnings);
|
|
144
|
+
}
|
|
145
|
+
if (result) {
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
// Timeout case - return with cached values
|
|
149
|
+
this.loadingStatus = 'Ready';
|
|
150
|
+
return createUpdateDetails(cachedResult != null, this.store.getSource(), performance.now() - startTime, cachedResult ? null : new Error('Timeout waiting for network'), warnings);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Fetch from network and update the store.
|
|
155
|
+
*/
|
|
156
|
+
_fetchAndUpdateFromNetwork(user, cachedResult, startTime, warnings) {
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
try {
|
|
159
|
+
const config = yield this.network.fetchConfig(user, this.store.getLastUpdateTime());
|
|
160
|
+
if (config) {
|
|
161
|
+
if (config.has_updates === true || config.has_updates === undefined) {
|
|
162
|
+
const networkResult = {
|
|
163
|
+
source: 'Network',
|
|
164
|
+
data: JSON.stringify(config),
|
|
165
|
+
receivedAt: Date.now(),
|
|
166
|
+
};
|
|
167
|
+
this.store.setValuesFromResult(networkResult, user);
|
|
168
|
+
}
|
|
169
|
+
else if (cachedResult && config.has_updates === false) {
|
|
170
|
+
// No updates, but we have cached values - mark as NetworkNotModified
|
|
171
|
+
const notModifiedResult = Object.assign(Object.assign({}, cachedResult), { source: 'NetworkNotModified' });
|
|
172
|
+
this.store.setValuesFromResult(notModifiedResult, user);
|
|
173
|
+
}
|
|
174
|
+
this.loadingStatus = 'Ready';
|
|
175
|
+
return createUpdateDetails(true, this.store.getSource(), performance.now() - startTime, null, warnings);
|
|
176
|
+
}
|
|
177
|
+
// Network fetch failed
|
|
178
|
+
this.loadingStatus = 'Ready';
|
|
179
|
+
return createUpdateDetails(cachedResult != null, this.store.getSource(), performance.now() - startTime, cachedResult ? null : new Error('Failed to fetch config from network'), [...warnings, 'NetworkFetchFailed']);
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
this.loadingStatus = 'Ready';
|
|
183
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
184
|
+
return createUpdateDetails(cachedResult != null, this.store.getSource(), performance.now() - startTime, cachedResult ? null : err, [...warnings, 'NetworkFetchFailed']);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Synchronously updates the user in the client and switches to cached values.
|
|
190
|
+
* After the initial switch to cached values, updates values in the background.
|
|
191
|
+
*
|
|
192
|
+
* Aligned with Statsig SDK behavior:
|
|
193
|
+
* - Immediately returns with cached values if available
|
|
194
|
+
* - If no cached values exist, returns with NoCachedValues warning
|
|
195
|
+
* - Triggers background network fetch (unless disableBackgroundCacheRefresh is true)
|
|
196
|
+
*
|
|
197
|
+
* @param user - The new user to switch to
|
|
198
|
+
* @param options - Optional settings
|
|
199
|
+
* @returns Update details including success status and any warnings
|
|
200
|
+
*/
|
|
201
|
+
updateUserSync(user, options) {
|
|
202
|
+
const startTime = performance.now();
|
|
203
|
+
try {
|
|
204
|
+
return this._updateUserSyncImpl(user, options, startTime);
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
208
|
+
return createUpdateDetails(false, this.store.getSource(), performance.now() - startTime, err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Internal implementation of sync user update.
|
|
213
|
+
*/
|
|
214
|
+
_updateUserSyncImpl(user, options, startTime) {
|
|
215
|
+
const warnings = [];
|
|
216
|
+
this._resetForUser(user);
|
|
217
|
+
// Load from cache synchronously
|
|
218
|
+
const cachedResult = this.store.getDataSync(user);
|
|
219
|
+
if (cachedResult == null) {
|
|
220
|
+
warnings.push('NoCachedValues');
|
|
221
|
+
}
|
|
222
|
+
this.store.setValuesFromResult(cachedResult, user);
|
|
223
|
+
this.loadingStatus = 'Ready';
|
|
224
|
+
// Trigger background refresh unless disabled
|
|
225
|
+
if (options === null || options === void 0 ? void 0 : options.disableBackgroundCacheRefresh) {
|
|
226
|
+
return createUpdateDetails(true, this.store.getSource(), performance.now() - startTime, null, warnings);
|
|
227
|
+
}
|
|
228
|
+
this._runBackgroundUpdate(user, cachedResult);
|
|
229
|
+
return createUpdateDetails(true, this.store.getSource(), performance.now() - startTime, null, warnings);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Run a background update to refresh the cache.
|
|
233
|
+
*/
|
|
234
|
+
_runBackgroundUpdate(user, cachedResult) {
|
|
235
|
+
this.network.fetchConfig(user, this.store.getLastUpdateTime())
|
|
236
|
+
.then((config) => {
|
|
65
237
|
if (config) {
|
|
66
|
-
|
|
238
|
+
if (config.has_updates === true || config.has_updates === undefined) {
|
|
239
|
+
const networkResult = {
|
|
240
|
+
source: 'Network',
|
|
241
|
+
data: JSON.stringify(config),
|
|
242
|
+
receivedAt: Date.now(),
|
|
243
|
+
};
|
|
244
|
+
this.store.setValuesFromResult(networkResult, user);
|
|
245
|
+
}
|
|
246
|
+
else if (cachedResult && config.has_updates === false) {
|
|
247
|
+
const notModifiedResult = Object.assign(Object.assign({}, cachedResult), { source: 'NetworkNotModified' });
|
|
248
|
+
this.store.setValuesFromResult(notModifiedResult, user);
|
|
249
|
+
}
|
|
67
250
|
}
|
|
251
|
+
})
|
|
252
|
+
.catch((err) => {
|
|
253
|
+
Log_1.Log.error('Background update failed', err);
|
|
68
254
|
});
|
|
69
|
-
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Reset the store and logger for a new user.
|
|
258
|
+
*/
|
|
259
|
+
_resetForUser(user) {
|
|
260
|
+
this.logger.reset();
|
|
261
|
+
this.store.reset();
|
|
262
|
+
this.user = user;
|
|
70
263
|
}
|
|
71
264
|
getExperiment(experimentName) {
|
|
72
265
|
var _a, _b, _c;
|
|
@@ -141,5 +334,11 @@ class WolvesClient {
|
|
|
141
334
|
};
|
|
142
335
|
this.logger.enqueue(exposureEvent);
|
|
143
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Get the underlying store (for testing purposes).
|
|
339
|
+
*/
|
|
340
|
+
getStore() {
|
|
341
|
+
return this.store;
|
|
342
|
+
}
|
|
144
343
|
}
|
|
145
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.4';
|
|
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
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export { WolvesClient } from './WolvesClient';
|
|
2
|
+
export type { WolvesUpdateDetails, SyncUpdateOptions, AsyncUpdateOptions, LoadingStatus } from './WolvesClient';
|
|
2
3
|
export { WolvesUser } from './WolvesUser';
|
|
3
4
|
export { Experiment } from './Types';
|
|
4
5
|
export { Log, LogLevel } from './Log';
|
|
5
6
|
export { SDK_VERSION, SDK_TYPE, WolvesMetadataProvider } from './WolvesMetadata';
|
|
6
7
|
export type { WolvesMetadata } from './WolvesMetadata';
|
|
8
|
+
export { Storage } from './StorageProvider';
|
|
9
|
+
export type { StorageProvider } from './StorageProvider';
|
|
10
|
+
export type { DataSource, DataAdapterResult } from './Store';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WolvesMetadataProvider = exports.SDK_TYPE = exports.SDK_VERSION = exports.LogLevel = exports.Log = exports.WolvesClient = void 0;
|
|
3
|
+
exports.Storage = exports.WolvesMetadataProvider = exports.SDK_TYPE = exports.SDK_VERSION = exports.LogLevel = exports.Log = exports.WolvesClient = void 0;
|
|
4
4
|
var WolvesClient_1 = require("./WolvesClient");
|
|
5
5
|
Object.defineProperty(exports, "WolvesClient", { enumerable: true, get: function () { return WolvesClient_1.WolvesClient; } });
|
|
6
6
|
var Log_1 = require("./Log");
|
|
@@ -10,3 +10,5 @@ var WolvesMetadata_1 = require("./WolvesMetadata");
|
|
|
10
10
|
Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return WolvesMetadata_1.SDK_VERSION; } });
|
|
11
11
|
Object.defineProperty(exports, "SDK_TYPE", { enumerable: true, get: function () { return WolvesMetadata_1.SDK_TYPE; } });
|
|
12
12
|
Object.defineProperty(exports, "WolvesMetadataProvider", { enumerable: true, get: function () { return WolvesMetadata_1.WolvesMetadataProvider; } });
|
|
13
|
+
var StorageProvider_1 = require("./StorageProvider");
|
|
14
|
+
Object.defineProperty(exports, "Storage", { enumerable: true, get: function () { return StorageProvider_1.Storage; } });
|
package/package.json
CHANGED
package/dist/DeviceInfo.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
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>;
|
package/dist/DeviceInfo.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
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/StatsigClient.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/StatsigClient.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
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/StatsigUser.d.ts
DELETED
package/dist/StatsigUser.js
DELETED