@statsig/client-core 0.0.1-beta.4 → 0.0.1-beta.6

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.4",
3
+ "version": "0.0.1-beta.6",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -4,7 +4,7 @@ import { StatsigEvent } from './StatsigEvent';
4
4
  import { DynamicConfig, Experiment, FeatureGate, Layer } from './StatsigTypes';
5
5
  import { StatsigUser } from './StatsigUser';
6
6
  export interface StatsigClientCommonInterface extends StatsigClientEventEmitterInterface {
7
- initialize(): Promise<void>;
7
+ initialize(): void;
8
8
  shutdown(): Promise<void>;
9
9
  }
10
10
  export interface OnDeviceEvaluationsInterface extends StatsigClientCommonInterface {
@@ -17,7 +17,7 @@ export interface OnDeviceEvaluationsInterface extends StatsigClientCommonInterfa
17
17
  }
18
18
  export interface PrecomputedEvaluationsInterface extends StatsigClientCommonInterface {
19
19
  getCurrentUser(): StatsigUser;
20
- updateUser(user: StatsigUser): Promise<void>;
20
+ updateUser(user: StatsigUser): void;
21
21
  checkGate(name: string, options: EvaluationOptions): boolean;
22
22
  getFeatureGate(name: string, options: EvaluationOptions): FeatureGate;
23
23
  getDynamicConfig(name: string, options: EvaluationOptions): DynamicConfig;
@@ -13,11 +13,18 @@ export declare class EventLogger {
13
13
  private _lastExposureMap;
14
14
  private _maxQueueSize;
15
15
  private _failedLogs;
16
+ private _hasRunQuickFlush;
17
+ private _creationTime;
16
18
  constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon | null);
17
19
  enqueue(event: StatsigEventInternal): void;
18
20
  reset(): void;
19
21
  onVisibilityChanged(visibility: Visibility): void;
20
22
  shutdown(): Promise<void>;
23
+ /**
24
+ * We 'Quick Flush' following the very first event enqueued
25
+ * within the quick flush window
26
+ */
27
+ private _quickFlushIfNeeded;
21
28
  private _shouldLogEvent;
22
29
  private _flushAndForget;
23
30
  private _flush;
@@ -60,6 +60,7 @@ var MAX_DEDUPER_KEYS = 1000;
60
60
  var DEDUPER_WINDOW_DURATION_MS = 60000;
61
61
  var MAX_FAILED_LOGS = 500;
62
62
  var DEFAULT_API = 'https://api.statsig.com/v1';
63
+ var QUICK_FLUSH_WINDOW_MS = 200;
63
64
  var EventLogger = /** @class */ (function () {
64
65
  function EventLogger(_sdkKey, _emitter, _network, _options) {
65
66
  var _this = this;
@@ -70,6 +71,8 @@ var EventLogger = /** @class */ (function () {
70
71
  this._options = _options;
71
72
  this._queue = [];
72
73
  this._lastExposureMap = {};
74
+ this._hasRunQuickFlush = false;
75
+ this._creationTime = Date.now();
73
76
  this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
74
77
  var flushInterval = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
75
78
  this._flushTimer = setInterval(function () { return _this._flushAndForget(); }, flushInterval);
@@ -87,6 +90,7 @@ var EventLogger = /** @class */ (function () {
87
90
  }
88
91
  var _a = StatsigMetadata_1.StatsigMetadataProvider.get(), sdkType = _a.sdkType, sdkVersion = _a.sdkVersion;
89
92
  this._queue.push(__assign(__assign({}, event), { statsigMetadata: { sdkType: sdkType, sdkVersion: sdkVersion } }));
93
+ this._quickFlushIfNeeded();
90
94
  if (this._queue.length > this._maxQueueSize) {
91
95
  this._flushAndForget();
92
96
  }
@@ -116,6 +120,21 @@ var EventLogger = /** @class */ (function () {
116
120
  });
117
121
  });
118
122
  };
123
+ /**
124
+ * We 'Quick Flush' following the very first event enqueued
125
+ * within the quick flush window
126
+ */
127
+ EventLogger.prototype._quickFlushIfNeeded = function () {
128
+ var _this = this;
129
+ if (this._hasRunQuickFlush) {
130
+ return;
131
+ }
132
+ this._hasRunQuickFlush = true;
133
+ if (Date.now() - this._creationTime > QUICK_FLUSH_WINDOW_MS) {
134
+ return;
135
+ }
136
+ setTimeout(function () { return _this._flushAndForget(); }, QUICK_FLUSH_WINDOW_MS);
137
+ };
119
138
  EventLogger.prototype._shouldLogEvent = function (event) {
120
139
  var _a, _b, _c, _d;
121
140
  if (!(0, StatsigEvent_1.isExposureEvent)(event)) {
@@ -213,15 +232,15 @@ var EventLogger = /** @class */ (function () {
213
232
  },
214
233
  url: "".concat(api, "/rgstr"),
215
234
  retries: 3,
216
- headers: {
217
- 'Content-Type': 'application/json',
218
- 'STATSIG-EVENT-COUNT': String(events.length),
235
+ params: {
236
+ // ec = Event Count
237
+ ec: String(events.length),
219
238
  },
220
239
  })];
221
240
  case 1:
222
241
  result = _a.sent();
223
- if (result) {
224
- return [2 /*return*/, JSON.parse(result)];
242
+ if (result === null || result === void 0 ? void 0 : result.body) {
243
+ return [2 /*return*/, JSON.parse(result.body)];
225
244
  }
226
245
  return [2 /*return*/, { success: false }];
227
246
  }
@@ -1,20 +1,27 @@
1
+ import { StatsigClientEmitEventFunc } from './StatsigClientBase';
1
2
  import { StatsigOptionsCommon } from './StatsigOptionsCommon';
2
3
  type RequestArgs = {
3
4
  sdkKey: string;
4
5
  url: string;
5
6
  timeoutMs?: number;
6
7
  retries?: number;
8
+ params?: Record<string, string>;
7
9
  headers?: Record<string, string>;
8
10
  };
9
11
  type RequestArgsWithData = RequestArgs & {
10
12
  data: Record<string, unknown>;
11
13
  };
14
+ type NetworkResponse = {
15
+ body: string | null;
16
+ code: number;
17
+ };
12
18
  export declare class NetworkCore {
13
19
  private _options;
20
+ private _emitter?;
14
21
  private readonly _timeout;
15
- constructor(_options: StatsigOptionsCommon | null);
16
- post(args: RequestArgsWithData): Promise<string | null>;
17
- get(args: RequestArgs): Promise<string | null>;
22
+ constructor(_options: StatsigOptionsCommon | null, _emitter?: StatsigClientEmitEventFunc | undefined);
23
+ post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
24
+ get(args: RequestArgs): Promise<NetworkResponse | null>;
18
25
  beacon(args: RequestArgsWithData): boolean;
19
26
  private _sendRequest;
20
27
  private _getPopulatedURL;
@@ -79,9 +79,10 @@ var NetworkError = /** @class */ (function (_super) {
79
79
  return NetworkError;
80
80
  }(Error));
81
81
  var NetworkCore = /** @class */ (function () {
82
- function NetworkCore(_options) {
82
+ function NetworkCore(_options, _emitter) {
83
83
  var _a;
84
84
  this._options = _options;
85
+ this._emitter = _emitter;
85
86
  this._timeout = (_a = _options === null || _options === void 0 ? void 0 : _options.networkTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT_MS;
86
87
  }
87
88
  NetworkCore.prototype.post = function (args) {
@@ -114,35 +115,33 @@ var NetworkCore = /** @class */ (function () {
114
115
  return navigator.sendBeacon(url, JSON.stringify(args.data));
115
116
  };
116
117
  NetworkCore.prototype._sendRequest = function (args) {
118
+ var _a;
117
119
  return __awaiter(this, void 0, void 0, function () {
118
120
  var method, body, retries, controller, handle, response, fullUrl, text, error_1, errorMessage;
119
121
  var _this = this;
120
- return __generator(this, function (_a) {
121
- switch (_a.label) {
122
+ return __generator(this, function (_b) {
123
+ switch (_b.label) {
122
124
  case 0:
123
125
  method = args.method, body = args.body, retries = args.retries;
124
126
  controller = new AbortController();
125
127
  handle = setTimeout(function () { return controller.abort("Timeout of ".concat(_this._timeout, "ms expired.")); }, this._timeout);
126
128
  response = null;
127
- _a.label = 1;
129
+ _b.label = 1;
128
130
  case 1:
129
- _a.trys.push([1, 4, , 5]);
131
+ _b.trys.push([1, 4, , 5]);
130
132
  fullUrl = this._getPopulatedURL(args);
131
133
  return [4 /*yield*/, fetch(fullUrl, {
132
134
  method: method,
133
135
  body: body,
134
- headers: __assign({
135
- // Must set this content type to bypass cors
136
- // can override via headers if necessary (recommended for logevent)
137
- 'Content-Type': 'text/plain' }, args.headers),
136
+ headers: __assign({}, args.headers),
138
137
  signal: controller.signal,
139
138
  })];
140
139
  case 2:
141
- response = _a.sent();
140
+ response = _b.sent();
142
141
  clearTimeout(handle);
143
142
  return [4 /*yield*/, response.text()];
144
143
  case 3:
145
- text = _a.sent();
144
+ text = _b.sent();
146
145
  if (!response.ok) {
147
146
  throw new NetworkError('Fetch Failure', text);
148
147
  }
@@ -150,9 +149,12 @@ var NetworkCore = /** @class */ (function () {
150
149
  status: response.status,
151
150
  contentLength: response.headers.get('content-length'),
152
151
  });
153
- return [2 /*return*/, text];
152
+ return [2 /*return*/, {
153
+ body: text,
154
+ code: response.status,
155
+ }];
154
156
  case 4:
155
- error_1 = _a.sent();
157
+ error_1 = _b.sent();
156
158
  errorMessage = _getErrorMessage(controller, error_1);
157
159
  Diagnostics_1.Diagnostics.mark('_sendRequest:error', {
158
160
  error: errorMessage,
@@ -160,6 +162,7 @@ var NetworkCore = /** @class */ (function () {
160
162
  contentLength: response === null || response === void 0 ? void 0 : response.headers.get('content-length'),
161
163
  });
162
164
  if (!retries || retries <= 0) {
165
+ (_a = this._emitter) === null || _a === void 0 ? void 0 : _a.call(this, { event: 'error', error: error_1 });
163
166
  Log_1.Log.error('A networking error occured.', errorMessage);
164
167
  return [2 /*return*/, null];
165
168
  }
@@ -170,14 +173,20 @@ var NetworkCore = /** @class */ (function () {
170
173
  });
171
174
  };
172
175
  NetworkCore.prototype._getPopulatedURL = function (args) {
173
- var statsigMetadata = StatsigMetadata_1.StatsigMetadataProvider.get();
174
- var fullUrl = new URL(args.url);
175
- fullUrl.searchParams.append('k', args.sdkKey);
176
- fullUrl.searchParams.append('st', statsigMetadata.sdkType);
177
- fullUrl.searchParams.append('sv', statsigMetadata.sdkVersion);
178
- fullUrl.searchParams.append('t', String(Date.now()));
179
- fullUrl.searchParams.append('sid', SessionID_1.SessionID.get(args.sdkKey));
180
- return fullUrl.toString();
176
+ var metadata = StatsigMetadata_1.StatsigMetadataProvider.get();
177
+ var url = new URL(args.url);
178
+ url.searchParams.append('k', args.sdkKey);
179
+ url.searchParams.append('st', metadata.sdkType);
180
+ url.searchParams.append('sv', metadata.sdkVersion);
181
+ url.searchParams.append('t', String(Date.now()));
182
+ url.searchParams.append('sid', SessionID_1.SessionID.get(args.sdkKey));
183
+ if (args.params) {
184
+ Object.entries(args.params).forEach(function (_a) {
185
+ var k = _a[0], v = _a[1];
186
+ url.searchParams.append(k, v);
187
+ });
188
+ }
189
+ return url.toString();
181
190
  };
182
191
  return NetworkCore;
183
192
  }());
package/src/StableID.js CHANGED
@@ -64,7 +64,7 @@ exports.StableID = {
64
64
  },
65
65
  };
66
66
  function _getStableIDStorageKey(sdkKey) {
67
- return "statsig.stable_id:".concat((0, Hashing_1.DJB2)(sdkKey));
67
+ return "statsig.stable_id.".concat((0, Hashing_1.DJB2)(sdkKey));
68
68
  }
69
69
  function _persistToStorage(stableID, sdkKey) {
70
70
  var storageKey = _getStableIDStorageKey(sdkKey);
@@ -3,35 +3,28 @@ import { ErrorBoundary } from './ErrorBoundary';
3
3
  import { EventLogger } from './EventLogger';
4
4
  import { NetworkCore } from './NetworkCore';
5
5
  import { StatsigClientEvent, StatsigClientEventCallback, StatsigClientEventData, StatsigClientEventEmitterInterface, StatsigLoadingStatus } from './StatsigClientEventEmitter';
6
- import { DataSource, StatsigDataProvider } from './StatsigDataProvider';
6
+ import { StatsigDataAdapter, StatsigDataAdapterResult } from './StatsigDataAdapter';
7
7
  import { StatsigEventInternal } from './StatsigEvent';
8
8
  import { StatsigOptionsCommon } from './StatsigOptionsCommon';
9
9
  import { StatsigUser } from './StatsigUser';
10
- type DataProviderResult = {
11
- data: string | null;
12
- source: DataSource;
13
- };
14
10
  export type EvaluationOptions = {
15
11
  disableExposureLog?: boolean;
16
12
  };
17
13
  export declare const DEFAULT_EVAL_OPTIONS: EvaluationOptions;
18
14
  export type StatsigClientEmitEventFunc = (data: StatsigClientEventData) => void;
19
- export declare class StatsigClientBase implements StatsigClientEventEmitterInterface {
15
+ export declare abstract class StatsigClientBase implements StatsigClientEventEmitterInterface {
16
+ protected readonly _sdkKey: string;
17
+ protected readonly _adapter: StatsigDataAdapter;
20
18
  loadingStatus: StatsigLoadingStatus;
21
- protected _errorBoundary: ErrorBoundary;
22
- protected _logger: EventLogger;
23
- protected _sdkKey: string;
24
- protected _dataProviders: StatsigDataProvider[];
19
+ protected readonly _errorBoundary: ErrorBoundary;
20
+ protected readonly _logger: EventLogger;
25
21
  private _listeners;
26
- constructor(sdkKey: string, network: NetworkCore, options: StatsigOptionsCommon | null, dataProviders: StatsigDataProvider[]);
22
+ constructor(_sdkKey: string, _adapter: StatsigDataAdapter, network: NetworkCore, options: StatsigOptionsCommon | null);
27
23
  on(event: StatsigClientEvent | '*', listener: StatsigClientEventCallback): void;
28
24
  off(event: StatsigClientEvent | '*', listener: StatsigClientEventCallback): void;
25
+ getDataAdapter(): StatsigDataAdapter;
29
26
  protected emit(data: StatsigClientEventData): void;
30
27
  protected _setStatus(newStatus: StatsigLoadingStatus): void;
31
- protected _getDataFromProviders(user?: StatsigUser): DataProviderResult;
32
- protected _getDataFromProvidersAsync(user?: StatsigUser): Promise<DataProviderResult>;
33
- protected _getDataPostInitFromProviders(currentData: string | null, user?: StatsigUser): Promise<DataProviderResult>;
34
- protected _saveToDataProviders(currentData: string | null, user?: StatsigUser): void;
35
28
  protected _enqueueExposure(options: EvaluationOptions, exposure: StatsigEventInternal): void;
29
+ protected _runPostUpdate(current: StatsigDataAdapterResult | null, user?: StatsigUser): void;
36
30
  }
37
- export {};
@@ -1,47 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- var __asyncValues = (this && this.__asyncValues) || function (o) {
39
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
40
- var m = o[Symbol.asyncIterator], i;
41
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
42
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
43
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
44
- };
45
2
  Object.defineProperty(exports, "__esModule", { value: true });
46
3
  exports.StatsigClientBase = exports.DEFAULT_EVAL_OPTIONS = void 0;
47
4
  require("./$_StatsigGlobal");
@@ -53,22 +10,23 @@ exports.DEFAULT_EVAL_OPTIONS = {
53
10
  disableExposureLog: false,
54
11
  };
55
12
  var StatsigClientBase = /** @class */ (function () {
56
- function StatsigClientBase(sdkKey, network, options, dataProviders) {
13
+ function StatsigClientBase(_sdkKey, _adapter, network, options) {
57
14
  var _a, _b;
15
+ this._sdkKey = _sdkKey;
16
+ this._adapter = _adapter;
58
17
  this.loadingStatus = 'Uninitialized';
59
18
  this._listeners = {};
60
- this._logger = new EventLogger_1.EventLogger(sdkKey, this.emit.bind(this), network, options);
61
- this._sdkKey = sdkKey;
62
- this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(sdkKey);
19
+ Log_1.Log.level = (_a = options === null || options === void 0 ? void 0 : options.logLevel) !== null && _a !== void 0 ? _a : Log_1.LogLevel.Warn;
20
+ this._logger = new EventLogger_1.EventLogger(_sdkKey, this.emit.bind(this), network, options);
21
+ this._errorBoundary = new ErrorBoundary_1.ErrorBoundary(_sdkKey);
63
22
  if (options === null || options === void 0 ? void 0 : options.overrideStableID) {
64
- StableID_1.StableID.setOverride(options.overrideStableID, sdkKey);
23
+ StableID_1.StableID.setOverride(options.overrideStableID, _sdkKey);
65
24
  }
66
25
  __STATSIG__ = __STATSIG__ !== null && __STATSIG__ !== void 0 ? __STATSIG__ : {};
67
- var instances = (_a = __STATSIG__.instances) !== null && _a !== void 0 ? _a : new Set();
26
+ var instances = (_b = __STATSIG__.instances) !== null && _b !== void 0 ? _b : new Set();
68
27
  instances.add(this);
69
28
  __STATSIG__.instances = instances;
70
- Log_1.Log.level = (_b = options === null || options === void 0 ? void 0 : options.logLevel) !== null && _b !== void 0 ? _b : Log_1.LogLevel.Warn;
71
- this._dataProviders = dataProviders;
29
+ this._adapter.attach(_sdkKey, options);
72
30
  }
73
31
  StatsigClientBase.prototype.on = function (event, listener) {
74
32
  if (!this._listeners[event]) {
@@ -84,6 +42,9 @@ var StatsigClientBase = /** @class */ (function () {
84
42
  }
85
43
  }
86
44
  };
45
+ StatsigClientBase.prototype.getDataAdapter = function () {
46
+ return this._adapter;
47
+ };
87
48
  StatsigClientBase.prototype.emit = function (data) {
88
49
  var _a;
89
50
  if (this._listeners[data.event]) {
@@ -95,178 +56,18 @@ var StatsigClientBase = /** @class */ (function () {
95
56
  this.loadingStatus = newStatus;
96
57
  this.emit({ event: 'status_change', loadingStatus: newStatus });
97
58
  };
98
- StatsigClientBase.prototype._getDataFromProviders = function (user) {
99
- var _a;
100
- for (var _i = 0, _b = this._dataProviders; _i < _b.length; _i++) {
101
- var provider = _b[_i];
102
- var data = (_a = provider.getData) === null || _a === void 0 ? void 0 : _a.call(provider, this._sdkKey, user);
103
- if (data) {
104
- return { data: data, source: provider.source };
105
- }
106
- }
107
- return { data: null, source: 'NoValues' };
108
- };
109
- StatsigClientBase.prototype._getDataFromProvidersAsync = function (user) {
110
- var _a, e_1, _b, _c;
111
- var _d;
112
- return __awaiter(this, void 0, void 0, function () {
113
- var _e, _f, _g, provider, data, e_1_1;
114
- return __generator(this, function (_h) {
115
- switch (_h.label) {
116
- case 0:
117
- _h.trys.push([0, 6, 7, 12]);
118
- _e = true, _f = __asyncValues(this._dataProviders);
119
- _h.label = 1;
120
- case 1: return [4 /*yield*/, _f.next()];
121
- case 2:
122
- if (!(_g = _h.sent(), _a = _g.done, !_a)) return [3 /*break*/, 5];
123
- _c = _g.value;
124
- _e = false;
125
- provider = _c;
126
- return [4 /*yield*/, ((_d = provider.getDataAsync) === null || _d === void 0 ? void 0 : _d.call(provider, this._sdkKey, user))];
127
- case 3:
128
- data = _h.sent();
129
- if (data) {
130
- return [2 /*return*/, { data: data, source: provider.source }];
131
- }
132
- _h.label = 4;
133
- case 4:
134
- _e = true;
135
- return [3 /*break*/, 1];
136
- case 5: return [3 /*break*/, 12];
137
- case 6:
138
- e_1_1 = _h.sent();
139
- e_1 = { error: e_1_1 };
140
- return [3 /*break*/, 12];
141
- case 7:
142
- _h.trys.push([7, , 10, 11]);
143
- if (!(!_e && !_a && (_b = _f.return))) return [3 /*break*/, 9];
144
- return [4 /*yield*/, _b.call(_f)];
145
- case 8:
146
- _h.sent();
147
- _h.label = 9;
148
- case 9: return [3 /*break*/, 11];
149
- case 10:
150
- if (e_1) throw e_1.error;
151
- return [7 /*endfinally*/];
152
- case 11: return [7 /*endfinally*/];
153
- case 12: return [2 /*return*/, { data: null, source: 'NoValues' }];
154
- }
155
- });
156
- });
157
- };
158
- StatsigClientBase.prototype._getDataPostInitFromProviders = function (currentData, user) {
159
- var _a, e_2, _b, _c;
160
- var _d;
161
- return __awaiter(this, void 0, void 0, function () {
162
- var _e, _f, _g, provider, data, e_2_1;
163
- return __generator(this, function (_h) {
164
- switch (_h.label) {
165
- case 0:
166
- _h.trys.push([0, 6, 7, 12]);
167
- _e = true, _f = __asyncValues(this._dataProviders);
168
- _h.label = 1;
169
- case 1: return [4 /*yield*/, _f.next()];
170
- case 2:
171
- if (!(_g = _h.sent(), _a = _g.done, !_a)) return [3 /*break*/, 5];
172
- _c = _g.value;
173
- _e = false;
174
- provider = _c;
175
- return [4 /*yield*/, ((_d = provider.getDataPostInit) === null || _d === void 0 ? void 0 : _d.call(provider, this._sdkKey, currentData, user))];
176
- case 3:
177
- data = _h.sent();
178
- if (data) {
179
- return [2 /*return*/, { data: data, source: provider.source }];
180
- }
181
- _h.label = 4;
182
- case 4:
183
- _e = true;
184
- return [3 /*break*/, 1];
185
- case 5: return [3 /*break*/, 12];
186
- case 6:
187
- e_2_1 = _h.sent();
188
- e_2 = { error: e_2_1 };
189
- return [3 /*break*/, 12];
190
- case 7:
191
- _h.trys.push([7, , 10, 11]);
192
- if (!(!_e && !_a && (_b = _f.return))) return [3 /*break*/, 9];
193
- return [4 /*yield*/, _b.call(_f)];
194
- case 8:
195
- _h.sent();
196
- _h.label = 9;
197
- case 9: return [3 /*break*/, 11];
198
- case 10:
199
- if (e_2) throw e_2.error;
200
- return [7 /*endfinally*/];
201
- case 11: return [7 /*endfinally*/];
202
- case 12: return [2 /*return*/, { data: null, source: 'NoValues' }];
203
- }
204
- });
205
- });
206
- };
207
- StatsigClientBase.prototype._saveToDataProviders = function (currentData, user) {
208
- var _this = this;
209
- (function () { return __awaiter(_this, void 0, void 0, function () {
210
- var latest, data, _a, _b, _c, provider, e_3_1;
211
- var _d, e_3, _e, _f;
212
- var _g, _h;
213
- return __generator(this, function (_j) {
214
- switch (_j.label) {
215
- case 0: return [4 /*yield*/, this._getDataPostInitFromProviders(currentData, user)];
216
- case 1:
217
- latest = _j.sent();
218
- data = (_g = latest.data) !== null && _g !== void 0 ? _g : currentData;
219
- if (!data) {
220
- return [2 /*return*/];
221
- }
222
- _j.label = 2;
223
- case 2:
224
- _j.trys.push([2, 8, 9, 14]);
225
- _a = true, _b = __asyncValues(this._dataProviders);
226
- _j.label = 3;
227
- case 3: return [4 /*yield*/, _b.next()];
228
- case 4:
229
- if (!(_c = _j.sent(), _d = _c.done, !_d)) return [3 /*break*/, 7];
230
- _f = _c.value;
231
- _a = false;
232
- provider = _f;
233
- return [4 /*yield*/, ((_h = provider.setDataPostInit) === null || _h === void 0 ? void 0 : _h.call(provider, this._sdkKey, data, user))];
234
- case 5:
235
- _j.sent();
236
- _j.label = 6;
237
- case 6:
238
- _a = true;
239
- return [3 /*break*/, 3];
240
- case 7: return [3 /*break*/, 14];
241
- case 8:
242
- e_3_1 = _j.sent();
243
- e_3 = { error: e_3_1 };
244
- return [3 /*break*/, 14];
245
- case 9:
246
- _j.trys.push([9, , 12, 13]);
247
- if (!(!_a && !_d && (_e = _b.return))) return [3 /*break*/, 11];
248
- return [4 /*yield*/, _e.call(_b)];
249
- case 10:
250
- _j.sent();
251
- _j.label = 11;
252
- case 11: return [3 /*break*/, 13];
253
- case 12:
254
- if (e_3) throw e_3.error;
255
- return [7 /*endfinally*/];
256
- case 13: return [7 /*endfinally*/];
257
- case 14: return [2 /*return*/];
258
- }
259
- });
260
- }); })().catch(function (error) {
261
- _this.emit({ event: 'error', error: error });
262
- });
263
- };
264
59
  StatsigClientBase.prototype._enqueueExposure = function (options, exposure) {
265
60
  if (options.disableExposureLog === true) {
266
61
  return;
267
62
  }
268
63
  this._logger.enqueue(exposure);
269
64
  };
65
+ StatsigClientBase.prototype._runPostUpdate = function (current, user) {
66
+ var _a, _b;
67
+ (_b = (_a = this._adapter).handlePostUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, current, user).catch(function (err) {
68
+ Log_1.Log.error('An error occurred after update.', err);
69
+ });
70
+ };
270
71
  return StatsigClientBase;
271
72
  }());
272
73
  exports.StatsigClientBase = StatsigClientBase;
@@ -0,0 +1,27 @@
1
+ import { StatsigOptionsCommon } from './StatsigOptionsCommon';
2
+ import { StatsigUser } from './StatsigUser';
3
+ export type DataSource = 'Uninitialized' | 'Loading' | 'NoValues' | 'Cache' | 'Network' | 'NetworkNotModified' | 'Bootstrap' | 'Prefetch';
4
+ export type StatsigDataAdapterResult = {
5
+ readonly source: DataSource;
6
+ readonly data: string;
7
+ };
8
+ export type StatsigDataAdapter = {
9
+ /**
10
+ * Called when the StatsigDataAdapter is attached to the Statsig client instance during construction.
11
+ * @param {string} sdkKey The SDK key being used by the Statsig client.
12
+ * @param {StatsigOptionsCommon | null} options The StatsigOptions being used by the Statsig client.
13
+ */
14
+ readonly attach: (sdkKey: string, options: StatsigOptionsCommon | null) => void;
15
+ /**
16
+ * Synchronously get data for the given user (if any). Called during initialization and updates.
17
+ * @param {StatsigUser | undefined} user The StatsigUser to get data for.
18
+ * @returns {StatsigDataAdapterResult | null} The data that was found for the given StatsigUser.
19
+ */
20
+ readonly getData: (user?: StatsigUser) => StatsigDataAdapterResult | null;
21
+ /**
22
+ *
23
+ * @param {StatsigDataAdapterResult | null} result The values that were found and applied during initialization or update.
24
+ * @param {StatsigUser | undefined} user The StatsigUser object that was used.
25
+ */
26
+ readonly handlePostUpdate: (result: StatsigDataAdapterResult | null, user?: StatsigUser) => Promise<void>;
27
+ };
@@ -1,5 +1,4 @@
1
- import { DataSource } from './StatsigDataProvider';
2
- import { DynamicConfig, FeatureGate } from './StatsigTypes';
1
+ import { DynamicConfig, EvaluationDetails, FeatureGate } from './StatsigTypes';
3
2
  import { StatsigUser } from './StatsigUser';
4
3
  export type SecondaryExposure = {
5
4
  gate: string;
@@ -27,5 +26,5 @@ export declare function createLayerParameterExposure(user: StatsigUser, layerNam
27
26
  undelegated_secondary_exposures?: SecondaryExposure[];
28
27
  secondary_exposures: SecondaryExposure[];
29
28
  allocated_experiment_name: string;
30
- source: DataSource;
29
+ details: EvaluationDetails;
31
30
  }): StatsigEventInternal;
@@ -4,12 +4,12 @@ exports.createLayerParameterExposure = exports.createConfigExposure = exports.cr
4
4
  var CONFIG_EXPOSURE_NAME = 'statsig::config_exposure';
5
5
  var GATE_EXPOSURE_NAME = 'statsig::gate_exposure';
6
6
  var LAYER_EXPOSURE_NAME = 'statsig::layer_exposure';
7
- function createExposure(eventName, user, metadata, secondaryExposures) {
7
+ function createExposure(eventName, user, details, metadata, secondaryExposures) {
8
8
  return {
9
9
  eventName: eventName,
10
10
  user: user,
11
11
  value: null,
12
- metadata: metadata,
12
+ metadata: _addEvaluationDetailsToMetadata(details, metadata),
13
13
  secondaryExposures: secondaryExposures,
14
14
  time: Date.now(),
15
15
  };
@@ -20,19 +20,17 @@ function isExposureEvent(_a) {
20
20
  }
21
21
  exports.isExposureEvent = isExposureEvent;
22
22
  function createGateExposure(user, gate, secondaryExposures) {
23
- return createExposure(GATE_EXPOSURE_NAME, user, {
23
+ return createExposure(GATE_EXPOSURE_NAME, user, gate.details, {
24
24
  gate: gate.name,
25
25
  gateValue: String(gate.value),
26
26
  ruleID: gate.ruleID,
27
- reason: gate.source,
28
27
  }, secondaryExposures !== null && secondaryExposures !== void 0 ? secondaryExposures : []);
29
28
  }
30
29
  exports.createGateExposure = createGateExposure;
31
30
  function createConfigExposure(user, config, secondaryExposures) {
32
- return createExposure(CONFIG_EXPOSURE_NAME, user, {
31
+ return createExposure(CONFIG_EXPOSURE_NAME, user, config.details, {
33
32
  config: config.name,
34
33
  ruleID: config.ruleID,
35
- reason: config.source,
36
34
  }, secondaryExposures !== null && secondaryExposures !== void 0 ? secondaryExposures : []);
37
35
  }
38
36
  exports.createConfigExposure = createConfigExposure;
@@ -45,13 +43,22 @@ function createLayerParameterExposure(user, layerName, parameterName, spec) {
45
43
  allocatedExperiment = spec.allocated_experiment_name;
46
44
  secondaryExposures = spec.secondary_exposures;
47
45
  }
48
- return createExposure(LAYER_EXPOSURE_NAME, user, {
46
+ return createExposure(LAYER_EXPOSURE_NAME, user, spec.details, {
49
47
  config: layerName,
50
48
  parameterName: parameterName,
51
49
  ruleID: spec.rule_id,
52
50
  allocatedExperiment: allocatedExperiment,
53
51
  isExplicitParameter: String(isExplicit),
54
- reason: spec.source,
55
52
  }, secondaryExposures);
56
53
  }
57
54
  exports.createLayerParameterExposure = createLayerParameterExposure;
55
+ function _addEvaluationDetailsToMetadata(details, metadata) {
56
+ metadata['reason'] = details.reason;
57
+ if (details.lcut) {
58
+ metadata['lcut'] = String(details.lcut);
59
+ }
60
+ if (details.receivedAt) {
61
+ metadata['receivedAt'] = String(details.receivedAt);
62
+ }
63
+ return metadata;
64
+ }
@@ -12,7 +12,7 @@ var __assign = (this && this.__assign) || function () {
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.StatsigMetadataProvider = void 0;
15
- var SDK_VERSION = '0.0.1-beta.4';
15
+ var SDK_VERSION = '0.0.1-beta.6';
16
16
  var metadata = {
17
17
  sdkVersion: SDK_VERSION,
18
18
  sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
@@ -1,5 +1,5 @@
1
1
  import { LogLevel } from './Log';
2
- import { StatsigDataProvider } from './StatsigDataProvider';
2
+ import { StatsigDataAdapter } from './StatsigDataAdapter';
3
3
  /** Common options for configuring the Statsig SDK. */
4
4
  export type StatsigOptionsCommon = {
5
5
  /**
@@ -22,10 +22,10 @@ export type StatsigOptionsCommon = {
22
22
  */
23
23
  logLevel?: LogLevel;
24
24
  /**
25
- * StatsigDataProvider implementors used to customize the initialization flow.
26
- * Default: LocalStorageCache then Network
25
+ * StatsigDataAdapter implementor used to customize the initialization flow.
26
+ * Default: EvaluationsDataAdapter (Precomputed) or SpecsDataAdapter (OnDevice)
27
27
  */
28
- dataProviders?: StatsigDataProvider[];
28
+ dataAdapter?: StatsigDataAdapter;
29
29
  /**
30
30
  * The maximum amount of time (in milliseconds) that any network request can take
31
31
  * before timing out. Default: 10,000 (10 seconds)
@@ -1,10 +1,15 @@
1
1
  export type Flatten<T> = {
2
2
  [K in keyof T]: T[K];
3
3
  } & {};
4
+ export type EvaluationDetails = {
5
+ reason: string;
6
+ lcut?: number;
7
+ receivedAt?: number;
8
+ };
4
9
  type EvaluatedSpec = {
5
10
  readonly name: string;
6
11
  readonly ruleID: string;
7
- readonly source: string;
12
+ readonly details: EvaluationDetails;
8
13
  };
9
14
  export type FeatureGate = EvaluatedSpec & {
10
15
  readonly value: boolean;
@@ -16,7 +21,7 @@ export type Experiment = DynamicConfig;
16
21
  export type Layer = EvaluatedSpec & {
17
22
  readonly getValue: (parameterName: string) => unknown;
18
23
  };
19
- export declare function makeFeatureGate(name: string, source: string, ruleID?: string, value?: boolean): FeatureGate;
20
- export declare function makeDynamicConfig(name: string, source: string, ruleID?: string, value?: Record<string, unknown>): DynamicConfig;
21
- export declare function makeLayer(name: string, source: string, ruleID?: string, getValue?: (param: string) => unknown): Layer;
24
+ export declare function makeFeatureGate(name: string, details: EvaluationDetails, ruleID?: string, value?: boolean): FeatureGate;
25
+ export declare function makeDynamicConfig(name: string, details: EvaluationDetails, ruleID?: string, value?: Record<string, unknown>): DynamicConfig;
26
+ export declare function makeLayer(name: string, details: EvaluationDetails, ruleID?: string, getValue?: (param: string) => unknown): Layer;
22
27
  export {};
@@ -2,28 +2,28 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeLayer = exports.makeDynamicConfig = exports.makeFeatureGate = void 0;
4
4
  var DEFAULT_RULE = 'default';
5
- function makeFeatureGate(name, source, ruleID, value) {
5
+ function makeFeatureGate(name, details, ruleID, value) {
6
6
  return {
7
7
  name: name,
8
- source: source,
8
+ details: details,
9
9
  ruleID: ruleID !== null && ruleID !== void 0 ? ruleID : DEFAULT_RULE,
10
10
  value: value === true,
11
11
  };
12
12
  }
13
13
  exports.makeFeatureGate = makeFeatureGate;
14
- function makeDynamicConfig(name, source, ruleID, value) {
14
+ function makeDynamicConfig(name, details, ruleID, value) {
15
15
  return {
16
16
  name: name,
17
- source: source,
17
+ details: details,
18
18
  ruleID: ruleID !== null && ruleID !== void 0 ? ruleID : DEFAULT_RULE,
19
19
  value: value !== null && value !== void 0 ? value : {},
20
20
  };
21
21
  }
22
22
  exports.makeDynamicConfig = makeDynamicConfig;
23
- function makeLayer(name, source, ruleID, getValue) {
23
+ function makeLayer(name, details, ruleID, getValue) {
24
24
  return {
25
25
  name: name,
26
- source: source,
26
+ details: details,
27
27
  getValue: getValue !== null && getValue !== void 0 ? getValue : (function () { return undefined; }),
28
28
  ruleID: ruleID !== null && ruleID !== void 0 ? ruleID : DEFAULT_RULE,
29
29
  };
@@ -16,8 +16,7 @@ function normalizeUser(original, environment) {
16
16
  }
17
17
  exports.normalizeUser = normalizeUser;
18
18
  function getUserStorageKey(sdkKey, user) {
19
- var key = (0, Hashing_1.DJB2)(JSON.stringify(_getSortedObject({ sdkKey: sdkKey, user: user })));
20
- return "statsig.user_cache.".concat(key);
19
+ return (0, Hashing_1.DJB2)(JSON.stringify(_getSortedObject({ sdkKey: sdkKey, user: user })));
21
20
  }
22
21
  exports.getUserStorageKey = getUserStorageKey;
23
22
  function _getSortedObject(object) {
package/src/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export * from './NetworkCore';
12
12
  export * from './StableID';
13
13
  export * from './StatsigClientBase';
14
14
  export * from './StatsigClientEventEmitter';
15
- export * from './StatsigDataProvider';
15
+ export * from './StatsigDataAdapter';
16
16
  export * from './StatsigEvent';
17
17
  export * from './StatsigMetadata';
18
18
  export * from './StatsigOptionsCommon';
package/src/index.js CHANGED
@@ -41,7 +41,7 @@ __exportStar(require("./NetworkCore"), exports);
41
41
  __exportStar(require("./StableID"), exports);
42
42
  __exportStar(require("./StatsigClientBase"), exports);
43
43
  __exportStar(require("./StatsigClientEventEmitter"), exports);
44
- __exportStar(require("./StatsigDataProvider"), exports);
44
+ __exportStar(require("./StatsigDataAdapter"), exports);
45
45
  __exportStar(require("./StatsigEvent"), exports);
46
46
  __exportStar(require("./StatsigMetadata"), exports);
47
47
  __exportStar(require("./StatsigOptionsCommon"), exports);
@@ -1,9 +0,0 @@
1
- import { StatsigUser } from './StatsigUser';
2
- export type DataSource = 'Uninitialized' | 'Loading' | 'NoValues' | 'Cache' | 'Network' | 'Bootstrap' | 'Prefetch';
3
- export type StatsigDataProvider = {
4
- readonly getData?: (sdkKey: string, user?: StatsigUser) => string | null;
5
- readonly getDataAsync?: (sdkKey: string, user?: StatsigUser) => Promise<string | null>;
6
- readonly getDataPostInit?: (sdkKey: string, currentData: string | null, user?: StatsigUser) => Promise<string | null>;
7
- readonly setDataPostInit?: (sdkKey: string, data: string, user?: StatsigUser) => Promise<void>;
8
- readonly source: DataSource;
9
- };