@statsig/client-core 0.0.1-beta.3 → 0.0.1-beta.5

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.3",
3
+ "version": "0.0.1-beta.5",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -13,11 +13,16 @@ export declare class EventLogger {
13
13
  private _lastExposureMap;
14
14
  private _maxQueueSize;
15
15
  private _failedLogs;
16
+ private _hasFiredQuickFlush;
16
17
  constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon | null);
17
18
  enqueue(event: StatsigEventInternal): void;
18
19
  reset(): void;
19
20
  onVisibilityChanged(visibility: Visibility): void;
20
21
  shutdown(): Promise<void>;
22
+ /**
23
+ * We 'Quick Flush' following the very first enqueued event
24
+ */
25
+ private _quickFlushIfNeeded;
21
26
  private _shouldLogEvent;
22
27
  private _flushAndForget;
23
28
  private _flush;
@@ -70,6 +70,7 @@ var EventLogger = /** @class */ (function () {
70
70
  this._options = _options;
71
71
  this._queue = [];
72
72
  this._lastExposureMap = {};
73
+ this._hasFiredQuickFlush = false;
73
74
  this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
74
75
  var flushInterval = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
75
76
  this._flushTimer = setInterval(function () { return _this._flushAndForget(); }, flushInterval);
@@ -87,6 +88,7 @@ var EventLogger = /** @class */ (function () {
87
88
  }
88
89
  var _a = StatsigMetadata_1.StatsigMetadataProvider.get(), sdkType = _a.sdkType, sdkVersion = _a.sdkVersion;
89
90
  this._queue.push(__assign(__assign({}, event), { statsigMetadata: { sdkType: sdkType, sdkVersion: sdkVersion } }));
91
+ this._quickFlushIfNeeded();
90
92
  if (this._queue.length > this._maxQueueSize) {
91
93
  this._flushAndForget();
92
94
  }
@@ -116,6 +118,17 @@ var EventLogger = /** @class */ (function () {
116
118
  });
117
119
  });
118
120
  };
121
+ /**
122
+ * We 'Quick Flush' following the very first enqueued event
123
+ */
124
+ EventLogger.prototype._quickFlushIfNeeded = function () {
125
+ var _this = this;
126
+ if (this._hasFiredQuickFlush) {
127
+ return;
128
+ }
129
+ this._hasFiredQuickFlush = true;
130
+ setTimeout(function () { return _this._flushAndForget(); }, 200);
131
+ };
119
132
  EventLogger.prototype._shouldLogEvent = function (event) {
120
133
  var _a, _b, _c, _d;
121
134
  if (!(0, StatsigEvent_1.isExposureEvent)(event)) {
@@ -213,9 +226,9 @@ var EventLogger = /** @class */ (function () {
213
226
  },
214
227
  url: "".concat(api, "/rgstr"),
215
228
  retries: 3,
216
- headers: {
217
- 'Content-Type': 'application/json',
218
- 'STATSIG-EVENT-COUNT': String(events.length),
229
+ params: {
230
+ // ec = Event Count
231
+ ec: String(events.length),
219
232
  },
220
233
  })];
221
234
  case 1:
@@ -1,9 +1,11 @@
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 & {
@@ -11,8 +13,9 @@ type RequestArgsWithData = RequestArgs & {
11
13
  };
12
14
  export declare class NetworkCore {
13
15
  private _options;
16
+ private _emitter?;
14
17
  private readonly _timeout;
15
- constructor(_options: StatsigOptionsCommon | null);
18
+ constructor(_options: StatsigOptionsCommon | null, _emitter?: StatsigClientEmitEventFunc | undefined);
16
19
  post(args: RequestArgsWithData): Promise<string | null>;
17
20
  get(args: RequestArgs): Promise<string | null>;
18
21
  beacon(args: RequestArgsWithData): boolean;
@@ -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
  }
@@ -152,7 +151,7 @@ var NetworkCore = /** @class */ (function () {
152
151
  });
153
152
  return [2 /*return*/, text];
154
153
  case 4:
155
- error_1 = _a.sent();
154
+ error_1 = _b.sent();
156
155
  errorMessage = _getErrorMessage(controller, error_1);
157
156
  Diagnostics_1.Diagnostics.mark('_sendRequest:error', {
158
157
  error: errorMessage,
@@ -160,6 +159,7 @@ var NetworkCore = /** @class */ (function () {
160
159
  contentLength: response === null || response === void 0 ? void 0 : response.headers.get('content-length'),
161
160
  });
162
161
  if (!retries || retries <= 0) {
162
+ (_a = this._emitter) === null || _a === void 0 ? void 0 : _a.call(this, { event: 'error', error: error_1 });
163
163
  Log_1.Log.error('A networking error occured.', errorMessage);
164
164
  return [2 /*return*/, null];
165
165
  }
@@ -170,14 +170,20 @@ var NetworkCore = /** @class */ (function () {
170
170
  });
171
171
  };
172
172
  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();
173
+ var metadata = StatsigMetadata_1.StatsigMetadataProvider.get();
174
+ var url = new URL(args.url);
175
+ url.searchParams.append('k', args.sdkKey);
176
+ url.searchParams.append('st', metadata.sdkType);
177
+ url.searchParams.append('sv', metadata.sdkVersion);
178
+ url.searchParams.append('t', String(Date.now()));
179
+ url.searchParams.append('sid', SessionID_1.SessionID.get(args.sdkKey));
180
+ if (args.params) {
181
+ Object.entries(args.params).forEach(function (_a) {
182
+ var k = _a[0], v = _a[1];
183
+ url.searchParams.append(k, v);
184
+ });
185
+ }
186
+ return url.toString();
181
187
  };
182
188
  return NetworkCore;
183
189
  }());
@@ -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.3';
15
+ var SDK_VERSION = '0.0.1-beta.5';
16
16
  var metadata = {
17
17
  sdkVersion: SDK_VERSION,
18
18
  sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
@@ -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
  };