@statsig/client-core 1.7.0 → 1.8.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/ClientInterfaces.d.ts +0 -6
- package/src/DataAdapterCore.js +1 -1
- package/src/Diagnostics.d.ts +66 -2
- package/src/Diagnostics.js +93 -4
- package/src/EventLogger.js +10 -4
- package/src/InitializeResponse.d.ts +6 -1
- package/src/NetworkConfig.d.ts +3 -3
- package/src/NetworkConfig.js +3 -3
- package/src/NetworkCore.d.ts +9 -1
- package/src/NetworkCore.js +113 -16
- package/src/NetworkProxy.d.ts +14 -0
- package/src/NetworkProxy.js +199 -0
- package/src/SessionID.d.ts +2 -2
- package/src/SessionID.js +25 -33
- package/src/StableID.d.ts +1 -1
- package/src/StableID.js +14 -22
- package/src/StatsigMetadata.d.ts +1 -1
- package/src/StatsigMetadata.js +1 -1
- package/src/StorageProvider.d.ts +6 -7
- package/src/StorageProvider.js +16 -29
- package/src/index.d.ts +3 -1
- package/src/index.js +4 -1
package/package.json
CHANGED
|
@@ -21,18 +21,14 @@ export type CommonContext = {
|
|
|
21
21
|
sdkKey: string;
|
|
22
22
|
options: AnyStatsigOptions;
|
|
23
23
|
errorBoundary: ErrorBoundary;
|
|
24
|
-
};
|
|
25
|
-
export type AsyncCommonContext = {
|
|
26
24
|
session: StatsigSession;
|
|
27
25
|
stableID: string;
|
|
28
26
|
};
|
|
29
27
|
export type OnDeviceEvaluationsContext = CommonContext & {
|
|
30
28
|
values: DownloadConfigSpecsResponse | null;
|
|
31
29
|
};
|
|
32
|
-
export type OnDeviceEvaluationsAsyncContext = OnDeviceEvaluationsContext & AsyncCommonContext;
|
|
33
30
|
export interface OnDeviceEvaluationsInterface extends StatsigClientCommonInterface {
|
|
34
31
|
readonly dataAdapter: SpecsDataAdapter;
|
|
35
|
-
getAsyncContext(): Promise<OnDeviceEvaluationsAsyncContext>;
|
|
36
32
|
getContext(): OnDeviceEvaluationsContext;
|
|
37
33
|
checkGate(name: string, user: StatsigUser, options?: FeatureGateEvaluationOptions): boolean;
|
|
38
34
|
getFeatureGate(name: string, user: StatsigUser, options?: FeatureGateEvaluationOptions): FeatureGate;
|
|
@@ -46,10 +42,8 @@ export type PrecomputedEvaluationsContext = Flatten<CommonContext & {
|
|
|
46
42
|
values: InitializeResponseWithUpdates | null;
|
|
47
43
|
user: StatsigUser;
|
|
48
44
|
}>;
|
|
49
|
-
export type PrecomputedEvaluationsAsyncContext = Flatten<AsyncCommonContext & PrecomputedEvaluationsContext>;
|
|
50
45
|
export interface PrecomputedEvaluationsInterface extends StatsigClientCommonInterface {
|
|
51
46
|
readonly dataAdapter: EvaluationsDataAdapter;
|
|
52
|
-
getAsyncContext(): Promise<PrecomputedEvaluationsAsyncContext>;
|
|
53
47
|
getContext(): PrecomputedEvaluationsContext;
|
|
54
48
|
updateUserSync(user: StatsigUser): void;
|
|
55
49
|
updateUserAsync(user: StatsigUser): Promise<void>;
|
package/src/DataAdapterCore.js
CHANGED
|
@@ -118,7 +118,7 @@ class DataAdapterCore {
|
|
|
118
118
|
}
|
|
119
119
|
_loadFromCache(cacheKey) {
|
|
120
120
|
var _a;
|
|
121
|
-
const cache = (_a = StorageProvider_1.Storage.
|
|
121
|
+
const cache = (_a = StorageProvider_1.Storage._getItem) === null || _a === void 0 ? void 0 : _a.call(StorageProvider_1.Storage, cacheKey);
|
|
122
122
|
if (cache == null) {
|
|
123
123
|
return null;
|
|
124
124
|
}
|
package/src/Diagnostics.d.ts
CHANGED
|
@@ -1,3 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { EvaluationDetails } from './EvaluationTypes';
|
|
2
|
+
import { EventLogger } from './EventLogger';
|
|
3
|
+
import { NetworkConfigCommon, StatsigOptionsCommon } from './StatsigOptionsCommon';
|
|
4
|
+
import { StatsigUserInternal } from './StatsigUser';
|
|
5
|
+
export type KeyType = 'initialize' | 'overall';
|
|
6
|
+
export type StepType = 'process' | 'network_request';
|
|
7
|
+
export type ActionType = 'start' | 'end';
|
|
8
|
+
export interface Marker {
|
|
9
|
+
key: KeyType;
|
|
10
|
+
action: ActionType;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
step?: StepType;
|
|
13
|
+
statusCode?: number;
|
|
14
|
+
success?: boolean;
|
|
15
|
+
url?: string;
|
|
16
|
+
idListCount?: number;
|
|
17
|
+
sdkRegion?: string | null;
|
|
18
|
+
markerID?: string;
|
|
19
|
+
attempt?: number;
|
|
20
|
+
isRetry?: boolean;
|
|
21
|
+
configName?: string;
|
|
22
|
+
message?: string | null;
|
|
23
|
+
evaluationDetails?: EvaluationDetails;
|
|
24
|
+
error?: Record<string, unknown>;
|
|
25
|
+
isDelta?: boolean;
|
|
3
26
|
}
|
|
27
|
+
export declare const Diagnostics: {
|
|
28
|
+
_getMarkers: (sdkKey: string) => Marker[] | undefined;
|
|
29
|
+
_markInitOverallStart: (sdkKey: string) => void;
|
|
30
|
+
_markInitOverallEnd: (sdkKey: string, success: boolean, evaluationDetails?: EvaluationDetails) => void;
|
|
31
|
+
_markInitNetworkReqStart: (sdkKey: string, data: InitializeDataType['networkRequest']['start']) => void;
|
|
32
|
+
_markInitNetworkReqEnd: (sdkKey: string, data: InitializeDataType['networkRequest']['end']) => void;
|
|
33
|
+
_markInitProcessStart: (sdkKey: string) => void;
|
|
34
|
+
_markInitProcessEnd: (sdkKey: string, data: InitializeDataType['process']['end']) => void;
|
|
35
|
+
_clearMarkers: (sdkKey: string) => void;
|
|
36
|
+
_formatError(e: unknown): Record<string, unknown> | undefined;
|
|
37
|
+
_getDiagnosticsData(res: Response | null, attempt: number, body: string, e?: unknown): {
|
|
38
|
+
success: boolean;
|
|
39
|
+
isDelta?: boolean | undefined;
|
|
40
|
+
sdkRegion?: string | null | undefined;
|
|
41
|
+
statusCode?: number | undefined;
|
|
42
|
+
attempt: number;
|
|
43
|
+
error?: Record<string, unknown> | undefined;
|
|
44
|
+
};
|
|
45
|
+
_enqueueDiagnosticsEvent(user: StatsigUserInternal | null, logger: EventLogger, sdk: string, options: StatsigOptionsCommon<NetworkConfigCommon> | null): void;
|
|
46
|
+
};
|
|
47
|
+
interface InitializeDataType {
|
|
48
|
+
process: {
|
|
49
|
+
end: {
|
|
50
|
+
success: boolean;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
networkRequest: {
|
|
54
|
+
start: {
|
|
55
|
+
attempt: number;
|
|
56
|
+
};
|
|
57
|
+
end: {
|
|
58
|
+
success: boolean;
|
|
59
|
+
attempt: number;
|
|
60
|
+
isDelta?: boolean;
|
|
61
|
+
sdkRegion?: string | null;
|
|
62
|
+
statusCode?: number;
|
|
63
|
+
error?: Record<string, unknown>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export {};
|
package/src/Diagnostics.js
CHANGED
|
@@ -1,9 +1,98 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Diagnostics = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const MARKER_MAP = new Map();
|
|
5
|
+
const ACT_START = 'start';
|
|
6
|
+
const ACT_END = 'end';
|
|
7
|
+
const DIAGNOSTICS_EVENT = 'statsig::diagnostics';
|
|
8
|
+
exports.Diagnostics = {
|
|
9
|
+
_getMarkers: (sdkKey) => {
|
|
10
|
+
return MARKER_MAP.get(sdkKey);
|
|
11
|
+
},
|
|
12
|
+
_markInitOverallStart: (sdkKey) => {
|
|
13
|
+
_addMarker(sdkKey, _createMarker({}, ACT_START));
|
|
14
|
+
},
|
|
15
|
+
_markInitOverallEnd: (sdkKey, success, evaluationDetails) => {
|
|
16
|
+
_addMarker(sdkKey, _createMarker({
|
|
17
|
+
success,
|
|
18
|
+
error: success
|
|
19
|
+
? undefined
|
|
20
|
+
: { name: 'InitializeError', message: 'Failed to initialize' },
|
|
21
|
+
evaluationDetails,
|
|
22
|
+
}, ACT_END));
|
|
23
|
+
},
|
|
24
|
+
_markInitNetworkReqStart: (sdkKey, data) => {
|
|
25
|
+
_addMarker(sdkKey, _createMarker(data, ACT_START));
|
|
26
|
+
},
|
|
27
|
+
_markInitNetworkReqEnd: (sdkKey, data) => {
|
|
28
|
+
_addMarker(sdkKey, _createMarker(data, ACT_END));
|
|
29
|
+
},
|
|
30
|
+
_markInitProcessStart: (sdkKey) => {
|
|
31
|
+
_addMarker(sdkKey, _createMarker({}, ACT_START));
|
|
32
|
+
},
|
|
33
|
+
_markInitProcessEnd: (sdkKey, data) => {
|
|
34
|
+
_addMarker(sdkKey, _createMarker(data, ACT_END));
|
|
35
|
+
},
|
|
36
|
+
_clearMarkers: (sdkKey) => {
|
|
37
|
+
MARKER_MAP.delete(sdkKey);
|
|
38
|
+
},
|
|
39
|
+
_formatError(e) {
|
|
40
|
+
if (!(e && typeof e === 'object')) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
code: _safeGetField(e, 'code'),
|
|
45
|
+
name: _safeGetField(e, 'name'),
|
|
46
|
+
message: _safeGetField(e, 'message'),
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
_getDiagnosticsData(res, attempt, body, e) {
|
|
50
|
+
var _a;
|
|
51
|
+
return {
|
|
52
|
+
success: (res === null || res === void 0 ? void 0 : res.ok) === true,
|
|
53
|
+
statusCode: res === null || res === void 0 ? void 0 : res.status,
|
|
54
|
+
sdkRegion: (_a = res === null || res === void 0 ? void 0 : res.headers) === null || _a === void 0 ? void 0 : _a.get('x-statsig-region'),
|
|
55
|
+
isDelta: body.includes('"is_delta":true') === true ? true : undefined,
|
|
56
|
+
attempt,
|
|
57
|
+
error: exports.Diagnostics._formatError(e),
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
_enqueueDiagnosticsEvent(user, logger, sdk, options) {
|
|
61
|
+
const markers = exports.Diagnostics._getMarkers(sdk);
|
|
62
|
+
if (markers == null || markers.length <= 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
exports.Diagnostics._clearMarkers(sdk);
|
|
66
|
+
const event = _makeDiagnosticsEvent(user, {
|
|
67
|
+
context: 'initialize',
|
|
68
|
+
markers: markers.slice(),
|
|
69
|
+
statsigOptions: options,
|
|
70
|
+
});
|
|
71
|
+
logger.enqueue(event);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
function _createMarker(data, action) {
|
|
75
|
+
return Object.assign({ key: 'initialize', action: action, timestamp: Date.now() }, data);
|
|
76
|
+
}
|
|
77
|
+
function _makeDiagnosticsEvent(user, data) {
|
|
78
|
+
const latencyEvent = {
|
|
79
|
+
eventName: DIAGNOSTICS_EVENT,
|
|
80
|
+
user,
|
|
81
|
+
value: null,
|
|
82
|
+
metadata: data,
|
|
83
|
+
time: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
return latencyEvent;
|
|
86
|
+
}
|
|
87
|
+
function _addMarker(sdkKey, marker) {
|
|
88
|
+
var _a;
|
|
89
|
+
const markers = (_a = MARKER_MAP.get(sdkKey)) !== null && _a !== void 0 ? _a : [];
|
|
90
|
+
markers.push(marker);
|
|
91
|
+
MARKER_MAP.set(sdkKey, markers);
|
|
92
|
+
}
|
|
93
|
+
function _safeGetField(data, field) {
|
|
94
|
+
if (field in data) {
|
|
95
|
+
return data[field];
|
|
7
96
|
}
|
|
97
|
+
return undefined;
|
|
8
98
|
}
|
|
9
|
-
exports.Diagnostics = Diagnostics;
|
package/src/EventLogger.js
CHANGED
|
@@ -118,6 +118,9 @@ class EventLogger {
|
|
|
118
118
|
setTimeout(() => _safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
|
|
119
119
|
}
|
|
120
120
|
_shouldLogEvent(event) {
|
|
121
|
+
if ((0, SafeJs_1._isServerEnv)()) {
|
|
122
|
+
return false; // do not run in server environments
|
|
123
|
+
}
|
|
121
124
|
if (!(0, StatsigEvent_1._isExposureEvent)(event)) {
|
|
122
125
|
return true;
|
|
123
126
|
}
|
|
@@ -206,18 +209,21 @@ class EventLogger {
|
|
|
206
209
|
events.shift();
|
|
207
210
|
}
|
|
208
211
|
const storageKey = this._getStorageKey();
|
|
209
|
-
|
|
212
|
+
try {
|
|
213
|
+
(0, StorageProvider_1._setObjectInStorage)(storageKey, events);
|
|
214
|
+
}
|
|
215
|
+
catch (_a) {
|
|
210
216
|
Log_1.Log.warn('Unable to save failed logs to storage');
|
|
211
|
-
}
|
|
217
|
+
}
|
|
212
218
|
}
|
|
213
219
|
_retryFailedLogs() {
|
|
214
220
|
const storageKey = this._getStorageKey();
|
|
215
221
|
(() => __awaiter(this, void 0, void 0, function* () {
|
|
216
|
-
const events =
|
|
222
|
+
const events = (0, StorageProvider_1._getObjectFromStorage)(storageKey);
|
|
217
223
|
if (!events) {
|
|
218
224
|
return;
|
|
219
225
|
}
|
|
220
|
-
|
|
226
|
+
StorageProvider_1.Storage._removeItem(storageKey);
|
|
221
227
|
yield this._sendEvents(events);
|
|
222
228
|
}))().catch(() => {
|
|
223
229
|
Log_1.Log.warn('Failed to flush stored logs');
|
|
@@ -5,7 +5,12 @@ type SessionReplayFields = {
|
|
|
5
5
|
can_record_session?: boolean;
|
|
6
6
|
session_recording_rate?: number;
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
type AutoCaptureFields = {
|
|
9
|
+
auto_capture_settings?: {
|
|
10
|
+
disabled_events: Record<string, boolean>;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type InitializeResponseWithUpdates = SessionReplayFields & AutoCaptureFields & {
|
|
9
14
|
feature_gates: Record<string, GateEvaluation>;
|
|
10
15
|
dynamic_configs: Record<string, DynamicConfigEvaluation>;
|
|
11
16
|
layer_configs: Record<string, LayerEvaluation>;
|
package/src/NetworkConfig.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const NetworkDefault: {
|
|
2
|
-
eventsApi: "https://
|
|
3
|
-
initializeApi: "https://
|
|
4
|
-
specsApi: "https://
|
|
2
|
+
eventsApi: "https://statsigapi.net/v1";
|
|
3
|
+
initializeApi: "https://statsigapi.net/v1";
|
|
4
|
+
specsApi: "https://statsigapi.net/v1";
|
|
5
5
|
};
|
|
6
6
|
export type NetworkPriority = 'high' | 'low' | 'auto';
|
|
7
7
|
export type NetworkArgs = RequestInit & {
|
package/src/NetworkConfig.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NetworkParam = exports.NetworkDefault = void 0;
|
|
4
4
|
exports.NetworkDefault = {
|
|
5
|
-
eventsApi: 'https://
|
|
6
|
-
initializeApi: 'https://
|
|
7
|
-
specsApi: 'https://
|
|
5
|
+
eventsApi: 'https://statsigapi.net/v1',
|
|
6
|
+
initializeApi: 'https://statsigapi.net/v1',
|
|
7
|
+
specsApi: 'https://statsigapi.net/v1',
|
|
8
8
|
};
|
|
9
9
|
var NetworkParam;
|
|
10
10
|
(function (NetworkParam) {
|
package/src/NetworkCore.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ type RequestArgs = {
|
|
|
8
8
|
url: string;
|
|
9
9
|
priority?: NetworkPriority;
|
|
10
10
|
retries?: number;
|
|
11
|
+
attempt?: number;
|
|
12
|
+
isInitialize?: boolean;
|
|
11
13
|
params?: Record<string, string>;
|
|
12
14
|
headers?: Record<string, string>;
|
|
13
15
|
};
|
|
@@ -16,7 +18,7 @@ export type RequestArgsWithData = Flatten<RequestArgs & {
|
|
|
16
18
|
isStatsigEncodable?: boolean;
|
|
17
19
|
isCompressable?: boolean;
|
|
18
20
|
}>;
|
|
19
|
-
type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'url' | 'params' | 'isCompressable'>;
|
|
21
|
+
type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'url' | 'params' | 'isCompressable' | 'attempt'>;
|
|
20
22
|
type NetworkResponse = {
|
|
21
23
|
body: string | null;
|
|
22
24
|
code: number;
|
|
@@ -26,6 +28,9 @@ export declare class NetworkCore {
|
|
|
26
28
|
private readonly _timeout;
|
|
27
29
|
private readonly _netConfig;
|
|
28
30
|
private readonly _options;
|
|
31
|
+
private _proxy;
|
|
32
|
+
private networkEvents;
|
|
33
|
+
private sdkKey;
|
|
29
34
|
constructor(options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
|
|
30
35
|
post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
|
|
31
36
|
get(args: RequestArgs): Promise<NetworkResponse | null>;
|
|
@@ -35,5 +40,8 @@ export declare class NetworkCore {
|
|
|
35
40
|
private _getPopulatedURL;
|
|
36
41
|
private _getPopulatedBody;
|
|
37
42
|
private _attemptToEncodeString;
|
|
43
|
+
private _getNetworkProxyArgs;
|
|
44
|
+
private _flushNetworkEvents;
|
|
45
|
+
private _startBackgroundFlushNetworkEvents;
|
|
38
46
|
}
|
|
39
47
|
export {};
|
package/src/NetworkCore.js
CHANGED
|
@@ -15,6 +15,7 @@ const __StatsigGlobal_1 = require("./$_StatsigGlobal");
|
|
|
15
15
|
const Diagnostics_1 = require("./Diagnostics");
|
|
16
16
|
const Log_1 = require("./Log");
|
|
17
17
|
const NetworkConfig_1 = require("./NetworkConfig");
|
|
18
|
+
const NetworkProxy_1 = require("./NetworkProxy");
|
|
18
19
|
const SDKType_1 = require("./SDKType");
|
|
19
20
|
const SafeJs_1 = require("./SafeJs");
|
|
20
21
|
const SessionID_1 = require("./SessionID");
|
|
@@ -22,12 +23,15 @@ const StableID_1 = require("./StableID");
|
|
|
22
23
|
const StatsigMetadata_1 = require("./StatsigMetadata");
|
|
23
24
|
const VisibilityObserving_1 = require("./VisibilityObserving");
|
|
24
25
|
const DEFAULT_TIMEOUT_MS = 10000;
|
|
26
|
+
const RETRYABLE_CODES = new Set([408, 500, 502, 503, 504, 522, 524, 599]);
|
|
25
27
|
class NetworkCore {
|
|
26
28
|
constructor(options, _emitter) {
|
|
27
29
|
this._emitter = _emitter;
|
|
28
30
|
this._timeout = DEFAULT_TIMEOUT_MS;
|
|
29
31
|
this._netConfig = {};
|
|
30
32
|
this._options = {};
|
|
33
|
+
this.networkEvents = [];
|
|
34
|
+
this.sdkKey = null;
|
|
31
35
|
if (options) {
|
|
32
36
|
this._options = options;
|
|
33
37
|
}
|
|
@@ -37,17 +41,26 @@ class NetworkCore {
|
|
|
37
41
|
if (this._netConfig.networkTimeoutMs) {
|
|
38
42
|
this._timeout = this._netConfig.networkTimeoutMs;
|
|
39
43
|
}
|
|
44
|
+
this._proxy = new NetworkProxy_1.NetworkProxy(this._options);
|
|
45
|
+
this._startBackgroundFlushNetworkEvents();
|
|
40
46
|
}
|
|
41
47
|
post(args) {
|
|
42
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
|
|
49
|
+
const proxyConfigArgs = this._getNetworkProxyArgs(args);
|
|
50
|
+
if (!this.sdkKey) {
|
|
51
|
+
this.sdkKey = args.sdkKey;
|
|
52
|
+
}
|
|
53
|
+
let body = yield this._getPopulatedBody(Object.assign(Object.assign({}, args), proxyConfigArgs));
|
|
44
54
|
if (args.isStatsigEncodable) {
|
|
45
55
|
body = this._attemptToEncodeString(args, body);
|
|
46
56
|
}
|
|
47
|
-
return this._sendRequest(Object.assign({ method: 'POST', body }, args));
|
|
57
|
+
return this._sendRequest(Object.assign(Object.assign({ method: 'POST', body }, proxyConfigArgs), args));
|
|
48
58
|
});
|
|
49
59
|
}
|
|
50
60
|
get(args) {
|
|
61
|
+
if (!this.sdkKey) {
|
|
62
|
+
this.sdkKey = args.sdkKey;
|
|
63
|
+
}
|
|
51
64
|
return this._sendRequest(Object.assign({ method: 'GET' }, args));
|
|
52
65
|
}
|
|
53
66
|
isBeaconSupported() {
|
|
@@ -59,14 +72,15 @@ class NetworkCore {
|
|
|
59
72
|
if (!_ensureValidSdkKey(args)) {
|
|
60
73
|
return false;
|
|
61
74
|
}
|
|
62
|
-
const
|
|
63
|
-
const
|
|
75
|
+
const networkProxyArgs = this._getNetworkProxyArgs(args);
|
|
76
|
+
const body = yield this._getPopulatedBody(Object.assign(Object.assign({}, args), networkProxyArgs));
|
|
77
|
+
const url = yield this._getPopulatedURL(Object.assign(Object.assign({}, args), networkProxyArgs));
|
|
64
78
|
const nav = navigator;
|
|
65
79
|
return nav.sendBeacon.bind(nav)(url, body);
|
|
66
80
|
});
|
|
67
81
|
}
|
|
68
82
|
_sendRequest(args) {
|
|
69
|
-
var _a, _b;
|
|
83
|
+
var _a, _b, _c, _d;
|
|
70
84
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
85
|
if (!_ensureValidSdkKey(args)) {
|
|
72
86
|
return null;
|
|
@@ -74,10 +88,17 @@ class NetworkCore {
|
|
|
74
88
|
if (this._netConfig.preventAllNetworkTraffic) {
|
|
75
89
|
return null;
|
|
76
90
|
}
|
|
77
|
-
const { method, body, retries } = args;
|
|
91
|
+
const { method, body, retries, attempt } = args;
|
|
92
|
+
const currentAttempt = attempt !== null && attempt !== void 0 ? attempt : 1;
|
|
78
93
|
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
79
|
-
const handle = setTimeout(() =>
|
|
80
|
-
|
|
94
|
+
const handle = setTimeout(() => {
|
|
95
|
+
controller === null || controller === void 0 ? void 0 : controller.abort(`Timeout of ${this._timeout}ms expired.`);
|
|
96
|
+
}, this._timeout);
|
|
97
|
+
const proxyUrl = (_a = args.proxyUrl) !== null && _a !== void 0 ? _a : this._proxy.getProxyUrl(args.sdkKey, args.url);
|
|
98
|
+
const url = yield this._getPopulatedURL(Object.assign({ proxyUrl }, args));
|
|
99
|
+
const isProxy = !url.includes('https://statsigapi.net/v1');
|
|
100
|
+
const parsedUrl = new URL(url);
|
|
101
|
+
const endpoint = parsedUrl.pathname;
|
|
81
102
|
let response = null;
|
|
82
103
|
const keepalive = (0, VisibilityObserving_1._isUnloading)();
|
|
83
104
|
try {
|
|
@@ -89,43 +110,87 @@ class NetworkCore {
|
|
|
89
110
|
priority: args.priority,
|
|
90
111
|
keepalive,
|
|
91
112
|
};
|
|
92
|
-
|
|
113
|
+
if (args.isInitialize) {
|
|
114
|
+
Diagnostics_1.Diagnostics._markInitNetworkReqStart(args.sdkKey, {
|
|
115
|
+
attempt: currentAttempt,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const func = (_b = this._netConfig.networkOverrideFunc) !== null && _b !== void 0 ? _b : fetch;
|
|
93
119
|
response = yield func(url, config);
|
|
94
120
|
clearTimeout(handle);
|
|
95
121
|
if (!response.ok) {
|
|
96
122
|
const text = yield response.text().catch(() => 'No Text');
|
|
97
123
|
const err = new Error(`NetworkError: ${url} ${text}`);
|
|
124
|
+
console.log('response not ok!!', err);
|
|
98
125
|
err.name = 'NetworkError';
|
|
99
126
|
throw err;
|
|
100
127
|
}
|
|
101
128
|
const text = yield response.text();
|
|
102
|
-
|
|
129
|
+
if (args.isInitialize) {
|
|
130
|
+
Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, currentAttempt, text));
|
|
131
|
+
}
|
|
132
|
+
console.log('network success!!');
|
|
133
|
+
if (!url.includes('https://prodregistryv2.org')) {
|
|
134
|
+
this.networkEvents.push({
|
|
135
|
+
value: isProxy ? 'proxy' : 'statsig',
|
|
136
|
+
eventName: 'proxy::network_request_tracking',
|
|
137
|
+
metadata: {
|
|
138
|
+
url,
|
|
139
|
+
success: true,
|
|
140
|
+
endpoint,
|
|
141
|
+
},
|
|
142
|
+
time: Date.now(),
|
|
143
|
+
user: null,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
103
146
|
return {
|
|
104
147
|
body: text,
|
|
105
148
|
code: response.status,
|
|
106
149
|
};
|
|
107
150
|
}
|
|
108
151
|
catch (error) {
|
|
152
|
+
console.log('network error!!', error);
|
|
153
|
+
if (!url.includes('https://prodregistryv2.org')) {
|
|
154
|
+
this.networkEvents.push({
|
|
155
|
+
value: isProxy ? 'proxy' : 'statsig',
|
|
156
|
+
eventName: 'proxy::network_request_tracking',
|
|
157
|
+
metadata: {
|
|
158
|
+
url,
|
|
159
|
+
success: false,
|
|
160
|
+
endpoint,
|
|
161
|
+
},
|
|
162
|
+
time: Date.now(),
|
|
163
|
+
user: null,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
109
166
|
const errorMessage = _getErrorMessage(controller, error);
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
(
|
|
167
|
+
const timedOut = _didTimeout(controller);
|
|
168
|
+
if (args.isInitialize) {
|
|
169
|
+
Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, currentAttempt, '', error));
|
|
170
|
+
}
|
|
171
|
+
yield this._proxy.attemptToUpdateProxyUrl(args.sdkKey, errorMessage, timedOut);
|
|
172
|
+
if (!retries ||
|
|
173
|
+
currentAttempt > retries ||
|
|
174
|
+
!RETRYABLE_CODES.has((_c = response === null || response === void 0 ? void 0 : response.status) !== null && _c !== void 0 ? _c : 500)) {
|
|
175
|
+
(_d = this._emitter) === null || _d === void 0 ? void 0 : _d.call(this, { name: 'error', error });
|
|
113
176
|
Log_1.Log.error(`A networking error occured during ${method} request to ${url}.`, errorMessage, error);
|
|
114
177
|
return null;
|
|
115
178
|
}
|
|
116
|
-
return this._sendRequest(Object.assign(Object.assign({}, args), { retries: retries
|
|
179
|
+
return this._sendRequest(Object.assign(Object.assign({}, args), { retries: retries, attempt: currentAttempt + 1 }));
|
|
117
180
|
}
|
|
118
181
|
});
|
|
119
182
|
}
|
|
120
183
|
_getPopulatedURL(args) {
|
|
184
|
+
var _a;
|
|
121
185
|
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
const url = (_a = args.proxyUrl) !== null && _a !== void 0 ? _a : args.url;
|
|
122
187
|
const params = Object.assign({ [NetworkConfig_1.NetworkParam.SdkKey]: args.sdkKey, [NetworkConfig_1.NetworkParam.SdkType]: SDKType_1.SDKType._get(args.sdkKey), [NetworkConfig_1.NetworkParam.SdkVersion]: StatsigMetadata_1.SDK_VERSION, [NetworkConfig_1.NetworkParam.Time]: String(Date.now()), [NetworkConfig_1.NetworkParam.SessionID]: yield SessionID_1.SessionID.get(args.sdkKey) }, args.params);
|
|
123
188
|
const query = Object.keys(params)
|
|
124
189
|
.map((key) => {
|
|
125
190
|
return `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
|
|
126
191
|
})
|
|
127
192
|
.join('&');
|
|
128
|
-
return `${
|
|
193
|
+
return `${url}${query ? `?${query}` : ''}`;
|
|
129
194
|
});
|
|
130
195
|
}
|
|
131
196
|
_getPopulatedBody(args) {
|
|
@@ -136,7 +201,7 @@ class NetworkCore {
|
|
|
136
201
|
const sdkType = SDKType_1.SDKType._get(sdkKey);
|
|
137
202
|
return JSON.stringify(Object.assign(Object.assign({}, data), { statsigMetadata: Object.assign(Object.assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID,
|
|
138
203
|
sessionID,
|
|
139
|
-
sdkType }) }));
|
|
204
|
+
sdkType, proxyUrl: args.proxyUrl }) }));
|
|
140
205
|
});
|
|
141
206
|
}
|
|
142
207
|
_attemptToEncodeString(args, input) {
|
|
@@ -158,6 +223,32 @@ class NetworkCore {
|
|
|
158
223
|
return input;
|
|
159
224
|
}
|
|
160
225
|
}
|
|
226
|
+
_getNetworkProxyArgs(args) {
|
|
227
|
+
const proxyUrl = this._proxy.getProxyUrl(args.sdkKey, args.url);
|
|
228
|
+
return {
|
|
229
|
+
proxyUrl: proxyUrl !== null && proxyUrl !== void 0 ? proxyUrl : null,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
_flushNetworkEvents() {
|
|
233
|
+
var _a;
|
|
234
|
+
if (this.networkEvents.length === 0) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
console.log('flushing network events!!', this.networkEvents);
|
|
238
|
+
this.post({
|
|
239
|
+
sdkKey: (_a = this.sdkKey) !== null && _a !== void 0 ? _a : '',
|
|
240
|
+
url: 'https://prodregistryv2.org/v1/rgstr',
|
|
241
|
+
data: {
|
|
242
|
+
events: [...this.networkEvents],
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
this.networkEvents = [];
|
|
246
|
+
}
|
|
247
|
+
_startBackgroundFlushNetworkEvents() {
|
|
248
|
+
setInterval(() => {
|
|
249
|
+
this._flushNetworkEvents();
|
|
250
|
+
}, 10000);
|
|
251
|
+
}
|
|
161
252
|
}
|
|
162
253
|
exports.NetworkCore = NetworkCore;
|
|
163
254
|
const _ensureValidSdkKey = (args) => {
|
|
@@ -180,3 +271,9 @@ function _getErrorMessage(controller, error) {
|
|
|
180
271
|
}
|
|
181
272
|
return 'Unknown Error';
|
|
182
273
|
}
|
|
274
|
+
function _didTimeout(controller) {
|
|
275
|
+
const timeout = (controller === null || controller === void 0 ? void 0 : controller.signal.aborted) &&
|
|
276
|
+
typeof controller.signal.reason === 'string' &&
|
|
277
|
+
controller.signal.reason.includes('Timeout');
|
|
278
|
+
return timeout || false;
|
|
279
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NetworkArgs } from './NetworkConfig';
|
|
2
|
+
import { AnyStatsigOptions } from './StatsigOptionsCommon';
|
|
3
|
+
export declare class NetworkProxy {
|
|
4
|
+
private _options;
|
|
5
|
+
private _proxyUrl;
|
|
6
|
+
private _expiryTime;
|
|
7
|
+
private _overrideFn;
|
|
8
|
+
constructor(_options: AnyStatsigOptions);
|
|
9
|
+
setOverrideFunc(networkOverrideFunc?: (url: string, args: NetworkArgs) => Promise<Response>): void;
|
|
10
|
+
getProxyUrl(sdkKey: string, url: string): string | null;
|
|
11
|
+
attemptToUpdateProxyUrl(sdkKey: string, errorMessage: string | null, timedOut: boolean): Promise<void>;
|
|
12
|
+
private _fetchProxyUrl;
|
|
13
|
+
private _getTxtEntries;
|
|
14
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
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.NetworkProxy = void 0;
|
|
13
|
+
const Hashing_1 = require("./Hashing");
|
|
14
|
+
const NetworkConfig_1 = require("./NetworkConfig");
|
|
15
|
+
const StorageProvider_1 = require("./StorageProvider");
|
|
16
|
+
const TypedJsonParse_1 = require("./TypedJsonParse");
|
|
17
|
+
const DNS_QUERY_ENDPOINT = 'https://cloudflare-dns.com/dns-query';
|
|
18
|
+
const DNS_QUERY_DOMAIN = 'featureregistry.org';
|
|
19
|
+
const DEFAULT_TTL = 72 * 60 * 60 * 1000; // 72 hours
|
|
20
|
+
class NetworkProxy {
|
|
21
|
+
constructor(_options) {
|
|
22
|
+
this._options = _options;
|
|
23
|
+
this._proxyUrl = null;
|
|
24
|
+
this._expiryTime = 0;
|
|
25
|
+
this._overrideFn = undefined;
|
|
26
|
+
if (_options.networkConfig) {
|
|
27
|
+
const netConfig = _options.networkConfig;
|
|
28
|
+
this.setOverrideFunc(netConfig.networkOverrideFunc);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
setOverrideFunc(networkOverrideFunc) {
|
|
32
|
+
this._overrideFn = networkOverrideFunc;
|
|
33
|
+
}
|
|
34
|
+
getProxyUrl(sdkKey, url) {
|
|
35
|
+
if (!_isDefaultUrl(url)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const endpoint = getEndpoint(url);
|
|
39
|
+
if (this._expiryTime > 0 && Date.now() > this._expiryTime) {
|
|
40
|
+
this._proxyUrl = null;
|
|
41
|
+
this._expiryTime = 0;
|
|
42
|
+
return this._proxyUrl;
|
|
43
|
+
}
|
|
44
|
+
if (this._proxyUrl) {
|
|
45
|
+
return this._proxyUrl + endpoint;
|
|
46
|
+
}
|
|
47
|
+
const hashkey = _getProxyStorageKey(sdkKey);
|
|
48
|
+
const cacheProxy = _getProxyFromCache(hashkey);
|
|
49
|
+
if (cacheProxy) {
|
|
50
|
+
this._proxyUrl = cacheProxy.url;
|
|
51
|
+
this._expiryTime = cacheProxy.expiryTime;
|
|
52
|
+
return this._proxyUrl + endpoint;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
attemptToUpdateProxyUrl(sdkKey, errorMessage, timedOut) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
if (!_containsUnresolvedDomain(errorMessage, timedOut)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const hashkey = _getProxyStorageKey(sdkKey);
|
|
62
|
+
const fetchedProxyUrl = yield this._fetchProxyUrl();
|
|
63
|
+
if (!fetchedProxyUrl) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this._proxyUrl = fetchedProxyUrl;
|
|
67
|
+
this._expiryTime = Date.now() + DEFAULT_TTL;
|
|
68
|
+
StorageProvider_1.Storage._setItem(hashkey, JSON.stringify({ url: this._proxyUrl, expiryTime: this._expiryTime }));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
_fetchProxyUrl() {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const txtEntries = yield this._getTxtEntries(DNS_QUERY_DOMAIN);
|
|
74
|
+
if (txtEntries.length === 0) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return _findClosestProxy(txtEntries);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
_getTxtEntries(domain) {
|
|
81
|
+
var _a;
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
const body = _encodeDomain(domain);
|
|
84
|
+
const func = (_a = this._overrideFn) !== null && _a !== void 0 ? _a : fetch;
|
|
85
|
+
const res = yield func(DNS_QUERY_ENDPOINT, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/dns-message',
|
|
89
|
+
Accept: 'application/dns-message',
|
|
90
|
+
},
|
|
91
|
+
body,
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const arrBuff = yield res.arrayBuffer();
|
|
97
|
+
return _decodeDnsResponse(arrBuff);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.NetworkProxy = NetworkProxy;
|
|
102
|
+
function _getProxyStorageKey(sdkKey) {
|
|
103
|
+
return `statsig.proxy.${(0, Hashing_1._DJB2)(sdkKey)}`;
|
|
104
|
+
}
|
|
105
|
+
function _getProxyFromCache(hashKey) {
|
|
106
|
+
const cacheProxyStr = StorageProvider_1.Storage._getItem(hashKey);
|
|
107
|
+
if (!cacheProxyStr) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const cacheProxy = (0, TypedJsonParse_1._typedJsonParse)(cacheProxyStr, 'url', 'cacheProxy');
|
|
111
|
+
if (cacheProxy && Date.now() < cacheProxy.expiryTime) {
|
|
112
|
+
return cacheProxy;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function _decodeDnsResponse(arrBuff) {
|
|
117
|
+
const respArr = Array.from(new Uint8Array(arrBuff));
|
|
118
|
+
let nextIndex = respArr.indexOf(0xc0);
|
|
119
|
+
const entries = [];
|
|
120
|
+
while (nextIndex > -1 && nextIndex < respArr.length - 2) {
|
|
121
|
+
const offset = respArr[nextIndex + 1];
|
|
122
|
+
if (respArr.length < nextIndex + offset + 1) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
const count = respArr[nextIndex + offset];
|
|
126
|
+
const entry = respArr.slice(nextIndex + offset + 1, nextIndex + offset + count + 1);
|
|
127
|
+
entries.push(entry.map((e) => String.fromCharCode(e)).join(''));
|
|
128
|
+
nextIndex = respArr.indexOf(0xc0, nextIndex + offset + count);
|
|
129
|
+
}
|
|
130
|
+
return entries;
|
|
131
|
+
}
|
|
132
|
+
function _encodeDomain(domain) {
|
|
133
|
+
const bin = [
|
|
134
|
+
0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
135
|
+
];
|
|
136
|
+
const entries = domain.split('.');
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
bin.push(entry.length);
|
|
139
|
+
for (const char of entry) {
|
|
140
|
+
bin.push(char.charCodeAt(0));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
bin.push(...[0x00, 0x00, 0x10, 0x00, 0x01]);
|
|
144
|
+
return Uint8Array.from(bin);
|
|
145
|
+
}
|
|
146
|
+
function _findClosestProxy(txtEntries) {
|
|
147
|
+
const [proxyList, closestIndex] = _extractProxyListFromTxt(txtEntries);
|
|
148
|
+
if (proxyList.length === 0) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return proxyList.length > closestIndex
|
|
152
|
+
? proxyList[closestIndex]
|
|
153
|
+
: proxyList[0];
|
|
154
|
+
}
|
|
155
|
+
function _extractProxyListFromTxt(txtEntries) {
|
|
156
|
+
let closestDistance = 1440;
|
|
157
|
+
let closestIndex = 0;
|
|
158
|
+
const proxyList = [];
|
|
159
|
+
const localTimezoneOffset = new Date().getTimezoneOffset() + 720;
|
|
160
|
+
for (const entry of txtEntries) {
|
|
161
|
+
const [key, value] = entry.split('=');
|
|
162
|
+
proxyList.push(value);
|
|
163
|
+
const tzoneOffsetStr = key === null || key === void 0 ? void 0 : key.split('_')[1];
|
|
164
|
+
const tzoneOffset = parseInt(tzoneOffsetStr);
|
|
165
|
+
if (tzoneOffset && !Number.isNaN(tzoneOffset)) {
|
|
166
|
+
const dist = Math.abs(tzoneOffset - localTimezoneOffset);
|
|
167
|
+
if (dist < closestDistance) {
|
|
168
|
+
closestIndex = proxyList.length - 1;
|
|
169
|
+
closestDistance = dist;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return [proxyList, closestIndex];
|
|
174
|
+
}
|
|
175
|
+
function _isDefaultUrl(url) {
|
|
176
|
+
for (const key in NetworkConfig_1.NetworkDefault) {
|
|
177
|
+
if (url.startsWith(NetworkConfig_1.NetworkDefault[key])) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
function _containsUnresolvedDomain(errorMsg, timedOut) {
|
|
184
|
+
const lowerErrorMsg = (errorMsg === null || errorMsg === void 0 ? void 0 : errorMsg.toLowerCase()) || '';
|
|
185
|
+
return (timedOut ||
|
|
186
|
+
lowerErrorMsg.includes('uncaught exception') ||
|
|
187
|
+
lowerErrorMsg.includes('failed to fetch') ||
|
|
188
|
+
lowerErrorMsg.includes('networkerror when attempting to fetch resource'));
|
|
189
|
+
}
|
|
190
|
+
function getEndpoint(urlString) {
|
|
191
|
+
try {
|
|
192
|
+
const url = new URL(urlString);
|
|
193
|
+
const endpoint = url.pathname.substring(1);
|
|
194
|
+
return endpoint;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
}
|
package/src/SessionID.d.ts
CHANGED
|
@@ -11,10 +11,10 @@ export type StatsigSession = {
|
|
|
11
11
|
idleTimeoutID?: SessionTimeoutID;
|
|
12
12
|
};
|
|
13
13
|
export declare const SessionID: {
|
|
14
|
-
get: (sdkKey: string) =>
|
|
14
|
+
get: (sdkKey: string) => string;
|
|
15
15
|
};
|
|
16
16
|
export declare const StatsigSession: {
|
|
17
|
-
get: (sdkKey: string) =>
|
|
17
|
+
get: (sdkKey: string) => StatsigSession;
|
|
18
18
|
overrideInitialSessionID: (override: string, sdkKey: string) => void;
|
|
19
19
|
};
|
|
20
20
|
export {};
|
package/src/SessionID.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.StatsigSession = exports.SessionID = void 0;
|
|
13
4
|
const CacheKey_1 = require("./CacheKey");
|
|
@@ -18,49 +9,47 @@ const MAX_SESSION_IDLE_TIME = 30 * 60 * 1000; // 30 minutes
|
|
|
18
9
|
const MAX_SESSION_AGE = 4 * 60 * 60 * 1000; // 4 hours
|
|
19
10
|
const PROMISE_MAP = {};
|
|
20
11
|
exports.SessionID = {
|
|
21
|
-
get: (sdkKey) =>
|
|
22
|
-
return exports.StatsigSession.get(sdkKey).
|
|
23
|
-
}
|
|
12
|
+
get: (sdkKey) => {
|
|
13
|
+
return exports.StatsigSession.get(sdkKey).data.sessionID;
|
|
14
|
+
},
|
|
24
15
|
};
|
|
25
16
|
exports.StatsigSession = {
|
|
26
|
-
get: (sdkKey) =>
|
|
17
|
+
get: (sdkKey) => {
|
|
27
18
|
if (PROMISE_MAP[sdkKey] == null) {
|
|
28
19
|
PROMISE_MAP[sdkKey] = _loadSession(sdkKey);
|
|
29
20
|
}
|
|
30
|
-
const session =
|
|
21
|
+
const session = PROMISE_MAP[sdkKey];
|
|
31
22
|
return _bumpSession(session);
|
|
32
|
-
}
|
|
23
|
+
},
|
|
33
24
|
overrideInitialSessionID: (override, sdkKey) => {
|
|
34
25
|
PROMISE_MAP[sdkKey] = _overrideSessionId(override, sdkKey);
|
|
35
26
|
},
|
|
36
27
|
};
|
|
37
28
|
function _loadSession(sdkKey) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
lastUpdate: now,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
data,
|
|
50
|
-
sdkKey,
|
|
29
|
+
let data = _loadFromStorage(sdkKey);
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
if (!data) {
|
|
32
|
+
data = {
|
|
33
|
+
sessionID: (0, UUID_1.getUUID)(),
|
|
34
|
+
startTime: now,
|
|
35
|
+
lastUpdate: now,
|
|
51
36
|
};
|
|
52
|
-
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
data,
|
|
40
|
+
sdkKey,
|
|
41
|
+
};
|
|
53
42
|
}
|
|
54
43
|
function _overrideSessionId(override, sdkKey) {
|
|
55
44
|
const now = Date.now();
|
|
56
|
-
return
|
|
45
|
+
return {
|
|
57
46
|
data: {
|
|
58
47
|
sessionID: override,
|
|
59
48
|
startTime: now,
|
|
60
49
|
lastUpdate: now,
|
|
61
50
|
},
|
|
62
51
|
sdkKey,
|
|
63
|
-
}
|
|
52
|
+
};
|
|
64
53
|
}
|
|
65
54
|
function _bumpSession(session) {
|
|
66
55
|
const now = Date.now();
|
|
@@ -98,9 +87,12 @@ function _getSessionIDStorageKey(sdkKey) {
|
|
|
98
87
|
}
|
|
99
88
|
function _persistToStorage(session, sdkKey) {
|
|
100
89
|
const storageKey = _getSessionIDStorageKey(sdkKey);
|
|
101
|
-
|
|
90
|
+
try {
|
|
91
|
+
(0, StorageProvider_1._setObjectInStorage)(storageKey, session);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
102
94
|
Log_1.Log.warn('Failed to save SessionID');
|
|
103
|
-
}
|
|
95
|
+
}
|
|
104
96
|
}
|
|
105
97
|
function _loadFromStorage(sdkKey) {
|
|
106
98
|
const storageKey = _getSessionIDStorageKey(sdkKey);
|
package/src/StableID.d.ts
CHANGED
package/src/StableID.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.StableID = void 0;
|
|
13
4
|
const CacheKey_1 = require("./CacheKey");
|
|
@@ -16,21 +7,19 @@ const StorageProvider_1 = require("./StorageProvider");
|
|
|
16
7
|
const UUID_1 = require("./UUID");
|
|
17
8
|
const PROMISE_MAP = {};
|
|
18
9
|
exports.StableID = {
|
|
19
|
-
get: (sdkKey) =>
|
|
10
|
+
get: (sdkKey) => {
|
|
20
11
|
if (PROMISE_MAP[sdkKey] == null) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return newStableID;
|
|
28
|
-
});
|
|
12
|
+
let stableID = _loadFromStorage(sdkKey);
|
|
13
|
+
if (stableID == null) {
|
|
14
|
+
stableID = (0, UUID_1.getUUID)();
|
|
15
|
+
_persistToStorage(stableID, sdkKey);
|
|
16
|
+
}
|
|
17
|
+
PROMISE_MAP[sdkKey] = stableID;
|
|
29
18
|
}
|
|
30
19
|
return PROMISE_MAP[sdkKey];
|
|
31
|
-
}
|
|
20
|
+
},
|
|
32
21
|
setOverride: (override, sdkKey) => {
|
|
33
|
-
PROMISE_MAP[sdkKey] =
|
|
22
|
+
PROMISE_MAP[sdkKey] = override;
|
|
34
23
|
_persistToStorage(override, sdkKey);
|
|
35
24
|
},
|
|
36
25
|
};
|
|
@@ -39,9 +28,12 @@ function _getStableIDStorageKey(sdkKey) {
|
|
|
39
28
|
}
|
|
40
29
|
function _persistToStorage(stableID, sdkKey) {
|
|
41
30
|
const storageKey = _getStableIDStorageKey(sdkKey);
|
|
42
|
-
|
|
31
|
+
try {
|
|
32
|
+
(0, StorageProvider_1._setObjectInStorage)(storageKey, stableID);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
43
35
|
Log_1.Log.warn('Failed to save StableID');
|
|
44
|
-
}
|
|
36
|
+
}
|
|
45
37
|
}
|
|
46
38
|
function _loadFromStorage(sdkKey) {
|
|
47
39
|
const storageKey = _getStableIDStorageKey(sdkKey);
|
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 = '1.
|
|
4
|
+
exports.SDK_VERSION = '1.8.0-beta.10';
|
|
5
5
|
let metadata = {
|
|
6
6
|
sdkVersion: exports.SDK_VERSION,
|
|
7
7
|
sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
|
package/src/StorageProvider.d.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
export type StorageProvider = {
|
|
2
2
|
_getProviderName: () => string;
|
|
3
|
-
_getItem: (key: string) =>
|
|
4
|
-
_setItem: (key: string, value: string) =>
|
|
5
|
-
_removeItem: (key: string) =>
|
|
6
|
-
_getAllKeys: () =>
|
|
7
|
-
_getItemSync?: (key: string) => string | null;
|
|
3
|
+
_getItem: (key: string) => string | null;
|
|
4
|
+
_setItem: (key: string, value: string) => void;
|
|
5
|
+
_removeItem: (key: string) => void;
|
|
6
|
+
_getAllKeys: () => readonly string[];
|
|
8
7
|
};
|
|
9
8
|
type StorageProviderManagment = {
|
|
10
9
|
_setProvider: (newProvider: StorageProvider) => void;
|
|
11
10
|
_setDisabled: (isDisabled: boolean) => void;
|
|
12
11
|
};
|
|
13
12
|
export declare const Storage: StorageProvider & StorageProviderManagment;
|
|
14
|
-
export declare function _getObjectFromStorage<T>(key: string):
|
|
15
|
-
export declare function _setObjectInStorage(key: string, obj: unknown):
|
|
13
|
+
export declare function _getObjectFromStorage<T>(key: string): T | null;
|
|
14
|
+
export declare function _setObjectInStorage(key: string, obj: unknown): void;
|
|
16
15
|
export {};
|
package/src/StorageProvider.js
CHANGED
|
@@ -1,26 +1,19 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports._setObjectInStorage = exports._getObjectFromStorage = exports.Storage = void 0;
|
|
13
4
|
const Log_1 = require("./Log");
|
|
14
5
|
const SafeJs_1 = require("./SafeJs");
|
|
15
6
|
const inMemoryStore = {};
|
|
16
|
-
const _resolve = (input) => Promise.resolve(input);
|
|
17
7
|
const _inMemoryProvider = {
|
|
18
8
|
_getProviderName: () => 'InMemory',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
_getItem: (key) => inMemoryStore[key] ? inMemoryStore[key] : null,
|
|
10
|
+
_setItem: (key, value) => {
|
|
11
|
+
inMemoryStore[key] = value;
|
|
12
|
+
},
|
|
13
|
+
_removeItem: (key) => {
|
|
14
|
+
delete inMemoryStore[key];
|
|
15
|
+
},
|
|
16
|
+
_getAllKeys: () => Object.keys(inMemoryStore),
|
|
24
17
|
};
|
|
25
18
|
let _localStorageProvider = null;
|
|
26
19
|
try {
|
|
@@ -30,11 +23,10 @@ try {
|
|
|
30
23
|
typeof win.localStorage.getItem === 'function') {
|
|
31
24
|
_localStorageProvider = {
|
|
32
25
|
_getProviderName: () => 'LocalStorage',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
_getAllKeys: () => _resolve(Object.keys(win.localStorage)),
|
|
26
|
+
_getItem: (key) => win.localStorage.getItem(key),
|
|
27
|
+
_setItem: (key, value) => win.localStorage.setItem(key, value),
|
|
28
|
+
_removeItem: (key) => win.localStorage.removeItem(key),
|
|
29
|
+
_getAllKeys: () => Object.keys(win.localStorage),
|
|
38
30
|
};
|
|
39
31
|
}
|
|
40
32
|
}
|
|
@@ -57,8 +49,7 @@ function _inMemoryBreaker(get) {
|
|
|
57
49
|
}
|
|
58
50
|
exports.Storage = {
|
|
59
51
|
_getProviderName: () => _current._getProviderName(),
|
|
60
|
-
_getItem: (key) =>
|
|
61
|
-
_getItemSync: (key) => _inMemoryBreaker(() => _current._getItemSync ? _current._getItemSync(key) : null),
|
|
52
|
+
_getItem: (key) => _inMemoryBreaker(() => _current._getItem(key)),
|
|
62
53
|
_setItem: (key, value) => _current._setItem(key, value),
|
|
63
54
|
_removeItem: (key) => _current._removeItem(key),
|
|
64
55
|
_getAllKeys: () => _current._getAllKeys(),
|
|
@@ -77,15 +68,11 @@ exports.Storage = {
|
|
|
77
68
|
},
|
|
78
69
|
};
|
|
79
70
|
function _getObjectFromStorage(key) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return JSON.parse(value !== null && value !== void 0 ? value : 'null');
|
|
83
|
-
});
|
|
71
|
+
const value = exports.Storage._getItem(key);
|
|
72
|
+
return JSON.parse(value !== null && value !== void 0 ? value : 'null');
|
|
84
73
|
}
|
|
85
74
|
exports._getObjectFromStorage = _getObjectFromStorage;
|
|
86
75
|
function _setObjectInStorage(key, obj) {
|
|
87
|
-
|
|
88
|
-
yield exports.Storage._setItem(key, JSON.stringify(obj));
|
|
89
|
-
});
|
|
76
|
+
exports.Storage._setItem(key, JSON.stringify(obj));
|
|
90
77
|
}
|
|
91
78
|
exports._setObjectInStorage = _setObjectInStorage;
|
package/src/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Statsig Global should go first */
|
|
2
2
|
import './$_StatsigGlobal';
|
|
3
|
+
import { Diagnostics } from './Diagnostics';
|
|
3
4
|
import { EventLogger } from './EventLogger';
|
|
4
5
|
import { Log } from './Log';
|
|
5
6
|
import { Storage } from './StorageProvider';
|
|
@@ -7,6 +8,7 @@ export * from './$_StatsigGlobal';
|
|
|
7
8
|
export * from './CacheKey';
|
|
8
9
|
export * from './ClientInterfaces';
|
|
9
10
|
export * from './DataAdapterCore';
|
|
11
|
+
export * from './Diagnostics';
|
|
10
12
|
export * from './DownloadConfigSpecsResponse';
|
|
11
13
|
export * from './ErrorBoundary';
|
|
12
14
|
export * from './EvaluationOptions';
|
|
@@ -37,4 +39,4 @@ export * from './TypingUtils';
|
|
|
37
39
|
export * from './UrlOverrides';
|
|
38
40
|
export * from './UUID';
|
|
39
41
|
export * from './VisibilityObserving';
|
|
40
|
-
export { EventLogger, Storage, Log };
|
|
42
|
+
export { EventLogger, Storage, Log, Diagnostics };
|
package/src/index.js
CHANGED
|
@@ -14,9 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.Log = exports.Storage = exports.EventLogger = void 0;
|
|
17
|
+
exports.Diagnostics = exports.Log = exports.Storage = exports.EventLogger = void 0;
|
|
18
18
|
/** Statsig Global should go first */
|
|
19
19
|
require("./$_StatsigGlobal");
|
|
20
|
+
const Diagnostics_1 = require("./Diagnostics");
|
|
21
|
+
Object.defineProperty(exports, "Diagnostics", { enumerable: true, get: function () { return Diagnostics_1.Diagnostics; } });
|
|
20
22
|
const EventLogger_1 = require("./EventLogger");
|
|
21
23
|
Object.defineProperty(exports, "EventLogger", { enumerable: true, get: function () { return EventLogger_1.EventLogger; } });
|
|
22
24
|
const Log_1 = require("./Log");
|
|
@@ -28,6 +30,7 @@ __exportStar(require("./$_StatsigGlobal"), exports);
|
|
|
28
30
|
__exportStar(require("./CacheKey"), exports);
|
|
29
31
|
__exportStar(require("./ClientInterfaces"), exports);
|
|
30
32
|
__exportStar(require("./DataAdapterCore"), exports);
|
|
33
|
+
__exportStar(require("./Diagnostics"), exports);
|
|
31
34
|
__exportStar(require("./DownloadConfigSpecsResponse"), exports);
|
|
32
35
|
__exportStar(require("./ErrorBoundary"), exports);
|
|
33
36
|
__exportStar(require("./EvaluationOptions"), exports);
|