@statsig/client-core 3.30.2-beta.1 → 3.31.0
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 +185 -85
- package/src/EventRetryConstants.d.ts +0 -2
- package/src/EventRetryConstants.js +0 -2
- package/src/InitializeResponse.d.ts +7 -0
- package/src/NetworkCore.d.ts +0 -1
- package/src/NetworkCore.js +3 -5
- package/src/SafeJs.d.ts +1 -0
- package/src/SafeJs.js +12 -1
- package/src/StatsigClientBase.js +1 -1
- package/src/StatsigMetadata.d.ts +1 -1
- package/src/StatsigMetadata.js +1 -1
- package/src/StatsigUser.js +11 -12
- 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 -332
- 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,22 @@ 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");
|
|
22
18
|
const StatsigEvent_1 = require("./StatsigEvent");
|
|
23
19
|
const StatsigOptionsCommon_1 = require("./StatsigOptionsCommon");
|
|
24
20
|
const StorageProvider_1 = require("./StorageProvider");
|
|
25
21
|
const UrlConfiguration_1 = require("./UrlConfiguration");
|
|
26
22
|
const VisibilityObserving_1 = require("./VisibilityObserving");
|
|
23
|
+
const DEFAULT_QUEUE_SIZE = 100;
|
|
24
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 10000;
|
|
27
25
|
const MAX_DEDUPER_KEYS = 1000;
|
|
28
26
|
const DEDUPER_WINDOW_DURATION_MS = 600000;
|
|
27
|
+
const MAX_FAILED_LOGS = 500;
|
|
28
|
+
const QUICK_FLUSH_WINDOW_MS = 200;
|
|
29
29
|
const EVENT_LOGGER_MAP = {};
|
|
30
30
|
class EventLogger {
|
|
31
31
|
static _safeFlushAndForget(sdkKey) {
|
|
@@ -34,17 +34,21 @@ class EventLogger {
|
|
|
34
34
|
// noop
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
static _safeRetryFailedLogs(sdkKey) {
|
|
38
|
+
var _a;
|
|
39
|
+
(_a = EVENT_LOGGER_MAP[sdkKey]) === null || _a === void 0 ? void 0 : _a._retryFailedLogs();
|
|
40
|
+
}
|
|
41
|
+
constructor(_sdkKey, _emitter, _network, _options) {
|
|
38
42
|
var _a, _b;
|
|
39
43
|
this._sdkKey = _sdkKey;
|
|
40
44
|
this._emitter = _emitter;
|
|
41
45
|
this._network = _network;
|
|
42
46
|
this._options = _options;
|
|
43
|
-
this.
|
|
47
|
+
this._queue = [];
|
|
44
48
|
this._lastExposureTimeMap = {};
|
|
45
49
|
this._nonExposedChecks = {};
|
|
46
|
-
this.
|
|
47
|
-
this.
|
|
50
|
+
this._hasRunQuickFlush = false;
|
|
51
|
+
this._creationTime = Date.now();
|
|
48
52
|
this._loggingEnabled =
|
|
49
53
|
(_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
54
|
? StatsigOptionsCommon_1.LoggingEnabledOption.disabled
|
|
@@ -52,45 +56,34 @@ class EventLogger {
|
|
|
52
56
|
if ((_options === null || _options === void 0 ? void 0 : _options.loggingEnabled) && _options.disableLogging !== undefined) {
|
|
53
57
|
Log_1.Log.warn('Detected both loggingEnabled and disableLogging options. loggingEnabled takes precedence - please remove disableLogging.');
|
|
54
58
|
}
|
|
59
|
+
this._maxQueueSize = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _b !== void 0 ? _b : DEFAULT_QUEUE_SIZE;
|
|
55
60
|
const config = _options === null || _options === void 0 ? void 0 : _options.networkConfig;
|
|
56
61
|
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
62
|
}
|
|
62
63
|
setLogEventCompressionMode(mode) {
|
|
63
|
-
this.
|
|
64
|
+
this._network.setLogEventCompressionMode(mode);
|
|
64
65
|
}
|
|
65
66
|
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
|
-
});
|
|
67
|
+
if (this._loggingEnabled === 'disabled' && loggingEnabled !== 'disabled') {
|
|
68
|
+
// load any pre consented events into memory
|
|
69
|
+
const storageKey = this._getStorageKey();
|
|
70
|
+
const events = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
71
|
+
if (events) {
|
|
72
|
+
this._queue.push(...events);
|
|
80
73
|
}
|
|
74
|
+
StorageProvider_1.Storage.removeItem(storageKey);
|
|
81
75
|
}
|
|
76
|
+
this._loggingEnabled = loggingEnabled;
|
|
82
77
|
}
|
|
83
78
|
enqueue(event) {
|
|
84
79
|
if (!this._shouldLogEvent(event)) {
|
|
85
80
|
return;
|
|
86
81
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
this._normalizeAndAppendEvent(event);
|
|
83
|
+
this._quickFlushIfNeeded();
|
|
84
|
+
if (this._queue.length > this._maxQueueSize) {
|
|
85
|
+
EventLogger._safeFlushAndForget(this._sdkKey);
|
|
91
86
|
}
|
|
92
|
-
this._flushCoordinator.addEvent(normalizedEvent);
|
|
93
|
-
this._flushCoordinator.checkQuickFlush();
|
|
94
87
|
}
|
|
95
88
|
incrementNonExposureCount(name) {
|
|
96
89
|
var _a;
|
|
@@ -117,41 +110,47 @@ class EventLogger {
|
|
|
117
110
|
EventLogger._safeFlushAndForget(this._sdkKey);
|
|
118
111
|
}
|
|
119
112
|
else if (visibility === 'foreground') {
|
|
120
|
-
this.
|
|
113
|
+
EventLogger._safeRetryFailedLogs(this._sdkKey);
|
|
121
114
|
}
|
|
122
115
|
});
|
|
123
116
|
}
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
});
|
|
127
|
-
this._flushCoordinator.startScheduledFlushCycle();
|
|
117
|
+
this._retryFailedLogs();
|
|
118
|
+
this._startBackgroundFlushInterval();
|
|
128
119
|
}
|
|
129
120
|
stop() {
|
|
130
121
|
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
-
this.
|
|
132
|
-
|
|
122
|
+
if (this._flushIntervalId) {
|
|
123
|
+
clearInterval(this._flushIntervalId);
|
|
124
|
+
this._flushIntervalId = null;
|
|
125
|
+
}
|
|
133
126
|
delete EVENT_LOGGER_MAP[this._sdkKey];
|
|
127
|
+
yield this.flush();
|
|
134
128
|
});
|
|
135
129
|
}
|
|
136
130
|
flush() {
|
|
137
131
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
-
|
|
132
|
+
this._appendAndResetNonExposedChecks();
|
|
133
|
+
if (this._queue.length === 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const events = this._queue;
|
|
137
|
+
this._queue = [];
|
|
138
|
+
yield this._sendEvents(events);
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
/**
|
|
142
|
+
* We 'Quick Flush' following the very first event enqueued
|
|
143
|
+
* within the quick flush window
|
|
144
|
+
*/
|
|
145
|
+
_quickFlushIfNeeded() {
|
|
146
|
+
if (this._hasRunQuickFlush) {
|
|
143
147
|
return;
|
|
144
148
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
checks: Object.assign({}, this._nonExposedChecks),
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
this._flushCoordinator.addEvent(event);
|
|
154
|
-
this._nonExposedChecks = {};
|
|
149
|
+
this._hasRunQuickFlush = true;
|
|
150
|
+
if (Date.now() - this._creationTime > QUICK_FLUSH_WINDOW_MS) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
setTimeout(() => EventLogger._safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
|
|
155
154
|
}
|
|
156
155
|
_shouldLogEvent(event) {
|
|
157
156
|
var _a;
|
|
@@ -186,54 +185,118 @@ class EventLogger {
|
|
|
186
185
|
this._lastExposureTimeMap[key] = now;
|
|
187
186
|
return true;
|
|
188
187
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
188
|
+
_sendEvents(events) {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
var _a, _b;
|
|
191
|
+
if (this._loggingEnabled === 'disabled') {
|
|
192
|
+
this._saveFailedLogsToStorage(events);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const isClosing = (0, VisibilityObserving_1._isUnloading)();
|
|
197
|
+
const shouldUseBeacon = isClosing &&
|
|
198
|
+
this._network.isBeaconSupported() &&
|
|
199
|
+
((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.networkConfig) === null || _b === void 0 ? void 0 : _b.networkOverrideFunc) == null;
|
|
200
|
+
this._emitter({
|
|
201
|
+
name: 'pre_logs_flushed',
|
|
202
|
+
events,
|
|
203
|
+
});
|
|
204
|
+
const response = shouldUseBeacon
|
|
205
|
+
? this._sendEventsViaBeacon(events)
|
|
206
|
+
: yield this._sendEventsViaPost(events);
|
|
207
|
+
if (response.success) {
|
|
208
|
+
this._emitter({
|
|
209
|
+
name: 'logs_flushed',
|
|
210
|
+
events,
|
|
211
|
+
});
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
Log_1.Log.warn('Failed to flush events.');
|
|
216
|
+
this._saveFailedLogsToStorage(events);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (_c) {
|
|
221
|
+
Log_1.Log.warn('Failed to flush events.');
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
195
225
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
226
|
+
_sendEventsViaPost(events) {
|
|
227
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
var _a;
|
|
229
|
+
const result = yield this._network.post(this._getRequestData(events));
|
|
230
|
+
const code = (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : -1;
|
|
231
|
+
return { success: code >= 200 && code < 300 };
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
_sendEventsViaBeacon(events) {
|
|
235
|
+
return {
|
|
236
|
+
success: this._network.beacon(this._getRequestData(events)),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
_getRequestData(events) {
|
|
240
|
+
return {
|
|
241
|
+
sdkKey: this._sdkKey,
|
|
242
|
+
data: {
|
|
243
|
+
events,
|
|
244
|
+
},
|
|
245
|
+
urlConfig: this._logEventUrlConfig,
|
|
246
|
+
retries: 3,
|
|
247
|
+
isCompressable: true,
|
|
248
|
+
params: {
|
|
249
|
+
[NetworkConfig_1.NetworkParam.EventCount]: String(events.length),
|
|
250
|
+
},
|
|
251
|
+
credentials: 'same-origin',
|
|
252
|
+
};
|
|
201
253
|
}
|
|
202
|
-
|
|
254
|
+
_saveFailedLogsToStorage(events) {
|
|
255
|
+
while (events.length > MAX_FAILED_LOGS) {
|
|
256
|
+
events.shift();
|
|
257
|
+
}
|
|
203
258
|
const storageKey = this._getStorageKey();
|
|
204
259
|
try {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (existingEvents.length > EventRetryConstants_1.EventRetryConstants.MAX_LOCAL_STORAGE) {
|
|
208
|
-
existingEvents = existingEvents.slice(-EventRetryConstants_1.EventRetryConstants.MAX_LOCAL_STORAGE);
|
|
209
|
-
}
|
|
210
|
-
(0, StorageProvider_1._setObjectInStorage)(storageKey, existingEvents);
|
|
260
|
+
const savedEvents = this._getFailedLogsFromStorage(storageKey);
|
|
261
|
+
(0, StorageProvider_1._setObjectInStorage)(storageKey, [...savedEvents, ...events]);
|
|
211
262
|
}
|
|
212
|
-
catch (
|
|
213
|
-
Log_1.Log.warn('Unable to save
|
|
263
|
+
catch (_a) {
|
|
264
|
+
Log_1.Log.warn('Unable to save failed logs to storage');
|
|
214
265
|
}
|
|
215
266
|
}
|
|
216
|
-
|
|
267
|
+
_getFailedLogsFromStorage(storageKey) {
|
|
268
|
+
let savedEvents = [];
|
|
217
269
|
try {
|
|
218
|
-
const
|
|
219
|
-
if (Array.isArray(
|
|
220
|
-
|
|
270
|
+
const retrieved = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
271
|
+
if (Array.isArray(retrieved)) {
|
|
272
|
+
savedEvents = retrieved;
|
|
221
273
|
}
|
|
222
|
-
return
|
|
274
|
+
return savedEvents;
|
|
223
275
|
}
|
|
224
276
|
catch (_a) {
|
|
225
277
|
return [];
|
|
226
278
|
}
|
|
227
279
|
}
|
|
228
|
-
|
|
280
|
+
_retryFailedLogs() {
|
|
229
281
|
const storageKey = this._getStorageKey();
|
|
230
|
-
|
|
231
|
-
|
|
282
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
283
|
+
if (!StorageProvider_1.Storage.isReady()) {
|
|
284
|
+
yield StorageProvider_1.Storage.isReadyResolver();
|
|
285
|
+
}
|
|
286
|
+
const events = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
287
|
+
if (!events) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
232
290
|
StorageProvider_1.Storage.removeItem(storageKey);
|
|
233
|
-
|
|
234
|
-
|
|
291
|
+
yield this._sendEvents(events);
|
|
292
|
+
}))().catch(() => {
|
|
293
|
+
Log_1.Log.warn('Failed to flush stored logs');
|
|
294
|
+
});
|
|
235
295
|
}
|
|
236
|
-
|
|
296
|
+
_getStorageKey() {
|
|
297
|
+
return `statsig.failed_logs.${(0, Hashing_1._DJB2)(this._sdkKey)}`;
|
|
298
|
+
}
|
|
299
|
+
_normalizeAndAppendEvent(event) {
|
|
237
300
|
if (event.user) {
|
|
238
301
|
event.user = Object.assign({}, event.user);
|
|
239
302
|
delete event.user.privateAttributes;
|
|
@@ -243,7 +306,44 @@ class EventLogger {
|
|
|
243
306
|
if (currentPage) {
|
|
244
307
|
extras.statsigMetadata = { currentPage };
|
|
245
308
|
}
|
|
246
|
-
|
|
309
|
+
const final = Object.assign(Object.assign({}, event), extras);
|
|
310
|
+
Log_1.Log.debug('Enqueued Event:', final);
|
|
311
|
+
this._queue.push(final);
|
|
312
|
+
}
|
|
313
|
+
_appendAndResetNonExposedChecks() {
|
|
314
|
+
if (Object.keys(this._nonExposedChecks).length === 0) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
this._normalizeAndAppendEvent({
|
|
318
|
+
eventName: 'statsig::non_exposed_checks',
|
|
319
|
+
user: null,
|
|
320
|
+
time: Date.now(),
|
|
321
|
+
metadata: {
|
|
322
|
+
checks: Object.assign({}, this._nonExposedChecks),
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
this._nonExposedChecks = {};
|
|
326
|
+
}
|
|
327
|
+
_getCurrentPageUrl() {
|
|
328
|
+
var _a;
|
|
329
|
+
if (((_a = this._options) === null || _a === void 0 ? void 0 : _a.includeCurrentPageUrlWithEvents) === false) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
return (0, SafeJs_1._getCurrentPageUrlSafe)();
|
|
333
|
+
}
|
|
334
|
+
_startBackgroundFlushInterval() {
|
|
335
|
+
var _a, _b;
|
|
336
|
+
const flushInterval = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
|
|
337
|
+
const intervalId = setInterval(() => {
|
|
338
|
+
const logger = EVENT_LOGGER_MAP[this._sdkKey];
|
|
339
|
+
if (!logger || logger._flushIntervalId !== intervalId) {
|
|
340
|
+
clearInterval(intervalId);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
EventLogger._safeFlushAndForget(this._sdkKey);
|
|
344
|
+
}
|
|
345
|
+
}, flushInterval);
|
|
346
|
+
this._flushIntervalId = intervalId;
|
|
247
347
|
}
|
|
248
348
|
}
|
|
249
349
|
exports.EventLogger = EventLogger;
|
|
@@ -9,11 +9,18 @@ type SessionReplayFields = {
|
|
|
9
9
|
passes_session_recording_targeting?: boolean;
|
|
10
10
|
session_recording_event_triggers?: Record<string, SessionReplayTrigger>;
|
|
11
11
|
session_recording_exposure_triggers?: Record<string, SessionReplayTrigger>;
|
|
12
|
+
session_recording_privacy_settings?: SessionReplayPrivacySettings;
|
|
12
13
|
};
|
|
13
14
|
type SessionReplayTrigger = {
|
|
14
15
|
values?: string[];
|
|
15
16
|
passes_sampling?: boolean;
|
|
16
17
|
};
|
|
18
|
+
export type SessionReplayPrivacySettings = {
|
|
19
|
+
privacy_mode?: 'min' | 'input' | 'max';
|
|
20
|
+
unmasked_elements?: string[];
|
|
21
|
+
masked_elements?: string[];
|
|
22
|
+
blocked_elements?: string[];
|
|
23
|
+
};
|
|
17
24
|
type AutoCaptureFields = {
|
|
18
25
|
auto_capture_settings?: {
|
|
19
26
|
disabled_events: Record<string, boolean>;
|
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/SafeJs.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export declare const _addWindowEventListenerSafe: (key: string, listener: () =>
|
|
|
5
5
|
export declare const _addDocumentEventListenerSafe: (key: string, listener: () => void) => void;
|
|
6
6
|
export declare const _getCurrentPageUrlSafe: () => string | undefined;
|
|
7
7
|
export declare const _getUnloadEvent: () => string;
|
|
8
|
+
export declare const _cloneObject: <T>(tag: string, obj: T) => T | null;
|
package/src/SafeJs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._getUnloadEvent = exports._getCurrentPageUrlSafe = exports._addDocumentEventListenerSafe = exports._addWindowEventListenerSafe = exports._isServerEnv = exports._getDocumentSafe = exports._getWindowSafe = void 0;
|
|
3
|
+
exports._cloneObject = exports._getUnloadEvent = exports._getCurrentPageUrlSafe = exports._addDocumentEventListenerSafe = exports._addWindowEventListenerSafe = exports._isServerEnv = exports._getDocumentSafe = exports._getWindowSafe = void 0;
|
|
4
|
+
const Log_1 = require("./Log");
|
|
4
5
|
const _getWindowSafe = () => {
|
|
5
6
|
return typeof window !== 'undefined' ? window : null;
|
|
6
7
|
};
|
|
@@ -55,3 +56,13 @@ const _getUnloadEvent = () => {
|
|
|
55
56
|
return eventType;
|
|
56
57
|
};
|
|
57
58
|
exports._getUnloadEvent = _getUnloadEvent;
|
|
59
|
+
const _cloneObject = (tag, obj) => {
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(JSON.stringify(obj));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
Log_1.Log.error(`Failed to clone object ${tag}`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
exports._cloneObject = _cloneObject;
|
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.
|
|
4
|
+
exports.SDK_VERSION = '3.31.0';
|
|
5
5
|
let metadata = {
|
|
6
6
|
sdkVersion: exports.SDK_VERSION,
|
|
7
7
|
sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
|