@statsig/client-core 0.0.1-beta.1 → 0.0.1-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +1 -1
- package/src/ClientInterfaces.d.ts +6 -2
- package/src/DataAdapterCore.d.ts +33 -0
- package/src/DataAdapterCore.js +241 -0
- package/src/ErrorBoundary.d.ts +1 -0
- package/src/ErrorBoundary.js +3 -0
- package/src/EventLogger.d.ts +7 -1
- package/src/EventLogger.js +55 -29
- package/src/Monitoring.d.ts +1 -2
- package/src/Monitoring.js +66 -13
- package/src/NetworkCore.d.ts +13 -4
- package/src/NetworkCore.js +68 -60
- package/src/StableID.js +1 -1
- package/src/StatsigClientBase.d.ts +10 -17
- package/src/StatsigClientBase.js +19 -219
- package/src/StatsigClientEventEmitter.d.ts +26 -4
- package/src/StatsigDataAdapter.d.ts +43 -0
- package/src/StatsigDataAdapter.js +4 -0
- package/src/StatsigEvent.d.ts +2 -3
- package/src/StatsigEvent.js +15 -8
- package/src/StatsigMetadata.js +1 -1
- package/src/StatsigOptionsCommon.d.ts +5 -5
- package/src/StatsigTypes.d.ts +9 -4
- package/src/StatsigTypes.js +6 -6
- package/src/StatsigUser.js +16 -17
- package/src/StorageProvider.d.ts +2 -0
- package/src/StorageProvider.js +11 -0
- package/src/VisibilityChangeObserver.js +4 -1
- package/src/index.d.ts +6 -3
- package/src/index.js +9 -4
- package/src/StatsigDataProvider.d.ts +0 -9
- package/src/StatsigDataProvider.js +0 -2
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Statsig - Core
|
|
2
2
|
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> This version of the SDK is still in beta. The non-beta version can be found [here](https://github.com/statsig-io/js-client).
|
|
5
|
+
|
|
3
6
|
The package that contains all the common logic shared by the various Statsig Javascript packages.
|
|
4
7
|
|
|
5
|
-
Learn more by visiting: https://docs.statsig.com/client/
|
|
8
|
+
Learn more by visiting: https://docs.statsig.com/client/javascript-sdk
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { EvaluationOptions } from './StatsigClientBase';
|
|
2
2
|
import { StatsigClientEventEmitterInterface } from './StatsigClientEventEmitter';
|
|
3
|
+
import { StatsigDataAdapter } from './StatsigDataAdapter';
|
|
3
4
|
import { StatsigEvent } from './StatsigEvent';
|
|
4
5
|
import { DynamicConfig, Experiment, FeatureGate, Layer } from './StatsigTypes';
|
|
5
6
|
import { StatsigUser } from './StatsigUser';
|
|
6
7
|
export interface StatsigClientCommonInterface extends StatsigClientEventEmitterInterface {
|
|
7
|
-
|
|
8
|
+
initializeSync(): void;
|
|
9
|
+
initializeAsync(): Promise<void>;
|
|
8
10
|
shutdown(): Promise<void>;
|
|
11
|
+
getDataAdapter(): StatsigDataAdapter;
|
|
9
12
|
}
|
|
10
13
|
export interface OnDeviceEvaluationsInterface extends StatsigClientCommonInterface {
|
|
11
14
|
checkGate(name: string, user: StatsigUser, options: EvaluationOptions): boolean;
|
|
@@ -17,7 +20,8 @@ export interface OnDeviceEvaluationsInterface extends StatsigClientCommonInterfa
|
|
|
17
20
|
}
|
|
18
21
|
export interface PrecomputedEvaluationsInterface extends StatsigClientCommonInterface {
|
|
19
22
|
getCurrentUser(): StatsigUser;
|
|
20
|
-
|
|
23
|
+
updateUserSync(user: StatsigUser): void;
|
|
24
|
+
updateUserAsync(user: StatsigUser): Promise<void>;
|
|
21
25
|
checkGate(name: string, options: EvaluationOptions): boolean;
|
|
22
26
|
getFeatureGate(name: string, options: EvaluationOptions): FeatureGate;
|
|
23
27
|
getDynamicConfig(name: string, options: EvaluationOptions): DynamicConfig;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ErrorBoundary } from './ErrorBoundary';
|
|
2
|
+
import { StatsigDataAdapter, StatsigDataAdapterResult } from './StatsigDataAdapter';
|
|
3
|
+
import { StatsigOptionsCommon } from './StatsigOptionsCommon';
|
|
4
|
+
import { StatsigUser } from './StatsigUser';
|
|
5
|
+
type UpdatesAwareObject = {
|
|
6
|
+
has_updates?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare abstract class DataAdapterCore<T extends UpdatesAwareObject> implements StatsigDataAdapter {
|
|
9
|
+
private _className;
|
|
10
|
+
private _cacheSuffix;
|
|
11
|
+
protected _errorBoundary: ErrorBoundary | null;
|
|
12
|
+
private _sdkKey;
|
|
13
|
+
private _inMemoryCache;
|
|
14
|
+
private _lastModifiedStoreKey;
|
|
15
|
+
protected constructor(_className: string, _cacheSuffix: string);
|
|
16
|
+
attach(sdkKey: string, _options: StatsigOptionsCommon | null): void;
|
|
17
|
+
getDataSync(user?: StatsigUser | undefined): StatsigDataAdapterResult | null;
|
|
18
|
+
getDataAsync(current: StatsigDataAdapterResult | null, user?: StatsigUser): Promise<StatsigDataAdapterResult | null>;
|
|
19
|
+
/**
|
|
20
|
+
* (Internal Use Only) - Used by @statsig/react-native-bindings to prime the cache from AsyncStorage
|
|
21
|
+
* @param {Record<string, StatsigDataAdapterResult>} cache The values to set for _inMemoryCache
|
|
22
|
+
*/
|
|
23
|
+
_setInMemoryCache(cache: Record<string, StatsigDataAdapterResult>): void;
|
|
24
|
+
protected abstract _fetchFromNetwork(current: string | null, user?: StatsigUser): Promise<string | null>;
|
|
25
|
+
private _fetchLatest;
|
|
26
|
+
protected _getSdkKey(): string;
|
|
27
|
+
protected _getCacheKey(user?: StatsigUser): string;
|
|
28
|
+
protected _addToInMemoryCache(cacheKey: string, result: StatsigDataAdapterResult): void;
|
|
29
|
+
private _loadFromCache;
|
|
30
|
+
private _writeToCache;
|
|
31
|
+
private _runLocalStorageCacheEviction;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.DataAdapterCore = void 0;
|
|
51
|
+
var ErrorBoundary_1 = require("./ErrorBoundary");
|
|
52
|
+
var Log_1 = require("./Log");
|
|
53
|
+
var Monitoring_1 = require("./Monitoring");
|
|
54
|
+
var StatsigDataAdapter_1 = require("./StatsigDataAdapter");
|
|
55
|
+
var StatsigUser_1 = require("./StatsigUser");
|
|
56
|
+
var StorageProvider_1 = require("./StorageProvider");
|
|
57
|
+
var CACHE_LIMIT = 10;
|
|
58
|
+
var DataAdapterCore = /** @class */ (function () {
|
|
59
|
+
function DataAdapterCore(_className, _cacheSuffix) {
|
|
60
|
+
this._className = _className;
|
|
61
|
+
this._cacheSuffix = _cacheSuffix;
|
|
62
|
+
this._errorBoundary = null;
|
|
63
|
+
this._sdkKey = null;
|
|
64
|
+
this._inMemoryCache = {};
|
|
65
|
+
this._lastModifiedStoreKey = "statsig.last_modified_time.".concat(_cacheSuffix);
|
|
66
|
+
}
|
|
67
|
+
DataAdapterCore.prototype.attach = function (sdkKey, _options) {
|
|
68
|
+
this._sdkKey = sdkKey;
|
|
69
|
+
this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey);
|
|
70
|
+
(0, Monitoring_1.monitorClass)(this._errorBoundary, this);
|
|
71
|
+
};
|
|
72
|
+
DataAdapterCore.prototype.getDataSync = function (user) {
|
|
73
|
+
var cacheKey = this._getCacheKey(user);
|
|
74
|
+
var result = this._inMemoryCache[cacheKey];
|
|
75
|
+
if (result) {
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
var cache = this._loadFromCache(cacheKey);
|
|
79
|
+
if (cache) {
|
|
80
|
+
this._addToInMemoryCache(cacheKey, cache);
|
|
81
|
+
return this._inMemoryCache[cacheKey];
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
85
|
+
DataAdapterCore.prototype.getDataAsync = function (current, user) {
|
|
86
|
+
var _a;
|
|
87
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
88
|
+
var cache, latest, cacheKey;
|
|
89
|
+
return __generator(this, function (_b) {
|
|
90
|
+
switch (_b.label) {
|
|
91
|
+
case 0:
|
|
92
|
+
cache = current !== null && current !== void 0 ? current : this.getDataSync(user);
|
|
93
|
+
return [4 /*yield*/, this._fetchLatest((_a = cache === null || cache === void 0 ? void 0 : cache.data) !== null && _a !== void 0 ? _a : null, user)];
|
|
94
|
+
case 1:
|
|
95
|
+
latest = _b.sent();
|
|
96
|
+
cacheKey = this._getCacheKey(user);
|
|
97
|
+
if (latest) {
|
|
98
|
+
this._addToInMemoryCache(cacheKey, latest);
|
|
99
|
+
}
|
|
100
|
+
if (!((latest === null || latest === void 0 ? void 0 : latest.source) === 'Network' ||
|
|
101
|
+
(latest === null || latest === void 0 ? void 0 : latest.source) === 'NetworkNotModified')) return [3 /*break*/, 3];
|
|
102
|
+
return [4 /*yield*/, this._writeToCache(cacheKey, latest)];
|
|
103
|
+
case 2:
|
|
104
|
+
_b.sent();
|
|
105
|
+
_b.label = 3;
|
|
106
|
+
case 3: return [2 /*return*/, latest];
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* (Internal Use Only) - Used by @statsig/react-native-bindings to prime the cache from AsyncStorage
|
|
113
|
+
* @param {Record<string, StatsigDataAdapterResult>} cache The values to set for _inMemoryCache
|
|
114
|
+
*/
|
|
115
|
+
DataAdapterCore.prototype._setInMemoryCache = function (cache) {
|
|
116
|
+
this._inMemoryCache = cache;
|
|
117
|
+
};
|
|
118
|
+
DataAdapterCore.prototype._fetchLatest = function (current, user) {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
120
|
+
var latest, response;
|
|
121
|
+
return __generator(this, function (_a) {
|
|
122
|
+
switch (_a.label) {
|
|
123
|
+
case 0: return [4 /*yield*/, this._fetchFromNetwork(current, user)];
|
|
124
|
+
case 1:
|
|
125
|
+
latest = _a.sent();
|
|
126
|
+
if (!latest) {
|
|
127
|
+
Log_1.Log.debug('No response returned for latest value');
|
|
128
|
+
return [2 /*return*/, null];
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
response = JSON.parse(latest);
|
|
132
|
+
if (current && response.has_updates === false) {
|
|
133
|
+
return [2 /*return*/, {
|
|
134
|
+
source: 'NetworkNotModified',
|
|
135
|
+
data: current,
|
|
136
|
+
receivedAt: Date.now(),
|
|
137
|
+
}];
|
|
138
|
+
}
|
|
139
|
+
if (response.has_updates !== true) {
|
|
140
|
+
return [2 /*return*/, null];
|
|
141
|
+
}
|
|
142
|
+
return [2 /*return*/, { source: 'Network', data: latest, receivedAt: Date.now() }];
|
|
143
|
+
}
|
|
144
|
+
catch (_b) {
|
|
145
|
+
Log_1.Log.debug('Failure while attempting to persist latest value');
|
|
146
|
+
}
|
|
147
|
+
return [2 /*return*/, null];
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
DataAdapterCore.prototype._getSdkKey = function () {
|
|
153
|
+
if (this._sdkKey) {
|
|
154
|
+
return this._sdkKey;
|
|
155
|
+
}
|
|
156
|
+
Log_1.Log.error("".concat(this._className, " is not attached to a Client"));
|
|
157
|
+
return '';
|
|
158
|
+
};
|
|
159
|
+
DataAdapterCore.prototype._getCacheKey = function (user) {
|
|
160
|
+
var key = (0, StatsigUser_1.getUserStorageKey)(this._getSdkKey(), user);
|
|
161
|
+
return "".concat(StatsigDataAdapter_1.DataAdapterCachePrefix, ".").concat(this._cacheSuffix, ".").concat(key);
|
|
162
|
+
};
|
|
163
|
+
DataAdapterCore.prototype._addToInMemoryCache = function (cacheKey, result) {
|
|
164
|
+
var entries = Object.entries(this._inMemoryCache);
|
|
165
|
+
if (entries.length < CACHE_LIMIT) {
|
|
166
|
+
this._inMemoryCache[cacheKey] = result;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
var oldest = entries.reduce(function (acc, curr) {
|
|
170
|
+
return curr[1] < acc[1] ? curr : acc;
|
|
171
|
+
})[0];
|
|
172
|
+
delete this._inMemoryCache[oldest];
|
|
173
|
+
this._inMemoryCache[cacheKey] = result;
|
|
174
|
+
};
|
|
175
|
+
DataAdapterCore.prototype._loadFromCache = function (cacheKey) {
|
|
176
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
var cache = window.localStorage.getItem(cacheKey);
|
|
180
|
+
if (cache == null) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
var result = JSON.parse(cache);
|
|
185
|
+
return __assign(__assign({}, result), { source: 'Cache' });
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
Log_1.Log.error('Failed to parse cached result');
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
DataAdapterCore.prototype._writeToCache = function (cacheKey, result) {
|
|
193
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
194
|
+
return __generator(this, function (_a) {
|
|
195
|
+
switch (_a.label) {
|
|
196
|
+
case 0: return [4 /*yield*/, StorageProvider_1.Storage.setItem(cacheKey, JSON.stringify(result))];
|
|
197
|
+
case 1:
|
|
198
|
+
_a.sent();
|
|
199
|
+
return [4 /*yield*/, this._runLocalStorageCacheEviction(cacheKey)];
|
|
200
|
+
case 2:
|
|
201
|
+
_a.sent();
|
|
202
|
+
return [2 /*return*/];
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
DataAdapterCore.prototype._runLocalStorageCacheEviction = function (cacheKey) {
|
|
208
|
+
var _a;
|
|
209
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
210
|
+
var lastModifiedTimeMap, entries, oldest;
|
|
211
|
+
return __generator(this, function (_b) {
|
|
212
|
+
switch (_b.label) {
|
|
213
|
+
case 0: return [4 /*yield*/, (0, StorageProvider_1.getObjectFromStorage)(this._lastModifiedStoreKey)];
|
|
214
|
+
case 1:
|
|
215
|
+
lastModifiedTimeMap = (_a = (_b.sent())) !== null && _a !== void 0 ? _a : {};
|
|
216
|
+
lastModifiedTimeMap[cacheKey] = Date.now();
|
|
217
|
+
entries = Object.entries(lastModifiedTimeMap);
|
|
218
|
+
if (!(entries.length <= CACHE_LIMIT)) return [3 /*break*/, 3];
|
|
219
|
+
return [4 /*yield*/, (0, StorageProvider_1.setObjectInStorage)(this._lastModifiedStoreKey, lastModifiedTimeMap)];
|
|
220
|
+
case 2:
|
|
221
|
+
_b.sent();
|
|
222
|
+
return [2 /*return*/];
|
|
223
|
+
case 3:
|
|
224
|
+
oldest = entries.reduce(function (acc, current) {
|
|
225
|
+
return current[1] < acc[1] ? current : acc;
|
|
226
|
+
});
|
|
227
|
+
delete lastModifiedTimeMap[oldest[0]];
|
|
228
|
+
return [4 /*yield*/, StorageProvider_1.Storage.removeItem(oldest[0])];
|
|
229
|
+
case 4:
|
|
230
|
+
_b.sent();
|
|
231
|
+
return [4 /*yield*/, (0, StorageProvider_1.setObjectInStorage)(this._lastModifiedStoreKey, lastModifiedTimeMap)];
|
|
232
|
+
case 5:
|
|
233
|
+
_b.sent();
|
|
234
|
+
return [2 /*return*/];
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
return DataAdapterCore;
|
|
240
|
+
}());
|
|
241
|
+
exports.DataAdapterCore = DataAdapterCore;
|
package/src/ErrorBoundary.d.ts
CHANGED
package/src/ErrorBoundary.js
CHANGED
|
@@ -70,6 +70,9 @@ var ErrorBoundary = /** @class */ (function () {
|
|
|
70
70
|
return null;
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
|
+
ErrorBoundary.prototype.logError = function (tag, error) {
|
|
74
|
+
this._onError(tag, error);
|
|
75
|
+
};
|
|
73
76
|
ErrorBoundary.prototype._onError = function (tag, error, emitter) {
|
|
74
77
|
var _this = this;
|
|
75
78
|
try {
|
package/src/EventLogger.d.ts
CHANGED
|
@@ -13,18 +13,24 @@ export declare class EventLogger {
|
|
|
13
13
|
private _lastExposureMap;
|
|
14
14
|
private _maxQueueSize;
|
|
15
15
|
private _failedLogs;
|
|
16
|
+
private _hasRunQuickFlush;
|
|
17
|
+
private _creationTime;
|
|
16
18
|
constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon | null);
|
|
17
19
|
enqueue(event: StatsigEventInternal): void;
|
|
18
20
|
reset(): void;
|
|
19
21
|
onVisibilityChanged(visibility: Visibility): void;
|
|
20
22
|
shutdown(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* We 'Quick Flush' following the very first event enqueued
|
|
25
|
+
* within the quick flush window
|
|
26
|
+
*/
|
|
27
|
+
private _quickFlushIfNeeded;
|
|
21
28
|
private _shouldLogEvent;
|
|
22
29
|
private _flushAndForget;
|
|
23
30
|
private _flush;
|
|
24
31
|
private _sendEvents;
|
|
25
32
|
private _sendEventsViaPost;
|
|
26
33
|
private _sendEventsViaBeacon;
|
|
27
|
-
private _isBeaconSupported;
|
|
28
34
|
private _saveFailedLogsToStorage;
|
|
29
35
|
private _retryFailedLogs;
|
|
30
36
|
private _getStorageKey;
|
package/src/EventLogger.js
CHANGED
|
@@ -60,6 +60,7 @@ var MAX_DEDUPER_KEYS = 1000;
|
|
|
60
60
|
var DEDUPER_WINDOW_DURATION_MS = 60000;
|
|
61
61
|
var MAX_FAILED_LOGS = 500;
|
|
62
62
|
var DEFAULT_API = 'https://api.statsig.com/v1';
|
|
63
|
+
var QUICK_FLUSH_WINDOW_MS = 200;
|
|
63
64
|
var EventLogger = /** @class */ (function () {
|
|
64
65
|
function EventLogger(_sdkKey, _emitter, _network, _options) {
|
|
65
66
|
var _this = this;
|
|
@@ -70,6 +71,8 @@ var EventLogger = /** @class */ (function () {
|
|
|
70
71
|
this._options = _options;
|
|
71
72
|
this._queue = [];
|
|
72
73
|
this._lastExposureMap = {};
|
|
74
|
+
this._hasRunQuickFlush = false;
|
|
75
|
+
this._creationTime = Date.now();
|
|
73
76
|
this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
|
|
74
77
|
var flushInterval = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
|
|
75
78
|
this._flushTimer = setInterval(function () { return _this._flushAndForget(); }, flushInterval);
|
|
@@ -87,6 +90,7 @@ var EventLogger = /** @class */ (function () {
|
|
|
87
90
|
}
|
|
88
91
|
var _a = StatsigMetadata_1.StatsigMetadataProvider.get(), sdkType = _a.sdkType, sdkVersion = _a.sdkVersion;
|
|
89
92
|
this._queue.push(__assign(__assign({}, event), { statsigMetadata: { sdkType: sdkType, sdkVersion: sdkVersion } }));
|
|
93
|
+
this._quickFlushIfNeeded();
|
|
90
94
|
if (this._queue.length > this._maxQueueSize) {
|
|
91
95
|
this._flushAndForget();
|
|
92
96
|
}
|
|
@@ -116,6 +120,21 @@ var EventLogger = /** @class */ (function () {
|
|
|
116
120
|
});
|
|
117
121
|
});
|
|
118
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* We 'Quick Flush' following the very first event enqueued
|
|
125
|
+
* within the quick flush window
|
|
126
|
+
*/
|
|
127
|
+
EventLogger.prototype._quickFlushIfNeeded = function () {
|
|
128
|
+
var _this = this;
|
|
129
|
+
if (this._hasRunQuickFlush) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this._hasRunQuickFlush = true;
|
|
133
|
+
if (Date.now() - this._creationTime > QUICK_FLUSH_WINDOW_MS) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
setTimeout(function () { return _this._flushAndForget(); }, QUICK_FLUSH_WINDOW_MS);
|
|
137
|
+
};
|
|
119
138
|
EventLogger.prototype._shouldLogEvent = function (event) {
|
|
120
139
|
var _a, _b, _c, _d;
|
|
121
140
|
if (!(0, StatsigEvent_1.isExposureEvent)(event)) {
|
|
@@ -170,17 +189,19 @@ var EventLogger = /** @class */ (function () {
|
|
|
170
189
|
return __generator(this, function (_e) {
|
|
171
190
|
switch (_e.label) {
|
|
172
191
|
case 0:
|
|
173
|
-
_e.trys.push([0,
|
|
192
|
+
_e.trys.push([0, 5, , 6]);
|
|
174
193
|
isInForeground = VisibilityChangeObserver_1.VisibilityChangeObserver.isCurrentlyVisible();
|
|
175
194
|
api = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.api) !== null && _b !== void 0 ? _b : DEFAULT_API;
|
|
176
|
-
if (!(!isInForeground && this.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
case 1: return [4 /*yield*/, this._sendEventsViaPost(api, events)];
|
|
180
|
-
case 2:
|
|
195
|
+
if (!(!isInForeground && this._network.isBeaconSupported())) return [3 /*break*/, 2];
|
|
196
|
+
return [4 /*yield*/, this._sendEventsViaBeacon(api, events)];
|
|
197
|
+
case 1:
|
|
181
198
|
_c = _e.sent();
|
|
182
|
-
|
|
199
|
+
return [3 /*break*/, 4];
|
|
200
|
+
case 2: return [4 /*yield*/, this._sendEventsViaPost(api, events)];
|
|
183
201
|
case 3:
|
|
202
|
+
_c = _e.sent();
|
|
203
|
+
_e.label = 4;
|
|
204
|
+
case 4:
|
|
184
205
|
response = _c;
|
|
185
206
|
if (response.success) {
|
|
186
207
|
this._emitter({
|
|
@@ -191,12 +212,12 @@ var EventLogger = /** @class */ (function () {
|
|
|
191
212
|
else {
|
|
192
213
|
this._saveFailedLogsToStorage(events);
|
|
193
214
|
}
|
|
194
|
-
return [3 /*break*/,
|
|
195
|
-
case
|
|
215
|
+
return [3 /*break*/, 6];
|
|
216
|
+
case 5:
|
|
196
217
|
_d = _e.sent();
|
|
197
218
|
Log_1.Log.warn('Failed to flush events.');
|
|
198
|
-
return [3 /*break*/,
|
|
199
|
-
case
|
|
219
|
+
return [3 /*break*/, 6];
|
|
220
|
+
case 6: return [2 /*return*/];
|
|
200
221
|
}
|
|
201
222
|
});
|
|
202
223
|
});
|
|
@@ -213,15 +234,15 @@ var EventLogger = /** @class */ (function () {
|
|
|
213
234
|
},
|
|
214
235
|
url: "".concat(api, "/rgstr"),
|
|
215
236
|
retries: 3,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
237
|
+
params: {
|
|
238
|
+
// ec = Event Count
|
|
239
|
+
ec: String(events.length),
|
|
219
240
|
},
|
|
220
241
|
})];
|
|
221
242
|
case 1:
|
|
222
243
|
result = _a.sent();
|
|
223
|
-
if (result) {
|
|
224
|
-
return [2 /*return*/, JSON.parse(result)];
|
|
244
|
+
if (result === null || result === void 0 ? void 0 : result.body) {
|
|
245
|
+
return [2 /*return*/, JSON.parse(result.body)];
|
|
225
246
|
}
|
|
226
247
|
return [2 /*return*/, { success: false }];
|
|
227
248
|
}
|
|
@@ -229,19 +250,24 @@ var EventLogger = /** @class */ (function () {
|
|
|
229
250
|
});
|
|
230
251
|
};
|
|
231
252
|
EventLogger.prototype._sendEventsViaBeacon = function (api, events) {
|
|
232
|
-
return {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
254
|
+
var _a;
|
|
255
|
+
return __generator(this, function (_b) {
|
|
256
|
+
switch (_b.label) {
|
|
257
|
+
case 0:
|
|
258
|
+
_a = {};
|
|
259
|
+
return [4 /*yield*/, this._network.beacon({
|
|
260
|
+
sdkKey: this._sdkKey,
|
|
261
|
+
data: {
|
|
262
|
+
events: events,
|
|
263
|
+
},
|
|
264
|
+
url: "".concat(api, "/log_event_beacon"),
|
|
265
|
+
})];
|
|
266
|
+
case 1: return [2 /*return*/, (_a.success = _b.sent(),
|
|
267
|
+
_a)];
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
245
271
|
};
|
|
246
272
|
EventLogger.prototype._saveFailedLogsToStorage = function (events) {
|
|
247
273
|
var _a;
|
package/src/Monitoring.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { ErrorBoundary } from './ErrorBoundary';
|
|
2
|
-
export declare function monitorClass
|
|
3
|
-
export declare function monitorFunction<T>(errorBoundary: ErrorBoundary, tag: string, func: () => T, instance: unknown): T;
|
|
2
|
+
export declare function monitorClass(errorBoundary: ErrorBoundary, instance: object): void;
|
package/src/Monitoring.js
CHANGED
|
@@ -1,13 +1,57 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.monitorClass = void 0;
|
|
4
4
|
var Diagnostics_1 = require("./Diagnostics");
|
|
5
5
|
var StatsigClientBase_1 = require("./StatsigClientBase");
|
|
6
|
-
function monitorClass(errorBoundary,
|
|
7
|
-
|
|
6
|
+
function monitorClass(errorBoundary, instance) {
|
|
7
|
+
try {
|
|
8
|
+
_monitorClassImpl(errorBoundary, instance);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
errorBoundary.logError('monitorClass', error);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.monitorClass = monitorClass;
|
|
15
|
+
function _monitorFunction(errorBoundary, tag, func, instance) {
|
|
16
|
+
var client = instance instanceof StatsigClientBase_1.StatsigClientBase ? instance['emit'] : undefined;
|
|
17
|
+
return errorBoundary.capture(tag, function () { return (0, Diagnostics_1.captureDiagnostics)(tag, function () { return func.apply(instance); }); }, client);
|
|
18
|
+
}
|
|
19
|
+
function _getProtoSafe(instance) {
|
|
20
|
+
if (typeof instance === 'object') {
|
|
21
|
+
var proto = Object.getPrototypeOf(instance);
|
|
22
|
+
return proto && typeof proto === 'object'
|
|
23
|
+
? proto
|
|
24
|
+
: null;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function _getAllInstanceMethodNames(instance) {
|
|
29
|
+
var names = new Set();
|
|
30
|
+
var proto = _getProtoSafe(instance);
|
|
31
|
+
while (proto && proto !== Object.prototype) {
|
|
32
|
+
Object.getOwnPropertyNames(proto)
|
|
33
|
+
.filter(function (prop) { return typeof (proto === null || proto === void 0 ? void 0 : proto[prop]) === 'function'; })
|
|
34
|
+
.forEach(function (name) { return names.add(name); });
|
|
35
|
+
proto = Object.getPrototypeOf(proto);
|
|
36
|
+
}
|
|
37
|
+
return Array.from(names);
|
|
38
|
+
}
|
|
39
|
+
function _getAllStaticMethodNames(instance) {
|
|
40
|
+
var names = new Set();
|
|
41
|
+
var proto = _getProtoSafe(instance);
|
|
42
|
+
Object.getOwnPropertyNames((proto === null || proto === void 0 ? void 0 : proto.constructor) || {})
|
|
43
|
+
.filter(function (prop) {
|
|
44
|
+
var _a;
|
|
45
|
+
return typeof ((_a = proto === null || proto === void 0 ? void 0 : proto.constructor) === null || _a === void 0 ? void 0 : _a[prop]) === 'function';
|
|
46
|
+
})
|
|
47
|
+
.forEach(function (name) { return names.add(name); });
|
|
48
|
+
return Array.from(names);
|
|
49
|
+
}
|
|
50
|
+
function _monitorClassImpl(errorBoundary, instance) {
|
|
51
|
+
var _a;
|
|
8
52
|
var obj = instance;
|
|
9
53
|
var _loop_1 = function (method) {
|
|
10
|
-
if (method === 'constructor'
|
|
54
|
+
if (method === 'constructor') {
|
|
11
55
|
return "continue";
|
|
12
56
|
}
|
|
13
57
|
var original = obj[method];
|
|
@@ -17,17 +61,26 @@ function monitorClass(errorBoundary, target, instance) {
|
|
|
17
61
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
18
62
|
args[_i] = arguments[_i];
|
|
19
63
|
}
|
|
20
|
-
return
|
|
64
|
+
return _monitorFunction(errorBoundary, method, function () { return original.apply(_this, args); }, instance);
|
|
21
65
|
};
|
|
22
66
|
};
|
|
23
|
-
for (var _i = 0,
|
|
24
|
-
var method =
|
|
67
|
+
for (var _i = 0, _b = _getAllInstanceMethodNames(obj); _i < _b.length; _i++) {
|
|
68
|
+
var method = _b[_i];
|
|
25
69
|
_loop_1(method);
|
|
26
70
|
}
|
|
71
|
+
var _loop_2 = function (method) {
|
|
72
|
+
var original = (_a = obj === null || obj === void 0 ? void 0 : obj.constructor) === null || _a === void 0 ? void 0 : _a[method];
|
|
73
|
+
(obj === null || obj === void 0 ? void 0 : obj.constructor)[method] =
|
|
74
|
+
function () {
|
|
75
|
+
var args = [];
|
|
76
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
77
|
+
args[_i] = arguments[_i];
|
|
78
|
+
}
|
|
79
|
+
return _monitorFunction(errorBoundary, "".concat(obj.constructor.name, ".").concat(method), function () { return original.apply(obj.constructor, args); }, instance);
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
for (var _c = 0, _d = _getAllStaticMethodNames(obj); _c < _d.length; _c++) {
|
|
83
|
+
var method = _d[_c];
|
|
84
|
+
_loop_2(method);
|
|
85
|
+
}
|
|
27
86
|
}
|
|
28
|
-
exports.monitorClass = monitorClass;
|
|
29
|
-
function monitorFunction(errorBoundary, tag, func, instance) {
|
|
30
|
-
var client = instance instanceof StatsigClientBase_1.StatsigClientBase ? instance['emit'] : undefined;
|
|
31
|
-
return errorBoundary.capture(tag, function () { return (0, Diagnostics_1.captureDiagnostics)(tag, function () { return func.apply(instance); }); }, client);
|
|
32
|
-
}
|
|
33
|
-
exports.monitorFunction = monitorFunction;
|
package/src/NetworkCore.d.ts
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
|
+
import { StatsigClientEmitEventFunc } from './StatsigClientBase';
|
|
1
2
|
import { StatsigOptionsCommon } from './StatsigOptionsCommon';
|
|
2
3
|
type RequestArgs = {
|
|
3
4
|
sdkKey: string;
|
|
4
5
|
url: string;
|
|
5
6
|
timeoutMs?: number;
|
|
6
7
|
retries?: number;
|
|
8
|
+
params?: Record<string, string>;
|
|
7
9
|
headers?: Record<string, string>;
|
|
8
10
|
};
|
|
9
11
|
type RequestArgsWithData = RequestArgs & {
|
|
10
12
|
data: Record<string, unknown>;
|
|
11
13
|
};
|
|
14
|
+
type NetworkResponse = {
|
|
15
|
+
body: string | null;
|
|
16
|
+
code: number;
|
|
17
|
+
};
|
|
12
18
|
export declare class NetworkCore {
|
|
13
19
|
private _options;
|
|
20
|
+
private _emitter?;
|
|
14
21
|
private readonly _timeout;
|
|
15
|
-
constructor(_options: StatsigOptionsCommon | null);
|
|
16
|
-
post(args: RequestArgsWithData): Promise<
|
|
17
|
-
get(args: RequestArgs): Promise<
|
|
18
|
-
|
|
22
|
+
constructor(_options: StatsigOptionsCommon | null, _emitter?: StatsigClientEmitEventFunc | undefined);
|
|
23
|
+
post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
|
|
24
|
+
get(args: RequestArgs): Promise<NetworkResponse | null>;
|
|
25
|
+
isBeaconSupported(): boolean;
|
|
26
|
+
beacon(args: RequestArgsWithData): Promise<boolean>;
|
|
19
27
|
private _sendRequest;
|
|
20
28
|
private _getPopulatedURL;
|
|
29
|
+
private _getPopulatedBody;
|
|
21
30
|
}
|
|
22
31
|
export {};
|