@statsig/client-core 3.3.0 → 3.4.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/client-core",
3
- "version": "3.3.0",
3
+ "version": "3.4.0-beta.1",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1,4 +1,5 @@
1
1
  import './$_StatsigGlobal';
2
+ import { ErrorBoundary } from './ErrorBoundary';
2
3
  import { NetworkPriority } from './NetworkConfig';
3
4
  import { StatsigClientEmitEventFunc } from './StatsigClientBase';
4
5
  import { AnyStatsigOptions } from './StatsigOptionsCommon';
@@ -28,14 +29,18 @@ export declare class NetworkCore {
28
29
  private readonly _timeout;
29
30
  private readonly _netConfig;
30
31
  private readonly _options;
32
+ private readonly _fallbackResolver;
33
+ private _errorBoundary;
31
34
  constructor(options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
32
- post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
33
- get(args: RequestArgs): Promise<NetworkResponse | null>;
35
+ setErrorBoundary(errorBoundary: ErrorBoundary): void;
34
36
  isBeaconSupported(): boolean;
35
37
  beacon(args: BeaconRequestArgs): Promise<boolean>;
38
+ post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
39
+ get(args: RequestArgs): Promise<NetworkResponse | null>;
36
40
  private _sendRequest;
37
41
  private _getPopulatedURL;
38
42
  private _getPopulatedBody;
39
43
  private _attemptToEncodeString;
44
+ private _getInternalRequestArgs;
40
45
  }
41
46
  export {};
@@ -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 NetworkFallbackResolver_1 = require("./NetworkFallbackResolver");
18
19
  const SDKType_1 = require("./SDKType");
19
20
  const SafeJs_1 = require("./SafeJs");
20
21
  const SessionID_1 = require("./SessionID");
@@ -30,6 +31,7 @@ class NetworkCore {
30
31
  this._timeout = DEFAULT_TIMEOUT_MS;
31
32
  this._netConfig = {};
32
33
  this._options = {};
34
+ this._errorBoundary = null;
33
35
  if (options) {
34
36
  this._options = options;
35
37
  }
@@ -39,18 +41,13 @@ class NetworkCore {
39
41
  if (this._netConfig.networkTimeoutMs) {
40
42
  this._timeout = this._netConfig.networkTimeoutMs;
41
43
  }
44
+ this._fallbackResolver = new NetworkFallbackResolver_1.NetworkFallbackResolver(this._options);
42
45
  }
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));
46
+ setErrorBoundary(errorBoundary) {
47
+ this._errorBoundary = errorBoundary;
48
+ this._errorBoundary.wrap(this);
49
+ this._errorBoundary.wrap(this._fallbackResolver);
50
+ this._fallbackResolver.setErrorBoundary(errorBoundary);
54
51
  }
55
52
  isBeaconSupported() {
56
53
  return (typeof navigator !== 'undefined' &&
@@ -61,12 +58,27 @@ class NetworkCore {
61
58
  if (!_ensureValidSdkKey(args)) {
62
59
  return false;
63
60
  }
64
- const body = yield this._getPopulatedBody(args);
65
- const url = yield this._getPopulatedURL(args);
61
+ const argsInternal = this._getInternalRequestArgs('POST', args);
62
+ const body = yield this._getPopulatedBody(argsInternal, args.data);
63
+ const url = yield this._getPopulatedURL(argsInternal);
66
64
  const nav = navigator;
67
65
  return nav.sendBeacon.bind(nav)(url, body);
68
66
  });
69
67
  }
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
+ }
70
82
  _sendRequest(args) {
71
83
  var _a, _b, _c;
72
84
  return __awaiter(this, void 0, void 0, function* () {
@@ -78,9 +90,11 @@ class NetworkCore {
78
90
  }
79
91
  const { method, body, retries, attempt } = args;
80
92
  const currentAttempt = attempt !== null && attempt !== void 0 ? attempt : 1;
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);
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);
84
98
  let response = null;
85
99
  const keepalive = (0, VisibilityObserving_1._isUnloading)();
86
100
  try {
@@ -88,37 +102,35 @@ class NetworkCore {
88
102
  method,
89
103
  body,
90
104
  headers: Object.assign({}, args.headers),
91
- signal: controller === null || controller === void 0 ? void 0 : controller.signal,
105
+ signal: abortController === null || abortController === void 0 ? void 0 : abortController.signal,
92
106
  priority: args.priority,
93
107
  keepalive,
94
108
  };
95
- if (args.isInitialize) {
96
- Diagnostics_1.Diagnostics._markInitNetworkReqStart(args.sdkKey, {
97
- attempt: currentAttempt,
98
- });
99
- }
109
+ _tryMarkInitStart(args, currentAttempt);
100
110
  const func = (_a = this._netConfig.networkOverrideFunc) !== null && _a !== void 0 ? _a : fetch;
101
- response = yield func(url, config);
102
- clearTimeout(handle);
111
+ response = yield func(populatedUrl, config);
112
+ clearTimeout(timeoutHandle);
103
113
  if (!response.ok) {
104
114
  const text = yield response.text().catch(() => 'No Text');
105
- const err = new Error(`NetworkError: ${url} ${text}`);
115
+ const err = new Error(`NetworkError: ${populatedUrl} ${text}`);
106
116
  err.name = 'NetworkError';
107
117
  throw err;
108
118
  }
109
119
  const text = yield response.text();
110
- if (args.isInitialize) {
111
- Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, currentAttempt, text));
112
- }
120
+ _tryMarkInitEnd(args, response, currentAttempt, text);
121
+ this._fallbackResolver.tryBumpExpiryTime(args.sdkKey, populatedUrl);
113
122
  return {
114
123
  body: text,
115
124
  code: response.status,
116
125
  };
