@statsig/client-core 3.3.0-beta.2 → 3.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/client-core",
3
- "version": "3.3.0-beta.2",
3
+ "version": "3.3.0",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -2,7 +2,7 @@ import { StatsigClientInterface } from './ClientInterfaces';
2
2
  export type StatsigGlobal = {
3
3
  [key: string]: unknown;
4
4
  instances?: Record<string, StatsigClientInterface>;
5
- lastInstance?: StatsigClientInterface;
5
+ firstInstance?: StatsigClientInterface;
6
6
  acInstances?: Record<string, unknown>;
7
7
  srInstances?: Record<string, unknown>;
8
8
  instance: (sdkKey?: string) => StatsigClientInterface | undefined;
@@ -3,6 +3,7 @@
3
3
  var _a, _b, _c;
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports._getInstance = exports._getStatsigGlobalFlag = exports._getStatsigGlobal = void 0;
6
+ const Log_1 = require("./Log");
6
7
  const _getStatsigGlobal = () => {
7
8
  return __STATSIG__ ? __STATSIG__ : statsigGlobal;
8
9
  };
@@ -14,7 +15,10 @@ exports._getStatsigGlobalFlag = _getStatsigGlobalFlag;
14
15
  const _getInstance = (sdkKey) => {
15
16
  const gbl = (0, exports._getStatsigGlobal)();
16
17
  if (!sdkKey) {
17
- return gbl.lastInstance;
18
+ if (gbl.instances && Object.keys(gbl.instances).length > 1) {
19
+ Log_1.Log.warn('Call made to Statsig global instance without an SDK key but there is more than one client instance. If you are using mulitple clients, please specify the SDK key.');
20
+ }
21
+ return gbl.firstInstance;
18
22
  }
19
23
  return gbl.instances && gbl.instances[sdkKey];
20
24
  };
@@ -1,5 +1,4 @@
1
1
  import './$_StatsigGlobal';
2
- import { ErrorBoundary } from './ErrorBoundary';
3
2
  import { NetworkPriority } from './NetworkConfig';
4
3
  import { StatsigClientEmitEventFunc } from './StatsigClientBase';
5
4
  import { AnyStatsigOptions } from './StatsigOptionsCommon';
@@ -29,18 +28,14 @@ export declare class NetworkCore {
29
28
  private readonly _timeout;
30
29
  private readonly _netConfig;
31
30
  private readonly _options;
32
- private readonly _fallbackResolver;
33
- private _errorBoundary;
34
31
  constructor(options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
35
- setErrorBoundary(errorBoundary: ErrorBoundary): void;
36
- isBeaconSupported(): boolean;
37
- beacon(args: BeaconRequestArgs): Promise<boolean>;
38
32
  post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
39
33
  get(args: RequestArgs): Promise<NetworkResponse | null>;
34
+ isBeaconSupported(): boolean;
35
+ beacon(args: BeaconRequestArgs): Promise<boolean>;
40
36
  private _sendRequest;
41
37
  private _getPopulatedURL;
42
38
  private _getPopulatedBody;
43
39
  private _attemptToEncodeString;
44
- private _getInternalRequestArgs;
45
40
  }
46
41
  export {};
@@ -15,7 +15,6 @@ 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 NetworkFallbackResolver_1 = require("./NetworkFallbackResolver");
19
18
  const SDKType_1 = require("./SDKType");
20
19
  const SafeJs_1 = require("./SafeJs");
21
20
  const SessionID_1 = require("./SessionID");
@@ -31,7 +30,6 @@ class NetworkCore {
31
30
  this._timeout = DEFAULT_TIMEOUT_MS;
32
31
  this._netConfig = {};
33
32
  this._options = {};
34
- this._errorBoundary = null;
35
33
  if (options) {
36
34
  this._options = options;
37
35
  }
@@ -41,13 +39,18 @@ class NetworkCore {
41
39
  if (this._netConfig.networkTimeoutMs) {
42
40
  this._timeout = this._netConfig.networkTimeoutMs;
43
41
  }
44
- this._fallbackResolver = new NetworkFallbackResolver_1.NetworkFallbackResolver(this._options);
45
42
  }
46
- setErrorBoundary(errorBoundary) {
47
- this._errorBoundary = errorBoundary;
48
- this._errorBoundary.wrap(this);
49
- this._errorBoundary.wrap(this._fallbackResolver);
50
- this._fallbackResolver.setErrorBoundary(errorBoundary);
43
+ post(args) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ let body = yield this._getPopulatedBody(args);
46
+ if (args.isStatsigEncodable) {
47
+ body = this._attemptToEncodeString(args, body);
48
+ }
49
+ return this._sendRequest(Object.assign({ method: 'POST', body }, args));
50
+ });
51
+ }
52
+ get(args) {
53
+ return this._sendRequest(Object.assign({ method: 'GET' }, args));
51
54
  }
52
55
  isBeaconSupported() {
53
56
  return (typeof navigator !== 'undefined' &&
@@ -58,27 +61,12 @@ class NetworkCore {
58
61
  if (!_ensureValidSdkKey(args)) {
59
62
  return false;
60
63
  }
61
- const argsInternal = this._getInternalRequestArgs('POST', args);
62
- const body = yield this._getPopulatedBody(argsInternal, args.data);
63
- const url = yield this._getPopulatedURL(argsInternal);
64
+ const body = yield this._getPopulatedBody(args);
65
+ const url = yield this._getPopulatedURL(args);
64
66
  const nav = navigator;
65
67
  return nav.sendBeacon.bind(nav)(url, body);
66
68
  });
67
69
  }
68
- post(args) {
69
- return __awaiter(this, void 0, void 0, function* () {
70
- const argsInternal = this._getInternalRequestArgs('POST', args);
71
- argsInternal.body = yield this._getPopulatedBody(argsInternal, args.data);
72
- if (args.isStatsigEncodable) {
73
- argsInternal.body = this._attemptToEncodeString(argsInternal, argsInternal.body);
74
- }
75
- return this._sendRequest(argsInternal);
76
- });
77
- }
78
- get(args) {
79
- const argsInternal = this._getInternalRequestArgs('GET', args);
80
- return this._sendRequest(argsInternal);
81
- }
82
70
  _sendRequest(args) {
83
71
  var _a, _b, _c;
84
72
  return __awaiter(this, void 0, void 0, function* () {
@@ -90,11 +78,9 @@ class NetworkCore {
90
78
  }
91
79
  const { method, body, retries, attempt } = args;
92
80
  const currentAttempt = attempt !== null && attempt !== void 0 ? attempt : 1;
93
- const abortController = typeof AbortController !== 'undefined' ? new AbortController() : null;
94
- const timeoutHandle = setTimeout(() => {
95
- abortController === null || abortController === void 0 ? void 0 : abortController.abort(`Timeout of ${this._timeout}ms expired.`);
96
- }, this._timeout);
97
- const populatedUrl = yield this._getPopulatedURL(args);
81
+ const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
82
+ const handle = setTimeout(() => controller === null || controller === void 0 ? void 0 : controller.abort(`Timeout of ${this._timeout}ms expired.`), this._timeout);
83
+ const url = yield this._getPopulatedURL(args);
98
84
  let response = null;
99
85
  const keepalive = (0, VisibilityObserving_1._isUnloading)();
100
86
  try {
@@ -102,76 +88,81 @@ class NetworkCore {
102
88
  method,
103
89
  body,
104
90
  headers: Object.assign({}, args.headers),
105
- signal: abortController === null || abortController === void 0 ? void 0 : abortController.signal,
91
+ signal: controller === null || controller === void 0 ? void 0 : controller.signal,
106
92
  priority: args.priority,
107
93
  keepalive,
108
94
  };
109
- _tryMarkInitStart(args, currentAttempt);
95
+ if (args.isInitialize) {
96
+ Diagnostics_1.Diagnostics._markInitNetworkReqStart(args.sdkKey, {
97
+ attempt: currentAttempt,
98
+ });
99
+ }
110
100
  const func = (_a = this._netConfig.networkOverrideFunc) !== null && _a !== void 0 ? _a : fetch;
111
- response = yield func(populatedUrl, config);
112
- clearTimeout(timeoutHandle);
101
+ response = yield func(url, config);
102
+ clearTimeout(handle);
113
103
  if (!response.ok) {
114
104
  const text = yield response.text().catch(() => 'No Text');
115
- const err = new Error(`NetworkError: ${populatedUrl} ${text}`);
105
+ const err = new Error(`NetworkError: ${url} ${text}`);
116
106
  err.name = 'NetworkError';
117
107
  throw err;
118
108
  }
119
109
  const text = yield response.text();
120
- _tryMarkInitEnd(args, response, currentAttempt, text);
121
- this._fallbackResolver.tryBumpExpiryTime(args.sdkKey, populatedUrl);
110
+ if (args.isInitialize) {
111
+ Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, currentAttempt, text));
112
+ }
122
113
  return {
123
114
  body: text,
124
115
  code: response.status,
125
116
  };
126
117
  }
127
118
  catch (error) {
128
- const errorMessage = _getErrorMessage(abortController, error);
129
- const timedOut = _didTimeout(abortController);
130
- _tryMarkInitEnd(args, response, currentAttempt, '', error);
131
- const fallbackUpdated = yield this._fallbackResolver.tryFetchUpdatedFallbackInfo(args.sdkKey, populatedUrl, errorMessage, timedOut);
132
- if (fallbackUpdated) {
133
- args.fallbackUrl = this._fallbackResolver.getFallbackUrl(args.sdkKey, args.url);
119
+ const errorMessage = _getErrorMessage(controller, error);
120
+ if (args.isInitialize) {
121
+ Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, currentAttempt, '', error));
134
122
  }
135
123
  if (!retries ||
136
124
  currentAttempt > retries ||
137
125
  !RETRYABLE_CODES.has((_b = response === null || response === void 0 ? void 0 : response.status) !== null && _b !== void 0 ? _b : 500)) {
138
- (_c = this._emitter) === null || _c === void 0 ? void 0 : _c.call(this, { name: 'error', error, tag: StatsigClientEventEmitter_1.ErrorTag.NetworkError });
139
- Log_1.Log.error(`A networking error occured during ${method} request to ${populatedUrl}.`, errorMessage, error);
126
+ (_c = this._emitter) === null || _c === void 0 ? void 0 : _c.call(this, {
127
+ name: 'error',
128
+ error,
129
+ tag: StatsigClientEventEmitter_1.ErrorTag.NetworkError,
130
+ requestArgs: args,
131
+ });
132
+ Log_1.Log.error(`A networking error occured during ${method} request to ${url}.`, errorMessage, error);
140
133
  return null;
141
134
  }
142
- return this._sendRequest(Object.assign(Object.assign({}, args), { retries, attempt: currentAttempt + 1 }));
135
+ return this._sendRequest(Object.assign(Object.assign({}, args), { retries: retries, attempt: currentAttempt + 1 }));
143
136
  }
144
137
  });
