@statsig/client-core 3.4.0-beta.1 → 3.5.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.4.0-beta.1",
3
+ "version": "3.5.0",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -32,6 +32,7 @@ export type Spec = {
32
32
  hasSharedParams: boolean;
33
33
  isActive?: boolean;
34
34
  targetAppIDs?: string[];
35
+ version?: number;
35
36
  };
36
37
  export type DownloadConfigSpecsResponse = {
37
38
  feature_gates: Spec[];
@@ -7,6 +7,7 @@ type EvaluationBase<T> = {
7
7
  rule_id: string;
8
8
  secondary_exposures: SecondaryExposure[];
9
9
  value: T;
10
+ version?: string;
10
11
  };
11
12
  export type SecondaryExposure = {
12
13
  gate: string;
@@ -21,6 +22,7 @@ export type ExperimentEvaluation = Flatten<EvaluationBase<Record<string, unknown
21
22
  is_device_based: boolean;
22
23
  is_experiment_active?: boolean;
23
24
  is_user_in_experiment?: boolean;
25
+ passed?: boolean;
24
26
  }>;
25
27
  export type DynamicConfigEvaluation = ExperimentEvaluation;
26
28
  export type LayerEvaluation = Flatten<Omit<ExperimentEvaluation, 'id_type'> & {
@@ -15,7 +15,7 @@ export declare class EventLogger {
15
15
  private _hasRunQuickFlush;
16
16
  private _creationTime;
17
17
  private _isLoggingDisabled;
18
- private _logEventUrl;
18
+ private _logEventUrlConfig;
19
19
  private static _safeFlushAndForget;
20
20
  private static _safeRetryFailedLogs;
21
21
  constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null);
@@ -17,7 +17,7 @@ const NetworkConfig_1 = require("./NetworkConfig");
17
17
  const SafeJs_1 = require("./SafeJs");
18
18
  const StatsigEvent_1 = require("./StatsigEvent");
19
19
  const StorageProvider_1 = require("./StorageProvider");
20
- const UrlOverrides_1 = require("./UrlOverrides");
20
+ const UrlConfiguration_1 = require("./UrlConfiguration");
21
21
  const VisibilityObserving_1 = require("./VisibilityObserving");
22
22
  const DEFAULT_QUEUE_SIZE = 50;
23
23
  const DEFAULT_FLUSH_INTERVAL_MS = 10000;
@@ -55,7 +55,7 @@ class EventLogger {
55
55
  this._isLoggingDisabled = (_options === null || _options === void 0 ? void 0 : _options.disableLogging) === true;
56
56
  this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
57
57
  const config = _options === null || _options === void 0 ? void 0 : _options.networkConfig;
58
- this._logEventUrl = (0, UrlOverrides_1._getOverridableUrl)(config === null || config === void 0 ? void 0 : config.logEventUrl, config === null || config === void 0 ? void 0 : config.api, '/rgstr', NetworkConfig_1.NetworkDefault.eventsApi);
58
+ this._logEventUrlConfig = new UrlConfiguration_1.UrlConfiguration(NetworkConfig_1.Endpoint._rgstr, config === null || config === void 0 ? void 0 : config.logEventUrl, config === null || config === void 0 ? void 0 : config.api, config === null || config === void 0 ? void 0 : config.logEventFallbackUrls);
59
59
  }
60
60
  setLoggingDisabled(isDisabled) {
61
61
  this._isLoggingDisabled = isDisabled;
@@ -212,7 +212,7 @@ class EventLogger {
212
212
  data: {
213
213
  events,
214
214
  },
215
- url: this._logEventUrl,
215
+ urlConfig: this._logEventUrlConfig,
216
216
  retries: 3,
217
217
  isCompressable: true,
218
218
  params: {
@@ -1,7 +1,13 @@
1
+ export declare const Endpoint: {
2
+ readonly _initialize: "initialize";
3
+ readonly _rgstr: "rgstr";
4
+ readonly _download_config_specs: "download_config_specs";
5
+ };
6
+ export type Endpoint = (typeof Endpoint)[keyof typeof Endpoint];
1
7
  export declare const NetworkDefault: {
2
- eventsApi: "https://prodregistryv2.org/v1";
3
- initializeApi: "https://featureassets.org/v1";
4
- specsApi: "https://assetsconfigcdn.org/v1";
8
+ rgstr: "https://prodregistryv2.org/v1";
9
+ initialize: "https://featureassets.org/v1";
10
+ download_config_specs: "https://assetsconfigcdn.org/v1";
5
11
  };
6
12
  export type NetworkPriority = 'high' | 'low' | 'auto';
7
13
  export type NetworkArgs = RequestInit & {
@@ -1,10 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NetworkParam = exports.NetworkDefault = void 0;
3
+ exports.NetworkParam = exports.NetworkDefault = exports.Endpoint = void 0;
4
+ exports.Endpoint = {
5
+ _initialize: 'initialize',
6
+ _rgstr: 'rgstr',
7
+ _download_config_specs: 'download_config_specs',
8
+ };
4
9
  exports.NetworkDefault = {
5
- eventsApi: 'https://prodregistryv2.org/v1',
6
- initializeApi: 'https://featureassets.org/v1',
7
- specsApi: 'https://assetsconfigcdn.org/v1',
10
+ [exports.Endpoint._rgstr]: 'https://prodregistryv2.org/v1',
11
+ [exports.Endpoint._initialize]: 'https://featureassets.org/v1',
12
+ [exports.Endpoint._download_config_specs]: 'https://assetsconfigcdn.org/v1',
8
13
  };
9
14
  exports.NetworkParam = {
10
15
  EventCount: 'ec',
@@ -4,13 +4,13 @@ import { NetworkPriority } from './NetworkConfig';
4
4
  import { StatsigClientEmitEventFunc } from './StatsigClientBase';
5
5
  import { AnyStatsigOptions } from './StatsigOptionsCommon';
6
6
  import { Flatten } from './TypingUtils';
7
+ import { UrlConfiguration } from './UrlConfiguration';
7
8
  type RequestArgs = {
8
9
  sdkKey: string;
9
- url: string;
10
+ urlConfig: UrlConfiguration;
10
11
  priority?: NetworkPriority;
11
12
  retries?: number;
12
13
  attempt?: number;
13
- isInitialize?: boolean;
14
14
  params?: Record<string, string>;
15
15
  headers?: Record<string, string>;
16
16
  };
@@ -19,7 +19,7 @@ export type RequestArgsWithData = Flatten<RequestArgs & {
19
19
  isStatsigEncodable?: boolean;
20
20
  isCompressable?: boolean;
21
21
  }>;
22
- type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'url' | 'params' | 'isCompressable' | 'attempt'>;
22
+ type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'urlConfig' | 'params' | 'isCompressable' | 'attempt'>;
23
23
  type NetworkResponse = {
24
24
  body: string | null;
25
25
  code: number;
@@ -118,7 +118,7 @@ class NetworkCore {
118
118
  }
119
119
  const text = yield response.text();
120
120
  _tryMarkInitEnd(args, response, currentAttempt, text);
121
- this._fallbackResolver.tryBumpExpiryTime(args.sdkKey, populatedUrl);
121
+ this._fallbackResolver.tryBumpExpiryTime(args.sdkKey, args.urlConfig);
122
122
  return {
123
123
  body: text,
124
124
  code: response.status,
@@ -128,9 +128,9 @@ class NetworkCore {
128
128
  const errorMessage = _getErrorMessage(abortController, error);
129
129
  const timedOut = _didTimeout(abortController);
130
130
  _tryMarkInitEnd(args, response, currentAttempt, '', error);
131
- const fallbackUpdated = yield this._fallbackResolver.tryFetchUpdatedFallbackInfo(args.sdkKey, populatedUrl, errorMessage, timedOut);
131
+ const fallbackUpdated = yield this._fallbackResolver.tryFetchUpdatedFallbackInfo(args.sdkKey, args.urlConfig, errorMessage, timedOut);
132
132
  if (fallbackUpdated) {
133
- args.fallbackUrl = this._fallbackResolver.getFallbackUrl(args.sdkKey, args.url);
133
+ args.fallbackUrl = this._fallbackResolver.getActiveFallbackUrl(args.sdkKey, args.urlConfig);
134
134
  }
135
135
  if (!retries ||
136
136
  currentAttempt > retries ||
@@ -151,7 +151,7 @@ class NetworkCore {
151
151
  _getPopulatedURL(args) {
152
152
  var _a;
153
153
  return __awaiter(this, void 0, void 0, function* () {
154
- const url = (_a = args.fallbackUrl) !== null && _a !== void 0 ? _a : args.url;
154
+ const url = (_a = args.fallbackUrl) !== null && _a !== void 0 ? _a : args.urlConfig.getUrl();
155
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);
156
156
  const query = Object.keys(params)
157
157
  .map((key) => {
@@ -187,12 +187,12 @@ class NetworkCore {
187
187
  return result;
188
188
  }
189
189
  catch (_c) {
190
- Log_1.Log.warn('/initialize request encoding failed');
190
+ Log_1.Log.warn(`Request encoding failed for ${args.urlConfig.getUrl()}`);
191
191
  return input;
192
192
  }
193
193
  }
194
194
  _getInternalRequestArgs(method, args) {
195
- const fallbackUrl = this._fallbackResolver.getFallbackUrl(args.sdkKey, args.url);
195
+ const fallbackUrl = this._fallbackResolver.getActiveFallbackUrl(args.sdkKey, args.urlConfig);
196
196
  return Object.assign(Object.assign({}, args), { method,
197
197
  fallbackUrl });
198
198
  }
@@ -225,7 +225,7 @@ function _didTimeout(controller) {
225
225
  return timeout || false;
226
226
  }
227
227
  function _tryMarkInitStart(args, attempt) {
228
- if (!args.isInitialize) {
228
+ if (args.urlConfig.endpoint !== NetworkConfig_1.Endpoint._initialize) {
229
229
  return;
230
230
  }
231
231
  Diagnostics_1.Diagnostics._markInitNetworkReqStart(args.sdkKey, {
@@ -233,7 +233,7 @@ function _tryMarkInitStart(args, attempt) {
233
233
  });
234
234
  }
235
235
  function _tryMarkInitEnd(args, response, attempt, body, err) {
236
- if (!args.isInitialize) {
236
+ if (args.urlConfig.endpoint !== NetworkConfig_1.Endpoint._initialize) {
237
237
  return;
238
238
  }
239
239
  Diagnostics_1.Diagnostics._markInitNetworkReqEnd(args.sdkKey, Diagnostics_1.Diagnostics._getDiagnosticsData(response, attempt, body, err));
@@ -1,5 +1,6 @@
1
1
  import { ErrorBoundary } from './ErrorBoundary';
2
2
  import { AnyStatsigOptions } from './StatsigOptionsCommon';
3
+ import { UrlConfiguration } from './UrlConfiguration';
3
4
  export type FallbackResolverArgs = {
4
5
  fallbackUrl: string | null;
5
6
  };
@@ -7,14 +8,15 @@ export declare class NetworkFallbackResolver {
7
8
  private _fallbackInfo;
8
9
  private _errorBoundary;
9
10
  private _networkOverrideFunc?;
10
- private _cooldowns;
11
+ private _dnsQueryCooldowns;
11
12
  constructor(options: AnyStatsigOptions);
12
13
  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>;
14
+ tryBumpExpiryTime(sdkKey: string, urlConfig: UrlConfiguration): void;
15
+ getActiveFallbackUrl(sdkKey: string, urlConfig: UrlConfiguration): string | null;
16
+ getFallbackFromProvided(url: string): string | null;
17
+ tryFetchUpdatedFallbackInfo(sdkKey: string, urlConfig: UrlConfiguration, errorMessage: string | null, timedOut: boolean): Promise<boolean>;
16
18
  private _updateFallbackInfoWithNewUrl;
17
- private _fetchFallbackUrl;
19
+ private _tryFetchFallbackUrlsFromNetwork;
20
+ private _pickNewFallbackUrl;
18
21
  }
19
- export declare function _isDefaultUrl(url: string): boolean;
20
22
  export declare function _isDomainFailure(errorMsg: string | null, timedOut: boolean): boolean;
@@ -9,11 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports._isDomainFailure = exports._isDefaultUrl = exports.NetworkFallbackResolver = void 0;
12
+ exports._isDomainFailure = exports.NetworkFallbackResolver = void 0;
13
13
  const DnsTxtQuery_1 = require("./DnsTxtQuery");
14
14
  const Hashing_1 = require("./Hashing");
15
15
  const Log_1 = require("./Log");
16
- const NetworkConfig_1 = require("./NetworkConfig");
17
16
  const StorageProvider_1 = require("./StorageProvider");
18
17
  const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
19
18
  const COOLDOWN_TIME_MS = 4 * 60 * 60 * 1000; // 4 hours
@@ -22,134 +21,138 @@ class NetworkFallbackResolver {
22
21
  var _a;
23
22
  this._fallbackInfo = null;
24
23
  this._errorBoundary = null;
25
- this._cooldowns = {};
24
+ this._dnsQueryCooldowns = {};
26
25
  this._networkOverrideFunc = (_a = options.networkConfig) === null || _a === void 0 ? void 0 : _a.networkOverrideFunc;
27
26
  }
28
27
  setErrorBoundary(errorBoundary) {
29
28
  this._errorBoundary = errorBoundary;
30
29
  }
31
- tryBumpExpiryTime(sdkKey, url) {
30
+ tryBumpExpiryTime(sdkKey, urlConfig) {
32
31
  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];
32
+ const info = (_a = this._fallbackInfo) === null || _a === void 0 ? void 0 : _a[urlConfig.endpoint];
38
33
  if (!info) {
39
34
  return;
40
35
  }
41
36
  info.expiryTime = Date.now() + DEFAULT_TTL_MS;
42
- _tryWriteFallbackInfoToCache(sdkKey, Object.assign(Object.assign({}, this._fallbackInfo), { [domainKey]: info }));
37
+ _tryWriteFallbackInfoToCache(sdkKey, Object.assign(Object.assign({}, this._fallbackInfo), { [urlConfig.endpoint]: info }));
43
38
  }
44
- getFallbackUrl(sdkKey, url) {
39
+ getActiveFallbackUrl(sdkKey, urlConfig) {
45
40
  var _a, _b;
46
- const domainKey = _getDomainKeyFromEndpoint(url);
47
- if (!_isDefaultUrl(url) || !domainKey) {
48
- return null;
49
- }
50
41
  let info = this._fallbackInfo;
51
42
  if (info == null) {
52
43
  info = (_a = _readFallbackInfoFromCache(sdkKey)) !== null && _a !== void 0 ? _a : {};
53
44
  this._fallbackInfo = info;
54
45
  }
55
- const entry = info[domainKey];
46
+ const entry = info[urlConfig.endpoint];
56
47
  if (!entry || Date.now() > ((_b = entry.expiryTime) !== null && _b !== void 0 ? _b : 0)) {
57
- delete info[domainKey];
48
+ delete info[urlConfig.endpoint];
58
49
  this._fallbackInfo = info;
59
50
  _tryWriteFallbackInfoToCache(sdkKey, this._fallbackInfo);
60
51
  return null;
61
52
  }
62
- const endpoint = _extractEndpointForUrl(url);
63
53
  if (entry.url) {
64
- return `https://${entry.url}/${endpoint}`;
54
+ return entry.url;
65
55
  }
66
56
  return null;
67
57
  }
68
- tryFetchUpdatedFallbackInfo(sdkKey, url, errorMessage, timedOut) {
69
- var _a;
58
+ getFallbackFromProvided(url) {
59
+ const path = _extractPathFromUrl(url);
60
+ if (path) {
61
+ return url.replace(path, '');
62
+ }
63
+ return null;
64
+ }
65
+ tryFetchUpdatedFallbackInfo(sdkKey, urlConfig, errorMessage, timedOut) {
66
+ var _a, _b;
70
67
  return __awaiter(this, void 0, void 0, function* () {
71
68
  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]) {
69
+ if (!_isDomainFailure(errorMessage, timedOut)) {
78
70
  return false;
79
71
  }
80
- this._cooldowns[domainKey] = Date.now() + COOLDOWN_TIME_MS;
81
- const newUrl = yield this._fetchFallbackUrl(domainKey);
72
+ const canUseNetworkFallbacks = urlConfig.customUrl == null && urlConfig.fallbackUrls == null;
73
+ const urls = canUseNetworkFallbacks
74
+ ? yield this._tryFetchFallbackUrlsFromNetwork(urlConfig)
75
+ : urlConfig.fallbackUrls;
76
+ const newUrl = this._pickNewFallbackUrl((_a = this._fallbackInfo) === null || _a === void 0 ? void 0 : _a[urlConfig.endpoint], urls);
82
77
  if (!newUrl) {
83
78
  return false;
84
79
  }
85
- this._updateFallbackInfoWithNewUrl(sdkKey, domainKey, newUrl);
80
+ this._updateFallbackInfoWithNewUrl(sdkKey, urlConfig.endpoint, newUrl);
86
81
  return true;
87
82
  }
88
83
  catch (error) {
89
- (_a = this._errorBoundary) === null || _a === void 0 ? void 0 : _a.logError('tryFetchUpdatedFallbackInfo', error);
84
+ (_b = this._errorBoundary) === null || _b === void 0 ? void 0 : _b.logError('tryFetchUpdatedFallbackInfo', error);
90
85
  return false;
91
86
  }
92
87
  });
93
88
  }
94
- _updateFallbackInfoWithNewUrl(sdkKey, domainKey, newUrl) {
89
+ _updateFallbackInfoWithNewUrl(sdkKey, endpoint, newUrl) {
95
90
  var _a, _b, _c;
96
91
  const newFallbackInfo = {
97
92
  url: newUrl,
98
93
  expiryTime: Date.now() + DEFAULT_TTL_MS,
99
94
  previous: [],
100
95
  };
101
- const previousInfo = (_a = this._fallbackInfo) === null || _a === void 0 ? void 0 : _a[domainKey];
96
+ const previousInfo = (_a = this._fallbackInfo) === null || _a === void 0 ? void 0 : _a[endpoint];
102
97
  if (previousInfo) {
103
98
  newFallbackInfo.previous.push(...previousInfo.previous);
104
99
  }
105
100
  if (newFallbackInfo.previous.length > 10) {
106
101
  newFallbackInfo.previous = [];
107
102
  }
108
- const previousUrl = (_c = (_b = this._fallbackInfo) === null || _b === void 0 ? void 0 : _b[domainKey]) === null || _c === void 0 ? void 0 : _c.url;
103
+ const previousUrl = (_c = (_b = this._fallbackInfo) === null || _b === void 0 ? void 0 : _b[endpoint]) === null || _c === void 0 ? void 0 : _c.url;
109
104
  if (previousUrl != null) {
110
105
  newFallbackInfo.previous.push(previousUrl);
111
106
  }
112
- this._fallbackInfo = Object.assign(Object.assign({}, this._fallbackInfo), { [domainKey]: newFallbackInfo });
107
+ this._fallbackInfo = Object.assign(Object.assign({}, this._fallbackInfo), { [endpoint]: newFallbackInfo });
113
108
  _tryWriteFallbackInfoToCache(sdkKey, this._fallbackInfo);
114
109
  }
115
- _fetchFallbackUrl(domainKey) {
116
- var _a, _b, _c, _d, _e, _f;
110
+ _tryFetchFallbackUrlsFromNetwork(urlConfig) {
111
+ var _a;
117
112
  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) {
113
+ const cooldown = this._dnsQueryCooldowns[urlConfig.endpoint];
114
+ if (cooldown && Date.now() < cooldown) {
120
115
  return null;
121
116
  }
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;
117
+ this._dnsQueryCooldowns[urlConfig.endpoint] = Date.now() + COOLDOWN_TIME_MS;
118
+ const result = [];
119
+ const records = yield (0, DnsTxtQuery_1._fetchTxtRecords)((_a = this._networkOverrideFunc) !== null && _a !== void 0 ? _a : fetch);
120
+ const path = _extractPathFromUrl(urlConfig.defaultUrl);
125
121
  for (const record of records) {
126
- const [recordKey, recordUrl] = record.split('=');
127
- if (!recordUrl || recordKey !== domainKey) {
122
+ if (!record.startsWith(urlConfig.endpointDnsKey + '=')) {
128
123
  continue;
129
124
  }
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;
125
+ const parts = record.split('=');
126
+ if (parts.length > 1) {
127
+ let baseUrl = parts[1];
128
+ if (baseUrl.endsWith('/')) {
129
+ baseUrl = baseUrl.slice(0, -1);
130
+ }
131
+ result.push(`https://${baseUrl}${path}`);
137
132
  }
138
133
  }
139
- return found;
134
+ return result;
140
135
  });
141
136
  }
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;
137
+ _pickNewFallbackUrl(currentFallbackInfo, urls) {
138
+ var _a;
139
+ if (urls == null) {
140
+ return null;
141
+ }
142
+ const previouslyUsed = new Set((_a = currentFallbackInfo === null || currentFallbackInfo === void 0 ? void 0 : currentFallbackInfo.previous) !== null && _a !== void 0 ? _a : []);
143
+ const currentFallbackUrl = currentFallbackInfo === null || currentFallbackInfo === void 0 ? void 0 : currentFallbackInfo.url;
144
+ let found = null;
145
+ for (const loopUrl of urls) {
146
+ const url = loopUrl.endsWith('/') ? loopUrl.slice(0, -1) : loopUrl;
147
+ if (!previouslyUsed.has(loopUrl) && url !== currentFallbackUrl) {
148
+ found = url;
149
+ break;
150
+ }
148
151
  }
152
+ return found;
149
153
  }
150
- return false;
151
154
  }
152
- exports._isDefaultUrl = _isDefaultUrl;
155
+ exports.NetworkFallbackResolver = NetworkFallbackResolver;
153
156
  function _isDomainFailure(errorMsg, timedOut) {
154
157
  var _a;
155
158
  const lowerErrorMsg = (_a = errorMsg === null || errorMsg === void 0 ? void 0 : errorMsg.toLowerCase()) !== null && _a !== void 0 ? _a : '';
@@ -184,25 +187,12 @@ function _readFallbackInfoFromCache(sdkKey) {
184
187
  return null;
185
188
  }
186
189
  }
187
- function _extractEndpointForUrl(urlString) {
190
+ function _extractPathFromUrl(urlString) {
188
191
  try {
189
192
  const url = new URL(urlString);
190
- const endpoint = url.pathname.substring(1);
191
- return endpoint;
193
+ return url.pathname;
192
194
  }
193
195
  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';
196
+ return null;
206
197
  }
207
- return null;
208
198
  }
@@ -22,24 +22,35 @@ const _isExposureEvent = ({ eventName, }) => {
22
22
  };
23
23
  exports._isExposureEvent = _isExposureEvent;
24
24
  const _createGateExposure = (user, gate) => {
25
- var _a, _b;
26
- return _createExposure(GATE_EXPOSURE_NAME, user, gate.details, {
25
+ var _a, _b, _c;
26
+ const metadata = {
27
27
  gate: gate.name,
28
28
  gateValue: String(gate.value),
29
29
  ruleID: gate.ruleID,
30
- }, (_b = (_a = gate.__evaluation) === null || _a === void 0 ? void 0 : _a.secondary_exposures) !== null && _b !== void 0 ? _b : []);
30
+ };
31
+ if (((_a = gate.__evaluation) === null || _a === void 0 ? void 0 : _a.version) != null) {
32
+ metadata['configVersion'] = gate.__evaluation.version;
33
+ }
34
+ return _createExposure(GATE_EXPOSURE_NAME, user, gate.details, metadata, (_c = (_b = gate.__evaluation) === null || _b === void 0 ? void 0 : _b.secondary_exposures) !== null && _c !== void 0 ? _c : []);
31
35
  };
32
36
  exports._createGateExposure = _createGateExposure;
33
37
  const _createConfigExposure = (user, config) => {
34
- var _a, _b;
35
- return _createExposure(CONFIG_EXPOSURE_NAME, user, config.details, {
38
+ var _a, _b, _c, _d;
39
+ const metadata = {
36
40
  config: config.name,
37
41
  ruleID: config.ruleID,
38
- }, (_b = (_a = config.__evaluation) === null || _a === void 0 ? void 0 : _a.secondary_exposures) !== null && _b !== void 0 ? _b : []);
42
+ };
43
+ if (((_a = config.__evaluation) === null || _a === void 0 ? void 0 : _a.version) != null) {
44
+ metadata['configVersion'] = config.__evaluation.version;
45
+ }
46
+ if (((_b = config.__evaluation) === null || _b === void 0 ? void 0 : _b.passed) != null) {
47
+ metadata['rulePassed'] = String(config.__evaluation.passed);
48
+ }
49
+ return _createExposure(CONFIG_EXPOSURE_NAME, user, config.details, metadata, (_d = (_c = config.__evaluation) === null || _c === void 0 ? void 0 : _c.secondary_exposures) !== null && _d !== void 0 ? _d : []);
39
50
  };
40
51
  exports._createConfigExposure = _createConfigExposure;
41
52
  const _createLayerParameterExposure = (user, layer, parameterName) => {
42
- var _a, _b, _c;
53
+ var _a, _b, _c, _d;
43
54
  const evaluation = layer.__evaluation;
44
55
  const isExplicit = ((_a = evaluation === null || evaluation === void 0 ? void 0 : evaluation.explicit_parameters) === null || _a === void 0 ? void 0 : _a.includes(parameterName)) === true;
45
56
  let allocatedExperiment = '';
@@ -48,13 +59,17 @@ const _createLayerParameterExposure = (user, layer, parameterName) => {
48
59
  allocatedExperiment = (_c = evaluation.allocated_experiment_name) !== null && _c !== void 0 ? _c : '';
49
60
  secondaryExposures = evaluation.secondary_exposures;
50
61
  }
51
- return _createExposure(LAYER_EXPOSURE_NAME, user, layer.details, {
62
+ const metadata = {
52
63
  config: layer.name,
53
64
  parameterName,
54
65
  ruleID: layer.ruleID,
55
66
  allocatedExperiment,
56
67
  isExplicitParameter: String(isExplicit),
57
- }, secondaryExposures);
68
+ };
69
+ if (((_d = layer.__evaluation) === null || _d === void 0 ? void 0 : _d.version) != null) {
70
+ metadata['configVersion'] = layer.__evaluation.version;
71
+ }
72
+ return _createExposure(LAYER_EXPOSURE_NAME, user, layer.details, metadata, secondaryExposures);
58
73
  };
59
74
  exports._createLayerParameterExposure = _createLayerParameterExposure;
60
75
  const _addEvaluationDetailsToMetadata = (details, metadata) => {
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "3.4.0-beta.1";
1
+ export declare const SDK_VERSION = "3.5.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.4.0-beta.1';
4
+ exports.SDK_VERSION = '3.5.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
@@ -27,6 +27,10 @@ export type NetworkConfigCommon = {
27
27
  * default: `https://featuregates.org/v1/initialize`
28
28
  */
29
29
  logEventUrl?: string;
30
+ /**
31
+ * A list of URLs to try if the primary logEventUrl fails.
32
+ */
33
+ logEventFallbackUrls?: string[];
30
34
  /**
31
35
  * Overrides the default networking layer used by the Statsig client.
32
36
  * By default, the client use `fetch`, but overriding this
@@ -0,0 +1,11 @@
1
+ import { Endpoint } from './NetworkConfig';
2
+ export type EndpointDnsKey = 'i' | 'e' | 'd';
3
+ export declare class UrlConfiguration {
4
+ readonly endpoint: Endpoint;
5
+ readonly endpointDnsKey: EndpointDnsKey;
6
+ readonly defaultUrl: string;
7
+ readonly customUrl: string | null;
8
+ readonly fallbackUrls: string[] | null;
9
+ constructor(endpoint: Endpoint, customUrl: string | undefined | null, customApi: string | undefined | null, fallbackUrls: string[] | undefined | null);
10
+ getUrl(): string;
11
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UrlConfiguration = void 0;
4
+ const NetworkConfig_1 = require("./NetworkConfig");
5
+ const ENDPOINT_DNS_KEY_MAP = {
6
+ [NetworkConfig_1.Endpoint._initialize]: 'i',
7
+ [NetworkConfig_1.Endpoint._rgstr]: 'e',
8
+ [NetworkConfig_1.Endpoint._download_config_specs]: 'd',
9
+ };
10
+ class UrlConfiguration {
11
+ constructor(endpoint, customUrl, customApi, fallbackUrls) {
12
+ this.customUrl = null;
13
+ this.fallbackUrls = null;
14
+ this.endpoint = endpoint;
15
+ this.endpointDnsKey = ENDPOINT_DNS_KEY_MAP[endpoint];
16
+ if (customUrl) {
17
+ this.customUrl = customUrl;
18
+ }
19
+ if (!customUrl && customApi) {
20
+ this.customUrl = `${customApi}/${endpoint}`;
21
+ }
22
+ if (fallbackUrls) {
23
+ this.fallbackUrls = fallbackUrls;
24
+ }
25
+ const defaultApi = NetworkConfig_1.NetworkDefault[endpoint];
26
+ this.defaultUrl = `${defaultApi}/${endpoint}`;
27
+ }
28
+ getUrl() {
29
+ var _a;
30
+ return (_a = this.customUrl) !== null && _a !== void 0 ? _a : this.defaultUrl;
31
+ }
32
+ }
33
+ exports.UrlConfiguration = UrlConfiguration;
package/src/index.d.ts CHANGED
@@ -37,7 +37,7 @@ export * from './StatsigUser';
37
37
  export * from './StorageProvider';
38
38
  export * from './TypedJsonParse';
39
39
  export * from './TypingUtils';
40
- export * from './UrlOverrides';
40
+ export * from './UrlConfiguration';
41
41
  export * from './UUID';
42
42
  export * from './VisibilityObserving';
43
43
  export { EventLogger, Storage, Log, Diagnostics };
package/src/index.js CHANGED
@@ -59,7 +59,7 @@ __exportStar(require("./StatsigUser"), exports);
59
59
  __exportStar(require("./StorageProvider"), exports);
60
60
  __exportStar(require("./TypedJsonParse"), exports);
61
61
  __exportStar(require("./TypingUtils"), exports);
62
- __exportStar(require("./UrlOverrides"), exports);
62
+ __exportStar(require("./UrlConfiguration"), exports);
63
63
  __exportStar(require("./UUID"), exports);
64
64
  __exportStar(require("./VisibilityObserving"), exports);
65
65
  __STATSIG__ = Object.assign(Object.assign({}, (__STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : {})), { Log: Log_1.Log,
@@ -1 +0,0 @@
1
- export declare function _getOverridableUrl(overrideUrl: string | undefined, overrideApi: string | undefined, defaultEndpoint: string, defaultApi: string): string;
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._getOverridableUrl = void 0;
4
- function _getOverridableUrl(overrideUrl, overrideApi, defaultEndpoint, defaultApi) {
5
- if (overrideUrl) {
6
- return overrideUrl;
7
- }
8
- else if (overrideApi) {
9
- return `${overrideApi}${defaultEndpoint}`;
10
- }
11
- else {
12
- return `${defaultApi}${defaultEndpoint}`;
13
- }
14
- }
15
- exports._getOverridableUrl = _getOverridableUrl;