@statsig/client-core 0.0.1-beta.41 → 0.0.1-beta.42

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": "0.0.1-beta.41",
3
+ "version": "0.0.1-beta.42",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -4,12 +4,15 @@ var _a, _b, _c;
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports._getInstance = exports._getStatsigGlobal = void 0;
6
6
  const _getStatsigGlobal = () => {
7
- return __STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : statsigGlobal;
7
+ return __STATSIG__ ? __STATSIG__ : statsigGlobal;
8
8
  };
9
9
  exports._getStatsigGlobal = _getStatsigGlobal;
10
10
  const _getInstance = (sdkKey) => {
11
- var _a;
12
- return sdkKey ? (_a = __STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__.instances) === null || _a === void 0 ? void 0 : _a[sdkKey] : __STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__.lastInstance;
11
+ const gbl = (0, exports._getStatsigGlobal)();
12
+ if (!sdkKey) {
13
+ return gbl.lastInstance;
14
+ }
15
+ return gbl.instances && gbl.instances[sdkKey];
13
16
  };
14
17
  exports._getInstance = _getInstance;
15
18
  const GLOBAL_KEY = '__STATSIG__';
package/src/CacheKey.js CHANGED
@@ -15,13 +15,13 @@ function _getUserStorageKey(sdkKey, user, customKeyGenerator) {
15
15
  .join(',')}`,
16
16
  `k:${sdkKey}`,
17
17
  ];
18
- return (0, Hashing_1.DJB2)(parts.join('|'));
18
+ return (0, Hashing_1._DJB2)(parts.join('|'));
19
19
  }
20
20
  exports._getUserStorageKey = _getUserStorageKey;
21
21
  function _getStorageKey(sdkKey, user, customKeyGenerator) {
22
22
  if (user) {
23
23
  return _getUserStorageKey(sdkKey, user, customKeyGenerator);
24
24
  }
25
- return (0, Hashing_1.DJB2)(`k:${sdkKey}`);
25
+ return (0, Hashing_1._DJB2)(`k:${sdkKey}`);
26
26
  }
27
27
  exports._getStorageKey = _getStorageKey;
@@ -24,7 +24,6 @@ export declare abstract class DataAdapterCore {
24
24
  protected abstract _getCacheKey(user?: StatsigUser): string;
25
25
  private _fetchAndPrepFromNetwork;
26
26
  protected _getSdkKey(): string;
27
- protected _addToInMemoryCache(cacheKey: string, result: DataAdapterResult): void;
28
27
  private _loadFromCache;
29
28
  private _writeToCache;
30
29
  private _runLocalStorageCacheEviction;
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DataAdapterCore = void 0;
13
13
  const Log_1 = require("./Log");
14
+ const StableID_1 = require("./StableID");
14
15
  const StatsigUser_1 = require("./StatsigUser");
15
16
  const StorageProvider_1 = require("./StorageProvider");
16
17
  const TypedJsonParse_1 = require("./TypedJsonParse");
@@ -21,8 +22,8 @@ class DataAdapterCore {
21
22
  this._cacheSuffix = _cacheSuffix;
22
23
  this._options = null;
23
24
  this._sdkKey = null;
24
- this._inMemoryCache = {};
25
25
  this._lastModifiedStoreKey = `statsig.last_modified_time.${_cacheSuffix}`;
26
+ this._inMemoryCache = new InMemoryCache();
26
27
  }
27
28
  attach(sdkKey, options) {
28
29
  this._sdkKey = sdkKey;
@@ -30,14 +31,14 @@ class DataAdapterCore {
30
31
  }
31
32
  getDataSync(user) {
32
33
  const cacheKey = this._getCacheKey(user);
33
- const result = this._inMemoryCache[cacheKey];
34
- if (result) {
35
- return result;
34
+ const inMem = this._inMemoryCache.get(cacheKey, user);
35
+ if (inMem) {
36
+ return inMem;
36
37
  }
37
38
  const cache = this._loadFromCache(cacheKey);
38
39
  if (cache) {
39
- this._addToInMemoryCache(cacheKey, cache);
40
- return this._inMemoryCache[cacheKey];
40
+ this._inMemoryCache.add(cacheKey, cache);
41
+ return this._inMemoryCache.get(cacheKey, user);
41
42
  }
42
43
  return null;
43
44
  }
@@ -45,11 +46,7 @@ class DataAdapterCore {
45
46
  var _a;
46
47
  const normalized = user && (0, StatsigUser_1._normalizeUser)(user, (_a = this._options) === null || _a === void 0 ? void 0 : _a.environment);
47
48
  const cacheKey = this._getCacheKey(normalized);
48
- this._addToInMemoryCache(cacheKey, {
49
- source: 'Bootstrap',
50
- data,
51
- receivedAt: Date.now(),
52
- });
49
+ this._inMemoryCache.add(cacheKey, _makeDataAdapterResult('Bootstrap', data, null));
53
50
  }
54
51
  /**
55
52
  * (Internal Use Only) - Used by \@statsig/react-native-bindings to prime the cache from AsyncStorage
@@ -57,7 +54,7 @@ class DataAdapterCore {
57
54
  * @param {Record<string, DataAdapterResult>} cache The values to merge into _inMemoryCache
58
55
  */
59
56
  __primeInMemoryCache(cache) {
60
- this._inMemoryCache = Object.assign(Object.assign({}, this._inMemoryCache), cache);
57
+ this._inMemoryCache.merge(cache);
61
58
  }
62
59
  _getDataAsyncImpl(current, user, options) {
63
60
  var _a;
@@ -80,7 +77,7 @@ class DataAdapterCore {
80
77
  const cacheKey = this._getCacheKey(user);
81
78
  const result = yield this._getDataAsyncImpl(null, user, options);
82
79
  if (result) {
83
- this._addToInMemoryCache(cacheKey, Object.assign(Object.assign({}, result), { source: 'Prefetch' }));
80
+ this._inMemoryCache.add(cacheKey, Object.assign(Object.assign({}, result), { source: 'Prefetch' }));
84
81
  }
85
82
  });
86
83
  }
@@ -91,23 +88,21 @@ class DataAdapterCore {
91
88
  Log_1.Log.debug('No response returned for latest value');
92
89
  return null;
93
90
  }
94
- const response = (0, TypedJsonParse_1.typedJsonParse)(latest, 'has_updates', 'Failure while attempting to persist latest value');
91
+ const response = (0, TypedJsonParse_1._typedJsonParse)(latest, 'has_updates', 'Initialize Response');
92
+ const sdkKey = this._getSdkKey();
93
+ const stableID = yield StableID_1.StableID.get(sdkKey);
95
94
  let result = null;
96
95
  if ((response === null || response === void 0 ? void 0 : response.has_updates) === true) {
97
- result = { source: 'Network', data: latest, receivedAt: Date.now() };
96
+ result = _makeDataAdapterResult('Network', latest, stableID);
98
97
  }
99
98
  else if (current && (response === null || response === void 0 ? void 0 : response.has_updates) === false) {
100
- result = {
101
- source: 'NetworkNotModified',
102
- data: current,
103
- receivedAt: Date.now(),
104
- };
99
+ result = _makeDataAdapterResult('NetworkNotModified', current, stableID);
105
100
  }
106
- if (!result) {
101
+ else {
107
102
  return null;
108
103
  }
109
104
  const cacheKey = this._getCacheKey(user);
110
- this._addToInMemoryCache(cacheKey, result);
105
+ this._inMemoryCache.add(cacheKey, result);
111
106
  yield this._writeToCache(cacheKey, result);
112
107
  return result;
113
108
  });
@@ -119,25 +114,13 @@ class DataAdapterCore {
119
114
  Log_1.Log.error(`${this._adapterName} is not attached to a Client`);
120
115
  return '';
121
116
  }
122
- _addToInMemoryCache(cacheKey, result) {
123
- const entries = Object.entries(this._inMemoryCache);
124
- if (entries.length < CACHE_LIMIT) {
125
- this._inMemoryCache[cacheKey] = result;
126
- return;
127
- }
128
- const [oldest] = entries.reduce((acc, curr) => {
129
- return curr[1] < acc[1] ? curr : acc;
130
- });
131
- delete this._inMemoryCache[oldest];
132
- this._inMemoryCache[cacheKey] = result;
133
- }
134
117
  _loadFromCache(cacheKey) {
135
118
  var _a;
136
119
  const cache = (_a = StorageProvider_1.Storage._getItemSync) === null || _a === void 0 ? void 0 : _a.call(StorageProvider_1.Storage, cacheKey);
137
120
  if (cache == null) {
138
121
  return null;
139
122
  }
140
- const result = (0, TypedJsonParse_1.typedJsonParse)(cache, 'source', 'Failed to parse cached result');
123
+ const result = (0, TypedJsonParse_1._typedJsonParse)(cache, 'source', 'Cached Result');
141
124
  return result ? Object.assign(Object.assign({}, result), { source: 'Cache' }) : null;
142
125
  }
143
126
  _writeToCache(cacheKey, result) {
@@ -166,3 +149,42 @@ class DataAdapterCore {
166
149
  }
167
150
  }
168
151
  exports.DataAdapterCore = DataAdapterCore;
152
+ function _makeDataAdapterResult(source, data, stableID) {
153
+ return {
154
+ source,
155
+ data,
156
+ receivedAt: Date.now(),
157
+ stableID,
158
+ };
159
+ }
160
+ class InMemoryCache {
161
+ constructor() {
162
+ this._data = {};
163
+ }
164
+ get(cacheKey, user) {
165
+ var _a;
166
+ const result = this._data[cacheKey];
167
+ const cached = result === null || result === void 0 ? void 0 : result.stableID;
168
+ const provided = (_a = user === null || user === void 0 ? void 0 : user.customIDs) === null || _a === void 0 ? void 0 : _a.stableID;
169
+ if (provided && cached && provided !== cached) {
170
+ Log_1.Log.warn("'StatsigUser.customIDs.stableID' mismatch");
171
+ return null;
172
+ }
173
+ return result;
174
+ }
175
+ add(cacheKey, value) {
176
+ const entries = Object.entries(this._data);
177
+ if (entries.length < CACHE_LIMIT) {
178
+ this._data[cacheKey] = value;
179
+ return;
180
+ }
181
+ const [oldest] = entries.reduce((acc, curr) => {
182
+ return curr[1] < acc[1] ? curr : acc;
183
+ });
184
+ delete this._data[oldest];
185
+ this._data[cacheKey] = value;
186
+ }
187
+ merge(values) {
188
+ this._data = Object.assign(Object.assign({}, this._data), values);
189
+ }
190
+ }
@@ -8,7 +8,7 @@ export declare class ErrorBoundary {
8
8
  private _seen;
9
9
  constructor(_sdkKey: string, _options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
10
10
  wrap(instance: unknown): void;
11
- capture(tag: string, task: () => unknown): unknown;
12
11
  logError(tag: string, error: unknown): void;
12
+ private _capture;
13
13
  private _onError;
14
14
  }
@@ -14,6 +14,7 @@ const Log_1 = require("./Log");
14
14
  const SDKType_1 = require("./SDKType");
15
15
  const StatsigMetadata_1 = require("./StatsigMetadata");
16
16
  exports.EXCEPTION_ENDPOINT = 'https://statsigapi.net/v1/sdk_exception';
17
+ const UNKNOWN_ERROR = '[Statsig] UnknownError';
17
18
  class ErrorBoundary {
18
19
  constructor(_sdkKey, _options, _emitter) {
19
20
  this._sdkKey = _sdkKey;
@@ -30,7 +31,7 @@ class ErrorBoundary {
30
31
  return;
31
32
  }
32
33
  obj[name] = (...args) => {
33
- return this.capture(name, () => original.apply(instance, args));
34
+ return this._capture(name, () => original.apply(instance, args));
34
35
  };
35
36
  obj[name].$EB = true;
36
37
  });
@@ -39,7 +40,10 @@ class ErrorBoundary {
39
40
  this._onError('eb:wrap', err);
40
41
  }
41
42
  }
42
- capture(tag, task) {
43
+ logError(tag, error) {
44
+ this._onError(tag, error);
45
+ }
46
+ _capture(tag, task) {
43
47
  try {
44
48
  const res = task();
45
49
  if (res && res instanceof Promise) {
@@ -52,15 +56,12 @@ class ErrorBoundary {
52
56
  return null;
53
57
  }
54
58
  }
55
- logError(tag, error) {
56
- this._onError(tag, error);
57
- }
58
59
  _onError(tag, error) {
59
60
  try {
60
61
  Log_1.Log.warn(`Caught error in ${tag}`, { error });
61
62
  const impl = () => __awaiter(this, void 0, void 0, function* () {
62
63
  var _a, _b, _c, _d, _e, _f, _g;
63
- const unwrapped = (error !== null && error !== void 0 ? error : Error('[Statsig] Error was empty'));
64
+ const unwrapped = (error ? error : Error(UNKNOWN_ERROR));
64
65
  const isError = unwrapped instanceof Error;
65
66
  const name = isError ? unwrapped.name : 'No Name';
66
67
  if (this._seen.has(name)) {
@@ -107,7 +108,7 @@ function _getDescription(obj) {
107
108
  return JSON.stringify(obj);
108
109
  }
109
110
  catch (_a) {
110
- return '[Statsig] Failed to get string for error.';
111
+ return UNKNOWN_ERROR;
111
112
  }
112
113
  }
113
114
  function _getAllInstanceMethodNames(instance) {
@@ -16,7 +16,6 @@ export declare class EventLogger {
16
16
  private _creationTime;
17
17
  private _isLoggingDisabled;
18
18
  private _logEventUrl;
19
- private _logEventBeaconUrl;
20
19
  constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null);
21
20
  setLoggingDisabled(isDisabled: boolean): void;
22
21
  enqueue(event: StatsigEventInternal): void;
@@ -33,10 +32,12 @@ export declare class EventLogger {
33
32
  private _sendEvents;
34
33
  private _sendEventsViaPost;
35
34
  private _sendEventsViaBeacon;
35
+ private _getRequestData;
36
36
  private _saveFailedLogsToStorage;
37
37
  private _retryFailedLogs;
38
38
  private _getStorageKey;
39
39
  private _normalizeAndAppendEvent;
40
40
  private _appendAndResetNonExposedChecks;
41
41
  private _getCurrentPageUrl;
42
+ private _startBackgroundFlushInterval;
42
43
  }
@@ -33,7 +33,7 @@ const _safeFlushAndForget = (sdkKey) => {
33
33
  };
34
34
  class EventLogger {
35
35
  constructor(_sdkKey, _emitter, _network, _options) {
36
- var _a, _b;
36
+ var _a;
37
37
  this._sdkKey = _sdkKey;
38
38
  this._emitter = _emitter;
39
39
  this._network = _network;
@@ -46,26 +46,15 @@ class EventLogger {
46
46
  EVENT_LOGGER_MAP[_sdkKey] = this;
47
47
  this._isLoggingDisabled = (_options === null || _options === void 0 ? void 0 : _options.disableLogging) === true;
48
48
  this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
49
- const flushInterval = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
50
- const intervalId = setInterval(() => {
51
- const logger = EVENT_LOGGER_MAP[_sdkKey];
52
- if (logger._flushIntervalId !== intervalId) {
53
- clearInterval(intervalId);
54
- }
55
- else {
56
- _safeFlushAndForget(_sdkKey);
57
- }
58
- }, flushInterval);
59
- this._flushIntervalId = intervalId;
60
49
  const config = _options === null || _options === void 0 ? void 0 : _options.networkConfig;
61
50
  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);
62
- this._logEventBeaconUrl = (0, UrlOverrides_1._getOverridableUrl)(config === null || config === void 0 ? void 0 : config.logEventBeaconUrl, config === null || config === void 0 ? void 0 : config.api, '/log_event_beacon', NetworkConfig_1.NetworkDefault.eventsApi);
63
51
  (0, VisibilityObserving_1._subscribeToVisiblityChanged)((visibility) => {
64
52
  if (visibility === 'background') {
65
53
  _safeFlushAndForget(_sdkKey);
66
54
  }
67
55
  });
68
56
  this._retryFailedLogs();
57
+ this._startBackgroundFlushInterval();
69
58
  }
70
59
  setLoggingDisabled(isDisabled) {
71
60
  this._isLoggingDisabled = isDisabled;
@@ -105,7 +94,7 @@ class EventLogger {
105
94
  }
106
95
  const events = this._queue;
107
96
  this._queue = [];
108
- yield this._sendEvents(events);
97
+ return this._sendEvents(events);
109
98
  });
110
99
  }
111
100
  /**
@@ -123,16 +112,17 @@ class EventLogger {
123
112
  setTimeout(() => _safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
124
113
  }
125
114
  _shouldLogEvent(event) {
126
- var _a, _b, _c, _d;
127
115
  if (!(0, StatsigEvent_1._isExposureEvent)(event)) {
128
116
  return true;
129
117
  }
118
+ const user = event.user ? event.user : {};
119
+ const metadata = event.metadata ? event.metadata : {};
130
120
  const key = [
131
121
  event.eventName,
132
- (_a = event.user) === null || _a === void 0 ? void 0 : _a.userID,
133
- (_b = event.metadata) === null || _b === void 0 ? void 0 : _b['gate'],
134
- (_c = event.metadata) === null || _c === void 0 ? void 0 : _c['config'],
135
- (_d = event.metadata) === null || _d === void 0 ? void 0 : _d['ruleID'],
122
+ user.userID,
123
+ metadata['gate'],
124
+ metadata['config'],
125
+ metadata['ruleID'],
136
126
  ].join('|');
137
127
  const previous = this._lastExposureTimeMap[key];
138
128
  const now = Date.now();
@@ -178,17 +168,7 @@ class EventLogger {
178
168
  _sendEventsViaPost(events) {
179
169
  var _a;
180
170
  return __awaiter(this, void 0, void 0, function* () {
181
- const result = yield this._network.post({
182
- sdkKey: this._sdkKey,
183
- data: {
184
- events,
185
- },
186
- url: this._logEventUrl,
187
- retries: 3,
188
- params: {
189
- [NetworkConfig_1.NetworkParam.EventCount]: String(events.length),
190
- },
191
- });
171
+ const result = yield this._network.post(this._getRequestData(events));
192
172
  const code = (_a = result === null || result === void 0 ? void 0 : result.code) !== null && _a !== void 0 ? _a : -1;
193
173
  return { success: code >= 200 && code < 300 };
194
174
  });
@@ -196,16 +176,24 @@ class EventLogger {
196
176
  _sendEventsViaBeacon(events) {
197
177
  return __awaiter(this, void 0, void 0, function* () {
198
178
  return {
199
- success: yield this._network.beacon({
200
- sdkKey: this._sdkKey,
201
- data: {
202
- events,
203
- },
204
- url: this._logEventBeaconUrl,
205
- }),
179
+ success: yield this._network.beacon(this._getRequestData(events)),
206
180
  };
207
181
  });
208
182
  }
183
+ _getRequestData(events) {
184
+ return {
185
+ sdkKey: this._sdkKey,
186
+ data: {
187
+ events,
188
+ },
189
+ url: this._logEventUrl,
190
+ retries: 3,
191
+ isCompressable: true,
192
+ params: {
193
+ [NetworkConfig_1.NetworkParam.EventCount]: String(events.length),
194
+ },
195
+ };
196
+ }
209
197
  _saveFailedLogsToStorage(events) {
210
198
  while (events.length > MAX_FAILED_LOGS) {
211
199
  events.shift();
@@ -229,7 +217,7 @@ class EventLogger {
229
217
  });
230
218
  }
231
219
  _getStorageKey() {
232
- return `statsig.failed_logs.${(0, Hashing_1.DJB2)(this._sdkKey)}`;
220
+ return `statsig.failed_logs.${(0, Hashing_1._DJB2)(this._sdkKey)}`;
233
221
  }
234
222
  _normalizeAndAppendEvent(event) {
235
223
  if (event.user) {
@@ -241,7 +229,9 @@ class EventLogger {
241
229
  if (currentPage) {
242
230
  extras.statsigMetadata = { currentPage };
243
231
  }
244
- this._queue.push(Object.assign(Object.assign({}, event), extras));
232
+ const final = Object.assign(Object.assign({}, event), extras);
233
+ Log_1.Log.debug('Enqueued Event:', final);
234
+ this._queue.push(final);
245
235
  }
246
236
  _appendAndResetNonExposedChecks() {
247
237
  if (Object.keys(this._nonExposedChecks).length === 0) {
@@ -264,5 +254,22 @@ class EventLogger {
264
254
  }
265
255
  return (0, SafeJs_1._getCurrentPageUrlSafe)();
266
256
  }
257
+ _startBackgroundFlushInterval() {
258
+ var _a, _b;
259
+ if (!(0, SafeJs_1._isBrowserEnv)()) {
260
+ return; // do not run in server environments
261
+ }
262
+ const flushInterval = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
263
+ const intervalId = setInterval(() => {
264
+ const logger = EVENT_LOGGER_MAP[this._sdkKey];
265
+ if (logger._flushIntervalId !== intervalId) {
266
+ clearInterval(intervalId);
267
+ }
268
+ else {
269
+ _safeFlushAndForget(this._sdkKey);
270
+ }
271
+ }, flushInterval);
272
+ this._flushIntervalId = intervalId;
273
+ }
267
274
  }
268
275
  exports.EventLogger = EventLogger;
package/src/Hashing.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const DJB2: (value: string) => string;
2
- export declare const DJB2Object: (value: Record<string, unknown> | null) => string;
1
+ export declare const _DJB2: (value: string) => string;
2
+ export declare const _DJB2Object: (value: Record<string, unknown> | null) => string;
package/src/Hashing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DJB2Object = exports.DJB2 = void 0;
4
- const DJB2 = (value) => {
3
+ exports._DJB2Object = exports._DJB2 = void 0;
4
+ const _DJB2 = (value) => {
5
5
  let hash = 0;
6
6
  for (let i = 0; i < value.length; i++) {
7
7
  const character = value.charCodeAt(i);
@@ -10,11 +10,11 @@ const DJB2 = (value) => {
10
10
  }
11
11
  return String(hash >>> 0);
12
12
  };
13
- exports.DJB2 = DJB2;
14
- const DJB2Object = (value) => {
15
- return (0, exports.DJB2)(JSON.stringify(_getSortedObject(value)));
13
+ exports._DJB2 = _DJB2;
14
+ const _DJB2Object = (value) => {
15
+ return (0, exports._DJB2)(JSON.stringify(_getSortedObject(value)));
16
16
  };
17
- exports.DJB2Object = DJB2Object;
17
+ exports._DJB2Object = _DJB2Object;
18
18
  const _getSortedObject = (object) => {
19
19
  if (object == null) {
20
20
  return null;
@@ -14,5 +14,6 @@ export declare enum NetworkParam {
14
14
  SdkVersion = "sv",
15
15
  Time = "t",
16
16
  SessionID = "sid",
17
- StatsigEncoded = "se"
17
+ StatsigEncoded = "se",
18
+ IsGzipped = "gz"
18
19
  }
@@ -15,4 +15,5 @@ var NetworkParam;
15
15
  NetworkParam["Time"] = "t";
16
16
  NetworkParam["SessionID"] = "sid";
17
17
  NetworkParam["StatsigEncoded"] = "se";
18
+ NetworkParam["IsGzipped"] = "gz";
18
19
  })(NetworkParam || (exports.NetworkParam = NetworkParam = {}));
@@ -2,6 +2,7 @@ import './$_StatsigGlobal';
2
2
  import { NetworkPriority } from './NetworkConfig';
3
3
  import { StatsigClientEmitEventFunc } from './StatsigClientBase';
4
4
  import { AnyStatsigOptions } from './StatsigOptionsCommon';
5
+ import { Flatten } from './UtitlityTypes';
5
6
  type RequestArgs = {
6
7
  sdkKey: string;
7
8
  url: string;
@@ -10,10 +11,12 @@ type RequestArgs = {
10
11
  params?: Record<string, string>;
11
12
  headers?: Record<string, string>;
12
13
  };
13
- type RequestArgsWithData = RequestArgs & {
14
+ export type RequestArgsWithData = Flatten<RequestArgs & {
14
15
  data: Record<string, unknown>;
15
16
  isStatsigEncodable?: boolean;
16
- };
17
+ isCompressable?: boolean;
18
+ }>;
19
+ type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'url' | 'params' | 'isCompressable'>;
17
20
  type NetworkResponse = {
18
21
  body: string | null;
19
22
  code: number;
@@ -22,11 +25,12 @@ export declare class NetworkCore {
22
25
  private _options;
23
26
  private _emitter?;
24
27
  private readonly _timeout;
28
+ private readonly _netConfig;
25
29
  constructor(_options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
26
30
  post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
27
31
  get(args: RequestArgs): Promise<NetworkResponse | null>;
28
32
  isBeaconSupported(): boolean;
29
- beacon(args: RequestArgsWithData): Promise<boolean>;
33
+ beacon(args: BeaconRequestArgs): Promise<boolean>;
30
34
  private _sendRequest;
31
35
  private _getPopulatedURL;
32
36
  private _getPopulatedBody;
@@ -23,16 +23,22 @@ const VisibilityObserving_1 = require("./VisibilityObserving");
23
23
  const DEFAULT_TIMEOUT_MS = 10000;
24
24
  class NetworkCore {
25
25
  constructor(_options, _emitter) {
26
- var _a, _b;
26
+ var _a, _b, _c;
27
27
  this._options = _options;
28
28
  this._emitter = _emitter;
29
- this._timeout =
30
- (_b = (_a = _options === null || _options === void 0 ? void 0 : _options.networkConfig) === null || _a === void 0 ? void 0 : _a.networkTimeoutMs) !== null && _b !== void 0 ? _b : DEFAULT_TIMEOUT_MS;
29
+ this._netConfig = (_a = _options === null || _options === void 0 ? void 0 : _options.networkConfig) !== null && _a !== void 0 ? _a : null;
30
+ this._timeout = (_c = (_b = this._netConfig) === null || _b === void 0 ? void 0 : _b.networkTimeoutMs) !== null && _c !== void 0 ? _c : DEFAULT_TIMEOUT_MS;
31
31
  }
32
32
  post(args) {
33
33
  return __awaiter(this, void 0, void 0, function* () {
34
- const body = yield this._getPopulatedBody(args);
35
- return this._sendRequest(Object.assign({ method: 'POST', body: this._attemptToEncodeString(args, body) }, args));
34
+ let body = yield this._getPopulatedBody(args);
35
+ if (args.isStatsigEncodable) {
36
+ body = this._attemptToEncodeString(args, body);
37
+ }
38
+ else if (args.isCompressable) {
39
+ body = yield _attemptToCompressBody(args, body);
40
+ }
41
+ return this._sendRequest(Object.assign({ method: 'POST', body }, args));
36
42
  });
37
43
  }
38
44
  get(args) {
@@ -40,30 +46,31 @@ class NetworkCore {
40
46
  }
41
47
  isBeaconSupported() {
42
48
  return (typeof navigator !== 'undefined' &&
43
- typeof (navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === 'function');
49
+ typeof navigator.sendBeacon === 'function');
44
50
  }
45
51
  beacon(args) {
46
52
  return __awaiter(this, void 0, void 0, function* () {
47
53
  if (!_ensureValidSdkKey(args)) {
48
54
  return false;
49
55
  }
56
+ let body = yield this._getPopulatedBody(args);
57
+ body = yield _attemptToCompressBody(args, body);
50
58
  const url = yield this._getPopulatedURL(args);
51
- const body = yield this._getPopulatedBody(args);
52
59
  return navigator.sendBeacon(url, body);
53
60
  });
54
61
  }
55
62
  _sendRequest(args) {
56
- var _a, _b, _c, _d, _e, _f;
63
+ var _a, _b, _c, _d;
57
64
  return __awaiter(this, void 0, void 0, function* () {
58
65
  if (!_ensureValidSdkKey(args)) {
59
66
  return null;
60
67
  }
61
- if ((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.networkConfig) === null || _b === void 0 ? void 0 : _b.preventAllNetworkTraffic) {
68
+ if ((_a = this._netConfig) === null || _a === void 0 ? void 0 : _a.preventAllNetworkTraffic) {
62
69
  return null;
63
70
  }
64
71
  const { method, body, retries } = args;
65
- const controller = new AbortController();
66
- const handle = setTimeout(() => controller.abort(`Timeout of ${this._timeout}ms expired.`), this._timeout);
72
+ const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
73
+ const handle = setTimeout(() => controller === null || controller === void 0 ? void 0 : controller.abort(`Timeout of ${this._timeout}ms expired.`), this._timeout);
67
74
  const url = yield this._getPopulatedURL(args);
68
75
  let response = null;
69
76
  const keepalive = (0, VisibilityObserving_1._isUnloading)();
@@ -72,11 +79,11 @@ class NetworkCore {
72
79
  method,
73
80
  body,
74
81
  headers: Object.assign({}, args.headers),
75
- signal: controller.signal,
82
+ signal: controller === null || controller === void 0 ? void 0 : controller.signal,
76
83
  priority: args.priority,
77
84
  keepalive,
78
85
  };
79
- const func = (_e = (_d = (_c = this._options) === null || _c === void 0 ? void 0 : _c.networkConfig) === null || _d === void 0 ? void 0 : _d.networkOverrideFunc) !== null && _e !== void 0 ? _e : fetch;
86
+ const func = (_c = (_b = this._netConfig) === null || _b === void 0 ? void 0 : _b.networkOverrideFunc) !== null && _c !== void 0 ? _c : fetch;
80
87
  response = yield func(url, config);
81
88
  clearTimeout(handle);
82
89
  if (!response.ok) {
@@ -96,7 +103,7 @@ class NetworkCore {
96
103
  const errorMessage = _getErrorMessage(controller, error);
97
104
  Diagnostics_1.Diagnostics.mark();
98
105
  if (!retries || retries <= 0) {
99
- (_f = this._emitter) === null || _f === void 0 ? void 0 : _f.call(this, { name: 'error', error });
106
+ (_d = this._emitter) === null || _d === void 0 ? void 0 : _d.call(this, { name: 'error', error });
100
107
  Log_1.Log.error(`A networking error occured during ${method} request to ${url}.`, errorMessage, error);
101
108
  return null;
102
109
  }
@@ -155,7 +162,7 @@ const _ensureValidSdkKey = (args) => {
155
162
  return true;
156
163
  };
157
164
  function _getErrorMessage(controller, error) {
158
- if (controller.signal.aborted &&
165
+ if ((controller === null || controller === void 0 ? void 0 : controller.signal.aborted) &&
159
166
  typeof controller.signal.reason === 'string') {
160
167
  return controller.signal.reason;
161
168
  }
@@ -167,3 +174,21 @@ function _getErrorMessage(controller, error) {
167
174
  }
168
175
  return 'Unknown Error';
169
176
  }
177
+ function _attemptToCompressBody(args, body) {
178
+ var _a;
179
+ return __awaiter(this, void 0, void 0, function* () {
180
+ if (!args.isCompressable ||
181
+ typeof CompressionStream === 'undefined' ||
182
+ typeof TextEncoder === 'undefined' ||
183
+ (__STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__['no-compress']) != null) {
184
+ return body;
185
+ }
186
+ const bytes = new TextEncoder().encode(body);
187
+ const stream = new CompressionStream('gzip');
188
+ const writer = stream.writable.getWriter();
189
+ writer.write(bytes).catch(Log_1.Log.error);
190
+ writer.close().catch(Log_1.Log.error);
191
+ args.params = Object.assign(Object.assign({}, ((_a = args.params) !== null && _a !== void 0 ? _a : {})), { [NetworkConfig_1.NetworkParam.IsGzipped]: '1' });
192
+ return yield new Response(stream.readable).arrayBuffer();
193
+ });
194
+ }
@@ -16,7 +16,6 @@ const ErrorBoundary_1 = require("./ErrorBoundary");
16
16
  const EventLogger_1 = require("./EventLogger");
17
17
  const Log_1 = require("./Log");
18
18
  const SafeJs_1 = require("./SafeJs");
19
- const StableID_1 = require("./StableID");
20
19
  const StorageProvider_1 = require("./StorageProvider");
21
20
  class StatsigClientBase {
22
21
  constructor(sdkKey, adapter, network, options) {
@@ -26,16 +25,15 @@ class StatsigClientBase {
26
25
  const emitter = this.$emt.bind(this);
27
26
  (options === null || options === void 0 ? void 0 : options.logLevel) != null && (Log_1.Log.level = options.logLevel);
28
27
  (options === null || options === void 0 ? void 0 : options.disableStorage) && StorageProvider_1.Storage._setDisabled(true);
29
- (options === null || options === void 0 ? void 0 : options.overrideStableID) &&
30
- StableID_1.StableID.setOverride(options.overrideStableID, sdkKey);
31
- this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey, options, emitter);
32
- this._errorBoundary.wrap(this);
33
- this._errorBoundary.wrap(network);
34
- this._errorBoundary.wrap(adapter);
35
28
  this._sdkKey = sdkKey;
36
29
  this._options = options !== null && options !== void 0 ? options : {};
37
30
  this._overrideAdapter = (_a = options === null || options === void 0 ? void 0 : options.overrideAdapter) !== null && _a !== void 0 ? _a : null;
38
31
  this._logger = new EventLogger_1.EventLogger(sdkKey, emitter, network, options);
32
+ this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey, options, emitter);
33
+ this._errorBoundary.wrap(this);
34
+ this._errorBoundary.wrap(network);
35
+ this._errorBoundary.wrap(adapter);
36
+ this._errorBoundary.wrap(this._logger);
39
37
  if ((0, SafeJs_1._isBrowserEnv)()) {
40
38
  const statsigGlobal = (0, __StatsigGlobal_1._getStatsigGlobal)();
41
39
  const instances = (_b = statsigGlobal.instances) !== null && _b !== void 0 ? _b : {};
@@ -6,6 +6,7 @@ export type DataAdapterResult = {
6
6
  readonly source: DataSource;
7
7
  readonly data: string;
8
8
  readonly receivedAt: number;
9
+ readonly stableID: string | null;
9
10
  };
10
11
  export type DataAdapterAsyncOptions = {
11
12
  /**
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "0.0.1-beta.41";
1
+ export declare const SDK_VERSION = "0.0.1-beta.42";
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 = '0.0.1-beta.41';
4
+ exports.SDK_VERSION = '0.0.1-beta.42';
5
5
  let metadata = {
6
6
  sdkVersion: exports.SDK_VERSION,
7
7
  sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
@@ -26,12 +26,6 @@ export type NetworkConfigCommon = {
26
26
  * default: `https://featuregates.org/v1/initialize`