145
138
  }
146
139
  _getPopulatedURL(args) {
147
- var _a;
148
140
  return __awaiter(this, void 0, void 0, function* () {
149
- const url = (_a = args.fallbackUrl) !== null && _a !== void 0 ? _a : args.url;
150
141
  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]: SessionID_1.SessionID.get(args.sdkKey) }, args.params);
151
142
  const query = Object.keys(params)
152
143
  .map((key) => {
153
144
  return `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
154
145
  })
155
146
  .join('&');
156
- return `${url}${query ? `?${query}` : ''}`;
147
+ return `${args.url}${query ? `?${query}` : ''}`;
157
148
  });
158
149
  }
159
- _getPopulatedBody(args, data) {
150
+ _getPopulatedBody(args) {
160
151
  return __awaiter(this, void 0, void 0, function* () {
161
- const { sdkKey, fallbackUrl } = args;
152
+ const { data, sdkKey } = args;
162
153
  const stableID = StableID_1.StableID.get(sdkKey);
163
154
  const sessionID = SessionID_1.SessionID.get(sdkKey);
164
155
  const sdkType = SDKType_1.SDKType._get(sdkKey);
165
156
  return JSON.stringify(Object.assign(Object.assign({}, data), { statsigMetadata: Object.assign(Object.assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID,
166
157
  sessionID,
167
- sdkType,
168
- fallbackUrl }) }));
158
+ sdkType }) }));
169
159
  });
170
160
  }
171
161
  _attemptToEncodeString(args, input) {
172
162
  var _a, _b;
173
163
  const win = (0, SafeJs_1._getWindowSafe)();
174
- if (this._options.disableStatsigEncoding ||
164
+ if (!args.isStatsigEncodable ||
165
+ this._options.disableStatsigEncoding ||
175
166
  (0, __StatsigGlobal_1._getStatsigGlobalFlag)('no-encode') != null ||
176
167
  !(win === null || win === void 0 ? void 0 : win.btoa)) {
177
168
  return input;
@@ -186,11 +177,6 @@ class NetworkCore {
186
177
  return input;
187
178
  }
188
179
  }
189
- _getInternalRequestArgs(method, args) {
190
- const fallbackUrl = this._fallbackResolver.getFallbackUrl(args.sdkKey, args.url);
191
- return Object.assign(Object.assign({}, args), { method,
192
- fallbackUrl });
193
- }
194
180
  }
195
181
  exports.NetworkCore = NetworkCore;
196
182
  const _ensureValidSdkKey = (args) => {
@@ -213,23 +199,3 @@ function _getErrorMessage(controller, error) {
213
199
  }
214
200
  return 'Unknown Error';
215
201
  }
216
- function _didTimeout(controller) {
217
- const timeout = (controller === null || controller === void 0 ? void 0 : controller.signal.aborted) &&
218
- typeof controller.signal.reason === 'string' &&
219
- controller.signal.reason.includes('Timeout');
220
- return timeout || false;
221
- }
222
- function _tryMarkInitStart(args, attempt) {
223
- if (!args.isInitialize) {
224
- return;
225
- }
226
- Diagnostics_1.Diagnostics._markInitNetworkReqStart(args.sdkKey, {
227
- attempt,
228
- });
229
- }
230
- function _tryMarkInitEnd(args, response, attempt, body, err) {
231
- if (!args.isInitialize) {
232
- return;
233
- }
234
- Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, attempt, body, err));
235
- }
@@ -36,9 +36,9 @@ class StatsigClientBase {
36
36
  this._logger = new EventLogger_1.EventLogger(sdkKey, emitter, network, options);
37
37
  this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey, options, emitter);
38
38
  this._errorBoundary.wrap(this);
39
+ this._errorBoundary.wrap(network);
39
40
  this._errorBoundary.wrap(adapter);
40
41
  this._errorBoundary.wrap(this._logger);
41
- network.setErrorBoundary(this._errorBoundary);
42
42
  this.dataAdapter = adapter;
43
43
  this.dataAdapter.attach(sdkKey, options);
44
44
  this.storageProvider = StorageProvider_1.Storage;
@@ -157,7 +157,9 @@ function _assignGlobalInstance(sdkKey, client) {
157
157
  Log_1.Log.warn('Creating multiple Statsig clients with the same SDK key can lead to unexpected behavior. Multi-instance support requires different SDK keys.');
158
158
  }
159
159
  instances[sdkKey] = inst;
160
- statsigGlobal.lastInstance = inst;
160
+ if (!statsigGlobal.firstInstance) {
161
+ statsigGlobal.firstInstance = inst;
162
+ }
161
163
  statsigGlobal.instances = instances;
162
164
  __STATSIG__ = statsigGlobal;
163
165
  }
@@ -6,16 +6,21 @@ export declare const ErrorTag: {
6
6
  readonly NetworkError: "NetworkError";
7
7
  };
8
8
  export type ErrorTag = (typeof ErrorTag)[keyof typeof ErrorTag];
9
+ type ErrorEventData = {
10
+ error: unknown;
11
+ tag: ErrorTag | string;
12
+ } | {
13
+ error: unknown;
14
+ tag: 'NetworkError';
15
+ requestArgs: Record<string, unknown>;
16
+ };
9
17
  type EventNameToEventDataMap = {
10
18
  values_updated: {
11
19
  status: StatsigLoadingStatus;
12
20
  values: DataAdapterResult | null;
13
21
  };
14
22
  session_expired: object;
15
- error: {
16
- error: unknown;
17
- tag: ErrorTag | string;
18
- };
23
+ error: ErrorEventData;
19
24
  logs_flushed: {
20
25
  events: Record<string, unknown>[];
21
26
  };
@@ -39,12 +44,16 @@ type EventNameToEventDataMap = {
39
44
  *
40
45
  * `values_updated` - When the Statsig clients internal values change as the result of an initialize/update operation.
41
46
  *
47
+ * `session_expired` - When the current session has expired.
48
+ *
42
49
  * `error` - When an unexpected error occurs within the Statsig client.
43
50
  *
44
51
  * `logs_flushed` - When queued StatsigEvents are flushed to Statsig servers.
45
52
  *
46
53
  * `pre_shutdown` - Fired just before the SDK is shutdown
47
54
  *
55
+ * `initialization_failure` - Fired when the client fails to initialize.
56
+ *
48
57
  * `gate_evaluation` - Fired when any gate is checked from the Statsig client.
49
58
  *
50
59
  * `dynamic_config_evaluation` - Fired when any dyanamic config is checked from the Statsig client.
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "3.3.0-beta.2";
1
+ export declare const SDK_VERSION = "3.3.0";
2
2
  export type StatsigMetadata = {
3
3
  readonly [key: string]: string | undefined;
4
4
  readonly appVersion?: string;
@@ -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.3.0-beta.2';
4
+ exports.SDK_VERSION = '3.3.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
@@ -1,20 +0,0 @@
1
- import { ErrorBoundary } from './ErrorBoundary';
2
- import { AnyStatsigOptions } from './StatsigOptionsCommon';
3
- export type FallbackResolverArgs = {
4
- fallbackUrl: string | null;
5
- };
6
- export declare class NetworkFallbackResolver {
7
- private _fallbackInfo;
8
- private _errorBoundary;
9
- private _networkOverrideFunc?;
10
- private _cooldowns;
11
- constructor(options: AnyStatsigOptions);
12
- setErrorBoundary(errorBoundary: ErrorBoundary): void;
13
- tryBumpExpiryTime(sdkKey: string, url: string): void;
14
- getFallbackUrl(sdkKey: string, url: string): string | null;
15
- tryFetchUpdatedFallbackInfo(sdkKey: string, url: string, errorMessage: string | null, timedOut: boolean): Promise<boolean>;
16
- private _updateFallbackInfoWithNewUrl;
17
- private _fetchFallbackUrl;
18
- }
19
- export declare function _isDefaultUrl(url: string): boolean;
20
- export declare function _isDomainFailure(errorMsg: string | null, timedOut: boolean): boolean;
@@ -1,208 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports._isDomainFailure = exports._isDefaultUrl = exports.NetworkFallbackResolver = void 0;
13
- const DnsTxtQuery_1 = require("./DnsTxtQuery");
14
- const Hashing_1 = require("./Hashing");
15
- const Log_1 = require("./Log");
16
- const NetworkConfig_1 = require("./NetworkConfig");
17
- const StorageProvider_1 = require("./StorageProvider");
18
- const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
19
- const COOLDOWN_TIME_MS = 4 * 60 * 60 * 1000; // 4 hours
20
- class NetworkFallbackResolver {
21
- constructor(options) {
22
- var _a;
23
- this._fallbackInfo = null;
24
- this._errorBoundary = null;
25
- this._cooldowns = {};
26
- this._networkOverrideFunc = (_a = options.networkConfig) === null || _a === void 0 ? void 0 : _a.networkOverrideFunc;
27
- }
28
- setErrorBoundary(errorBoundary) {
29
- this._errorBoundary = errorBoundary;
30
- }
31
- tryBumpExpiryTime(sdkKey, url) {
32
- var _a;
33
- const domainKey = _getDomainKeyFromEndpoint(url);
34
- if (!domainKey) {
35
- return;
36
- }
37
- const info = (_a = this._fallbackInfo) === null || _a === void 0 ? void 0 : _a[domainKey];
38
- if (!info) {
39
- return;
40
- }
41
- info.expiryTime = Date.now() + DEFAULT_TTL_MS;
42
- _tryWriteFallbackInfoToCache(sdkKey, Object.assign(Object.assign({}, this._fallbackInfo), { [domainKey]: info }));
43
- }
44
- getFallbackUrl(sdkKey, url) {
45
- var _a, _b;
46
- const domainKey = _getDomainKeyFromEndpoint(url);
47
- if (!_isDefaultUrl(url) || !domainKey) {
48
- return null;
49
- }
50
- let info = this._fallbackInfo;
51
- if (info == null) {
52
- info = (_a = _readFallbackInfoFromCache(sdkKey)) !== null && _a !== void 0 ? _a : {};
53
- this._fallbackInfo = info;
54
- }
55
- const entry = info[domainKey];
56
- if (!entry || Date.now() > ((_b = entry.expiryTime) !== null && _b !== void 0 ? _b : 0)) {
57
- delete info[domainKey];
58
- this._fallbackInfo = info;
59
- _tryWriteFallbackInfoToCache(sdkKey, this._fallbackInfo);
60
- return null;
61
- }
62
- const endpoint = _extractEndpointForUrl(url);
63
- if (entry.url) {
64
- return `https://${entry.url}/${endpoint}`;
65
- }
66
- return null;
67
- }
68
- tryFetchUpdatedFallbackInfo(sdkKey, url, errorMessage, timedOut) {
69
- var _a;
70
- return __awaiter(this, void 0, void 0, function* () {
71
- try {
72
- const domainKey = _getDomainKeyFromEndpoint(url);
73
- if (!_isDomainFailure(errorMessage, timedOut) || !domainKey) {
74
- return false;
75
- }
76
- if (this._cooldowns[domainKey] &&
77
- Date.now() < this._cooldowns[domainKey]) {
78
- return false;
79
- }
80
- this._cooldowns[domainKey] = Date.now() + COOLDOWN_TIME_MS;
81
- const newUrl = yield this._fetchFallbackUrl(domainKey);
82
- if (!newUrl) {
83
- return false;
84
- }
85
- this._updateFallbackInfoWithNewUrl(sdkKey, domainKey, newUrl);
86
- return true;
87
- }
88
- catch (error) {
89
- (_a = this._errorBoundary) === null || _a === void 0 ? void 0 : _a.logError('tryFetchUpdatedFallbackInfo', error);
90
- return false;
91
- }
92
- });
93
- }
94
- _updateFallbackInfoWithNewUrl(sdkKey, domainKey, newUrl) {
95
- var _a, _b, _c;
96
- const newFallbackInfo = {
97
- url: newUrl,
98
- expiryTime: Date.now() + DEFAULT_TTL_MS,
99
- previous: [],
100
- };
101
- const previousInfo = (_a = this._fallbackInfo) === null || _a === void 0 ? void 0 : _a[domainKey];
102
- if (previousInfo) {
103
- newFallbackInfo.previous.push(...previousInfo.previous);
104
- }
105
- if (newFallbackInfo.previous.length > 10) {
106
- newFallbackInfo.previous = [];
107
- }
108
- const previousUrl = (_c = (_b = this._fallbackInfo) === null || _b === void 0 ? void 0 : _b[domainKey]) === null || _c === void 0 ? void 0 : _c.url;
109
- if (previousUrl != null) {
110
- newFallbackInfo.previous.push(previousUrl);
111
- }
112
- this._fallbackInfo = Object.assign(Object.assign({}, this._fallbackInfo), { [domainKey]: newFallbackInfo });
113
- _tryWriteFallbackInfoToCache(sdkKey, this._fallbackInfo);
114
- }
115
- _fetchFallbackUrl(domainKey) {
116
- var _a, _b, _c, _d, _e, _f;
117
- return __awaiter(this, void 0, void 0, function* () {
118
- const records = yield (0, DnsTxtQuery_1._fetchTxtRecords)((_a = this._networkOverrideFunc) !== null && _a !== void 0 ? _a : fetch);
119
- if (records.length === 0) {
120
- return null;
121
- }
122
- const seen = new Set((_d = (_c = (_b = this._fallbackInfo) === null || _b === void 0 ? void 0 : _b[domainKey]) === null || _c === void 0 ? void 0 : _c.previous) !== null && _d !== void 0 ? _d : []);
123
- const currentUrl = (_f = (_e = this._fallbackInfo) === null || _e === void 0 ? void 0 : _e[domainKey]) === null || _f === void 0 ? void 0 : _f.url;
124
- let found = null;
125
- for (const record of records) {
126
- const [recordKey, recordUrl] = record.split('=');
127
- if (!recordUrl || recordKey !== domainKey) {
128
- continue;
129
- }
130
- let url = recordUrl;
131
- if (recordUrl.endsWith('/')) {
132
- url = recordUrl.slice(0, -1);
133
- }
134
- if (!seen.has(recordUrl) && url !== currentUrl) {
135
- found = url;
136
- break;
137
- }
138
- }
139
- return found;
140
- });
141
- }
142
- }
143
- exports.NetworkFallbackResolver = NetworkFallbackResolver;
144
- function _isDefaultUrl(url) {
145
- for (const key in NetworkConfig_1.NetworkDefault) {
146
- if (url.startsWith(NetworkConfig_1.NetworkDefault[key])) {
147
- return true;
148
- }
149
- }
150
- return false;
151
- }
152
- exports._isDefaultUrl = _isDefaultUrl;
153
- function _isDomainFailure(errorMsg, timedOut) {
154
- var _a;
155
- const lowerErrorMsg = (_a = errorMsg === null || errorMsg === void 0 ? void 0 : errorMsg.toLowerCase()) !== null && _a !== void 0 ? _a : '';
156
- return (timedOut ||
157
- lowerErrorMsg.includes('uncaught exception') ||
158
- lowerErrorMsg.includes('failed to fetch') ||
159
- lowerErrorMsg.includes('networkerror when attempting to fetch resource'));
160
- }
161
- exports._isDomainFailure = _isDomainFailure;
162
- function _getFallbackInfoStorageKey(sdkKey) {
163
- return `statsig.network_fallback.${(0, Hashing_1._DJB2)(sdkKey)}`;
164
- }
165
- function _tryWriteFallbackInfoToCache(sdkKey, info) {
166
- const hashKey = _getFallbackInfoStorageKey(sdkKey);
167
- if (!info || Object.keys(info).length === 0) {
168
- StorageProvider_1.Storage.removeItem(hashKey);
169
- return;
170
- }
171
- StorageProvider_1.Storage.setItem(hashKey, JSON.stringify(info));
172
- }
173
- function _readFallbackInfoFromCache(sdkKey) {
174
- const hashKey = _getFallbackInfoStorageKey(sdkKey);
175
- const data = StorageProvider_1.Storage.getItem(hashKey);
176
- if (!data) {
177
- return null;
178
- }
179
- try {
180
- return JSON.parse(data);
181
- }
182
- catch (_a) {
183
- Log_1.Log.error('Failed to parse FallbackInfo');
184
- return null;
185
- }
186
- }
187
- function _extractEndpointForUrl(urlString) {
188
- try {
189
- const url = new URL(urlString);
190
- const endpoint = url.pathname.substring(1);
191
- return endpoint;
192
- }
193
- catch (error) {
194
- return '';
195
- }
196
- }
197
- function _getDomainKeyFromEndpoint(endpoint) {
198
- if (endpoint.includes('initialize')) {
199
- return 'i';
200
- }
201
- if (endpoint.includes('rgstr')) {
202
- return 'e';
203
- }
204
- if (endpoint.includes('download_config_specs')) {
205
- return 'd';
206
- }
207
- return null;
208
- }