117
126
  }
118
127
  catch (error) {
119
- const errorMessage = _getErrorMessage(controller, error);
120
- if (args.isInitialize) {
121
- Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, currentAttempt, '', 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);
122
134
  }
123
135
  if (!retries ||
124
136
  currentAttempt > retries ||
@@ -129,40 +141,42 @@ class NetworkCore {
129
141
  tag: StatsigClientEventEmitter_1.ErrorTag.NetworkError,
130
142
  requestArgs: args,
131
143
  });
132
- Log_1.Log.error(`A networking error occured during ${method} request to ${url}.`, errorMessage, error);
144
+ Log_1.Log.error(`A networking error occured during ${method} request to ${populatedUrl}.`, errorMessage, error);
133
145
  return null;
134
146
  }
135
- return this._sendRequest(Object.assign(Object.assign({}, args), { retries: retries, attempt: currentAttempt + 1 }));
147
+ return this._sendRequest(Object.assign(Object.assign({}, args), { retries, attempt: currentAttempt + 1 }));
136
148
  }
137
149
  });
138
150
  }
139
151
  _getPopulatedURL(args) {
152
+ var _a;
140
153
  return __awaiter(this, void 0, void 0, function* () {
154
+ const url = (_a = args.fallbackUrl) !== null && _a !== void 0 ? _a : args.url;
141
155
  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);
142
156
  const query = Object.keys(params)
143
157
  .map((key) => {
144
158
  return `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
145
159
  })
146
160
  .join('&');
147
- return `${args.url}${query ? `?${query}` : ''}`;
161
+ return `${url}${query ? `?${query}` : ''}`;
148
162
  });
149
163
  }
150
- _getPopulatedBody(args) {
164
+ _getPopulatedBody(args, data) {
151
165
  return __awaiter(this, void 0, void 0, function* () {
152
- const { data, sdkKey } = args;
166
+ const { sdkKey, fallbackUrl } = args;
153
167
  const stableID = StableID_1.StableID.get(sdkKey);
154
168
  const sessionID = SessionID_1.SessionID.get(sdkKey);
155
169
  const sdkType = SDKType_1.SDKType._get(sdkKey);
156
170
  return JSON.stringify(Object.assign(Object.assign({}, data), { statsigMetadata: Object.assign(Object.assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID,
157
171
  sessionID,
158
- sdkType }) }));
172
+ sdkType,
173
+ fallbackUrl }) }));
159
174
  });
160
175
  }
161
176
  _attemptToEncodeString(args, input) {
162
177
  var _a, _b;
163
178
  const win = (0, SafeJs_1._getWindowSafe)();
164
- if (!args.isStatsigEncodable ||
165
- this._options.disableStatsigEncoding ||
179
+ if (this._options.disableStatsigEncoding ||
166
180
  (0, __StatsigGlobal_1._getStatsigGlobalFlag)('no-encode') != null ||
167
181
  !(win === null || win === void 0 ? void 0 : win.btoa)) {
168
182
  return input;
@@ -177,6 +191,11 @@ class NetworkCore {
177
191
  return input;
178
192
  }
179
193
  }
194
+ _getInternalRequestArgs(method, args) {
195
+ const fallbackUrl = this._fallbackResolver.getFallbackUrl(args.sdkKey, args.url);
196
+ return Object.assign(Object.assign({}, args), { method,
197
+ fallbackUrl });
198
+ }
180
199
  }
181
200
  exports.NetworkCore = NetworkCore;
182
201
  const _ensureValidSdkKey = (args) => {
@@ -199,3 +218,23 @@ function _getErrorMessage(controller, error) {
199
218
  }
200
219
  return 'Unknown Error';
201
220
  }
221
+ function _didTimeout(controller) {
222
+ const timeout = (controller === null || controller === void 0 ? void 0 : controller.signal.aborted) &&
223
+ typeof controller.signal.reason === 'string' &&
224
+ controller.signal.reason.includes('Timeout');
225
+ return timeout || false;
226
+ }
227
+ function _tryMarkInitStart(args, attempt) {
228
+ if (!args.isInitialize) {
229
+ return;
230
+ }
231
+ Diagnostics_1.Diagnostics._markInitNetworkReqStart(args.sdkKey, {
232
+ attempt,
233
+ });
234
+ }
235
+ function _tryMarkInitEnd(args, response, attempt, body, err) {
236
+ if (!args.isInitialize) {
237
+ return;
238
+ }
239
+ Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, attempt, body, err));
240
+ }
@@ -0,0 +1,20 @@
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;
@@ -0,0 +1,208 @@
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
+ }
@@ -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);
40
39
  this._errorBoundary.wrap(adapter);
41
40
  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;
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "3.3.0";
1
+ export declare const SDK_VERSION = "3.4.0-beta.1";
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';
4
+ exports.SDK_VERSION = '3.4.0-beta.1';
5
5
  let metadata = {
6
6
  sdkVersion: exports.SDK_VERSION,
7
7
  sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
@@ -5,4 +5,4 @@ export declare function _makeDynamicConfig(name: string, details: EvaluationDeta
5
5
  export declare function _makeExperiment(name: string, details: EvaluationDetails, evaluation: DynamicConfigEvaluation | null): Experiment;
6
6
  export declare function _makeLayer(name: string, details: EvaluationDetails, evaluation: LayerEvaluation | null, exposeFunc?: (param: string) => void): Layer;
7
7
  export declare function _mergeOverride<T extends AnyConfigBasedStatsigType>(original: T, overridden: T | null | undefined, value: Record<string, unknown>, exposeFunc?: (param: string) => void): T;
8
- export declare function _makeTypedGet(value: Record<string, unknown> | undefined, exposeFunc?: (param: string) => void): TypedGet;
8
+ export declare function _makeTypedGet(name: string, value: Record<string, unknown> | undefined, exposeFunc?: (param: string) => void): TypedGet;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports._makeTypedGet = exports._mergeOverride = exports._makeLayer = exports._makeExperiment = exports._makeDynamicConfig = exports._makeFeatureGate = void 0;
4
+ const Log_1 = require("./Log");
4
5
  const TypingUtils_1 = require("./TypingUtils");
5
6
  const DEFAULT_RULE = 'default';
6
7
  function _makeEvaluation(name, details, evaluation, value) {
@@ -20,7 +21,7 @@ exports._makeFeatureGate = _makeFeatureGate;
20
21
  function _makeDynamicConfig(name, details, evaluation) {
21
22
  var _a;
22
23
  const value = (_a = evaluation === null || evaluation === void 0 ? void 0 : evaluation.value) !== null && _a !== void 0 ? _a : {};
23
- return Object.assign(Object.assign({}, _makeEvaluation(name, details, evaluation, value)), { get: _makeTypedGet(evaluation === null || evaluation === void 0 ? void 0 : evaluation.value) });
24
+ return Object.assign(Object.assign({}, _makeEvaluation(name, details, evaluation, value)), { get: _makeTypedGet(name, evaluation === null || evaluation === void 0 ? void 0 : evaluation.value) });
24
25
  }
25
26
  exports._makeDynamicConfig = _makeDynamicConfig;
26
27
  function _makeExperiment(name, details, evaluation) {
@@ -31,14 +32,14 @@ function _makeExperiment(name, details, evaluation) {
31
32
  exports._makeExperiment = _makeExperiment;
32
33
  function _makeLayer(name, details, evaluation, exposeFunc) {
33
34
  var _a, _b;
34
- return Object.assign(Object.assign({}, _makeEvaluation(name, details, evaluation, undefined)), { get: _makeTypedGet(evaluation === null || evaluation === void 0 ? void 0 : evaluation.value, exposeFunc), groupName: (_a = evaluation === null || evaluation === void 0 ? void 0 : evaluation.group_name) !== null && _a !== void 0 ? _a : null, __value: (_b = evaluation === null || evaluation === void 0 ? void 0 : evaluation.value) !== null && _b !== void 0 ? _b : {} });
35
+ return Object.assign(Object.assign({}, _makeEvaluation(name, details, evaluation, undefined)), { get: _makeTypedGet(name, evaluation === null || evaluation === void 0 ? void 0 : evaluation.value, exposeFunc), groupName: (_a = evaluation === null || evaluation === void 0 ? void 0 : evaluation.group_name) !== null && _a !== void 0 ? _a : null, __value: (_b = evaluation === null || evaluation === void 0 ? void 0 : evaluation.value) !== null && _b !== void 0 ? _b : {} });
35
36
  }
36
37
  exports._makeLayer = _makeLayer;
37
38
  function _mergeOverride(original, overridden, value, exposeFunc) {
38
- return Object.assign(Object.assign(Object.assign({}, original), overridden), { get: _makeTypedGet(value, exposeFunc) });
39
+ return Object.assign(Object.assign(Object.assign({}, original), overridden), { get: _makeTypedGet(original.name, value, exposeFunc) });
39
40
  }
40
41
  exports._mergeOverride = _mergeOverride;
41
- function _makeTypedGet(value, exposeFunc) {
42
+ function _makeTypedGet(name, value, exposeFunc) {
42
43
  return (param, fallback) => {
43
44
  var _a;
44
45
  const found = (_a = value === null || value === void 0 ? void 0 : value[param]) !== null && _a !== void 0 ? _a : null;
@@ -46,6 +47,7 @@ function _makeTypedGet(value, exposeFunc) {
46
47
  return (fallback !== null && fallback !== void 0 ? fallback : null);
47
48
  }
48
49
  if (fallback != null && !(0, TypingUtils_1._isTypeMatch)(found, fallback)) {
50
+ Log_1.Log.warn(`Parameter type mismatch. '${name}.${param}' was found to be type '${typeof found}' but fallback/return type is '${typeof fallback}'. See https://docs.statsig.com/client/javascript-sdk/#typed-getters`);
49
51
  return (fallback !== null && fallback !== void 0 ? fallback : null);
50
52
  }
51
53
  exposeFunc === null || exposeFunc === void 0 ? void 0 : exposeFunc(param);
package/src/index.d.ts CHANGED
@@ -16,8 +16,8 @@ export * from './EvaluationTypes';
16
16
  export * from './Hashing';
17
17
  export * from './InitializeResponse';
18
18
  export * from './Log';
19
- export * from './NetworkCore';
20
19
  export * from './NetworkConfig';
20
+ export * from './NetworkCore';
21
21
  export * from './OverrideAdapter';
22
22
  export * from './ParamStoreTypes';
23
23
  export * from './SafeJs';
@@ -30,10 +30,10 @@ export * from './StatsigDataAdapter';
30
30
  export * from './StatsigEvent';
31
31
  export * from './StatsigMetadata';
32
32
  export * from './StatsigOptionsCommon';
33
+ export * from './StatsigPlugin';
33
34
  export * from './StatsigTypeFactories';
34
35
  export * from './StatsigTypes';
35
36
  export * from './StatsigUser';
36
- export * from './StatsigPlugin';
37
37
  export * from './StorageProvider';
38
38
  export * from './TypedJsonParse';
39
39
  export * from './TypingUtils';
package/src/index.js CHANGED
@@ -38,8 +38,8 @@ __exportStar(require("./EvaluationTypes"), exports);
38
38
  __exportStar(require("./Hashing"), exports);
39
39
  __exportStar(require("./InitializeResponse"), exports);
40
40
  __exportStar(require("./Log"), exports);
41
- __exportStar(require("./NetworkCore"), exports);
42
41
  __exportStar(require("./NetworkConfig"), exports);
42
+ __exportStar(require("./NetworkCore"), exports);
43
43
  __exportStar(require("./OverrideAdapter"), exports);
44
44
  __exportStar(require("./ParamStoreTypes"), exports);
45
45
  __exportStar(require("./SafeJs"), exports);
@@ -52,10 +52,10 @@ __exportStar(require("./StatsigDataAdapter"), exports);
52
52
  __exportStar(require("./StatsigEvent"), exports);
53
53
  __exportStar(require("./StatsigMetadata"), exports);
54
54
  __exportStar(require("./StatsigOptionsCommon"), exports);
55
+ __exportStar(require("./StatsigPlugin"), exports);
55
56
  __exportStar(require("./StatsigTypeFactories"), exports);
56
57
  __exportStar(require("./StatsigTypes"), exports);
57
58
  __exportStar(require("./StatsigUser"), exports);
58
- __exportStar(require("./StatsigPlugin"), exports);
59
59
  __exportStar(require("./StorageProvider"), exports);
60
60
  __exportStar(require("./TypedJsonParse"), exports);
61
61
  __exportStar(require("./TypingUtils"), exports);