@statsig/client-core 3.32.4 → 3.32.6
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 +1 -1
- package/src/ErrorBoundary.js +20 -3
- package/src/EventRetryConstants.d.ts +2 -2
- package/src/EventRetryConstants.js +2 -2
- package/src/EventSender.d.ts +3 -0
- package/src/EventSender.js +30 -22
- package/src/FlushCoordinator.d.ts +1 -0
- package/src/FlushCoordinator.js +25 -5
- package/src/NetworkCore.d.ts +3 -0
- package/src/NetworkCore.js +95 -0
- package/src/StatsigMetadata.d.ts +1 -1
- package/src/StatsigMetadata.js +1 -1
- package/src/StatsigOptionsCommon.d.ts +6 -0
package/package.json
CHANGED
package/src/ErrorBoundary.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare class ErrorBoundary {
|
|
|
11
11
|
wrap(instance: unknown, namePrefix?: string): void;
|
|
12
12
|
logError(tag: string, error: unknown): void;
|
|
13
13
|
logDroppedEvents(count: number, reason: string, metadata?: Record<string, unknown>): void;
|
|
14
|
-
logEventRequestFailure(count: number, reason: string, flushType: string, statusCode: number, retries: number, failurePath?: string): void;
|
|
14
|
+
logEventRequestFailure(count: number, reason: string, flushType: string, statusCode: number, retries: number, failurePath?: string, failureErrorMessage?: string, failureDiagnosticBucket?: string, failureDiagnosticMetadata?: Record<string, string>): void;
|
|
15
15
|
getLastSeenErrorAndReset(): Error | null;
|
|
16
16
|
attachErrorIfNoneExists(error: unknown): void;
|
|
17
17
|
private _capture;
|
package/src/ErrorBoundary.js
CHANGED
|
@@ -56,7 +56,7 @@ class ErrorBoundary {
|
|
|
56
56
|
}
|
|
57
57
|
this._onError(`statsig::log_event_dropped_event_count`, new Error(reason), true, extra);
|
|
58
58
|
}
|
|
59
|
-
logEventRequestFailure(count, reason, flushType, statusCode, retries, failurePath) {
|
|
59
|
+
logEventRequestFailure(count, reason, flushType, statusCode, retries, failurePath, failureErrorMessage, failureDiagnosticBucket, failureDiagnosticMetadata) {
|
|
60
60
|
const extra = {
|
|
61
61
|
eventCount: String(count),
|
|
62
62
|
flushType: flushType,
|
|
@@ -67,6 +67,22 @@ class ErrorBoundary {
|
|
|
67
67
|
if (failurePath) {
|
|
68
68
|
extra['failurePath'] = failurePath;
|
|
69
69
|
}
|
|
70
|
+
if (typeof failureErrorMessage === 'string' &&
|
|
71
|
+
failureErrorMessage.length > 0) {
|
|
72
|
+
extra['failureErrorMessage'] = failureErrorMessage;
|
|
73
|
+
}
|
|
74
|
+
if (typeof failureDiagnosticBucket === 'string' &&
|
|
75
|
+
failureDiagnosticBucket.length > 0) {
|
|
76
|
+
extra['failureDiagnosticBucket'] = failureDiagnosticBucket;
|
|
77
|
+
}
|
|
78
|
+
if (failureDiagnosticMetadata) {
|
|
79
|
+
Object.keys(failureDiagnosticMetadata).forEach((key) => {
|
|
80
|
+
const value = failureDiagnosticMetadata[key];
|
|
81
|
+
if (value.length > 0) {
|
|
82
|
+
extra[`failureDiagnostic_${key}`] = value;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
70
86
|
this._onError(`statsig::log_event_failed`, new Error(reason), true, extra);
|
|
71
87
|
}
|
|
72
88
|
getLastSeenErrorAndReset() {
|
|
@@ -119,8 +135,9 @@ class ErrorBoundary {
|
|
|
119
135
|
const statsigMetadata = StatsigMetadata_1.StatsigMetadataProvider.get();
|
|
120
136
|
const info = isError ? unwrapped.stack : _getDescription(unwrapped);
|
|
121
137
|
const body = Object.assign(Object.assign({ tag, exception: name, info, statsigOptions: _getStatsigOptionLoggingCopy(this._options) }, Object.assign(Object.assign({}, statsigMetadata), { sdkType })), (extra !== null && extra !== void 0 ? extra : {}));
|
|
122
|
-
const
|
|
123
|
-
|
|
138
|
+
const networkConfig = (_d = this._options) === null || _d === void 0 ? void 0 : _d.networkConfig;
|
|
139
|
+
const func = (_e = networkConfig === null || networkConfig === void 0 ? void 0 : networkConfig.networkOverrideFunc) !== null && _e !== void 0 ? _e : fetch;
|
|
140
|
+
yield func((_f = networkConfig === null || networkConfig === void 0 ? void 0 : networkConfig.sdkExceptionUrl) !== null && _f !== void 0 ? _f : exports.EXCEPTION_ENDPOINT, {
|
|
124
141
|
method: 'POST',
|
|
125
142
|
headers: {
|
|
126
143
|
'STATSIG-API-KEY': this._sdkKey,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const EventRetryConstants: {
|
|
2
|
-
readonly MAX_RETRY_ATTEMPTS:
|
|
2
|
+
readonly MAX_RETRY_ATTEMPTS: 8;
|
|
3
3
|
readonly DEFAULT_BATCH_SIZE: 100;
|
|
4
|
-
readonly MAX_PENDING_BATCHES:
|
|
4
|
+
readonly MAX_PENDING_BATCHES: 40;
|
|
5
5
|
readonly TICK_INTERVAL_MS: 1000;
|
|
6
6
|
readonly QUICK_FLUSH_WINDOW_MS: 200;
|
|
7
7
|
readonly MAX_LOCAL_STORAGE: 500;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EventRetryConstants = void 0;
|
|
4
4
|
exports.EventRetryConstants = {
|
|
5
|
-
MAX_RETRY_ATTEMPTS:
|
|
5
|
+
MAX_RETRY_ATTEMPTS: 8,
|
|
6
6
|
DEFAULT_BATCH_SIZE: 100,
|
|
7
|
-
MAX_PENDING_BATCHES:
|
|
7
|
+
MAX_PENDING_BATCHES: 40,
|
|
8
8
|
TICK_INTERVAL_MS: 1000,
|
|
9
9
|
QUICK_FLUSH_WINDOW_MS: 200,
|
|
10
10
|
MAX_LOCAL_STORAGE: 500,
|
package/src/EventSender.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ type EventSendResult = {
|
|
|
7
7
|
success: boolean;
|
|
8
8
|
statusCode: number;
|
|
9
9
|
failurePath?: string;
|
|
10
|
+
failureErrorMessage?: string;
|
|
11
|
+
failureDiagnosticBucket?: string;
|
|
12
|
+
failureDiagnosticMetadata?: Record<string, string>;
|
|
10
13
|
};
|
|
11
14
|
export declare class EventSender {
|
|
12
15
|
private _network;
|
package/src/EventSender.js
CHANGED
|
@@ -55,19 +55,23 @@ class EventSender {
|
|
|
55
55
|
});
|
|
56
56
|
return response;
|
|
57
57
|
}
|
|
58
|
-
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
return Object.assign(Object.assign(Object.assign({ success: false, statusCode: response.statusCode, failurePath: response.failurePath }, (response.failureErrorMessage
|
|
59
|
+
? { failureErrorMessage: response.failureErrorMessage }
|
|
60
|
+
: {})), (response.failureDiagnosticBucket
|
|
61
|
+
? { failureDiagnosticBucket: response.failureDiagnosticBucket }
|
|
62
|
+
: {})), (response.failureDiagnosticMetadata
|
|
63
|
+
? { failureDiagnosticMetadata: response.failureDiagnosticMetadata }
|
|
64
|
+
: {}));
|
|
63
65
|
}
|
|
64
66
|
catch (error) {
|
|
65
67
|
Log_1.Log.warn('Failed to send batch:', error);
|
|
66
|
-
return {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
return Object.assign(Object.assign(Object.assign({ success: false, statusCode: -1, failurePath: (_c = transportFailure.path) !== null && _c !== void 0 ? _c : failurePath }, (transportFailure.errorMessage
|
|
69
|
+
? { failureErrorMessage: transportFailure.errorMessage }
|
|
70
|
+
: {})), (transportFailure.diagnosticBucket
|
|
71
|
+
? { failureDiagnosticBucket: transportFailure.diagnosticBucket }
|
|
72
|
+
: {})), (transportFailure.diagnosticMetadata
|
|
73
|
+
? { failureDiagnosticMetadata: transportFailure.diagnosticMetadata }
|
|
74
|
+
: {}));
|
|
71
75
|
}
|
|
72
76
|
});
|
|
73
77
|
}
|
|
@@ -77,13 +81,15 @@ class EventSender {
|
|
|
77
81
|
const result = yield this._network.post(this._getRequestData(batch), failureInfo);
|
|
78
82
|
const code = (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : -1;
|
|
79
83
|
if (code === -1) {
|
|
80
|
-
return {
|
|
81
|
-
success: false,
|
|
82
|
-
statusCode: -1,
|
|
83
|
-
failurePath: (_b = failureInfo.path) !== null && _b !== void 0 ? _b : (result === undefined
|
|
84
|
+
return Object.assign(Object.assign(Object.assign({ success: false, statusCode: -1, failurePath: (_b = failureInfo.path) !== null && _b !== void 0 ? _b : (result === undefined
|
|
84
85
|
? 'event_sender_post_returned_undefined'
|
|
85
|
-
: 'event_sender_post_returned_null'),
|
|
86
|
-
|
|
86
|
+
: 'event_sender_post_returned_null') }, (failureInfo.errorMessage
|
|
87
|
+
? { failureErrorMessage: failureInfo.errorMessage }
|
|
88
|
+
: {})), (failureInfo.diagnosticBucket
|
|
89
|
+
? { failureDiagnosticBucket: failureInfo.diagnosticBucket }
|
|
90
|
+
: {})), (failureInfo.diagnosticMetadata
|
|
91
|
+
? { failureDiagnosticMetadata: failureInfo.diagnosticMetadata }
|
|
92
|
+
: {}));
|
|
87
93
|
}
|
|
88
94
|
return { success: code >= 200 && code < 300, statusCode: code };
|
|
89
95
|
});
|
|
@@ -91,13 +97,15 @@ class EventSender {
|
|
|
91
97
|
_sendEventsViaBeacon(batch, failureInfo) {
|
|
92
98
|
var _a;
|
|
93
99
|
const success = this._network.beacon(this._getRequestData(batch), failureInfo);
|
|
94
|
-
return {
|
|
95
|
-
success,
|
|
96
|
-
statusCode: success ? 200 : -1,
|
|
97
|
-
failurePath: success
|
|
100
|
+
return Object.assign(Object.assign(Object.assign({ success, statusCode: success ? 200 : -1, failurePath: success
|
|
98
101
|
? undefined
|
|
99
|
-
: (_a = failureInfo.path) !== null && _a !== void 0 ? _a : 'beacon_send_false',
|
|
100
|
-
|
|
102
|
+
: (_a = failureInfo.path) !== null && _a !== void 0 ? _a : 'beacon_send_false' }, (!success && failureInfo.errorMessage
|
|
103
|
+
? { failureErrorMessage: failureInfo.errorMessage }
|
|
104
|
+
: {})), (!success && failureInfo.diagnosticBucket
|
|
105
|
+
? { failureDiagnosticBucket: failureInfo.diagnosticBucket }
|
|
106
|
+
: {})), (!success && failureInfo.diagnosticMetadata
|
|
107
|
+
? { failureDiagnosticMetadata: failureInfo.diagnosticMetadata }
|
|
108
|
+
: {}));
|
|
101
109
|
}
|
|
102
110
|
_getRequestData(batch) {
|
|
103
111
|
return {
|
|
@@ -44,6 +44,7 @@ export declare class FlushCoordinator {
|
|
|
44
44
|
private _prepareQueueForFlush;
|
|
45
45
|
containsAtLeastOneFullBatch(): boolean;
|
|
46
46
|
convertPendingEventsToBatches(): number;
|
|
47
|
+
private _isRetryableBatch;
|
|
47
48
|
private _handleFailure;
|
|
48
49
|
loadAndRetryShutdownFailedEvents(): Promise<void>;
|
|
49
50
|
private _getStorageKey;
|
package/src/FlushCoordinator.js
CHANGED
|
@@ -21,6 +21,13 @@ const SafeJs_1 = require("./SafeJs");
|
|
|
21
21
|
const SessionID_1 = require("./SessionID");
|
|
22
22
|
const StatsigOptionsCommon_1 = require("./StatsigOptionsCommon");
|
|
23
23
|
const StorageProvider_1 = require("./StorageProvider");
|
|
24
|
+
const RETRYABLE_NO_RESPONSE_FAILURE_PATHS = new Set([
|
|
25
|
+
'network_request_timed_out_no_response',
|
|
26
|
+
'network_request_exception_no_response',
|
|
27
|
+
'event_sender_post_returned_null',
|
|
28
|
+
'event_sender_post_returned_undefined',
|
|
29
|
+
'event_sender_post_exception',
|
|
30
|
+
]);
|
|
24
31
|
class FlushCoordinator {
|
|
25
32
|
constructor(batchQueue, pendingEvents, onPrepareFlush,
|
|
26
33
|
// For Event Sender
|
|
@@ -254,7 +261,7 @@ class FlushCoordinator {
|
|
|
254
261
|
return true;
|
|
255
262
|
}
|
|
256
263
|
this._flushInterval.adjustForFailure();
|
|
257
|
-
this._handleFailure(batch, flushType, result.statusCode, result.failurePath);
|
|
264
|
+
this._handleFailure(batch, flushType, result.statusCode, result.failurePath, result.failureErrorMessage, result.failureDiagnosticBucket, result.failureDiagnosticMetadata);
|
|
258
265
|
return false;
|
|
259
266
|
});
|
|
260
267
|
}
|
|
@@ -283,23 +290,36 @@ class FlushCoordinator {
|
|
|
283
290
|
const allEvents = this._pendingEvents.takeAll();
|
|
284
291
|
return this._batchQueue.createBatches(allEvents);
|
|
285
292
|
}
|
|
286
|
-
|
|
293
|
+
_isRetryableBatch(statusCode, failurePath) {
|
|
294
|
+
if (NetworkCore_1.RETRYABLE_CODES.has(statusCode)) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
if (statusCode === -1 &&
|
|
298
|
+
failurePath &&
|
|
299
|
+
RETRYABLE_NO_RESPONSE_FAILURE_PATHS.has(failurePath)) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
_handleFailure(batch, flushType, statusCode, failurePath, failureErrorMessage, failureDiagnosticBucket, failureDiagnosticMetadata) {
|
|
287
305
|
if (flushType === FlushTypes_1.FlushType.Shutdown) {
|
|
288
306
|
Log_1.Log.warn(`${flushType} flush failed during shutdown. ` +
|
|
289
307
|
`${batch.events.length} event(s) will be saved to storage for retry in next session.`);
|
|
290
308
|
this._saveShutdownFailedEventsToStorage(batch.events);
|
|
291
309
|
return;
|
|
292
310
|
}
|
|
293
|
-
if (!
|
|
311
|
+
if (!this._isRetryableBatch(statusCode, failurePath)) {
|
|
312
|
+
const reason = `non-retryable error`;
|
|
294
313
|
Log_1.Log.warn(`${flushType} flush failed after ${batch.attempts} attempt(s). ` +
|
|
295
314
|
`${batch.events.length} event(s) will be dropped. Non-retryable error: ${statusCode}`);
|
|
296
|
-
this._errorBoundary.logEventRequestFailure(batch.events.length,
|
|
315
|
+
this._errorBoundary.logEventRequestFailure(batch.events.length, reason, flushType, statusCode, batch.attempts, failurePath, failureErrorMessage, failureDiagnosticBucket, failureDiagnosticMetadata);
|
|
297
316
|
return;
|
|
298
317
|
}
|
|
299
318
|
if (batch.attempts >= EventRetryConstants_1.EventRetryConstants.MAX_RETRY_ATTEMPTS) {
|
|
319
|
+
const reason = `max retry attempts exceeded`;
|
|
300
320
|
Log_1.Log.warn(`${flushType} flush failed after ${batch.attempts} attempt(s). ` +
|
|
301
321
|
`${batch.events.length} event(s) will be dropped.`);
|
|
302
|
-
this._errorBoundary.logEventRequestFailure(batch.events.length,
|
|
322
|
+
this._errorBoundary.logEventRequestFailure(batch.events.length, reason, flushType, statusCode, batch.attempts, failurePath, failureErrorMessage, failureDiagnosticBucket, failureDiagnosticMetadata);
|
|
303
323
|
return;
|
|
304
324
|
}
|
|
305
325
|
batch.incrementAttempts();
|
package/src/NetworkCore.d.ts
CHANGED
|
@@ -26,6 +26,9 @@ export type RequestArgsWithData = Flatten<RequestArgs & {
|
|
|
26
26
|
} & DataFlags>;
|
|
27
27
|
export type RequestFailureInfo = {
|
|
28
28
|
path?: string;
|
|
29
|
+
errorMessage?: string;
|
|
30
|
+
diagnosticBucket?: string;
|
|
31
|
+
diagnosticMetadata?: Record<string, string>;
|
|
29
32
|
};
|
|
30
33
|
type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'urlConfig' | 'params' | 'isCompressable' | 'attempt'>;
|
|
31
34
|
type NetworkResponse = {
|
package/src/NetworkCore.js
CHANGED
|
@@ -138,6 +138,7 @@ class NetworkCore {
|
|
|
138
138
|
const currentAttempt = attempt !== null && attempt !== void 0 ? attempt : 1;
|
|
139
139
|
let reqTimedOut = false;
|
|
140
140
|
const populatedUrl = this._getPopulatedURL(args);
|
|
141
|
+
const startTime = Date.now();
|
|
141
142
|
let response = null;
|
|
142
143
|
const keepalive = (0, VisibilityObserving_1._isUnloading)();
|
|
143
144
|
try {
|
|
@@ -210,6 +211,17 @@ class NetworkCore {
|
|
|
210
211
|
failureInfo.path = timedOut
|
|
211
212
|
? 'network_request_timed_out_no_response'
|
|
212
213
|
: 'network_request_exception_no_response';
|
|
214
|
+
if (errorMessage) {
|
|
215
|
+
failureInfo.errorMessage = errorMessage;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const diagnostics = _getNoResponseDiagnostics(args, populatedUrl, timedOut, Date.now() - startTime);
|
|
219
|
+
failureInfo.diagnosticBucket = diagnostics.bucket;
|
|
220
|
+
failureInfo.diagnosticMetadata = diagnostics.metadata;
|
|
221
|
+
}
|
|
222
|
+
catch (_e) {
|
|
223
|
+
// Diagnostics should not affect request failure handling.
|
|
224
|
+
}
|
|
213
225
|
}
|
|
214
226
|
}
|
|
215
227
|
return null;
|
|
@@ -386,6 +398,89 @@ function _didTimeout(errorMsg, abortedByTimeout) {
|
|
|
386
398
|
const timeout = errorMsg.includes('Timeout'); // probably not needed but just in case
|
|
387
399
|
return timeout || abortedByTimeout;
|
|
388
400
|
}
|
|
401
|
+
function _getNoResponseDiagnostics(args, populatedUrl, timedOut, elapsedMs) {
|
|
402
|
+
var _a, _b, _c;
|
|
403
|
+
const win = (0, SafeJs_1._getWindowSafe)();
|
|
404
|
+
const doc = win === null || win === void 0 ? void 0 : win.document;
|
|
405
|
+
const nav = typeof navigator !== 'undefined' ? navigator : null;
|
|
406
|
+
const isUnloading = (0, VisibilityObserving_1._isUnloading)();
|
|
407
|
+
const online = nav && typeof nav.onLine === 'boolean' ? String(nav.onLine) : 'unknown';
|
|
408
|
+
const visibilityState = (_a = doc === null || doc === void 0 ? void 0 : doc.visibilityState) !== null && _a !== void 0 ? _a : 'unknown';
|
|
409
|
+
const hasCustomHeaders = Object.keys((_b = args.headers) !== null && _b !== void 0 ? _b : {}).length > 0;
|
|
410
|
+
const crossOrigin = _isCrossOrigin(populatedUrl, (_c = win === null || win === void 0 ? void 0 : win.location) === null || _c === void 0 ? void 0 : _c.origin);
|
|
411
|
+
const hasCustomUrl = args.urlConfig.customUrl != null;
|
|
412
|
+
const hasFallbackUrl = args.fallbackUrl != null;
|
|
413
|
+
const elapsedMsBucket = _bucketNumber(elapsedMs, [250, 1000, 5000, 10000]);
|
|
414
|
+
const bodySizeBucket = _bucketNumber(_getBodySize(args.body), [16384, 65536, 262144, 1048576]);
|
|
415
|
+
let bucket = 'unknown_no_response';
|
|
416
|
+
if (timedOut) {
|
|
417
|
+
bucket = 'timeout';
|
|
418
|
+
}
|
|
419
|
+
else if (online === 'false') {
|
|
420
|
+
bucket = 'browser_offline';
|
|
421
|
+
}
|
|
422
|
+
else if (isUnloading) {
|
|
423
|
+
bucket = 'page_unloading';
|
|
424
|
+
}
|
|
425
|
+
else if (visibilityState === 'hidden') {
|
|
426
|
+
bucket = 'page_hidden';
|
|
427
|
+
}
|
|
428
|
+
else if (crossOrigin && hasCustomHeaders) {
|
|
429
|
+
bucket = 'cross_origin_custom_headers_preflight_risk';
|
|
430
|
+
}
|
|
431
|
+
else if (hasCustomUrl || hasFallbackUrl) {
|
|
432
|
+
bucket = 'custom_url_no_response';
|
|
433
|
+
}
|
|
434
|
+
else if (elapsedMs < 250) {
|
|
435
|
+
bucket = 'immediate_network_rejection';
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
bucket,
|
|
439
|
+
metadata: {
|
|
440
|
+
elapsedMsBucket,
|
|
441
|
+
bodySizeBucket,
|
|
442
|
+
online,
|
|
443
|
+
visibilityState,
|
|
444
|
+
isUnloading: String(isUnloading),
|
|
445
|
+
crossOrigin: String(crossOrigin),
|
|
446
|
+
hasCustomUrl: String(hasCustomUrl),
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function _isCrossOrigin(url, currentOrigin) {
|
|
451
|
+
if (!currentOrigin) {
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
return (!url.startsWith(`${currentOrigin}/`) &&
|
|
455
|
+
!url.startsWith(`${currentOrigin}?`) &&
|
|
456
|
+
url !== currentOrigin);
|
|
457
|
+
}
|
|
458
|
+
function _getBodySize(body) {
|
|
459
|
+
if (body == null) {
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
if (typeof body === 'string') {
|
|
463
|
+
return body.length;
|
|
464
|
+
}
|
|
465
|
+
if (body instanceof Uint8Array) {
|
|
466
|
+
return body.byteLength;
|
|
467
|
+
}
|
|
468
|
+
if (typeof Blob !== 'undefined' && body instanceof Blob) {
|
|
469
|
+
return body.size;
|
|
470
|
+
}
|
|
471
|
+
return -1;
|
|
472
|
+
}
|
|
473
|
+
function _bucketNumber(value, thresholds) {
|
|
474
|
+
if (value < 0) {
|
|
475
|
+
return 'unknown';
|
|
476
|
+
}
|
|
477
|
+
for (const threshold of thresholds) {
|
|
478
|
+
if (value < threshold) {
|
|
479
|
+
return `<${threshold}`;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return `>=${thresholds[thresholds.length - 1]}`;
|
|
483
|
+
}
|
|
389
484
|
function _tryMarkInitStart(args, attempt) {
|
|
390
485
|
if (args.urlConfig.endpoint !== NetworkConfig_1.Endpoint._initialize) {
|
|
391
486
|
return;
|
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.32.
|
|
4
|
+
exports.SDK_VERSION = '3.32.6';
|
|
5
5
|
let metadata = {
|
|
6
6
|
sdkVersion: exports.SDK_VERSION,
|
|
7
7
|
sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
|
|
@@ -57,6 +57,12 @@ export type NetworkConfigCommon = {
|
|
|
57
57
|
* default: `https://prodregistryv2.org/v1/rgstr`
|
|
58
58
|
*/
|
|
59
59
|
logEventUrl?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The URL used to report SDK exceptions via a POST request.
|
|
62
|
+
*
|
|
63
|
+
* default: `https://statsigapi.net/v1/sdk_exception`
|
|
64
|
+
*/
|
|
65
|
+
sdkExceptionUrl?: string;
|
|
60
66
|
/**
|
|
61
67
|
* A list of URLs to try if the primary logEventUrl fails.
|
|
62
68
|
*/
|