27
27
  */
28
28
  logEventUrl?: string;
29
- /**
30
- * The URL used to flush queued events via {@link window.navigator.sendBeacon} (web only). Takes precedence over {@link StatsigOptionsCommon.api}.
31
- *
32
- * default: `https://featuregates.org/v1/initialize`
33
- */
34
- logEventBeaconUrl?: string;
35
29
  /**
36
30
  * The maximum amount of time (in milliseconds) that any network request can take
37
31
  * before timing out.
@@ -69,10 +63,6 @@ export type StatsigOptionsCommon<NetworkConfig extends NetworkConfigCommon> = St
69
63
  * in the same session.
70
64
  */
71
65
  environment?: StatsigEnvironment;
72
- /**
73
- * Overrides the auto-generated StableID that is set for the device.
74
- */
75
- overrideStableID?: string;
76
66
  /**
77
67
  * How much information is allowed to be printed to the console.
78
68
  *
@@ -2,7 +2,10 @@ import type { StatsigEnvironment } from './StatsigOptionsCommon';
2
2
  type StatsigUserPrimitives = string | number | boolean | Array<string> | undefined;
3
3
  export type StatsigUser = {
4
4
  userID?: string;
5
- customIDs?: Record<string, string>;
5
+ customIDs?: {
6
+ [key: string]: string | undefined;
7
+ stableID?: string;
8
+ };
6
9
  email?: string;
7
10
  ip?: string;
8
11
  userAgent?: string;
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var _a;
12
11
  Object.defineProperty(exports, "__esModule", { value: true });
13
12
  exports._setObjectInStorage = exports._getObjectFromStorage = exports.Storage = void 0;
14
13
  const Log_1 = require("./Log");
@@ -17,50 +16,25 @@ const inMemoryStore = {};
17
16
  const _resolve = (input) => Promise.resolve(input);
18
17
  const _inMemoryProvider = {
19
18
  _getProviderName: () => 'InMemory',
20
- _getItemSync(key) {
21
- var _a;
22
- return (_a = inMemoryStore[key]) !== null && _a !== void 0 ? _a : null;
23
- },
24
- _getItem(key) {
25
- var _a;
26
- return _resolve((_a = inMemoryStore[key]) !== null && _a !== void 0 ? _a : null);
27
- },
28
- _setItem(key, value) {
29
- inMemoryStore[key] = value;
30
- return _resolve();
31
- },
32
- _removeItem(key) {
33
- delete inMemoryStore[key];
34
- return _resolve();
35
- },
36
- _getAllKeys() {
37
- return _resolve(Object.keys(inMemoryStore));
38
- },
19
+ _getItemSync: (key) => inMemoryStore[key] ? inMemoryStore[key] : null,
20
+ _getItem: (key) => _resolve(inMemoryStore[key] ? inMemoryStore[key] : null),
21
+ _setItem: (key, value) => ((inMemoryStore[key] = value), _resolve()),
22
+ _removeItem: (key) => (delete inMemoryStore[key], _resolve()),
23
+ _getAllKeys: () => _resolve(Object.keys(inMemoryStore)),
39
24
  };
40
25
  let _localStorageProvider = null;
41
26
  try {
42
27
  const win = (0, SafeJs_1._getWindowSafe)();
43
- if (typeof ((_a = win === null || win === void 0 ? void 0 : win.localStorage) === null || _a === void 0 ? void 0 : _a.getItem) === 'function') {
28
+ if (win &&
29
+ win.localStorage &&
30
+ typeof win.localStorage.getItem === 'function') {
44
31
  _localStorageProvider = {
45
32
  _getProviderName: () => 'LocalStorage',
46
- _getItemSync(key) {
47
- return win.localStorage.getItem(key);
48
- },
49
- _getItem(key) {
50
- return _resolve(win.localStorage.getItem(key));
51
- },
52
- _setItem(key, value) {
53
- win.localStorage.setItem(key, value);
54
- return _resolve();
55
- },
56
- _removeItem(key) {
57
- win.localStorage.removeItem(key);
58
- return _resolve();
59
- },
60
- _getAllKeys() {
61
- const keys = Object.keys(win.localStorage);
62
- return _resolve(keys);
63
- },
33
+ _getItemSync: (key) => win.localStorage.getItem(key),
34
+ _getItem: (key) => _resolve(win.localStorage.getItem(key)),
35
+ _setItem: (key, value) => (win.localStorage.setItem(key, value), _resolve()),
36
+ _removeItem: (key) => (win.localStorage.removeItem(key), _resolve()),
37
+ _getAllKeys: () => _resolve(Object.keys(win.localStorage)),
64
38
  };
65
39
  }
66
40
  }
@@ -84,7 +58,7 @@ function _inMemoryBreaker(get) {
84
58
  exports.Storage = {
85
59
  _getProviderName: () => _current._getProviderName(),
86
60
  _getItem: (key) => __awaiter(void 0, void 0, void 0, function* () { return _inMemoryBreaker(() => _current._getItem(key)); }),
87
- _getItemSync: (key) => _inMemoryBreaker(() => { var _a, _b; return (_b = (_a = _current._getItemSync) === null || _a === void 0 ? void 0 : _a.call(_current, key)) !== null && _b !== void 0 ? _b : null; }),
61
+ _getItemSync: (key) => _inMemoryBreaker(() => _current._getItemSync ? _current._getItemSync(key) : null),
88
62
  _setItem: (key, value) => _current._setItem(key, value),
89
63
  _removeItem: (key) => _current._removeItem(key),
90
64
  _getAllKeys: () => _current._getAllKeys(),
@@ -5,4 +5,4 @@
5
5
  * @param {string} error An error to print via Log.error() when parsing fails
6
6
  * @returns {T | null} The parse object T or null if it failed
7
7
  */
