@statsig/client-core 0.0.1-beta.40 → 0.0.1-beta.42
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/package.json +1 -1
- package/src/$_StatsigGlobal.js +6 -3
- package/src/CacheKey.d.ts +4 -0
- package/src/CacheKey.js +27 -0
- package/src/DataAdapterCore.d.ts +4 -4
- package/src/DataAdapterCore.js +63 -42
- package/src/ErrorBoundary.d.ts +1 -1
- package/src/ErrorBoundary.js +8 -7
- package/src/EventLogger.d.ts +3 -2
- package/src/EventLogger.js +52 -46
- package/src/Hashing.d.ts +2 -2
- package/src/Hashing.js +6 -6
- package/src/NetworkConfig.d.ts +2 -1
- package/src/NetworkConfig.js +1 -0
- package/src/NetworkCore.d.ts +7 -3
- package/src/NetworkCore.js +44 -17
- package/src/SessionID.js +2 -2
- package/src/StableID.js +2 -2
- package/src/StatsigClientBase.d.ts +1 -0
- package/src/StatsigClientBase.js +18 -17
- package/src/StatsigDataAdapter.d.ts +1 -0
- package/src/StatsigMetadata.d.ts +1 -1
- package/src/StatsigMetadata.js +1 -1
- package/src/StatsigOptionsCommon.d.ts +0 -10
- package/src/StatsigUser.d.ts +5 -3
- package/src/StatsigUser.js +3 -8
- package/src/StorageProvider.js +16 -39
- package/src/TypedJsonParse.d.ts +1 -1
- package/src/TypedJsonParse.js +4 -4
- package/src/VisibilityObserving.d.ts +1 -0
- package/src/VisibilityObserving.js +12 -3
- package/src/index.d.ts +1 -0
- package/src/index.js +2 -5
package/package.json
CHANGED
package/src/$_StatsigGlobal.js
CHANGED
|
@@ -4,12 +4,15 @@ var _a, _b, _c;
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports._getInstance = exports._getStatsigGlobal = void 0;
|
|
6
6
|
const _getStatsigGlobal = () => {
|
|
7
|
-
return __STATSIG__
|
|
7
|
+
return __STATSIG__ ? __STATSIG__ : statsigGlobal;
|
|
8
8
|
};
|
|
9
9
|
exports._getStatsigGlobal = _getStatsigGlobal;
|
|
10
10
|
const _getInstance = (sdkKey) => {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const gbl = (0, exports._getStatsigGlobal)();
|
|
12
|
+
if (!sdkKey) {
|
|
13
|
+
return gbl.lastInstance;
|
|
14
|
+
}
|
|
15
|
+
return gbl.instances && gbl.instances[sdkKey];
|
|
13
16
|
};
|
|
14
17
|
exports._getInstance = _getInstance;
|
|
15
18
|
const GLOBAL_KEY = '__STATSIG__';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StatsigUser } from './StatsigUser';
|
|
2
|
+
export type CustomCacheKeyGenerator = (sdkKey: string, user: StatsigUser) => string;
|
|
3
|
+
export declare function _getUserStorageKey(sdkKey: string, user: StatsigUser, customKeyGenerator?: CustomCacheKeyGenerator): string;
|
|
4
|
+
export declare function _getStorageKey(sdkKey: string, user?: StatsigUser, customKeyGenerator?: CustomCacheKeyGenerator): string;
|
package/src/CacheKey.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._getStorageKey = exports._getUserStorageKey = void 0;
|
|
4
|
+
const Hashing_1 = require("./Hashing");
|
|
5
|
+
function _getUserStorageKey(sdkKey, user, customKeyGenerator) {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
if (customKeyGenerator) {
|
|
8
|
+
return customKeyGenerator(sdkKey, user);
|
|
9
|
+
}
|
|
10
|
+
const parts = [
|
|
11
|
+
`uid:${(_a = user === null || user === void 0 ? void 0 : user.userID) !== null && _a !== void 0 ? _a : ''}`,
|
|
12
|
+
`cids:${Object.entries((_b = user === null || user === void 0 ? void 0 : user.customIDs) !== null && _b !== void 0 ? _b : {})
|
|
13
|
+
.sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey))
|
|
14
|
+
.map(([key, value]) => `${key}-${value}`)
|
|
15
|
+
.join(',')}`,
|
|
16
|
+
`k:${sdkKey}`,
|
|
17
|
+
];
|
|
18
|
+
return (0, Hashing_1._DJB2)(parts.join('|'));
|
|
19
|
+
}
|
|
20
|
+
exports._getUserStorageKey = _getUserStorageKey;
|
|
21
|
+
function _getStorageKey(sdkKey, user, customKeyGenerator) {
|
|
22
|
+
if (user) {
|
|
23
|
+
return _getUserStorageKey(sdkKey, user, customKeyGenerator);
|
|
24
|
+
}
|
|
25
|
+
return (0, Hashing_1._DJB2)(`k:${sdkKey}`);
|
|
26
|
+
}
|
|
27
|
+
exports._getStorageKey = _getStorageKey;
|
package/src/DataAdapterCore.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ import { AnyStatsigOptions } from './StatsigOptionsCommon';
|
|
|
3
3
|
import { StatsigUser } from './StatsigUser';
|
|
4
4
|
export declare abstract class DataAdapterCore {
|
|
5
5
|
private _adapterName;
|
|
6
|
-
|
|
6
|
+
protected _cacheSuffix: string;
|
|
7
|
+
protected _options: AnyStatsigOptions | null;
|
|
7
8
|
private _sdkKey;
|
|
8
9
|
private _inMemoryCache;
|
|
9
10
|
private _lastModifiedStoreKey;
|
|
10
11
|
protected constructor(_adapterName: string, _cacheSuffix: string);
|
|
11
|
-
attach(sdkKey: string,
|
|
12
|
+
attach(sdkKey: string, options: AnyStatsigOptions | null): void;
|
|
12
13
|
getDataSync(user?: StatsigUser | undefined): DataAdapterResult | null;
|
|
13
14
|
setData(data: string, user?: StatsigUser): void;
|
|
14
15
|
/**
|
|
@@ -20,10 +21,9 @@ export declare abstract class DataAdapterCore {
|
|
|
20
21
|
protected _getDataAsyncImpl(current: DataAdapterResult | null, user?: StatsigUser, options?: DataAdapterAsyncOptions): Promise<DataAdapterResult | null>;
|
|
21
22
|
protected _prefetchDataImpl(user?: StatsigUser, options?: DataAdapterAsyncOptions): Promise<void>;
|
|
22
23
|
protected abstract _fetchFromNetwork(current: string | null, user?: StatsigUser, options?: DataAdapterAsyncOptions): Promise<string | null>;
|
|
24
|
+
protected abstract _getCacheKey(user?: StatsigUser): string;
|
|
23
25
|
private _fetchAndPrepFromNetwork;
|
|
24
26
|
protected _getSdkKey(): string;
|
|
25
|
-
protected _getCacheKey(user?: StatsigUser): string;
|
|
26
|
-
protected _addToInMemoryCache(cacheKey: string, result: DataAdapterResult): void;
|
|
27
27
|
private _loadFromCache;
|
|
28
28
|
private _writeToCache;
|
|
29
29
|
private _runLocalStorageCacheEviction;
|
package/src/DataAdapterCore.js
CHANGED
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.DataAdapterCore = void 0;
|
|
13
13
|
const Log_1 = require("./Log");
|
|
14
|
-
const
|
|
14
|
+
const StableID_1 = require("./StableID");
|
|
15
15
|
const StatsigUser_1 = require("./StatsigUser");
|
|
16
16
|
const StorageProvider_1 = require("./StorageProvider");
|
|
17
17
|
const TypedJsonParse_1 = require("./TypedJsonParse");
|
|
@@ -20,33 +20,33 @@ class DataAdapterCore {
|
|
|
20
20
|
constructor(_adapterName, _cacheSuffix) {
|
|
21
21
|
this._adapterName = _adapterName;
|
|
22
22
|
this._cacheSuffix = _cacheSuffix;
|
|
23
|
+
this._options = null;
|
|
23
24
|
this._sdkKey = null;
|
|
24
|
-
this._inMemoryCache = {};
|
|
25
25
|
this._lastModifiedStoreKey = `statsig.last_modified_time.${_cacheSuffix}`;
|
|
26
|
+
this._inMemoryCache = new InMemoryCache();
|
|
26
27
|
}
|
|
27
|
-
attach(sdkKey,
|
|
28
|
+
attach(sdkKey, options) {
|
|
28
29
|
this._sdkKey = sdkKey;
|
|
30
|
+
this._options = options;
|
|
29
31
|
}
|
|
30
32
|
getDataSync(user) {
|
|
31
33
|
const cacheKey = this._getCacheKey(user);
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
34
|
+
const inMem = this._inMemoryCache.get(cacheKey, user);
|
|
35
|
+
if (inMem) {
|
|
36
|
+
return inMem;
|
|
35
37
|
}
|
|
36
38
|
const cache = this._loadFromCache(cacheKey);
|
|
37
39
|
if (cache) {
|
|
38
|
-
this.
|
|
39
|
-
return this._inMemoryCache
|
|
40
|
+
this._inMemoryCache.add(cacheKey, cache);
|
|
41
|
+
return this._inMemoryCache.get(cacheKey, user);
|
|
40
42
|
}
|
|
41
43
|
return null;
|
|
42
44
|
}
|
|
43
45
|
setData(data, user) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
receivedAt: Date.now(),
|
|
49
|
-
});
|
|
46
|
+
var _a;
|
|
47
|
+
const normalized = user && (0, StatsigUser_1._normalizeUser)(user, (_a = this._options) === null || _a === void 0 ? void 0 : _a.environment);
|
|
48
|
+
const cacheKey = this._getCacheKey(normalized);
|
|
49
|
+
this._inMemoryCache.add(cacheKey, _makeDataAdapterResult('Bootstrap', data, null));
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* (Internal Use Only) - Used by \@statsig/react-native-bindings to prime the cache from AsyncStorage
|
|
@@ -54,7 +54,7 @@ class DataAdapterCore {
|
|
|
54
54
|
* @param {Record<string, DataAdapterResult>} cache The values to merge into _inMemoryCache
|
|
55
55
|
*/
|
|
56
56
|
__primeInMemoryCache(cache) {
|
|
57
|
-
this._inMemoryCache
|
|
57
|
+
this._inMemoryCache.merge(cache);
|
|
58
58
|
}
|
|
59
59
|
_getDataAsyncImpl(current, user, options) {
|
|
60
60
|
var _a;
|
|
@@ -77,7 +77,7 @@ class DataAdapterCore {
|
|
|
77
77
|
const cacheKey = this._getCacheKey(user);
|
|
78
78
|
const result = yield this._getDataAsyncImpl(null, user, options);
|
|
79
79
|
if (result) {
|
|
80
|
-
this.
|
|
80
|
+
this._inMemoryCache.add(cacheKey, Object.assign(Object.assign({}, result), { source: 'Prefetch' }));
|
|
81
81
|
}
|
|
82
82
|
});
|
|
83
83
|
}
|
|
@@ -88,23 +88,21 @@ class DataAdapterCore {
|
|
|
88
88
|
Log_1.Log.debug('No response returned for latest value');
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
|
-
const response = (0, TypedJsonParse_1.
|
|
91
|
+
const response = (0, TypedJsonParse_1._typedJsonParse)(latest, 'has_updates', 'Initialize Response');
|
|
92
|
+
const sdkKey = this._getSdkKey();
|
|
93
|
+
const stableID = yield StableID_1.StableID.get(sdkKey);
|
|
92
94
|
let result = null;
|
|
93
95
|
if ((response === null || response === void 0 ? void 0 : response.has_updates) === true) {
|
|
94
|
-
result =
|
|
96
|
+
result = _makeDataAdapterResult('Network', latest, stableID);
|
|
95
97
|
}
|
|
96
98
|
else if (current && (response === null || response === void 0 ? void 0 : response.has_updates) === false) {
|
|
97
|
-
result =
|
|
98
|
-
source: 'NetworkNotModified',
|
|
99
|
-
data: current,
|
|
100
|
-
receivedAt: Date.now(),
|
|
101
|
-
};
|
|
99
|
+
result = _makeDataAdapterResult('NetworkNotModified', current, stableID);
|
|
102
100
|
}
|
|
103
|
-
|
|
101
|
+
else {
|
|
104
102
|
return null;
|
|
105
103
|
}
|
|
106
104
|
const cacheKey = this._getCacheKey(user);
|
|
107
|
-
this.
|
|
105
|
+
this._inMemoryCache.add(cacheKey, result);
|
|
108
106
|
yield this._writeToCache(cacheKey, result);
|
|
109
107
|
return result;
|
|
110
108
|
});
|
|
@@ -116,29 +114,13 @@ class DataAdapterCore {
|
|
|
116
114
|
Log_1.Log.error(`${this._adapterName} is not attached to a Client`);
|
|
117
115
|
return '';
|
|
118
116
|
}
|
|
119
|
-
_getCacheKey(user) {
|
|
120
|
-
const key = (0, StatsigUser_1.getUserStorageKey)(this._getSdkKey(), user);
|
|
121
|
-
return `${StatsigDataAdapter_1.DataAdapterCachePrefix}.${this._cacheSuffix}.${key}`;
|
|
122
|
-
}
|
|
123
|
-
_addToInMemoryCache(cacheKey, result) {
|
|
124
|
-
const entries = Object.entries(this._inMemoryCache);
|
|
125
|
-
if (entries.length < CACHE_LIMIT) {
|
|
126
|
-
this._inMemoryCache[cacheKey] = result;
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const [oldest] = entries.reduce((acc, curr) => {
|
|
130
|
-
return curr[1] < acc[1] ? curr : acc;
|
|
131
|
-
});
|
|
132
|
-
delete this._inMemoryCache[oldest];
|
|
133
|
-
this._inMemoryCache[cacheKey] = result;
|
|
134
|
-
}
|
|
135
117
|
_loadFromCache(cacheKey) {
|
|
136
118
|
var _a;
|
|
137
119
|
const cache = (_a = StorageProvider_1.Storage._getItemSync) === null || _a === void 0 ? void 0 : _a.call(StorageProvider_1.Storage, cacheKey);
|
|
138
120
|
if (cache == null) {
|
|
139
121
|
return null;
|
|
140
122
|
}
|
|
141
|
-
const result = (0, TypedJsonParse_1.
|
|
123
|
+
const result = (0, TypedJsonParse_1._typedJsonParse)(cache, 'source', 'Cached Result');
|
|
142
124
|
return result ? Object.assign(Object.assign({}, result), { source: 'Cache' }) : null;
|
|
143
125
|
}
|
|
144
126
|
_writeToCache(cacheKey, result) {
|
|
@@ -167,3 +149,42 @@ class DataAdapterCore {
|
|
|
167
149
|
}
|
|
168
150
|
}
|
|
169
151
|
exports.DataAdapterCore = DataAdapterCore;
|
|
152
|
+
function _makeDataAdapterResult(source, data, stableID) {
|
|
153
|
+
return {
|
|
154
|
+
source,
|
|
155
|
+
data,
|
|
156
|
+
receivedAt: Date.now(),
|
|
157
|
+
stableID,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
class InMemoryCache {
|
|
161
|
+
constructor() {
|
|
162
|
+
this._data = {};
|
|
163
|
+
}
|
|
164
|
+
get(cacheKey, user) {
|
|
165
|
+
var _a;
|
|
166
|
+
const result = this._data[cacheKey];
|
|
167
|
+
const cached = result === null || result === void 0 ? void 0 : result.stableID;
|
|
168
|
+
const provided = (_a = user === null || user === void 0 ? void 0 : user.customIDs) === null || _a === void 0 ? void 0 : _a.stableID;
|
|
169
|
+
if (provided && cached && provided !== cached) {
|
|
170
|
+
Log_1.Log.warn("'StatsigUser.customIDs.stableID' mismatch");
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
add(cacheKey, value) {
|
|
176
|
+
const entries = Object.entries(this._data);
|
|
177
|
+
if (entries.length < CACHE_LIMIT) {
|
|
178
|
+
this._data[cacheKey] = value;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const [oldest] = entries.reduce((acc, curr) => {
|
|
182
|
+
return curr[1] < acc[1] ? curr : acc;
|
|
183
|
+
});
|
|
184
|
+
delete this._data[oldest];
|
|
185
|
+
this._data[cacheKey] = value;
|
|
186
|
+
}
|
|
187
|
+
merge(values) {
|
|
188
|
+
this._data = Object.assign(Object.assign({}, this._data), values);
|
|
189
|
+
}
|
|
190
|
+
}
|
package/src/ErrorBoundary.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class ErrorBoundary {
|
|
|
8
8
|
private _seen;
|
|
9
9
|
constructor(_sdkKey: string, _options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
|
|
10
10
|
wrap(instance: unknown): void;
|
|
11
|
-
capture(tag: string, task: () => unknown): unknown;
|
|
12
11
|
logError(tag: string, error: unknown): void;
|
|
12
|
+
private _capture;
|
|
13
13
|
private _onError;
|
|
14
14
|
}
|
package/src/ErrorBoundary.js
CHANGED
|
@@ -14,6 +14,7 @@ const Log_1 = require("./Log");
|
|
|
14
14
|
const SDKType_1 = require("./SDKType");
|
|
15
15
|
const StatsigMetadata_1 = require("./StatsigMetadata");
|
|
16
16
|
exports.EXCEPTION_ENDPOINT = 'https://statsigapi.net/v1/sdk_exception';
|
|
17
|
+
const UNKNOWN_ERROR = '[Statsig] UnknownError';
|
|
17
18
|
class ErrorBoundary {
|
|
18
19
|
constructor(_sdkKey, _options, _emitter) {
|
|
19
20
|
this._sdkKey = _sdkKey;
|
|
@@ -30,7 +31,7 @@ class ErrorBoundary {
|
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
obj[name] = (...args) => {
|
|
33
|
-
return this.
|
|
34
|
+
return this._capture(name, () => original.apply(instance, args));
|
|
34
35
|
};
|
|
35
36
|
obj[name].$EB = true;
|
|
36
37
|
});
|
|
@@ -39,7 +40,10 @@ class ErrorBoundary {
|
|
|
39
40
|
this._onError('eb:wrap', err);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
logError(tag, error) {
|
|
44
|
+
this._onError(tag, error);
|
|
45
|
+
}
|
|
46
|
+
_capture(tag, task) {
|
|
43
47
|
try {
|
|
44
48
|
const res = task();
|
|
45
49
|
if (res && res instanceof Promise) {
|
|
@@ -52,15 +56,12 @@ class ErrorBoundary {
|
|
|
52
56
|
return null;
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
|
-
logError(tag, error) {
|
|
56
|
-
this._onError(tag, error);
|
|
57
|
-
}
|
|
58
59
|
_onError(tag, error) {
|
|
59
60
|
try {
|
|
60
61
|
Log_1.Log.warn(`Caught error in ${tag}`, { error });
|
|
61
62
|
const impl = () => __awaiter(this, void 0, void 0, function* () {
|
|
62
63
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
63
|
-
const unwrapped = (error
|
|
64
|
+
const unwrapped = (error ? error : Error(UNKNOWN_ERROR));
|
|
64
65
|
const isError = unwrapped instanceof Error;
|
|
65
66
|
const name = isError ? unwrapped.name : 'No Name';
|
|
66
67
|
if (this._seen.has(name)) {
|
|
@@ -107,7 +108,7 @@ function _getDescription(obj) {
|
|
|
107
108
|
return JSON.stringify(obj);
|
|
108
109
|
}
|
|
109
110
|
catch (_a) {
|
|
110
|
-
return
|
|
111
|
+
return UNKNOWN_ERROR;
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
function _getAllInstanceMethodNames(instance) {
|
package/src/EventLogger.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class EventLogger {
|
|
|
8
8
|
private _network;
|
|
9
9
|
private _options;
|
|
10
10
|
private _queue;
|
|
11
|
-
private
|
|
11
|
+
private _flushIntervalId;
|
|
12
12
|
private _lastExposureTimeMap;
|
|
13
13
|
private _nonExposedChecks;
|
|
14
14
|
private _maxQueueSize;
|
|
@@ -16,7 +16,6 @@ export declare class EventLogger {
|
|
|
16
16
|
private _creationTime;
|
|
17
17
|
private _isLoggingDisabled;
|
|
18
18
|
private _logEventUrl;
|
|
19
|
-
private _logEventBeaconUrl;
|
|
20
19
|
constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null);
|
|
21
20
|
setLoggingDisabled(isDisabled: boolean): void;
|
|
22
21
|
enqueue(event: StatsigEventInternal): void;
|
|
@@ -33,10 +32,12 @@ export declare class EventLogger {
|
|
|
33
32
|
private _sendEvents;
|
|
34
33
|
private _sendEventsViaPost;
|
|
35
34
|
private _sendEventsViaBeacon;
|
|
35
|
+
private _getRequestData;
|
|
36
36
|
private _saveFailedLogsToStorage;
|
|
37
37
|
private _retryFailedLogs;
|
|
38
38
|
private _getStorageKey;
|
|
39
39
|
private _normalizeAndAppendEvent;
|
|
40
40
|
private _appendAndResetNonExposedChecks;
|
|
41
41
|
private _getCurrentPageUrl;
|
|
42
|
+
private _startBackgroundFlushInterval;
|
|
42
43
|
}
|
package/src/EventLogger.js
CHANGED
|
@@ -16,7 +16,6 @@ const NetworkConfig_1 = require("./NetworkConfig");
|
|
|
16
16
|
const SafeJs_1 = require("./SafeJs");
|
|
17
17
|
const StatsigEvent_1 = require("./StatsigEvent");
|
|
18
18
|
const StorageProvider_1 = require("./StorageProvider");
|
|
19
|
-
const TypedJsonParse_1 = require("./TypedJsonParse");
|
|
20
19
|
const UrlOverrides_1 = require("./UrlOverrides");
|
|
21
20
|
const VisibilityObserving_1 = require("./VisibilityObserving");
|
|
22
21
|
const DEFAULT_QUEUE_SIZE = 50;
|
|
@@ -34,7 +33,7 @@ const _safeFlushAndForget = (sdkKey) => {
|
|
|
34
33
|
};
|
|
35
34
|
class EventLogger {
|
|
36
35
|
constructor(_sdkKey, _emitter, _network, _options) {
|
|
37
|
-
var _a
|
|
36
|
+
var _a;
|
|
38
37
|
this._sdkKey = _sdkKey;
|
|
39
38
|
this._emitter = _emitter;
|
|
40
39
|
this._network = _network;
|
|
@@ -47,25 +46,15 @@ class EventLogger {
|
|
|
47
46
|
EVENT_LOGGER_MAP[_sdkKey] = this;
|
|
48
47
|
this._isLoggingDisabled = (_options === null || _options === void 0 ? void 0 : _options.disableLogging) === true;
|
|
49
48
|
this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
|
|
50
|
-
const flushInterval = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
|
|
51
|
-
const intervalId = setInterval(() => {
|
|
52
|
-
const logger = EVENT_LOGGER_MAP[_sdkKey];
|
|
53
|
-
if (!logger) {
|
|
54
|
-
clearInterval(intervalId);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
_safeFlushAndForget(_sdkKey);
|
|
58
|
-
}, flushInterval);
|
|
59
|
-
this._flushTimer = intervalId;
|
|
60
49
|
const config = _options === null || _options === void 0 ? void 0 : _options.networkConfig;
|
|
61
50
|
this._logEventUrl = (0, UrlOverrides_1._getOverridableUrl)(config === null || config === void 0 ? void 0 : config.logEventUrl, config === null || config === void 0 ? void 0 : config.api, '/rgstr', NetworkConfig_1.NetworkDefault.eventsApi);
|
|
62
|
-
this._logEventBeaconUrl = (0, UrlOverrides_1._getOverridableUrl)(config === null || config === void 0 ? void 0 : config.logEventBeaconUrl, config === null || config === void 0 ? void 0 : config.api, '/log_event_beacon', NetworkConfig_1.NetworkDefault.eventsApi);
|
|
63
51
|
(0, VisibilityObserving_1._subscribeToVisiblityChanged)((visibility) => {
|
|
64
52
|
if (visibility === 'background') {
|
|
65
53
|
_safeFlushAndForget(_sdkKey);
|
|
66
54
|
}
|
|
67
55
|
});
|
|
68
56
|
this._retryFailedLogs();
|
|
57
|
+
this._startBackgroundFlushInterval();
|
|
69
58
|
}
|
|
70
59
|
setLoggingDisabled(isDisabled) {
|
|
71
60
|
this._isLoggingDisabled = isDisabled;
|
|
@@ -90,9 +79,9 @@ class EventLogger {
|
|
|
90
79
|
}
|
|
91
80
|
shutdown() {
|
|
92
81
|
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
-
if (this.
|
|
94
|
-
clearInterval(this.
|
|
95
|
-
this.
|
|
82
|
+
if (this._flushIntervalId) {
|
|
83
|
+
clearInterval(this._flushIntervalId);
|
|
84
|
+
this._flushIntervalId = null;
|
|
96
85
|
}
|
|
97
86
|
yield this.flush();
|
|
98
87
|
});
|
|
@@ -105,7 +94,7 @@ class EventLogger {
|
|
|
105
94
|
}
|
|
106
95
|
const events = this._queue;
|
|
107
96
|
this._queue = [];
|
|
108
|
-
|
|
97
|
+
return this._sendEvents(events);
|
|
109
98
|
});
|
|
110
99
|
}
|
|
111
100
|
/**
|
|
@@ -123,16 +112,17 @@ class EventLogger {
|
|
|
123
112
|
setTimeout(() => _safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
|
|
124
113
|
}
|
|
125
114
|
_shouldLogEvent(event) {
|
|
126
|
-
var _a, _b, _c, _d;
|
|
127
115
|
if (!(0, StatsigEvent_1._isExposureEvent)(event)) {
|
|
128
116
|
return true;
|
|
129
117
|
}
|
|
118
|
+
const user = event.user ? event.user : {};
|
|
119
|
+
const metadata = event.metadata ? event.metadata : {};
|
|
130
120
|
const key = [
|
|
131
121
|
event.eventName,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
122
|
+
user.userID,
|
|
123
|
+
metadata['gate'],
|
|
124
|
+
metadata['config'],
|
|
125
|
+
metadata['ruleID'],
|
|
136
126
|
].join('|');
|
|
137
127
|
const previous = this._lastExposureTimeMap[key];
|
|
138
128
|
const now = Date.now();
|
|
@@ -176,37 +166,34 @@ class EventLogger {
|
|
|
176
166
|
});
|
|
177
167
|
}
|
|
178
168
|
_sendEventsViaPost(events) {
|
|
169
|
+
var _a;
|
|
179
170
|
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
-
const result = yield this._network.post(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
events,
|
|
184
|
-
},
|
|
185
|
-
url: this._logEventUrl,
|
|
186
|
-
retries: 3,
|
|
187
|
-
params: {
|
|
188
|
-
[NetworkConfig_1.NetworkParam.EventCount]: String(events.length),
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
const response = (result === null || result === void 0 ? void 0 : result.body)
|
|
192
|
-
? (0, TypedJsonParse_1.typedJsonParse)(result.body, 'success', 'Failed to parse SendEventsResponse')
|
|
193
|
-
: null;
|
|
194
|
-
return { success: (response === null || response === void 0 ? void 0 : response.success) === true };
|
|
171
|
+
const result = yield this._network.post(this._getRequestData(events));
|
|
172
|
+
const code = (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : -1;
|
|
173
|
+
return { success: code >= 200 && code < 300 };
|
|
195
174
|
});
|
|
196
175
|
}
|
|
197
176
|
_sendEventsViaBeacon(events) {
|
|
198
177
|
return __awaiter(this, void 0, void 0, function* () {
|
|
199
178
|
return {
|
|
200
|
-
success: yield this._network.beacon(
|
|
201
|
-
sdkKey: this._sdkKey,
|
|
202
|
-
data: {
|
|
203
|
-
events,
|
|
204
|
-
},
|
|
205
|
-
url: this._logEventBeaconUrl,
|
|
206
|
-
}),
|
|
179
|
+
success: yield this._network.beacon(this._getRequestData(events)),
|
|
207
180
|
};
|
|
208
181
|
});
|
|
209
182
|
}
|
|
183
|
+
_getRequestData(events) {
|
|
184
|
+
return {
|
|
185
|
+
sdkKey: this._sdkKey,
|
|
186
|
+
data: {
|
|
187
|
+
events,
|
|
188
|
+
},
|
|
189
|
+
url: this._logEventUrl,
|
|
190
|
+
retries: 3,
|
|
191
|
+
isCompressable: true,
|
|
192
|
+
params: {
|
|
193
|
+
[NetworkConfig_1.NetworkParam.EventCount]: String(events.length),
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
210
197
|
_saveFailedLogsToStorage(events) {
|
|
211
198
|
while (events.length > MAX_FAILED_LOGS) {
|
|
212
199
|
events.shift();
|
|
@@ -230,7 +217,7 @@ class EventLogger {
|
|
|
230
217
|
});
|
|
231
218
|
}
|
|
232
219
|
_getStorageKey() {
|
|
233
|
-
return `statsig.failed_logs.${(0, Hashing_1.
|
|
220
|
+
return `statsig.failed_logs.${(0, Hashing_1._DJB2)(this._sdkKey)}`;
|
|
234
221
|
}
|
|
235
222
|
_normalizeAndAppendEvent(event) {
|
|
236
223
|
if (event.user) {
|
|
@@ -242,7 +229,9 @@ class EventLogger {
|
|
|
242
229
|
if (currentPage) {
|
|
243
230
|
extras.statsigMetadata = { currentPage };
|
|
244
231
|
}
|
|
245
|
-
|
|
232
|
+
const final = Object.assign(Object.assign({}, event), extras);
|
|
233
|
+
Log_1.Log.debug('Enqueued Event:', final);
|
|
234
|
+
this._queue.push(final);
|
|
246
235
|
}
|
|
247
236
|
_appendAndResetNonExposedChecks() {
|
|
248
237
|
if (Object.keys(this._nonExposedChecks).length === 0) {
|
|
@@ -265,5 +254,22 @@ class EventLogger {
|
|
|
265
254
|
}
|
|
266
255
|
return (0, SafeJs_1._getCurrentPageUrlSafe)();
|
|
267
256
|
}
|
|
257
|
+
_startBackgroundFlushInterval() {
|
|
258
|
+
var _a, _b;
|
|
259
|
+
if (!(0, SafeJs_1._isBrowserEnv)()) {
|
|
260
|
+
return; // do not run in server environments
|
|
261
|
+
}
|
|
262
|
+
const flushInterval = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
|
|
263
|
+
const intervalId = setInterval(() => {
|
|
264
|
+
const logger = EVENT_LOGGER_MAP[this._sdkKey];
|
|
265
|
+
if (logger._flushIntervalId !== intervalId) {
|
|
266
|
+
clearInterval(intervalId);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
_safeFlushAndForget(this._sdkKey);
|
|
270
|
+
}
|
|
271
|
+
}, flushInterval);
|
|
272
|
+
this._flushIntervalId = intervalId;
|
|
273
|
+
}
|
|
268
274
|
}
|
|
269
275
|
exports.EventLogger = EventLogger;
|
package/src/Hashing.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const
|
|
1
|
+
export declare const _DJB2: (value: string) => string;
|
|
2
|
+
export declare const _DJB2Object: (value: Record<string, unknown> | null) => string;
|
package/src/Hashing.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
3
|
+
exports._DJB2Object = exports._DJB2 = void 0;
|
|
4
|
+
const _DJB2 = (value) => {
|
|
5
5
|
let hash = 0;
|
|
6
6
|
for (let i = 0; i < value.length; i++) {
|
|
7
7
|
const character = value.charCodeAt(i);
|
|
@@ -10,11 +10,11 @@ const DJB2 = (value) => {
|
|
|
10
10
|
}
|
|
11
11
|
return String(hash >>> 0);
|
|
12
12
|
};
|
|
13
|
-
exports.
|
|
14
|
-
const
|
|
15
|
-
return (0, exports.
|
|
13
|
+
exports._DJB2 = _DJB2;
|
|
14
|
+
const _DJB2Object = (value) => {
|
|
15
|
+
return (0, exports._DJB2)(JSON.stringify(_getSortedObject(value)));
|
|
16
16
|
};
|
|
17
|
-
exports.
|
|
17
|
+
exports._DJB2Object = _DJB2Object;
|
|
18
18
|
const _getSortedObject = (object) => {
|
|
19
19
|
if (object == null) {
|
|
20
20
|
return null;
|
package/src/NetworkConfig.d.ts
CHANGED
package/src/NetworkConfig.js
CHANGED
package/src/NetworkCore.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import './$_StatsigGlobal';
|
|
|
2
2
|
import { NetworkPriority } from './NetworkConfig';
|
|
3
3
|
import { StatsigClientEmitEventFunc } from './StatsigClientBase';
|
|
4
4
|
import { AnyStatsigOptions } from './StatsigOptionsCommon';
|
|
5
|
+
import { Flatten } from './UtitlityTypes';
|
|
5
6
|
type RequestArgs = {
|
|
6
7
|
sdkKey: string;
|
|
7
8
|
url: string;
|
|
@@ -10,10 +11,12 @@ type RequestArgs = {
|
|
|
10
11
|
params?: Record<string, string>;
|
|
11
12
|
headers?: Record<string, string>;
|
|
12
13
|
};
|
|
13
|
-
type RequestArgsWithData = RequestArgs & {
|
|
14
|
+
export type RequestArgsWithData = Flatten<RequestArgs & {
|
|
14
15
|
data: Record<string, unknown>;
|
|
15
16
|
isStatsigEncodable?: boolean;
|
|
16
|
-
|
|
17
|
+
isCompressable?: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'url' | 'params' | 'isCompressable'>;
|
|
17
20
|
type NetworkResponse = {
|
|
18
21
|
body: string | null;
|
|
19
22
|
code: number;
|
|
@@ -22,11 +25,12 @@ export declare class NetworkCore {
|
|
|
22
25
|
private _options;
|
|
23
26
|
private _emitter?;
|
|
24
27
|
private readonly _timeout;
|
|
28
|
+
private readonly _netConfig;
|
|
25
29
|
constructor(_options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
|
|
26
30
|
post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
|
|
27
31
|
get(args: RequestArgs): Promise<NetworkResponse | null>;
|
|
28
32
|
isBeaconSupported(): boolean;
|
|
29
|
-
beacon(args:
|
|
33
|
+
beacon(args: BeaconRequestArgs): Promise<boolean>;
|
|
30
34
|
private _sendRequest;
|
|
31
35
|
private _getPopulatedURL;
|
|
32
36
|
private _getPopulatedBody;
|
package/src/NetworkCore.js
CHANGED
|
@@ -19,19 +19,26 @@ const SafeJs_1 = require("./SafeJs");
|
|
|
19
19
|
const SessionID_1 = require("./SessionID");
|
|
20
20
|
const StableID_1 = require("./StableID");
|
|
21
21
|
const StatsigMetadata_1 = require("./StatsigMetadata");
|
|
22
|
+
const VisibilityObserving_1 = require("./VisibilityObserving");
|
|
22
23
|
const DEFAULT_TIMEOUT_MS = 10000;
|
|
23
24
|
class NetworkCore {
|
|
24
25
|
constructor(_options, _emitter) {
|
|
25
|
-
var _a, _b;
|
|
26
|
+
var _a, _b, _c;
|
|
26
27
|
this._options = _options;
|
|
27
28
|
this._emitter = _emitter;
|
|
28
|
-
this.
|
|
29
|
-
|
|
29
|
+
this._netConfig = (_a = _options === null || _options === void 0 ? void 0 : _options.networkConfig) !== null && _a !== void 0 ? _a : null;
|
|
30
|
+
this._timeout = (_c = (_b = this._netConfig) === null || _b === void 0 ? void 0 : _b.networkTimeoutMs) !== null && _c !== void 0 ? _c : DEFAULT_TIMEOUT_MS;
|
|
30
31
|
}
|
|
31
32
|
post(args) {
|
|
32
33
|
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
let body = yield this._getPopulatedBody(args);
|
|
35
|
+
if (args.isStatsigEncodable) {
|
|
36
|
+
body = this._attemptToEncodeString(args, body);
|
|
37
|
+
}
|
|
38
|
+
else if (args.isCompressable) {
|
|
39
|
+
body = yield _attemptToCompressBody(args, body);
|
|
40
|
+
}
|
|
41
|
+
return this._sendRequest(Object.assign({ method: 'POST', body }, args));
|
|
35
42
|
});
|
|
36
43
|
}
|
|
37
44
|
get(args) {
|
|
@@ -39,47 +46,49 @@ class NetworkCore {
|
|
|
39
46
|
}
|
|
40
47
|
isBeaconSupported() {
|
|
41
48
|
return (typeof navigator !== 'undefined' &&
|
|
42
|
-
typeof
|
|
49
|
+
typeof navigator.sendBeacon === 'function');
|
|
43
50
|
}
|
|
44
51
|
beacon(args) {
|
|
45
52
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
53
|
if (!_ensureValidSdkKey(args)) {
|
|
47
54
|
return false;
|
|
48
55
|
}
|
|
56
|
+
let body = yield this._getPopulatedBody(args);
|
|
57
|
+
body = yield _attemptToCompressBody(args, body);
|
|
49
58
|
const url = yield this._getPopulatedURL(args);
|
|
50
|
-
const body = yield this._getPopulatedBody(args);
|
|
51
59
|
return navigator.sendBeacon(url, body);
|
|
52
60
|
});
|
|
53
61
|
}
|
|
54
62
|
_sendRequest(args) {
|
|
55
|
-
var _a, _b, _c, _d
|
|
63
|
+
var _a, _b, _c, _d;
|
|
56
64
|
return __awaiter(this, void 0, void 0, function* () {
|
|
57
65
|
if (!_ensureValidSdkKey(args)) {
|
|
58
66
|
return null;
|
|
59
67
|
}
|
|
60
|
-
if ((
|
|
68
|
+
if ((_a = this._netConfig) === null || _a === void 0 ? void 0 : _a.preventAllNetworkTraffic) {
|
|
61
69
|
return null;
|
|
62
70
|
}
|
|
63
71
|
const { method, body, retries } = args;
|
|
64
|
-
const controller = new AbortController();
|
|
65
|
-
const handle = setTimeout(() => controller.abort(`Timeout of ${this._timeout}ms expired.`), this._timeout);
|
|
72
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
73
|
+
const handle = setTimeout(() => controller === null || controller === void 0 ? void 0 : controller.abort(`Timeout of ${this._timeout}ms expired.`), this._timeout);
|
|
66
74
|
const url = yield this._getPopulatedURL(args);
|
|
67
75
|
let response = null;
|
|
76
|
+
const keepalive = (0, VisibilityObserving_1._isUnloading)();
|
|
68
77
|
try {
|
|
69
78
|
const config = {
|
|
70
79
|
method,
|
|
71
80
|
body,
|
|
72
81
|
headers: Object.assign({}, args.headers),
|
|
73
|
-
signal: controller.signal,
|
|
82
|
+
signal: controller === null || controller === void 0 ? void 0 : controller.signal,
|
|
74
83
|
priority: args.priority,
|
|
75
|
-
keepalive
|
|
84
|
+
keepalive,
|
|
76
85
|
};
|
|
77
|
-
const func = (
|
|
86
|
+
const func = (_c = (_b = this._netConfig) === null || _b === void 0 ? void 0 : _b.networkOverrideFunc) !== null && _c !== void 0 ? _c : fetch;
|
|
78
87
|
response = yield func(url, config);
|
|
79
88
|
clearTimeout(handle);
|
|
80
89
|
if (!response.ok) {
|
|
81
90
|
const text = yield response.text().catch(() => 'No Text');
|
|
82
|
-
const err = new Error(`
|
|
91
|
+
const err = new Error(`NetworkError: ${url} ${text}`);
|
|
83
92
|
err.name = 'NetworkError';
|
|
84
93
|
throw err;
|
|
85
94
|
}
|
|
@@ -94,7 +103,7 @@ class NetworkCore {
|
|
|
94
103
|
const errorMessage = _getErrorMessage(controller, error);
|
|
95
104
|
Diagnostics_1.Diagnostics.mark();
|
|
96
105
|
if (!retries || retries <= 0) {
|
|
97
|
-
(
|
|
106
|
+
(_d = this._emitter) === null || _d === void 0 ? void 0 : _d.call(this, { name: 'error', error });
|
|
98
107
|
Log_1.Log.error(`A networking error occured during ${method} request to ${url}.`, errorMessage, error);
|
|
99
108
|
return null;
|
|
100
109
|
}
|
|
@@ -153,7 +162,7 @@ const _ensureValidSdkKey = (args) => {
|
|
|
153
162
|
return true;
|
|
154
163
|
};
|
|
155
164
|
function _getErrorMessage(controller, error) {
|
|
156
|
-
if (controller.signal.aborted &&
|
|
165
|
+
if ((controller === null || controller === void 0 ? void 0 : controller.signal.aborted) &&
|
|
157
166
|
typeof controller.signal.reason === 'string') {
|
|
158
167
|
return controller.signal.reason;
|
|
159
168
|
}
|
|
@@ -165,3 +174,21 @@ function _getErrorMessage(controller, error) {
|
|
|
165
174
|
}
|
|
166
175
|
return 'Unknown Error';
|
|
167
176
|
}
|
|
177
|
+
function _attemptToCompressBody(args, body) {
|
|
178
|
+
var _a;
|
|
179
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
+
if (!args.isCompressable ||
|
|
181
|
+
typeof CompressionStream === 'undefined' ||
|
|
182
|
+
typeof TextEncoder === 'undefined' ||
|
|
183
|
+
(__STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__['no-compress']) != null) {
|
|
184
|
+
return body;
|
|
185
|
+
}
|
|
186
|
+
const bytes = new TextEncoder().encode(body);
|
|
187
|
+
const stream = new CompressionStream('gzip');
|
|
188
|
+
const writer = stream.writable.getWriter();
|
|
189
|
+
writer.write(bytes).catch(Log_1.Log.error);
|
|
190
|
+
writer.close().catch(Log_1.Log.error);
|
|
191
|
+
args.params = Object.assign(Object.assign({}, ((_a = args.params) !== null && _a !== void 0 ? _a : {})), { [NetworkConfig_1.NetworkParam.IsGzipped]: '1' });
|
|
192
|
+
return yield new Response(stream.readable).arrayBuffer();
|
|
193
|
+
});
|
|
194
|
+
}
|
package/src/SessionID.js
CHANGED
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.StatsigSession = exports.SessionID = void 0;
|
|
13
|
-
const
|
|
13
|
+
const CacheKey_1 = require("./CacheKey");
|
|
14
14
|
const Log_1 = require("./Log");
|
|
15
15
|
const StorageProvider_1 = require("./StorageProvider");
|
|
16
16
|
const UUID_1 = require("./UUID");
|
|
@@ -80,7 +80,7 @@ function _hasRunTooLong({ startTime }) {
|
|
|
80
80
|
return Date.now() - startTime > MAX_SESSION_AGE;
|
|
81
81
|
}
|
|
82
82
|
function _getSessionIDStorageKey(sdkKey) {
|
|
83
|
-
return `statsig.session_id.${(0,
|
|
83
|
+
return `statsig.session_id.${(0, CacheKey_1._getStorageKey)(sdkKey)}`;
|
|
84
84
|
}
|
|
85
85
|
function _persistToStorage(session, sdkKey) {
|
|
86
86
|
const storageKey = _getSessionIDStorageKey(sdkKey);
|
package/src/StableID.js
CHANGED
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.StableID = void 0;
|
|
13
|
-
const
|
|
13
|
+
const CacheKey_1 = require("./CacheKey");
|
|
14
14
|
const Log_1 = require("./Log");
|
|
15
15
|
const StorageProvider_1 = require("./StorageProvider");
|
|
16
16
|
const UUID_1 = require("./UUID");
|
|
@@ -35,7 +35,7 @@ exports.StableID = {
|
|
|
35
35
|
},
|
|
36
36
|
};
|
|
37
37
|
function _getStableIDStorageKey(sdkKey) {
|
|
38
|
-
return `statsig.stable_id.${(0,
|
|
38
|
+
return `statsig.stable_id.${(0, CacheKey_1._getStorageKey)(sdkKey)}`;
|
|
39
39
|
}
|
|
40
40
|
function _persistToStorage(stableID, sdkKey) {
|
|
41
41
|
const storageKey = _getStableIDStorageKey(sdkKey);
|
|
@@ -63,4 +63,5 @@ export declare abstract class StatsigClientBase<TAdapter extends EvaluationsData
|
|
|
63
63
|
$emt(event: AnyStatsigClientEvent): void;
|
|
64
64
|
protected _setStatus(newStatus: StatsigLoadingStatus, values: DataAdapterResult | null): void;
|
|
65
65
|
protected _enqueueExposure(name: string, exposure: StatsigEventInternal, options?: EvaluationOptionsCommon): void;
|
|
66
|
+
protected abstract _primeReadyRipcord(): void;
|
|
66
67
|
}
|
package/src/StatsigClientBase.js
CHANGED
|
@@ -16,7 +16,6 @@ const ErrorBoundary_1 = require("./ErrorBoundary");
|
|
|
16
16
|
const EventLogger_1 = require("./EventLogger");
|
|
17
17
|
const Log_1 = require("./Log");
|
|
18
18
|
const SafeJs_1 = require("./SafeJs");
|
|
19
|
-
const StableID_1 = require("./StableID");
|
|
20
19
|
const StorageProvider_1 = require("./StorageProvider");
|
|
21
20
|
class StatsigClientBase {
|
|
22
21
|
constructor(sdkKey, adapter, network, options) {
|
|
@@ -24,30 +23,32 @@ class StatsigClientBase {
|
|
|
24
23
|
this.loadingStatus = 'Uninitialized';
|
|
25
24
|
this._listeners = {};
|
|
26
25
|
const emitter = this.$emt.bind(this);
|
|
27
|
-
|
|
28
|
-
const instances = (_a = statsigGlobal.instances) !== null && _a !== void 0 ? _a : {};
|
|
29
|
-
const inst = this;
|
|
30
|
-
(options === null || options === void 0 ? void 0 : options.logLevel) && (Log_1.Log.level = options === null || options === void 0 ? void 0 : options.logLevel);
|
|
26
|
+
(options === null || options === void 0 ? void 0 : options.logLevel) != null && (Log_1.Log.level = options.logLevel);
|
|
31
27
|
(options === null || options === void 0 ? void 0 : options.disableStorage) && StorageProvider_1.Storage._setDisabled(true);
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
this._sdkKey = sdkKey;
|
|
29
|
+
this._options = options !== null && options !== void 0 ? options : {};
|
|
30
|
+
this._overrideAdapter = (_a = options === null || options === void 0 ? void 0 : options.overrideAdapter) !== null && _a !== void 0 ? _a : null;
|
|
31
|
+
this._logger = new EventLogger_1.EventLogger(sdkKey, emitter, network, options);
|
|
34
32
|
this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey, options, emitter);
|
|
35
33
|
this._errorBoundary.wrap(this);
|
|
36
34
|
this._errorBoundary.wrap(network);
|
|
37
35
|
this._errorBoundary.wrap(adapter);
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
this._errorBoundary.wrap(this._logger);
|
|
37
|
+
if ((0, SafeJs_1._isBrowserEnv)()) {
|
|
38
|
+
const statsigGlobal = (0, __StatsigGlobal_1._getStatsigGlobal)();
|
|
39
|
+
const instances = (_b = statsigGlobal.instances) !== null && _b !== void 0 ? _b : {};
|
|
40
|
+
const inst = this;
|
|
41
|
+
if (instances[sdkKey] != null) {
|
|
42
|
+
Log_1.Log.warn('Creating multiple Statsig clients with the same SDK key can lead to unexpected behavior. Multi-instance support requires different SDK keys.');
|
|
43
|
+
}
|
|
44
|
+
instances[sdkKey] = inst;
|
|
45
|
+
statsigGlobal.lastInstance = inst;
|
|
46
|
+
statsigGlobal.instances = instances;
|
|
47
|
+
__STATSIG__ = statsigGlobal;
|
|
44
48
|
}
|
|
45
|
-
instances[sdkKey] = inst;
|
|
46
|
-
statsigGlobal.lastInstance = inst;
|
|
47
|
-
statsigGlobal.instances = instances;
|
|
48
|
-
__STATSIG__ = statsigGlobal;
|
|
49
49
|
this.dataAdapter = adapter;
|
|
50
50
|
this.dataAdapter.attach(sdkKey, options);
|
|
51
|
+
this._primeReadyRipcord();
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
54
|
* Updates runtime configuration options for the SDK, allowing toggling of certain behaviors such as logging and storage to comply with user preferences or regulations such as GDPR.
|
package/src/StatsigMetadata.d.ts
CHANGED
package/src/StatsigMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StatsigMetadataProvider = exports.SDK_VERSION = void 0;
|
|
4
|
-
exports.SDK_VERSION = '0.0.1-beta.
|
|
4
|
+
exports.SDK_VERSION = '0.0.1-beta.42';
|
|
5
5
|
let metadata = {
|
|
6
6
|
sdkVersion: exports.SDK_VERSION,
|
|
7
7
|
sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
|
|
@@ -26,12 +26,6 @@ export type NetworkConfigCommon = {
|
|
|
26
26
|
* default: `https://featuregates.org/v1/initialize`
|
|
27
27
|
*/
|
|
28
28
|
logEventUrl?: string;
|
|
29
|
-
/**
|
|
30
|
-
* The URL used to flush queued events via {@link window.navigator.sendBeacon} (web only). Takes precedence over {@link StatsigOptionsCommon.api}.
|
|
31
|
-
*
|
|
32
|
-
* default: `https://featuregates.org/v1/initialize`
|
|
33
|
-
*/
|
|
34
|
-
logEventBeaconUrl?: string;
|
|
35
29
|
/**
|
|
36
30
|
* The maximum amount of time (in milliseconds) that any network request can take
|
|
37
31
|
* before timing out.
|
|
@@ -69,10 +63,6 @@ export type StatsigOptionsCommon<NetworkConfig extends NetworkConfigCommon> = St
|
|
|
69
63
|
* in the same session.
|
|
70
64
|
*/
|
|
71
65
|
environment?: StatsigEnvironment;
|
|
72
|
-
/**
|
|
73
|
-
* Overrides the auto-generated StableID that is set for the device.
|
|
74
|
-
*/
|
|
75
|
-
overrideStableID?: string;
|
|
76
66
|
/**
|
|
77
67
|
* How much information is allowed to be printed to the console.
|
|
78
68
|
*
|
package/src/StatsigUser.d.ts
CHANGED
|
@@ -2,7 +2,10 @@ import type { StatsigEnvironment } from './StatsigOptionsCommon';
|
|
|
2
2
|
type StatsigUserPrimitives = string | number | boolean | Array<string> | undefined;
|
|
3
3
|
export type StatsigUser = {
|
|
4
4
|
userID?: string;
|
|
5
|
-
customIDs?:
|
|
5
|
+
customIDs?: {
|
|
6
|
+
[key: string]: string | undefined;
|
|
7
|
+
stableID?: string;
|
|
8
|
+
};
|
|
6
9
|
email?: string;
|
|
7
10
|
ip?: string;
|
|
8
11
|
userAgent?: string;
|
|
@@ -15,7 +18,6 @@ export type StatsigUser = {
|
|
|
15
18
|
export type StatsigUserInternal = StatsigUser & {
|
|
16
19
|
statsigEnvironment?: StatsigEnvironment;
|
|
17
20
|
};
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function getUserStorageKey(sdkKey: string, user?: StatsigUser): string;
|
|
21
|
+
export declare function _normalizeUser(original: StatsigUser, environment?: StatsigEnvironment): StatsigUser;
|
|
20
22
|
export declare function getUnitIDFromUser(user: StatsigUser, idType: string): string | undefined;
|
|
21
23
|
export {};
|
package/src/StatsigUser.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getUnitIDFromUser = exports.
|
|
4
|
-
|
|
5
|
-
function normalizeUser(original, environment) {
|
|
3
|
+
exports.getUnitIDFromUser = exports._normalizeUser = void 0;
|
|
4
|
+
function _normalizeUser(original, environment) {
|
|
6
5
|
try {
|
|
7
6
|
const copy = JSON.parse(JSON.stringify(original));
|
|
8
7
|
if (environment != null) {
|
|
@@ -14,11 +13,7 @@ function normalizeUser(original, environment) {
|
|
|
14
13
|
throw new Error('User object must be convertable to JSON string.');
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
exports.
|
|
18
|
-
function getUserStorageKey(sdkKey, user) {
|
|
19
|
-
return (0, Hashing_1.DJB2Object)({ sdkKey, user });
|
|
20
|
-
}
|
|
21
|
-
exports.getUserStorageKey = getUserStorageKey;
|
|
16
|
+
exports._normalizeUser = _normalizeUser;
|
|
22
17
|
function getUnitIDFromUser(user, idType) {
|
|
23
18
|
var _a, _b, _c;
|
|
24
19
|
if (typeof idType === 'string' && idType.toLowerCase() !== 'userid') {
|
package/src/StorageProvider.js
CHANGED
|
@@ -11,53 +11,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports._setObjectInStorage = exports._getObjectFromStorage = exports.Storage = void 0;
|
|
13
13
|
const Log_1 = require("./Log");
|
|
14
|
+
const SafeJs_1 = require("./SafeJs");
|
|
14
15
|
const inMemoryStore = {};
|
|
15
16
|
const _resolve = (input) => Promise.resolve(input);
|
|
16
17
|
const _inMemoryProvider = {
|
|
17
18
|
_getProviderName: () => 'InMemory',
|
|
18
|
-
_getItemSync(key)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
var _a;
|
|
24
|
-
return _resolve((_a = inMemoryStore[key]) !== null && _a !== void 0 ? _a : null);
|
|
25
|
-
},
|
|
26
|
-
_setItem(key, value) {
|
|
27
|
-
inMemoryStore[key] = value;
|
|
28
|
-
return _resolve();
|
|
29
|
-
},
|
|
30
|
-
_removeItem(key) {
|
|
31
|
-
delete inMemoryStore[key];
|
|
32
|
-
return _resolve();
|
|
33
|
-
},
|
|
34
|
-
_getAllKeys() {
|
|
35
|
-
return _resolve(Object.keys(inMemoryStore));
|
|
36
|
-
},
|
|
19
|
+
_getItemSync: (key) => inMemoryStore[key] ? inMemoryStore[key] : null,
|
|
20
|
+
_getItem: (key) => _resolve(inMemoryStore[key] ? inMemoryStore[key] : null),
|
|
21
|
+
_setItem: (key, value) => ((inMemoryStore[key] = value), _resolve()),
|
|
22
|
+
_removeItem: (key) => (delete inMemoryStore[key], _resolve()),
|
|
23
|
+
_getAllKeys: () => _resolve(Object.keys(inMemoryStore)),
|
|
37
24
|
};
|
|
38
25
|
let _localStorageProvider = null;
|
|
39
26
|
try {
|
|
40
|
-
|
|
27
|
+
const win = (0, SafeJs_1._getWindowSafe)();
|
|
28
|
+
if (win &&
|
|
29
|
+
win.localStorage &&
|
|
30
|
+
typeof win.localStorage.getItem === 'function') {
|
|
41
31
|
_localStorageProvider = {
|
|
42
32
|
_getProviderName: () => 'LocalStorage',
|
|
43
|
-
_getItemSync(key)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
},
|
|
49
|
-
_setItem(key, value) {
|
|
50
|
-
localStorage.setItem(key, value);
|
|
51
|
-
return _resolve();
|
|
52
|
-
},
|
|
53
|
-
_removeItem(key) {
|
|
54
|
-
localStorage.removeItem(key);
|
|
55
|
-
return _resolve();
|
|
56
|
-
},
|
|
57
|
-
_getAllKeys() {
|
|
58
|
-
const keys = Object.keys(localStorage);
|
|
59
|
-
return _resolve(keys);
|
|
60
|
-
},
|
|
33
|
+
_getItemSync: (key) => win.localStorage.getItem(key),
|
|
34
|
+
_getItem: (key) => _resolve(win.localStorage.getItem(key)),
|
|
35
|
+
_setItem: (key, value) => (win.localStorage.setItem(key, value), _resolve()),
|
|
36
|
+
_removeItem: (key) => (win.localStorage.removeItem(key), _resolve()),
|
|
37
|
+
_getAllKeys: () => _resolve(Object.keys(win.localStorage)),
|
|
61
38
|
};
|
|
62
39
|
}
|
|
63
40
|
}
|
|
@@ -81,7 +58,7 @@ function _inMemoryBreaker(get) {
|
|
|
81
58
|
exports.Storage = {
|
|
82
59
|
_getProviderName: () => _current._getProviderName(),
|
|
83
60
|
_getItem: (key) => __awaiter(void 0, void 0, void 0, function* () { return _inMemoryBreaker(() => _current._getItem(key)); }),
|
|
84
|
-
_getItemSync: (key) => _inMemoryBreaker(() =>
|
|
61
|
+
_getItemSync: (key) => _inMemoryBreaker(() => _current._getItemSync ? _current._getItemSync(key) : null),
|
|
85
62
|
_setItem: (key, value) => _current._setItem(key, value),
|
|
86
63
|
_removeItem: (key) => _current._removeItem(key),
|
|
87
64
|
_getAllKeys: () => _current._getAllKeys(),
|
package/src/TypedJsonParse.d.ts
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* @param {string} error An error to print via Log.error() when parsing fails
|
|
6
6
|
* @returns {T | null} The parse object T or null if it failed
|
|
7
7
|
*/
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function _typedJsonParse<T>(data: string, guard: string, typeName: string): T | null;
|
package/src/TypedJsonParse.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports._typedJsonParse = void 0;
|
|
4
4
|
const Log_1 = require("./Log");
|
|
5
5
|
/**
|
|
6
6
|
*
|
|
@@ -9,7 +9,7 @@ const Log_1 = require("./Log");
|
|
|
9
9
|
* @param {string} error An error to print via Log.error() when parsing fails
|
|
10
10
|
* @returns {T | null} The parse object T or null if it failed
|
|
11
11
|
*/
|
|
12
|
-
function
|
|
12
|
+
function _typedJsonParse(data, guard, typeName) {
|
|
13
13
|
try {
|
|
14
14
|
const result = JSON.parse(data);
|
|
15
15
|
if (typeof result === 'object' &&
|
|
@@ -21,7 +21,7 @@ function typedJsonParse(data, guard, error) {
|
|
|
21
21
|
catch (_a) {
|
|
22
22
|
// noop
|
|
23
23
|
}
|
|
24
|
-
Log_1.Log.error(
|
|
24
|
+
Log_1.Log.error(`Failed to parse ${typeName}`);
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
-
exports.
|
|
27
|
+
exports._typedJsonParse = _typedJsonParse;
|
|
@@ -3,6 +3,7 @@ declare const BACKGROUND = "background";
|
|
|
3
3
|
export type Visibility = typeof FOREGROUND | typeof BACKGROUND;
|
|
4
4
|
type VisibilityChangedCallback = (visibility: Visibility) => void;
|
|
5
5
|
export declare const _isCurrentlyVisible: () => boolean;
|
|
6
|
+
export declare const _isUnloading: () => boolean;
|
|
6
7
|
export declare const _subscribeToVisiblityChanged: (listener: VisibilityChangedCallback) => void;
|
|
7
8
|
export declare const _notifyVisibilityChanged: (visibility: Visibility) => void;
|
|
8
9
|
export {};
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._notifyVisibilityChanged = exports._subscribeToVisiblityChanged = exports._isCurrentlyVisible = void 0;
|
|
3
|
+
exports._notifyVisibilityChanged = exports._subscribeToVisiblityChanged = exports._isUnloading = exports._isCurrentlyVisible = void 0;
|
|
4
4
|
const SafeJs_1 = require("./SafeJs");
|
|
5
5
|
const FOREGROUND = 'foreground';
|
|
6
6
|
const BACKGROUND = 'background';
|
|
7
7
|
const LISTENERS = [];
|
|
8
8
|
let current = FOREGROUND;
|
|
9
|
+
let isUnloading = false;
|
|
9
10
|
const _isCurrentlyVisible = () => {
|
|
10
11
|
return current === FOREGROUND;
|
|
11
12
|
};
|
|
12
13
|
exports._isCurrentlyVisible = _isCurrentlyVisible;
|
|
14
|
+
const _isUnloading = () => isUnloading;
|
|
15
|
+
exports._isUnloading = _isUnloading;
|
|
13
16
|
const _subscribeToVisiblityChanged = (listener) => {
|
|
14
17
|
LISTENERS.unshift(listener);
|
|
15
18
|
};
|
|
@@ -22,9 +25,15 @@ const _notifyVisibilityChanged = (visibility) => {
|
|
|
22
25
|
LISTENERS.forEach((l) => l(visibility));
|
|
23
26
|
};
|
|
24
27
|
exports._notifyVisibilityChanged = _notifyVisibilityChanged;
|
|
25
|
-
(0, SafeJs_1._addWindowEventListenerSafe)('focus', () =>
|
|
28
|
+
(0, SafeJs_1._addWindowEventListenerSafe)('focus', () => {
|
|
29
|
+
isUnloading = false;
|
|
30
|
+
(0, exports._notifyVisibilityChanged)(FOREGROUND);
|
|
31
|
+
});
|
|
26
32
|
(0, SafeJs_1._addWindowEventListenerSafe)('blur', () => (0, exports._notifyVisibilityChanged)(BACKGROUND));
|
|
27
|
-
(0, SafeJs_1._addWindowEventListenerSafe)('beforeunload', () =>
|
|
33
|
+
(0, SafeJs_1._addWindowEventListenerSafe)('beforeunload', () => {
|
|
34
|
+
isUnloading = true;
|
|
35
|
+
(0, exports._notifyVisibilityChanged)(BACKGROUND);
|
|
36
|
+
});
|
|
28
37
|
(0, SafeJs_1._addDocumentEventListenerSafe)('visibilitychange', () => {
|
|
29
38
|
(0, exports._notifyVisibilityChanged)(document.visibilityState === 'visible' ? FOREGROUND : BACKGROUND);
|
|
30
39
|
});
|
package/src/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { EventLogger } from './EventLogger';
|
|
|
4
4
|
import { Log } from './Log';
|
|
5
5
|
import { Storage } from './StorageProvider';
|
|
6
6
|
export * from './$_StatsigGlobal';
|
|
7
|
+
export * from './CacheKey';
|
|
7
8
|
export * from './ClientInterfaces';
|
|
8
9
|
export * from './DataAdapterCore';
|
|
9
10
|
export * from './DownloadConfigSpecsResponse';
|
package/src/index.js
CHANGED
|
@@ -24,8 +24,8 @@ Object.defineProperty(exports, "Log", { enumerable: true, get: function () { ret
|
|
|
24
24
|
const StatsigMetadata_1 = require("./StatsigMetadata");
|
|
25
25
|
const StorageProvider_1 = require("./StorageProvider");
|
|
26
26
|
Object.defineProperty(exports, "Storage", { enumerable: true, get: function () { return StorageProvider_1.Storage; } });
|
|
27
|
-
const UUID_1 = require("./UUID");
|
|
28
27
|
__exportStar(require("./$_StatsigGlobal"), exports);
|
|
28
|
+
__exportStar(require("./CacheKey"), exports);
|
|
29
29
|
__exportStar(require("./ClientInterfaces"), exports);
|
|
30
30
|
__exportStar(require("./DataAdapterCore"), exports);
|
|
31
31
|
__exportStar(require("./DownloadConfigSpecsResponse"), exports);
|
|
@@ -57,8 +57,5 @@ __exportStar(require("./UrlOverrides"), exports);
|
|
|
57
57
|
__exportStar(require("./UtitlityTypes"), exports);
|
|
58
58
|
__exportStar(require("./UUID"), exports);
|
|
59
59
|
__exportStar(require("./VisibilityObserving"), exports);
|
|
60
|
-
__STATSIG__ = Object.assign(Object.assign({}, (__STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : {})), {
|
|
61
|
-
Log: Log_1.Log,
|
|
62
|
-
getUUID: UUID_1.getUUID,
|
|
63
|
-
Storage: StorageProvider_1.Storage,
|
|
60
|
+
__STATSIG__ = Object.assign(Object.assign({}, (__STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : {})), { Log: Log_1.Log,
|
|
64
61
|
SDK_VERSION: StatsigMetadata_1.SDK_VERSION });
|