@statsig/client-core 3.31.1-beta.3 → 3.31.2-beta.1
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/ErrorBoundary.d.ts +0 -2
- package/src/ErrorBoundary.js +3 -23
- package/src/EventLogger.d.ts +23 -14
- package/src/EventLogger.js +187 -86
- package/src/EventRetryConstants.d.ts +1 -3
- package/src/EventRetryConstants.js +1 -3
- package/src/NetworkCore.d.ts +0 -1
- package/src/NetworkCore.js +3 -5
- package/src/StatsigClientBase.js +1 -1
- package/src/StatsigMetadata.d.ts +1 -1
- package/src/StatsigMetadata.js +1 -1
- package/src/BatchedEventsQueue.d.ts +0 -14
- package/src/BatchedEventsQueue.js +0 -51
- package/src/EventSender.d.ts +0 -23
- package/src/EventSender.js +0 -96
- package/src/FlushCoordinator.d.ts +0 -50
- package/src/FlushCoordinator.js +0 -338
- package/src/FlushInterval.d.ts +0 -13
- package/src/FlushInterval.js +0 -44
- package/src/FlushTypes.d.ts +0 -7
- package/src/FlushTypes.js +0 -12
- package/src/PendingEvents.d.ts +0 -10
- package/src/PendingEvents.js +0 -26
package/package.json
CHANGED
package/src/ErrorBoundary.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ export declare class ErrorBoundary {
|
|
|
10
10
|
constructor(_sdkKey: string, _options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined, _lastSeenError?: Error | undefined);
|
|
11
11
|
wrap(instance: unknown, namePrefix?: string): void;
|
|
12
12
|
logError(tag: string, error: unknown): void;
|
|
13
|
-
logDroppedEvents(count: number, reason: string, metadata?: Record<string, unknown>): void;
|
|
14
|
-
logEventRequestFailure(count: number, reason: string, flushType: string, statusCode: number): void;
|
|
15
13
|
getLastSeenErrorAndReset(): Error | null;
|
|
16
14
|
attachErrorIfNoneExists(error: unknown): void;
|
|
17
15
|
private _capture;
|
package/src/ErrorBoundary.js
CHANGED
|
@@ -44,25 +44,6 @@ class ErrorBoundary {
|
|
|
44
44
|
logError(tag, error) {
|
|
45
45
|
this._onError(tag, error);
|
|
46
46
|
}
|
|
47
|
-
logDroppedEvents(count, reason, metadata) {
|
|
48
|
-
const extra = {
|
|
49
|
-
eventCount: String(count),
|
|
50
|
-
};
|
|
51
|
-
if (metadata) {
|
|
52
|
-
Object.entries(metadata).forEach(([key, value]) => {
|
|
53
|
-
extra[key] = String(value);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
this._onError(`statsig::log_event_dropped_event_count`, new Error(reason), true, extra);
|
|
57
|
-
}
|
|
58
|
-
logEventRequestFailure(count, reason, flushType, statusCode) {
|
|
59
|
-
const extra = {
|
|
60
|
-
eventCount: String(count),
|
|
61
|
-
flushType: flushType,
|
|
62
|
-
statusCode: String(statusCode),
|
|
63
|
-
};
|
|
64
|
-
this._onError(`statsig::log_event_failed`, new Error(reason), true, extra);
|
|
65
|
-
}
|
|
66
47
|
getLastSeenErrorAndReset() {
|
|
67
48
|
const tempError = this._lastSeenError;
|
|
68
49
|
this._lastSeenError = undefined;
|
|
@@ -87,7 +68,7 @@ class ErrorBoundary {
|
|
|
87
68
|
return null;
|
|
88
69
|
}
|
|
89
70
|
}
|
|
90
|
-
_onError(tag, error
|
|
71
|
+
_onError(tag, error) {
|
|
91
72
|
try {
|
|
92
73
|
Log_1.Log.warn(`Caught error in ${tag}`, { error });
|
|
93
74
|
const impl = () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -97,7 +78,7 @@ class ErrorBoundary {
|
|
|
97
78
|
const name = isError ? unwrapped.name : 'No Name';
|
|
98
79
|
const resolvedError = _resolveError(unwrapped);
|
|
99
80
|
this._lastSeenError = resolvedError;
|
|
100
|
-
if (
|
|
81
|
+
if (this._seen.has(name)) {
|
|
101
82
|
return;
|
|
102
83
|
}
|
|
103
84
|
this._seen.add(name);
|
|
@@ -112,8 +93,7 @@ class ErrorBoundary {
|
|
|
112
93
|
const sdkType = SDKType_1.SDKType._get(this._sdkKey);
|
|
113
94
|
const statsigMetadata = StatsigMetadata_1.StatsigMetadataProvider.get();
|
|
114
95
|
const info = isError ? unwrapped.stack : _getDescription(unwrapped);
|
|
115
|
-
const body = Object.assign({ tag, exception: name, info,
|
|
116
|
-
extra, statsigOptions: _getStatsigOptionLoggingCopy(this._options) }, Object.assign(Object.assign({}, statsigMetadata), { sdkType }));
|
|
96
|
+
const body = Object.assign({ tag, exception: name, info, statsigOptions: _getStatsigOptionLoggingCopy(this._options) }, Object.assign(Object.assign({}, statsigMetadata), { sdkType }));
|
|
117
97
|
const func = (_f = (_e = (_d = this._options) === null || _d === void 0 ? void 0 : _d.networkConfig) === null || _e === void 0 ? void 0 : _e.networkOverrideFunc) !== null && _f !== void 0 ? _f : fetch;
|
|
118
98
|
yield func(exports.EXCEPTION_ENDPOINT, {
|
|
119
99
|
method: 'POST',
|
package/src/EventLogger.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ErrorBoundary } from './ErrorBoundary';
|
|
2
1
|
import { NetworkCore } from './NetworkCore';
|
|
3
2
|
import { StatsigClientEmitEventFunc } from './StatsigClientBase';
|
|
4
3
|
import { StatsigEventInternal } from './StatsigEvent';
|
|
@@ -8,18 +7,18 @@ export declare class EventLogger {
|
|
|
8
7
|
private _emitter;
|
|
9
8
|
private _network;
|
|
10
9
|
private _options;
|
|
11
|
-
private
|
|
12
|
-
private
|
|
13
|
-
private _batchQueue;
|
|
14
|
-
private _flushCoordinator;
|
|
10
|
+
private _queue;
|
|
11
|
+
private _flushIntervalId;
|
|
15
12
|
private _lastExposureTimeMap;
|
|
16
13
|
private _nonExposedChecks;
|
|
14
|
+
private _maxQueueSize;
|
|
15
|
+
private _hasRunQuickFlush;
|
|
16
|
+
private _creationTime;
|
|
17
17
|
private _loggingEnabled;
|
|
18
18
|
private _logEventUrlConfig;
|
|
19
|
-
private _isShuttingDown;
|
|
20
|
-
private _storageKey;
|
|
21
19
|
private static _safeFlushAndForget;
|
|
22
|
-
|
|
20
|
+
private static _safeRetryFailedLogs;
|
|
21
|
+
constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null);
|
|
23
22
|
setLogEventCompressionMode(mode: LogEventCompressionMode): void;
|
|
24
23
|
setLoggingEnabled(loggingEnabled: LoggingEnabledOption): void;
|
|
25
24
|
enqueue(event: StatsigEventInternal): void;
|
|
@@ -28,12 +27,22 @@ export declare class EventLogger {
|
|
|
28
27
|
start(): void;
|
|
29
28
|
stop(): Promise<void>;
|
|
30
29
|
flush(): Promise<void>;
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* We 'Quick Flush' following the very first event enqueued
|
|
32
|
+
* within the quick flush window
|
|
33
|
+
*/
|
|
34
|
+
private _quickFlushIfNeeded;
|
|
32
35
|
private _shouldLogEvent;
|
|
33
|
-
private
|
|
36
|
+
private _sendEvents;
|
|
37
|
+
private _sendEventsViaPost;
|
|
38
|
+
private _sendEventsViaBeacon;
|
|
39
|
+
private _getRequestData;
|
|
40
|
+
private _saveFailedLogsToStorage;
|
|
41
|
+
private _getFailedLogsFromStorage;
|
|
42
|
+
private _retryFailedLogs;
|
|
34
43
|
private _getStorageKey;
|
|
35
|
-
private
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
private
|
|
44
|
+
private _normalizeAndAppendEvent;
|
|
45
|
+
private _appendAndResetNonExposedChecks;
|
|
46
|
+
private _getCurrentPageUrl;
|
|
47
|
+
private _startBackgroundFlushInterval;
|
|
39
48
|
}
|
package/src/EventLogger.js
CHANGED
|
@@ -10,22 +10,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.EventLogger = void 0;
|
|
13
|
-
const BatchedEventsQueue_1 = require("./BatchedEventsQueue");
|
|
14
13
|
const CacheKey_1 = require("./CacheKey");
|
|
15
|
-
const EventRetryConstants_1 = require("./EventRetryConstants");
|
|
16
|
-
const FlushCoordinator_1 = require("./FlushCoordinator");
|
|
17
14
|
const Hashing_1 = require("./Hashing");
|
|
18
15
|
const Log_1 = require("./Log");
|
|
19
16
|
const NetworkConfig_1 = require("./NetworkConfig");
|
|
20
|
-
const PendingEvents_1 = require("./PendingEvents");
|
|
21
17
|
const SafeJs_1 = require("./SafeJs");
|
|
18
|
+
const SessionID_1 = require("./SessionID");
|
|
22
19
|
const StatsigEvent_1 = require("./StatsigEvent");
|
|
23
20
|
const StatsigOptionsCommon_1 = require("./StatsigOptionsCommon");
|
|
24
21
|
const StorageProvider_1 = require("./StorageProvider");
|
|
25
22
|
const UrlConfiguration_1 = require("./UrlConfiguration");
|
|
26
23
|
const VisibilityObserving_1 = require("./VisibilityObserving");
|
|
24
|
+
const DEFAULT_QUEUE_SIZE = 100;
|
|
25
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 10000;
|
|
27
26
|
const MAX_DEDUPER_KEYS = 1000;
|
|
28
27
|
const DEDUPER_WINDOW_DURATION_MS = 600000;
|
|
28
|
+
const MAX_FAILED_LOGS = 500;
|
|
29
|
+
const QUICK_FLUSH_WINDOW_MS = 200;
|
|
29
30
|
const EVENT_LOGGER_MAP = {};
|
|
30
31
|
class EventLogger {
|
|
31
32
|
static _safeFlushAndForget(sdkKey) {
|
|
@@ -34,17 +35,21 @@ class EventLogger {
|
|
|
34
35
|
// noop
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
static _safeRetryFailedLogs(sdkKey) {
|
|
39
|
+
var _a;
|
|
40
|
+
(_a = EVENT_LOGGER_MAP[sdkKey]) === null || _a === void 0 ? void 0 : _a._retryFailedLogs();
|
|
41
|
+
}
|
|
42
|
+
constructor(_sdkKey, _emitter, _network, _options) {
|
|
38
43
|
var _a, _b;
|
|
39
44
|
this._sdkKey = _sdkKey;
|
|
40
45
|
this._emitter = _emitter;
|
|
41
46
|
this._network = _network;
|
|
42
47
|
this._options = _options;
|
|
43
|
-
this.
|
|
48
|
+
this._queue = [];
|
|
44
49
|
this._lastExposureTimeMap = {};
|
|
45
50
|
this._nonExposedChecks = {};
|
|
46
|
-
this.
|
|
47
|
-
this.
|
|
51
|
+
this._hasRunQuickFlush = false;
|
|
52
|
+
this._creationTime = Date.now();
|
|
48
53
|
this._loggingEnabled =
|
|
49
54
|
(_a = _options === null || _options === void 0 ? void 0 : _options.loggingEnabled) !== null && _a !== void 0 ? _a : ((_options === null || _options === void 0 ? void 0 : _options.disableLogging) === true
|
|
50
55
|
? StatsigOptionsCommon_1.LoggingEnabledOption.disabled
|
|
@@ -52,45 +57,34 @@ class EventLogger {
|
|
|
52
57
|
if ((_options === null || _options === void 0 ? void 0 : _options.loggingEnabled) && _options.disableLogging !== undefined) {
|
|
53
58
|
Log_1.Log.warn('Detected both loggingEnabled and disableLogging options. loggingEnabled takes precedence - please remove disableLogging.');
|
|
54
59
|
}
|
|
60
|
+
this._maxQueueSize = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _b !== void 0 ? _b : DEFAULT_QUEUE_SIZE;
|
|
55
61
|
const config = _options === null || _options === void 0 ? void 0 : _options.networkConfig;
|
|
56
62
|
this._logEventUrlConfig = new UrlConfiguration_1.UrlConfiguration(NetworkConfig_1.Endpoint._rgstr, config === null || config === void 0 ? void 0 : config.logEventUrl, config === null || config === void 0 ? void 0 : config.api, config === null || config === void 0 ? void 0 : config.logEventFallbackUrls);
|
|
57
|
-
const batchSize = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _b !== void 0 ? _b : EventRetryConstants_1.EventRetryConstants.DEFAULT_BATCH_SIZE;
|
|
58
|
-
this._pendingEvents = new PendingEvents_1.PendingEvents(batchSize);
|
|
59
|
-
this._batchQueue = new BatchedEventsQueue_1.BatchQueue(batchSize);
|
|
60
|
-
this._flushCoordinator = new FlushCoordinator_1.FlushCoordinator(this._batchQueue, this._pendingEvents, () => this.appendAndResetNonExposedChecks(), this._sdkKey, this._network, this._emitter, this._logEventUrlConfig, this._options, this._loggingEnabled, this._errorBoundary);
|
|
61
63
|
}
|
|
62
64
|
setLogEventCompressionMode(mode) {
|
|
63
|
-
this.
|
|
65
|
+
this._network.setLogEventCompressionMode(mode);
|
|
64
66
|
}
|
|
65
67
|
setLoggingEnabled(loggingEnabled) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Log_1.Log.debug(`Loaded ${events.length} stored event(s) from storage`);
|
|
73
|
-
if (events.length > 0) {
|
|
74
|
-
events.forEach((event) => {
|
|
75
|
-
this._flushCoordinator.addEvent(event);
|
|
76
|
-
});
|
|
77
|
-
this.flush().catch((error) => {
|
|
78
|
-
Log_1.Log.warn('Failed to flush events after enabling logging:', error);
|
|
79
|
-
});
|
|
68
|
+
if (this._loggingEnabled === 'disabled' && loggingEnabled !== 'disabled') {
|
|
69
|
+
// load any pre consented events into memory
|
|
70
|
+
const storageKey = this._getStorageKey();
|
|
71
|
+
const events = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
72
|
+
if (events) {
|
|
73
|
+
this._queue.push(...events);
|
|
80
74
|
}
|
|
75
|
+
StorageProvider_1.Storage.removeItem(storageKey);
|
|
81
76
|
}
|
|
77
|
+
this._loggingEnabled = loggingEnabled;
|
|
82
78
|
}
|
|
83
79
|
enqueue(event) {
|
|
84
80
|
if (!this._shouldLogEvent(event)) {
|
|
85
81
|
return;
|
|
86
82
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
this._normalizeAndAppendEvent(event);
|
|
84
|
+
this._quickFlushIfNeeded();
|
|
85
|
+
if (this._queue.length > this._maxQueueSize) {
|
|
86
|
+
EventLogger._safeFlushAndForget(this._sdkKey);
|
|
91
87
|
}
|
|
92
|
-
this._flushCoordinator.addEvent(normalizedEvent);
|
|
93
|
-
this._flushCoordinator.checkQuickFlush();
|
|
94
88
|
}
|
|
95
89
|
incrementNonExposureCount(name) {
|
|
96
90
|
var _a;
|
|
@@ -117,42 +111,47 @@ class EventLogger {
|
|
|
117
111
|
EventLogger._safeFlushAndForget(this._sdkKey);
|
|
118
112
|
}
|
|
119
113
|
else if (visibility === 'foreground') {
|
|
120
|
-
this.
|
|
114
|
+
EventLogger._safeRetryFailedLogs(this._sdkKey);
|
|
121
115
|
}
|
|
122
116
|
});
|
|
123
117
|
}
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
});
|
|
127
|
-
this._flushCoordinator.startScheduledFlushCycle();
|
|
118
|
+
this._retryFailedLogs();
|
|
119
|
+
this._startBackgroundFlushInterval();
|
|
128
120
|
}
|
|
129
121
|
stop() {
|
|
130
122
|
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
-
this.
|
|
132
|
-
|
|
123
|
+
if (this._flushIntervalId) {
|
|
124
|
+
clearInterval(this._flushIntervalId);
|
|
125
|
+
this._flushIntervalId = null;
|
|
126
|
+
}
|
|
133
127
|
delete EVENT_LOGGER_MAP[this._sdkKey];
|
|
128
|
+
yield this.flush();
|
|
134
129
|
});
|
|
135
130
|
}
|
|
136
131
|
flush() {
|
|
137
132
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
-
|
|
133
|
+
this._appendAndResetNonExposedChecks();
|
|
134
|
+
if (this._queue.length === 0) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const events = this._queue;
|
|
138
|
+
this._queue = [];
|
|
139
|
+
yield this._sendEvents(events);
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
/**
|
|
143
|
+
* We 'Quick Flush' following the very first event enqueued
|
|
144
|
+
* within the quick flush window
|
|
145
|
+
*/
|
|
146
|
+
_quickFlushIfNeeded() {
|
|
147
|
+
if (this._hasRunQuickFlush) {
|
|
143
148
|
return;
|
|
144
149
|
}
|
|
145
|
-
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
time: Date.now(),
|
|
151
|
-
metadata: {
|
|
152
|
-
checks,
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
this._flushCoordinator.addEvent(event);
|
|
150
|
+
this._hasRunQuickFlush = true;
|
|
151
|
+
if (Date.now() - this._creationTime > QUICK_FLUSH_WINDOW_MS) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
setTimeout(() => EventLogger._safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
|
|
156
155
|
}
|
|
157
156
|
_shouldLogEvent(event) {
|
|
158
157
|
var _a;
|
|
@@ -187,54 +186,118 @@ class EventLogger {
|
|
|
187
186
|
this._lastExposureTimeMap[key] = now;
|
|
188
187
|
return true;
|
|
189
188
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
189
|
+
_sendEvents(events) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
var _a, _b;
|
|
192
|
+
if (this._loggingEnabled === 'disabled') {
|
|
193
|
+
this._saveFailedLogsToStorage(events);
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const isClosing = (0, VisibilityObserving_1._isUnloading)();
|
|
198
|
+
const shouldUseBeacon = isClosing &&
|
|
199
|
+
this._network.isBeaconSupported() &&
|
|
200
|
+
((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.networkConfig) === null || _b === void 0 ? void 0 : _b.networkOverrideFunc) == null;
|
|
201
|
+
this._emitter({
|
|
202
|
+
name: 'pre_logs_flushed',
|
|
203
|
+
events,
|
|
204
|
+
});
|
|
205
|
+
const response = shouldUseBeacon
|
|
206
|
+
? this._sendEventsViaBeacon(events)
|
|
207
|
+
: yield this._sendEventsViaPost(events);
|
|
208
|
+
if (response.success) {
|
|
209
|
+
this._emitter({
|
|
210
|
+
name: 'logs_flushed',
|
|
211
|
+
events,
|
|
212
|
+
});
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
Log_1.Log.warn('Failed to flush events.');
|
|
217
|
+
this._saveFailedLogsToStorage(events);
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (_c) {
|
|
222
|
+
Log_1.Log.warn('Failed to flush events.');
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
196
226
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
227
|
+
_sendEventsViaPost(events) {
|
|
228
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
229
|
+
var _a;
|
|
230
|
+
const result = yield this._network.post(this._getRequestData(events));
|
|
231
|
+
const code = (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : -1;
|
|
232
|
+
return { success: code >= 200 && code < 300 };
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
_sendEventsViaBeacon(events) {
|
|
236
|
+
return {
|
|
237
|
+
success: this._network.beacon(this._getRequestData(events)),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
_getRequestData(events) {
|
|
241
|
+
return {
|
|
242
|
+
sdkKey: this._sdkKey,
|
|
243
|
+
data: {
|
|
244
|
+
events,
|
|
245
|
+
},
|
|
246
|
+
urlConfig: this._logEventUrlConfig,
|
|
247
|
+
retries: 3,
|
|
248
|
+
isCompressable: true,
|
|
249
|
+
params: {
|
|
250
|
+
[NetworkConfig_1.NetworkParam.EventCount]: String(events.length),
|
|
251
|
+
},
|
|
252
|
+
credentials: 'same-origin',
|
|
253
|
+
};
|
|
202
254
|
}
|
|
203
|
-
|
|
255
|
+
_saveFailedLogsToStorage(events) {
|
|
256
|
+
while (events.length > MAX_FAILED_LOGS) {
|
|
257
|
+
events.shift();
|
|
258
|
+
}
|
|
204
259
|
const storageKey = this._getStorageKey();
|
|
205
260
|
try {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (existingEvents.length > EventRetryConstants_1.EventRetryConstants.MAX_LOCAL_STORAGE) {
|
|
209
|
-
existingEvents = existingEvents.slice(-EventRetryConstants_1.EventRetryConstants.MAX_LOCAL_STORAGE);
|
|
210
|
-
}
|
|
211
|
-
(0, StorageProvider_1._setObjectInStorage)(storageKey, existingEvents);
|
|
261
|
+
const savedEvents = this._getFailedLogsFromStorage(storageKey);
|
|
262
|
+
(0, StorageProvider_1._setObjectInStorage)(storageKey, [...savedEvents, ...events]);
|
|
212
263
|
}
|
|
213
|
-
catch (
|
|
214
|
-
Log_1.Log.warn('Unable to save
|
|
264
|
+
catch (_a) {
|
|
265
|
+
Log_1.Log.warn('Unable to save failed logs to storage');
|
|
215
266
|
}
|
|
216
267
|
}
|
|
217
|
-
|
|
268
|
+
_getFailedLogsFromStorage(storageKey) {
|
|
269
|
+
let savedEvents = [];
|
|
218
270
|
try {
|
|
219
|
-
const
|
|
220
|
-
if (Array.isArray(
|
|
221
|
-
|
|
271
|
+
const retrieved = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
272
|
+
if (Array.isArray(retrieved)) {
|
|
273
|
+
savedEvents = retrieved;
|
|
222
274
|
}
|
|
223
|
-
return
|
|
275
|
+
return savedEvents;
|
|
224
276
|
}
|
|
225
277
|
catch (_a) {
|
|
226
278
|
return [];
|
|
227
279
|
}
|
|
228
280
|
}
|
|
229
|
-
|
|
281
|
+
_retryFailedLogs() {
|
|
230
282
|
const storageKey = this._getStorageKey();
|
|
231
|
-
|
|
232
|
-
|
|
283
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
284
|
+
if (!StorageProvider_1.Storage.isReady()) {
|
|
285
|
+
yield StorageProvider_1.Storage.isReadyResolver();
|
|
286
|
+
}
|
|
287
|
+
const events = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
288
|
+
if (!events) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
233
291
|
StorageProvider_1.Storage.removeItem(storageKey);
|
|
234
|
-
|
|
235
|
-
|
|
292
|
+
yield this._sendEvents(events);
|
|
293
|
+
}))().catch(() => {
|
|
294
|
+
Log_1.Log.warn('Failed to flush stored logs');
|
|
295
|
+
});
|
|
236
296
|
}
|
|
237
|
-
|
|
297
|
+
_getStorageKey() {
|
|
298
|
+
return `statsig.failed_logs.${(0, Hashing_1._DJB2)(this._sdkKey)}`;
|
|
299
|
+
}
|
|
300
|
+
_normalizeAndAppendEvent(event) {
|
|
238
301
|
if (event.user) {
|
|
239
302
|
event.user = Object.assign({}, event.user);
|
|
240
303
|
delete event.user.privateAttributes;
|
|
@@ -244,7 +307,45 @@ class EventLogger {
|
|
|
244
307
|
if (currentPage) {
|
|
245
308
|
extras.statsigMetadata = { currentPage };
|
|
246
309
|
}
|
|
247
|
-
|
|
310
|
+
const final = Object.assign(Object.assign({}, event), extras);
|
|
311
|
+
Log_1.Log.debug('Enqueued Event:', final);
|
|
312
|
+
this._queue.push(final);
|
|
313
|
+
}
|
|
314
|
+
_appendAndResetNonExposedChecks() {
|
|
315
|
+
if (Object.keys(this._nonExposedChecks).length === 0) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
this._normalizeAndAppendEvent({
|
|
319
|
+
eventName: 'statsig::non_exposed_checks',
|
|
320
|
+
user: null,
|
|
321
|
+
time: Date.now(),
|
|
322
|
+
metadata: {
|
|
323
|
+
checks: Object.assign({}, this._nonExposedChecks),
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
this._nonExposedChecks = {};
|
|
327
|
+
}
|
|
328
|
+
_getCurrentPageUrl() {
|
|
329
|
+
var _a;
|
|
330
|
+
if (((_a = this._options) === null || _a === void 0 ? void 0 : _a.includeCurrentPageUrlWithEvents) === false) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
return (0, SafeJs_1._getCurrentPageUrlSafe)();
|
|
334
|
+
}
|
|
335
|
+
_startBackgroundFlushInterval() {
|
|
336
|
+
var _a, _b;
|
|
337
|
+
const flushInterval = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
|
|
338
|
+
const intervalId = setInterval(() => {
|
|
339
|
+
const logger = EVENT_LOGGER_MAP[this._sdkKey];
|
|
340
|
+
if (!logger || logger._flushIntervalId !== intervalId) {
|
|
341
|
+
clearInterval(intervalId);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
EventLogger._safeFlushAndForget(this._sdkKey);
|
|
345
|
+
SessionID_1.StatsigSession.checkForIdleSession(this._sdkKey);
|
|
346
|
+
}
|
|
347
|
+
}, flushInterval);
|
|
348
|
+
this._flushIntervalId = intervalId;
|
|
248
349
|
}
|
|
249
350
|
}
|
|
250
351
|
exports.EventLogger = EventLogger;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export declare const EventRetryConstants: {
|
|
2
2
|
readonly MAX_RETRY_ATTEMPTS: 5;
|
|
3
3
|
readonly DEFAULT_BATCH_SIZE: 100;
|
|
4
|
-
readonly MAX_PENDING_BATCHES:
|
|
4
|
+
readonly MAX_PENDING_BATCHES: 10;
|
|
5
5
|
readonly TICK_INTERVAL_MS: 1000;
|
|
6
|
-
readonly QUICK_FLUSH_WINDOW_MS: 200;
|
|
7
|
-
readonly MAX_LOCAL_STORAGE: 500;
|
|
8
6
|
readonly MAX_QUEUED_EVENTS: number;
|
|
9
7
|
};
|
|
@@ -4,10 +4,8 @@ exports.EventRetryConstants = void 0;
|
|
|
4
4
|
exports.EventRetryConstants = {
|
|
5
5
|
MAX_RETRY_ATTEMPTS: 5,
|
|
6
6
|
DEFAULT_BATCH_SIZE: 100,
|
|
7
|
-
MAX_PENDING_BATCHES:
|
|
7
|
+
MAX_PENDING_BATCHES: 10,
|
|
8
8
|
TICK_INTERVAL_MS: 1000,
|
|
9
|
-
QUICK_FLUSH_WINDOW_MS: 200,
|
|
10
|
-
MAX_LOCAL_STORAGE: 500,
|
|
11
9
|
get MAX_QUEUED_EVENTS() {
|
|
12
10
|
return this.DEFAULT_BATCH_SIZE * this.MAX_PENDING_BATCHES;
|
|
13
11
|
},
|
package/src/NetworkCore.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { StatsigClientEmitEventFunc } from './StatsigClientBase';
|
|
|
5
5
|
import { AnyStatsigOptions, LogEventCompressionMode } from './StatsigOptionsCommon';
|
|
6
6
|
import { Flatten } from './TypingUtils';
|
|
7
7
|
import { UrlConfiguration } from './UrlConfiguration';
|
|
8
|
-
export declare const RETRYABLE_CODES: Set<number>;
|
|
9
8
|
type RequestArgs = {
|
|
10
9
|
sdkKey: string;
|
|
11
10
|
urlConfig: UrlConfiguration;
|
package/src/NetworkCore.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.NetworkCore =
|
|
12
|
+
exports.NetworkCore = void 0;
|
|
13
13
|
require("./$_StatsigGlobal");
|
|
14
14
|
const __StatsigGlobal_1 = require("./$_StatsigGlobal");
|
|
15
15
|
const Diagnostics_1 = require("./Diagnostics");
|
|
@@ -31,9 +31,7 @@ const BACKOFF_MAX_MS = 30000;
|
|
|
31
31
|
const RATE_LIMIT_WINDOW_MS = 1000;
|
|
32
32
|
const RATE_LIMIT_MAX_REQ_COUNT = 50;
|
|
33
33
|
const LEAK_RATE = RATE_LIMIT_MAX_REQ_COUNT / RATE_LIMIT_WINDOW_MS;
|
|
34
|
-
|
|
35
|
-
408, 500, 502, 503, 504, 522, 524, 599,
|
|
36
|
-
]);
|
|
34
|
+
const RETRYABLE_CODES = new Set([408, 500, 502, 503, 504, 522, 524, 599]);
|
|
37
35
|
class NetworkCore {
|
|
38
36
|
constructor(options, _emitter) {
|
|
39
37
|
this._emitter = _emitter;
|
|
@@ -163,7 +161,7 @@ class NetworkCore {
|
|
|
163
161
|
}
|
|
164
162
|
if (!retries ||
|
|
165
163
|
currentAttempt > retries ||
|
|
166
|
-
!
|
|
164
|
+
!RETRYABLE_CODES.has((_b = response === null || response === void 0 ? void 0 : response.status) !== null && _b !== void 0 ? _b : 500)) {
|
|
167
165
|
(_c = this._emitter) === null || _c === void 0 ? void 0 : _c.call(this, {
|
|
168
166
|
name: 'error',
|
|
169
167
|
error,
|
package/src/StatsigClientBase.js
CHANGED
|
@@ -41,8 +41,8 @@ class StatsigClientBase {
|
|
|
41
41
|
this._options = options !== null && options !== void 0 ? options : {};
|
|
42
42
|
this._memoCache = {};
|
|
43
43
|
this.overrideAdapter = (_a = options === null || options === void 0 ? void 0 : options.overrideAdapter) !== null && _a !== void 0 ? _a : null;
|
|
44
|
+
this._logger = new EventLogger_1.EventLogger(sdkKey, emitter, network, options);
|
|
44
45
|
this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey, options, emitter);
|
|
45
|
-
this._logger = new EventLogger_1.EventLogger(sdkKey, emitter, network, options, this._errorBoundary);
|
|
46
46
|
this._errorBoundary.wrap(this);
|
|
47
47
|
this._errorBoundary.wrap(adapter);
|
|
48
48
|
this._errorBoundary.wrap(this._logger);
|
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 = '3.31.
|
|
4
|
+
exports.SDK_VERSION = '3.31.2-beta.1';
|
|
5
5
|
let metadata = {
|
|
6
6
|
sdkVersion: exports.SDK_VERSION,
|
|
7
7
|
sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { EventBatch } from './EventBatch';
|
|
2
|
-
import { StatsigEventInternal } from './StatsigEvent';
|
|
3
|
-
export declare class BatchQueue {
|
|
4
|
-
private _batches;
|
|
5
|
-
private _batchSize;
|
|
6
|
-
constructor(batchSize?: number);
|
|
7
|
-
batchSize(): number;
|
|
8
|
-
requeueBatch(batch: EventBatch): number;
|
|
9
|
-
hasFullBatch(): boolean;
|
|
10
|
-
takeNextBatch(): EventBatch | null;
|
|
11
|
-
takeAllBatches(): EventBatch[];
|
|
12
|
-
createBatches(events: StatsigEventInternal[]): number;
|
|
13
|
-
private _enqueueBatch;
|
|
14
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BatchQueue = void 0;
|
|
4
|
-
const EventBatch_1 = require("./EventBatch");
|
|
5
|
-
const EventRetryConstants_1 = require("./EventRetryConstants");
|
|
6
|
-
class BatchQueue {
|
|
7
|
-
constructor(batchSize = EventRetryConstants_1.EventRetryConstants.DEFAULT_BATCH_SIZE) {
|
|
8
|
-
this._batches = [];
|
|
9
|
-
this._batchSize = batchSize;
|
|
10
|
-
}
|
|
11
|
-
batchSize() {
|
|
12
|
-
return this._batchSize;
|
|
13
|
-
}
|
|
14
|
-
requeueBatch(batch) {
|
|
15
|
-
return this._enqueueBatch(batch);
|
|
16
|
-
}
|
|
17
|
-
hasFullBatch() {
|
|
18
|
-
return this._batches.some((batch) => batch.events.length >= this._batchSize);
|
|
19
|
-
}
|
|
20
|
-
takeNextBatch() {
|
|
21
|
-
var _a;
|
|
22
|
-
return (_a = this._batches.shift()) !== null && _a !== void 0 ? _a : null;
|
|
23
|
-
}
|
|
24
|
-
takeAllBatches() {
|
|
25
|
-
const batches = this._batches;
|
|
26
|
-
this._batches = [];
|
|
27
|
-
return batches;
|
|
28
|
-
}
|
|
29
|
-
createBatches(events) {
|
|
30
|
-
let i = 0;
|
|
31
|
-
let droppedCount = 0;
|
|
32
|
-
while (i < events.length) {
|
|
33
|
-
const chunk = events.slice(i, i + this._batchSize);
|
|
34
|
-
droppedCount += this._enqueueBatch(new EventBatch_1.EventBatch(chunk));
|
|
35
|
-
i += this._batchSize;
|
|
36
|
-
}
|
|
37
|
-
return droppedCount;
|
|
38
|
-
}
|
|
39
|
-
_enqueueBatch(batch) {
|
|
40
|
-
this._batches.push(batch);
|
|
41
|
-
let droppedEventCount = 0;
|
|
42
|
-
while (this._batches.length > EventRetryConstants_1.EventRetryConstants.MAX_PENDING_BATCHES) {
|
|
43
|
-
const dropped = this._batches.shift();
|
|
44
|
-
if (dropped) {
|
|
45
|
-
droppedEventCount += dropped.events.length;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return droppedEventCount;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
exports.BatchQueue = BatchQueue;
|
package/src/EventSender.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { EventBatch } from './EventBatch';
|
|
2
|
-
import { NetworkCore } from './NetworkCore';
|
|
3
|
-
import { StatsigClientEmitEventFunc } from './StatsigClientBase';
|
|
4
|
-
import { LogEventCompressionMode, LoggingEnabledOption, NetworkConfigCommon, StatsigOptionsCommon } from './StatsigOptionsCommon';
|
|
5
|
-
import { UrlConfiguration } from './UrlConfiguration';
|
|
6
|
-
export declare class EventSender {
|
|
7
|
-
private _network;
|
|
8
|
-
private _sdkKey;
|
|
9
|
-
private _options;
|
|
10
|
-
private _logEventUrlConfig;
|
|
11
|
-
private _emitter;
|
|
12
|
-
private _loggingEnabled;
|
|
13
|
-
constructor(sdkKey: string, network: NetworkCore, emitter: StatsigClientEmitEventFunc, logEventUrlConfig: UrlConfiguration, options: StatsigOptionsCommon<NetworkConfigCommon> | null, loggingEnabled: LoggingEnabledOption);
|
|
14
|
-
setLogEventCompressionMode(mode: LogEventCompressionMode): void;
|
|
15
|
-
setLoggingEnabled(enabled: LoggingEnabledOption): void;
|
|
16
|
-
sendBatch(batch: EventBatch): Promise<{
|
|
17
|
-
success: boolean;
|
|
18
|
-
statusCode: number;
|
|
19
|
-
}>;
|
|
20
|
-
private _sendEventsViaPost;
|
|
21
|
-
private _sendEventsViaBeacon;
|
|
22
|
-
private _getRequestData;
|
|
23
|
-
}
|
package/src/EventSender.js
DELETED
|
@@ -1,96 +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.EventSender = void 0;
|
|
13
|
-
const Log_1 = require("./Log");
|
|
14
|
-
const NetworkConfig_1 = require("./NetworkConfig");
|
|
15
|
-
const VisibilityObserving_1 = require("./VisibilityObserving");
|
|
16
|
-
class EventSender {
|
|
17
|
-
constructor(sdkKey, network, emitter, logEventUrlConfig, options, loggingEnabled) {
|
|
18
|
-
this._sdkKey = sdkKey;
|
|
19
|
-
this._network = network;
|
|
20
|
-
this._emitter = emitter;
|
|
21
|
-
this._options = options;
|
|
22
|
-
this._logEventUrlConfig = logEventUrlConfig;
|
|
23
|
-
this._loggingEnabled = loggingEnabled;
|
|
24
|
-
}
|
|
25
|
-
setLogEventCompressionMode(mode) {
|
|
26
|
-
this._network.setLogEventCompressionMode(mode);
|
|
27
|
-
}
|
|
28
|
-
setLoggingEnabled(enabled) {
|
|
29
|
-
this._loggingEnabled = enabled;
|
|
30
|
-
}
|
|
31
|
-
sendBatch(batch) {
|
|
32
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
-
var _a, _b;
|
|
34
|
-
try {
|
|
35
|
-
const isClosing = (0, VisibilityObserving_1._isUnloading)();
|
|
36
|
-
const shouldUseBeacon = isClosing &&
|
|
37
|
-
this._network.isBeaconSupported() &&
|
|
38
|
-
((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.networkConfig) === null || _b === void 0 ? void 0 : _b.networkOverrideFunc) == null;
|
|
39
|
-
this._emitter({
|
|
40
|
-
name: 'pre_logs_flushed',
|
|
41
|
-
events: batch.events,
|
|
42
|
-
});
|
|
43
|
-
const response = shouldUseBeacon
|
|
44
|
-
? this._sendEventsViaBeacon(batch)
|
|
45
|
-
: yield this._sendEventsViaPost(batch);
|
|
46
|
-
if (response.success) {
|
|
47
|
-
this._emitter({
|
|
48
|
-
name: 'logs_flushed',
|
|
49
|
-
events: batch.events,
|
|
50
|
-
});
|
|
51
|
-
return response;
|
|
52
|
-
}
|
|
53
|
-
return { success: false, statusCode: -1 };
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
Log_1.Log.warn('Failed to send batch:', error);
|
|
57
|
-
return { success: false, statusCode: -1 };
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
_sendEventsViaPost(batch) {
|
|
62
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
var _a;
|
|
64
|
-
const result = yield this._network.post(this._getRequestData(batch));
|
|
65
|
-
const code = (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : -1;
|
|
66
|
-
return { success: code >= 200 && code < 300, statusCode: code };
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
_sendEventsViaBeacon(batch) {
|
|
70
|
-
const success = this._network.beacon(this._getRequestData(batch));
|
|
71
|
-
return {
|
|
72
|
-
success,
|
|
73
|
-
statusCode: success ? 200 : -1,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
_getRequestData(batch) {
|
|
77
|
-
return {
|
|
78
|
-
sdkKey: this._sdkKey,
|
|
79
|
-
data: {
|
|
80
|
-
events: batch.events,
|
|
81
|
-
},
|
|
82
|
-
urlConfig: this._logEventUrlConfig,
|
|
83
|
-
retries: 3,
|
|
84
|
-
isCompressable: true,
|
|
85
|
-
params: {
|
|
86
|
-
[NetworkConfig_1.NetworkParam.EventCount]: String(batch.events.length),
|
|
87
|
-
},
|
|
88
|
-
headers: {
|
|
89
|
-
'statsig-event-count': String(batch.events.length),
|
|
90
|
-
'statsig-retry-count': String(batch.attempts),
|
|
91
|
-
},
|
|
92
|
-
credentials: 'same-origin',
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
exports.EventSender = EventSender;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { BatchQueue } from './BatchedEventsQueue';
|
|
2
|
-
import { ErrorBoundary } from './ErrorBoundary';
|
|
3
|
-
import { NetworkCore } from './NetworkCore';
|
|
4
|
-
import { PendingEvents } from './PendingEvents';
|
|
5
|
-
import { StatsigClientEmitEventFunc } from './StatsigClientBase';
|
|
6
|
-
import { StatsigEventInternal } from './StatsigEvent';
|
|
7
|
-
import { LogEventCompressionMode, LoggingEnabledOption, NetworkConfigCommon, StatsigOptionsCommon } from './StatsigOptionsCommon';
|
|
8
|
-
import { UrlConfiguration } from './UrlConfiguration';
|
|
9
|
-
type PrepareFlushCallBack = () => void;
|
|
10
|
-
export declare class FlushCoordinator {
|
|
11
|
-
private _flushInterval;
|
|
12
|
-
private _batchQueue;
|
|
13
|
-
private _pendingEvents;
|
|
14
|
-
private _eventSender;
|
|
15
|
-
private _onPrepareFlush;
|
|
16
|
-
private _errorBoundary;
|
|
17
|
-
private _cooldownTimer;
|
|
18
|
-
private _maxIntervalTimer;
|
|
19
|
-
private _hasRunQuickFlush;
|
|
20
|
-
private _currentFlushPromise;
|
|
21
|
-
private _creationTime;
|
|
22
|
-
private _sdkKey;
|
|
23
|
-
private _storageKey;
|
|
24
|
-
constructor(batchQueue: BatchQueue, pendingEvents: PendingEvents, onPrepareFlush: PrepareFlushCallBack, sdkKey: string, network: NetworkCore, emitter: StatsigClientEmitEventFunc, logEventUrlConfig: UrlConfiguration, options: StatsigOptionsCommon<NetworkConfigCommon> | null, loggingEnabled: LoggingEnabledOption, errorBoundary: ErrorBoundary);
|
|
25
|
-
setLoggingEnabled(loggingEnabled: LoggingEnabledOption): void;
|
|
26
|
-
setLogEventCompressionMode(mode: LogEventCompressionMode): void;
|
|
27
|
-
startScheduledFlushCycle(): void;
|
|
28
|
-
stopScheduledFlushCycle(): void;
|
|
29
|
-
addEvent(event: StatsigEventInternal): void;
|
|
30
|
-
processManualFlush(): Promise<void>;
|
|
31
|
-
processShutdown(): Promise<void>;
|
|
32
|
-
private _executeFlush;
|
|
33
|
-
checkQuickFlush(): void;
|
|
34
|
-
private _attemptScheduledFlush;
|
|
35
|
-
processLimitFlush(): void;
|
|
36
|
-
private _processLimitFlushInternal;
|
|
37
|
-
private _scheduleNextFlush;
|
|
38
|
-
private _clearAllTimers;
|
|
39
|
-
private _processNextBatch;
|
|
40
|
-
private _processOneBatch;
|
|
41
|
-
private _prepareQueueForFlush;
|
|
42
|
-
containsAtLeastOneFullBatch(): boolean;
|
|
43
|
-
convertPendingEventsToBatches(): number;
|
|
44
|
-
private _handleFailure;
|
|
45
|
-
loadAndRetryShutdownFailedEvents(): Promise<void>;
|
|
46
|
-
private _getStorageKey;
|
|
47
|
-
private _saveShutdownFailedEventsToStorage;
|
|
48
|
-
private _getShutdownFailedEventsFromStorage;
|
|
49
|
-
}
|
|
50
|
-
export {};
|
package/src/FlushCoordinator.js
DELETED
|
@@ -1,338 +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.FlushCoordinator = void 0;
|
|
13
|
-
const EventRetryConstants_1 = require("./EventRetryConstants");
|
|
14
|
-
const EventSender_1 = require("./EventSender");
|
|
15
|
-
const FlushInterval_1 = require("./FlushInterval");
|
|
16
|
-
const FlushTypes_1 = require("./FlushTypes");
|
|
17
|
-
const Hashing_1 = require("./Hashing");
|
|
18
|
-
const Log_1 = require("./Log");
|
|
19
|
-
const NetworkCore_1 = require("./NetworkCore");
|
|
20
|
-
const SessionID_1 = require("./SessionID");
|
|
21
|
-
const StorageProvider_1 = require("./StorageProvider");
|
|
22
|
-
class FlushCoordinator {
|
|
23
|
-
constructor(batchQueue, pendingEvents, onPrepareFlush,
|
|
24
|
-
// For Event Sender
|
|
25
|
-
sdkKey, network, emitter, logEventUrlConfig, options, loggingEnabled, errorBoundary) {
|
|
26
|
-
this._cooldownTimer = null;
|
|
27
|
-
this._maxIntervalTimer = null;
|
|
28
|
-
this._hasRunQuickFlush = false;
|
|
29
|
-
this._currentFlushPromise = null;
|
|
30
|
-
this._creationTime = Date.now();
|
|
31
|
-
this._storageKey = null;
|
|
32
|
-
this._flushInterval = new FlushInterval_1.FlushInterval();
|
|
33
|
-
this._batchQueue = batchQueue;
|
|
34
|
-
this._pendingEvents = pendingEvents;
|
|
35
|
-
this._onPrepareFlush = onPrepareFlush;
|
|
36
|
-
this._errorBoundary = errorBoundary;
|
|
37
|
-
this._sdkKey = sdkKey;
|
|
38
|
-
this._eventSender = new EventSender_1.EventSender(sdkKey, network, emitter, logEventUrlConfig, options, loggingEnabled);
|
|
39
|
-
}
|
|
40
|
-
setLoggingEnabled(loggingEnabled) {
|
|
41
|
-
this._eventSender.setLoggingEnabled(loggingEnabled);
|
|
42
|
-
}
|
|
43
|
-
setLogEventCompressionMode(mode) {
|
|
44
|
-
this._eventSender.setLogEventCompressionMode(mode);
|
|
45
|
-
}
|
|
46
|
-
startScheduledFlushCycle() {
|
|
47
|
-
this._scheduleNextFlush();
|
|
48
|
-
}
|
|
49
|
-
stopScheduledFlushCycle() {
|
|
50
|
-
this._clearAllTimers();
|
|
51
|
-
}
|
|
52
|
-
addEvent(event) {
|
|
53
|
-
this._pendingEvents.addToPendingEventsQueue(event);
|
|
54
|
-
if (this._currentFlushPromise) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (this._pendingEvents.hasEventsForFullBatch()) {
|
|
58
|
-
this.processLimitFlush();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
processManualFlush() {
|
|
62
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
if (this._currentFlushPromise) {
|
|
64
|
-
yield this._currentFlushPromise;
|
|
65
|
-
}
|
|
66
|
-
this._currentFlushPromise = this._executeFlush(FlushTypes_1.FlushType.Manual).finally(() => {
|
|
67
|
-
this._currentFlushPromise = null;
|
|
68
|
-
this._scheduleNextFlush();
|
|
69
|
-
});
|
|
70
|
-
return this._currentFlushPromise;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
processShutdown() {
|
|
74
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
-
if (this._currentFlushPromise) {
|
|
76
|
-
yield this._currentFlushPromise;
|
|
77
|
-
}
|
|
78
|
-
this._currentFlushPromise = this._executeFlush(FlushTypes_1.FlushType.Shutdown)
|
|
79
|
-
.catch((error) => {
|
|
80
|
-
Log_1.Log.error(`Error during shutdown flush: ${error}`);
|
|
81
|
-
})
|
|
82
|
-
.finally(() => {
|
|
83
|
-
this._currentFlushPromise = null;
|
|
84
|
-
});
|
|
85
|
-
return this._currentFlushPromise;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
_executeFlush(flushType) {
|
|
89
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
this._clearAllTimers();
|
|
91
|
-
try {
|
|
92
|
-
this._prepareQueueForFlush(flushType);
|
|
93
|
-
const batches = this._batchQueue.takeAllBatches();
|
|
94
|
-
if (batches.length === 0) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
yield Promise.all(batches.map((batch) => this._processOneBatch(batch, flushType)));
|
|
98
|
-
}
|
|
99
|
-
finally {
|
|
100
|
-
this._scheduleNextFlush();
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
checkQuickFlush() {
|
|
105
|
-
if (this._hasRunQuickFlush) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (Date.now() - this._creationTime >
|
|
109
|
-
EventRetryConstants_1.EventRetryConstants.QUICK_FLUSH_WINDOW_MS) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
this._hasRunQuickFlush = true;
|
|
113
|
-
setTimeout(() => {
|
|
114
|
-
this.processManualFlush().catch((error) => {
|
|
115
|
-
Log_1.Log.warn('Quick flush failed:', error);
|
|
116
|
-
});
|
|
117
|
-
}, EventRetryConstants_1.EventRetryConstants.QUICK_FLUSH_WINDOW_MS);
|
|
118
|
-
}
|
|
119
|
-
_attemptScheduledFlush() {
|
|
120
|
-
if (this._currentFlushPromise) {
|
|
121
|
-
this._scheduleNextFlush();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const shouldFlushBySize = this.containsAtLeastOneFullBatch();
|
|
125
|
-
const shouldFlushByTime = this._flushInterval.hasReachedMaxInterval();
|
|
126
|
-
if (!shouldFlushBySize && !shouldFlushByTime) {
|
|
127
|
-
this._scheduleNextFlush();
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
this._flushInterval.markFlushAttempt();
|
|
131
|
-
let flushType;
|
|
132
|
-
if (shouldFlushBySize) {
|
|
133
|
-
flushType = FlushTypes_1.FlushType.ScheduledFullBatch;
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
flushType = FlushTypes_1.FlushType.ScheduledMaxTime;
|
|
137
|
-
}
|
|
138
|
-
this._currentFlushPromise = this._processNextBatch(flushType)
|
|
139
|
-
.then(() => {
|
|
140
|
-
//This discards boolean result. Main goal here is to track completion
|
|
141
|
-
})
|
|
142
|
-
.catch((error) => {
|
|
143
|
-
Log_1.Log.error('Error during scheduled flush:', error);
|
|
144
|
-
})
|
|
145
|
-
.finally(() => {
|
|
146
|
-
this._currentFlushPromise = null;
|
|
147
|
-
this._scheduleNextFlush();
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
processLimitFlush() {
|
|
151
|
-
if (!this._flushInterval.hasCompletelyRecoveredFromBackoff()) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (this._currentFlushPromise) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
this._currentFlushPromise = this._processLimitFlushInternal()
|
|
158
|
-
.catch((error) => {
|
|
159
|
-
Log_1.Log.error(`Error during limit flush`, error);
|
|
160
|
-
})
|
|
161
|
-
.finally(() => {
|
|
162
|
-
this._currentFlushPromise = null;
|
|
163
|
-
this._scheduleNextFlush();
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
_processLimitFlushInternal() {
|
|
167
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
168
|
-
const success = yield this._processNextBatch(FlushTypes_1.FlushType.Limit);
|
|
169
|
-
if (!success) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
while (this._flushInterval.hasCompletelyRecoveredFromBackoff() &&
|
|
173
|
-
this.containsAtLeastOneFullBatch()) {
|
|
174
|
-
const success = yield this._processNextBatch(FlushTypes_1.FlushType.Limit);
|
|
175
|
-
if (!success) {
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
_scheduleNextFlush() {
|
|
182
|
-
this._clearAllTimers();
|
|
183
|
-
const cooldownDelay = Math.max(0, this._flushInterval.getTimeUntilNextFlush());
|
|
184
|
-
this._cooldownTimer = setTimeout(() => {
|
|
185
|
-
this._cooldownTimer = null;
|
|
186
|
-
SessionID_1.StatsigSession.checkForIdleSession(this._sdkKey);
|
|
187
|
-
this._attemptScheduledFlush();
|
|
188
|
-
}, cooldownDelay);
|
|
189
|
-
const maxIntervalDelay = Math.max(0, this._flushInterval.getTimeTillMaxInterval());
|
|
190
|
-
this._maxIntervalTimer = setTimeout(() => {
|
|
191
|
-
this._maxIntervalTimer = null;
|
|
192
|
-
SessionID_1.StatsigSession.checkForIdleSession(this._sdkKey);
|
|
193
|
-
this._attemptScheduledFlush();
|
|
194
|
-
}, maxIntervalDelay);
|
|
195
|
-
}
|
|
196
|
-
_clearAllTimers() {
|
|
197
|
-
if (this._cooldownTimer !== null) {
|
|
198
|
-
clearTimeout(this._cooldownTimer);
|
|
199
|
-
this._cooldownTimer = null;
|
|
200
|
-
}
|
|
201
|
-
if (this._maxIntervalTimer !== null) {
|
|
202
|
-
clearTimeout(this._maxIntervalTimer);
|
|
203
|
-
this._maxIntervalTimer = null;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
_processNextBatch(flushType) {
|
|
207
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
208
|
-
this._prepareQueueForFlush(flushType);
|
|
209
|
-
const batch = this._batchQueue.takeNextBatch();
|
|
210
|
-
if (!batch) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
return this._processOneBatch(batch, flushType);
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
_processOneBatch(batch, flushType) {
|
|
217
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
-
const result = yield this._eventSender.sendBatch(batch);
|
|
219
|
-
if (result.success) {
|
|
220
|
-
this._flushInterval.adjustForSuccess();
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
this._flushInterval.adjustForFailure();
|
|
224
|
-
this._handleFailure(batch, flushType, result.statusCode);
|
|
225
|
-
return false;
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
_prepareQueueForFlush(flushType) {
|
|
229
|
-
this._onPrepareFlush();
|
|
230
|
-
const droppedCount = this.convertPendingEventsToBatches();
|
|
231
|
-
if (droppedCount > 0) {
|
|
232
|
-
Log_1.Log.warn(`Dropped ${droppedCount} events`);
|
|
233
|
-
this._errorBoundary.logDroppedEvents(droppedCount, `Batch queue limit reached`, {
|
|
234
|
-
loggingInterval: this._flushInterval.getCurrentIntervalMs(),
|
|
235
|
-
batchSize: this._batchQueue.batchSize(),
|
|
236
|
-
maxPendingBatches: EventRetryConstants_1.EventRetryConstants.MAX_PENDING_BATCHES,
|
|
237
|
-
flushType: flushType,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
containsAtLeastOneFullBatch() {
|
|
242
|
-
return (this._pendingEvents.hasEventsForFullBatch() ||
|
|
243
|
-
this._batchQueue.hasFullBatch());
|
|
244
|
-
}
|
|
245
|
-
convertPendingEventsToBatches() {
|
|
246
|
-
if (this._pendingEvents.isEmpty()) {
|
|
247
|
-
return 0;
|
|
248
|
-
}
|
|
249
|
-
const allEvents = this._pendingEvents.takeAll();
|
|
250
|
-
return this._batchQueue.createBatches(allEvents);
|
|
251
|
-
}
|
|
252
|
-
_handleFailure(batch, flushType, statusCode) {
|
|
253
|
-
if (flushType === FlushTypes_1.FlushType.Shutdown) {
|
|
254
|
-
Log_1.Log.warn(`${flushType} flush failed during shutdown. ` +
|
|
255
|
-
`${batch.events.length} event(s) will be saved to storage for retry in next session.`);
|
|
256
|
-
this._saveShutdownFailedEventsToStorage(batch.events);
|
|
257
|
-
this._errorBoundary.logEventRequestFailure(batch.events.length, `flush failed during shutdown - saved to storage`, flushType, statusCode);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (!NetworkCore_1.RETRYABLE_CODES.has(statusCode)) {
|
|
261
|
-
Log_1.Log.warn(`${flushType} flush failed after ${batch.attempts} attempt(s). ` +
|
|
262
|
-
`${batch.events.length} event(s) will be dropped. Non-retryable error: ${statusCode}`);
|
|
263
|
-
this._errorBoundary.logEventRequestFailure(batch.events.length, `non-retryable error`, flushType, statusCode);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (batch.attempts >= EventRetryConstants_1.EventRetryConstants.MAX_RETRY_ATTEMPTS) {
|
|
267
|
-
Log_1.Log.warn(`${flushType} flush failed after ${batch.attempts} attempt(s). ` +
|
|
268
|
-
`${batch.events.length} event(s) will be dropped.`);
|
|
269
|
-
this._errorBoundary.logEventRequestFailure(batch.events.length, `max retry attempts exceeded`, flushType, statusCode);
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
batch.incrementAttempts();
|
|
273
|
-
const droppedCount = this._batchQueue.requeueBatch(batch);
|
|
274
|
-
if (droppedCount > 0) {
|
|
275
|
-
Log_1.Log.warn(`Failed to requeue batch : dropped ${droppedCount} events due to full queue`);
|
|
276
|
-
this._errorBoundary.logDroppedEvents(droppedCount, `Batch queue limit reached`, {
|
|
277
|
-
loggingInterval: this._flushInterval.getCurrentIntervalMs(),
|
|
278
|
-
batchSize: this._batchQueue.batchSize(),
|
|
279
|
-
maxPendingBatches: EventRetryConstants_1.EventRetryConstants.MAX_PENDING_BATCHES,
|
|
280
|
-
flushType: flushType,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
loadAndRetryShutdownFailedEvents() {
|
|
285
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
286
|
-
const storageKey = this._getStorageKey();
|
|
287
|
-
try {
|
|
288
|
-
const events = this._getShutdownFailedEventsFromStorage(storageKey);
|
|
289
|
-
if (events.length === 0) {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
Log_1.Log.debug(`Loading ${events.length} failed shutdown event(s) from storage for retry`);
|
|
293
|
-
StorageProvider_1.Storage.removeItem(storageKey);
|
|
294
|
-
events.forEach((event) => {
|
|
295
|
-
this.addEvent(event);
|
|
296
|
-
});
|
|
297
|
-
yield this.processManualFlush();
|
|
298
|
-
}
|
|
299
|
-
catch (error) {
|
|
300
|
-
Log_1.Log.warn('Failed to load and retry failed shutdown events:', error);
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
_getStorageKey() {
|
|
305
|
-
if (!this._storageKey) {
|
|
306
|
-
this._storageKey = `statsig.failed_shutdown_events.${(0, Hashing_1._DJB2)(this._sdkKey)}`;
|
|
307
|
-
}
|
|
308
|
-
return this._storageKey;
|
|
309
|
-
}
|
|
310
|
-
_saveShutdownFailedEventsToStorage(events) {
|
|
311
|
-
const storageKey = this._getStorageKey();
|
|
312
|
-
try {
|
|
313
|
-
const existingEvents = this._getShutdownFailedEventsFromStorage(storageKey);
|
|
314
|
-
let allEvents = [...existingEvents, ...events];
|
|
315
|
-
if (allEvents.length > EventRetryConstants_1.EventRetryConstants.MAX_LOCAL_STORAGE) {
|
|
316
|
-
allEvents = allEvents.slice(-EventRetryConstants_1.EventRetryConstants.MAX_LOCAL_STORAGE);
|
|
317
|
-
}
|
|
318
|
-
(0, StorageProvider_1._setObjectInStorage)(storageKey, allEvents);
|
|
319
|
-
Log_1.Log.debug(`Saved ${events.length} failed shutdown event(s) to storage (total stored: ${allEvents.length})`);
|
|
320
|
-
}
|
|
321
|
-
catch (error) {
|
|
322
|
-
Log_1.Log.warn('Unable to save failed shutdown events to storage:', error);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
_getShutdownFailedEventsFromStorage(storageKey) {
|
|
326
|
-
try {
|
|
327
|
-
const events = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
328
|
-
if (Array.isArray(events)) {
|
|
329
|
-
return events;
|
|
330
|
-
}
|
|
331
|
-
return [];
|
|
332
|
-
}
|
|
333
|
-
catch (_a) {
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
exports.FlushCoordinator = FlushCoordinator;
|
package/src/FlushInterval.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare class FlushInterval {
|
|
2
|
-
private _currentIntervalMs;
|
|
3
|
-
private _lastFlushAttemptTime;
|
|
4
|
-
getCurrentIntervalMs(): number;
|
|
5
|
-
markFlushAttempt(): void;
|
|
6
|
-
getTimeSinceLastAttempt(): number;
|
|
7
|
-
hasReachedMaxInterval(): boolean;
|
|
8
|
-
getTimeTillMaxInterval(): number;
|
|
9
|
-
hasCompletelyRecoveredFromBackoff(): boolean;
|
|
10
|
-
adjustForSuccess(): void;
|
|
11
|
-
adjustForFailure(): void;
|
|
12
|
-
getTimeUntilNextFlush(): number;
|
|
13
|
-
}
|
package/src/FlushInterval.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FlushInterval = void 0;
|
|
4
|
-
const MIN_FLUSH_INTERVAL_MS = 1000;
|
|
5
|
-
const MAX_FLUSH_INTERVAL_MS = 60000;
|
|
6
|
-
class FlushInterval {
|
|
7
|
-
constructor() {
|
|
8
|
-
this._currentIntervalMs = MIN_FLUSH_INTERVAL_MS;
|
|
9
|
-
this._lastFlushAttemptTime = Date.now();
|
|
10
|
-
}
|
|
11
|
-
getCurrentIntervalMs() {
|
|
12
|
-
return this._currentIntervalMs;
|
|
13
|
-
}
|
|
14
|
-
markFlushAttempt() {
|
|
15
|
-
this._lastFlushAttemptTime = Date.now();
|
|
16
|
-
}
|
|
17
|
-
getTimeSinceLastAttempt() {
|
|
18
|
-
return Date.now() - this._lastFlushAttemptTime;
|
|
19
|
-
}
|
|
20
|
-
hasReachedMaxInterval() {
|
|
21
|
-
return this.getTimeSinceLastAttempt() >= MAX_FLUSH_INTERVAL_MS;
|
|
22
|
-
}
|
|
23
|
-
getTimeTillMaxInterval() {
|
|
24
|
-
return MAX_FLUSH_INTERVAL_MS - this.getTimeSinceLastAttempt();
|
|
25
|
-
}
|
|
26
|
-
hasCompletelyRecoveredFromBackoff() {
|
|
27
|
-
return this._currentIntervalMs <= MIN_FLUSH_INTERVAL_MS;
|
|
28
|
-
}
|
|
29
|
-
adjustForSuccess() {
|
|
30
|
-
const current = this._currentIntervalMs;
|
|
31
|
-
if (current === MIN_FLUSH_INTERVAL_MS) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
this._currentIntervalMs = Math.max(MIN_FLUSH_INTERVAL_MS, Math.floor(current / 2));
|
|
35
|
-
}
|
|
36
|
-
adjustForFailure() {
|
|
37
|
-
const current = this._currentIntervalMs;
|
|
38
|
-
this._currentIntervalMs = Math.min(MAX_FLUSH_INTERVAL_MS, current * 2);
|
|
39
|
-
}
|
|
40
|
-
getTimeUntilNextFlush() {
|
|
41
|
-
return this.getCurrentIntervalMs() - this.getTimeSinceLastAttempt();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
exports.FlushInterval = FlushInterval;
|
package/src/FlushTypes.d.ts
DELETED
package/src/FlushTypes.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FlushType = void 0;
|
|
4
|
-
/* eslint-disable no-restricted-syntax */
|
|
5
|
-
var FlushType;
|
|
6
|
-
(function (FlushType) {
|
|
7
|
-
FlushType["ScheduledMaxTime"] = "scheduled:max_time";
|
|
8
|
-
FlushType["ScheduledFullBatch"] = "scheduled:full_batch";
|
|
9
|
-
FlushType["Limit"] = "limit";
|
|
10
|
-
FlushType["Manual"] = "manual";
|
|
11
|
-
FlushType["Shutdown"] = "shutdown";
|
|
12
|
-
})(FlushType || (exports.FlushType = FlushType = {}));
|
package/src/PendingEvents.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { StatsigEventInternal } from './StatsigEvent';
|
|
2
|
-
export declare class PendingEvents {
|
|
3
|
-
private _pendingEvents;
|
|
4
|
-
private _batchSize;
|
|
5
|
-
constructor(batchSize: number);
|
|
6
|
-
addToPendingEventsQueue(event: StatsigEventInternal): void;
|
|
7
|
-
hasEventsForFullBatch(): boolean;
|
|
8
|
-
takeAll(): StatsigEventInternal[];
|
|
9
|
-
isEmpty(): boolean;
|
|
10
|
-
}
|
package/src/PendingEvents.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PendingEvents = void 0;
|
|
4
|
-
const Log_1 = require("./Log");
|
|
5
|
-
class PendingEvents {
|
|
6
|
-
constructor(batchSize) {
|
|
7
|
-
this._pendingEvents = [];
|
|
8
|
-
this._batchSize = batchSize;
|
|
9
|
-
}
|
|
10
|
-
addToPendingEventsQueue(event) {
|
|
11
|
-
this._pendingEvents.push(event);
|
|
12
|
-
Log_1.Log.debug('Enqueued Event:', event);
|
|
13
|
-
}
|
|
14
|
-
hasEventsForFullBatch() {
|
|
15
|
-
return this._pendingEvents.length >= this._batchSize;
|
|
16
|
-
}
|
|
17
|
-
takeAll() {
|
|
18
|
-
const events = this._pendingEvents;
|
|
19
|
-
this._pendingEvents = [];
|
|
20
|
-
return events;
|
|
21
|
-
}
|
|
22
|
-
isEmpty() {
|
|
23
|
-
return this._pendingEvents.length === 0;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
exports.PendingEvents = PendingEvents;
|