8
- export declare function typedJsonParse<T>(data: string, guard: string, error: string): T | null;
8
+ export declare function _typedJsonParse<T>(data: string, guard: string, typeName: string): T | null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.typedJsonParse = void 0;
3
+ exports._typedJsonParse = void 0;
4
4
  const Log_1 = require("./Log");
5
5
  /**
6
6
  *
@@ -9,7 +9,7 @@ const Log_1 = require("./Log");
9
9
  * @param {string} error An error to print via Log.error() when parsing fails
10
10
  * @returns {T | null} The parse object T or null if it failed
11
11
  */
12
- function typedJsonParse(data, guard, error) {
12
+ function _typedJsonParse(data, guard, typeName) {
13
13
  try {
14
14
  const result = JSON.parse(data);
15
15
  if (typeof result === 'object' &&
@@ -21,7 +21,7 @@ function typedJsonParse(data, guard, error) {
21
21
  catch (_a) {
22
22
  // noop
23
23
  }
24
- Log_1.Log.error(error);
24
+ Log_1.Log.error(`Failed to parse ${typeName}`);
25
25
  return null;
26
26
  }
27
- exports.typedJsonParse = typedJsonParse;
27
+ exports._typedJsonParse = _typedJsonParse;
package/src/index.js CHANGED
@@ -24,7 +24,6 @@ Object.defineProperty(exports, "Log", { enumerable: true, get: function () { ret
24
24
  const StatsigMetadata_1 = require("./StatsigMetadata");
25
25
  const StorageProvider_1 = require("./StorageProvider");
26
26
  Object.defineProperty(exports, "Storage", { enumerable: true, get: function () { return StorageProvider_1.Storage; } });
27
- const UUID_1 = require("./UUID");
28
27
  __exportStar(require("./$_StatsigGlobal"), exports);
29
28
  __exportStar(require("./CacheKey"), exports);
30
29
  __exportStar(require("./ClientInterfaces"), exports);
@@ -58,8 +57,5 @@ __exportStar(require("./UrlOverrides"), exports);
58
57
  __exportStar(require("./UtitlityTypes"), exports);
59
58
  __exportStar(require("./UUID"), exports);
60
59
  __exportStar(require("./VisibilityObserving"), exports);
61
- __STATSIG__ = Object.assign(Object.assign({}, (__STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : {})), { EventLogger: EventLogger_1.EventLogger,
62
- Log: Log_1.Log,
63
- getUUID: UUID_1.getUUID,
64
- Storage: StorageProvider_1.Storage,
60
+ __STATSIG__ = Object.assign(Object.assign({}, (__STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : {})), { Log: Log_1.Log,
65
61
  SDK_VERSION: StatsigMetadata_1.SDK_VERSION });