@statsig/client-core 3.0.0 → 3.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/client-core",
3
- "version": "3.0.0",
3
+ "version": "3.2.0-beta.1",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1,3 +1,4 @@
1
+ import { StatsigUser } from './StatsigUser';
1
2
  export type SpecCondition = {
2
3
  type: string;
3
4
  targetValue: unknown;
@@ -38,4 +39,6 @@ export type DownloadConfigSpecsResponse = {
38
39
  layer_configs: Spec[];
39
40
  time: number;
40
41
  has_updates: boolean;
42
+ sdkInfo?: Record<string, string>;
43
+ user?: StatsigUser;
41
44
  };
@@ -1,4 +1,5 @@
1
1
  import { ParamStoreConfig } from './ParamStoreTypes';
2
+ import { BootstrapMetadata } from './StatsigEvent';
2
3
  import { Flatten } from './TypingUtils';
3
4
  type EvaluationBase<T> = {
4
5
  id_type: string;
@@ -32,9 +33,12 @@ export type EvaluationDetails = {
32
33
  reason: string;
33
34
  lcut?: number;
34
35
  receivedAt?: number;
36
+ warnings?: EvaluationWarning[];
37
+ bootstrapMetadata?: BootstrapMetadata;
35
38
  };
36
39
  export type DetailedStoreResult<T extends AnyEvaluation | ParamStoreConfig> = {
37
40
  result: T | null;
38
41
  details: EvaluationDetails;
39
42
  };
43
+ export type EvaluationWarning = 'PartialUserMatch' | 'StableIDMismatch';
40
44
  export {};
@@ -16,6 +16,8 @@ export declare class EventLogger {
16
16
  private _creationTime;
17
17
  private _isLoggingDisabled;
18
18
  private _logEventUrl;
19
+ private static _safeFlushAndForget;
20
+ private static _safeRetryFailedLogs;
19
21
  constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null);
20
22
  setLoggingDisabled(isDisabled: boolean): void;
21
23
  enqueue(event: StatsigEventInternal): void;
@@ -26,13 +26,21 @@ const DEDUPER_WINDOW_DURATION_MS = 60000;
26
26
  const MAX_FAILED_LOGS = 500;
27
27
  const QUICK_FLUSH_WINDOW_MS = 200;
28
28
  const EVENT_LOGGER_MAP = {};
29
- const _safeFlushAndForget = (sdkKey) => {
30
- var _a;
31
- (_a = EVENT_LOGGER_MAP[sdkKey]) === null || _a === void 0 ? void 0 : _a.flush().catch(() => {
32
- // noop
33
- });
29
+ const RetryFailedLogsTrigger = {
30
+ Startup: 'startup',
31
+ GainedFocus: 'gained_focus',
34
32
  };
35
33
  class EventLogger {
34
+ static _safeFlushAndForget(sdkKey) {
35
+ var _a;
36
+ (_a = EVENT_LOGGER_MAP[sdkKey]) === null || _a === void 0 ? void 0 : _a.flush().catch(() => {
37
+ // noop
38
+ });
39
+ }
40
+ static _safeRetryFailedLogs(sdkKey) {
41
+ var _a;
42
+ (_a = EVENT_LOGGER_MAP[sdkKey]) === null || _a === void 0 ? void 0 : _a._retryFailedLogs(RetryFailedLogsTrigger.GainedFocus);
43
+ }
36
44
  constructor(_sdkKey, _emitter, _network, _options) {
37
45
  var _a;
38
46
  this._sdkKey = _sdkKey;
@@ -59,7 +67,7 @@ class EventLogger {
59
67
  this._normalizeAndAppendEvent(event);
60
68
  this._quickFlushIfNeeded();
61
69
  if (this._queue.length > this._maxQueueSize) {
62
- _safeFlushAndForget(this._sdkKey);
70
+ EventLogger._safeFlushAndForget(this._sdkKey);
63
71
  }
64
72
  }
65
73
  incrementNonExposureCount(name) {
@@ -77,10 +85,13 @@ class EventLogger {
77
85
  EVENT_LOGGER_MAP[this._sdkKey] = this;
78
86
  (0, VisibilityObserving_1._subscribeToVisiblityChanged)((visibility) => {
79
87
  if (visibility === 'background') {
80
- _safeFlushAndForget(this._sdkKey);
88
+ EventLogger._safeFlushAndForget(this._sdkKey);
89
+ }
90
+ else if (visibility === 'foreground') {
91
+ EventLogger._safeRetryFailedLogs(this._sdkKey);
81
92
  }
82
93
  });
83
- this._retryFailedLogs();
94
+ this._retryFailedLogs(RetryFailedLogsTrigger.Startup);
84
95
  this._startBackgroundFlushInterval();
85
96
  }
86
97
  stop() {
@@ -89,6 +100,7 @@ class EventLogger {
89
100
  clearInterval(this._flushIntervalId);
90
101
  this._flushIntervalId = null;
91
102
  }
103
+ delete EVENT_LOGGER_MAP[this._sdkKey];
92
104
  yield this.flush();
93
105
  });
94
106
  }
@@ -100,7 +112,7 @@ class EventLogger {
100
112
  }
101
113
  const events = this._queue;
102
114
  this._queue = [];
103
- return this._sendEvents(events);
115
+ yield this._sendEvents(events);
104
116
  });
105
117
  }
106
118
  /**
@@ -115,7 +127,7 @@ class EventLogger {
115
127
  if (Date.now() - this._creationTime > QUICK_FLUSH_WINDOW_MS) {
116
128
  return;
117
129
  }
118
- setTimeout(() => _safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
130
+ setTimeout(() => EventLogger._safeFlushAndForget(this._sdkKey), QUICK_FLUSH_WINDOW_MS);
119
131
  }
120
132
  _shouldLogEvent(event) {
121
133
  if ((0, SafeJs_1._isServerEnv)()) {
@@ -150,7 +162,7 @@ class EventLogger {
150
162
  return __awaiter(this, void 0, void 0, function* () {
151
163
  if (this._isLoggingDisabled) {
152
164
  this._saveFailedLogsToStorage(events);
153
- return;
165
+ return false;
154
166
  }
155
167
  try {
156
168
  const isInBackground = !(0, VisibilityObserving_1._isCurrentlyVisible)();
@@ -165,13 +177,17 @@ class EventLogger {
165
177
  name: 'logs_flushed',
166
178
  events,
167
179
  });
180
+ return true;
168
181
  }
169
182
  else {
183
+ Log_1.Log.warn('Failed to flush events.');
170
184
  this._saveFailedLogsToStorage(events);
185
+ return false;
171
186
  }
172
187
  }
173
188
  catch (_c) {
174
189
  Log_1.Log.warn('Failed to flush events.');
190
+ return false;
175
191
  }
176
192
  });
177
193
  }
@@ -216,7 +232,7 @@ class EventLogger {
216
232
  Log_1.Log.warn('Unable to save failed logs to storage');
217
233
  }
218
234
  }
219
- _retryFailedLogs() {
235
+ _retryFailedLogs(trigger) {
220
236
  const storageKey = this._getStorageKey();
221
237
  (() => __awaiter(this, void 0, void 0, function* () {
222
238
  if (!StorageProvider_1.Storage.isReady()) {
@@ -226,8 +242,13 @@ class EventLogger {
226
242
  if (!events) {
227
243
  return;
228
244
  }
229
- StorageProvider_1.Storage.removeItem(storageKey);
230
- yield this._sendEvents(events);
245
+ if (trigger === RetryFailedLogsTrigger.Startup) {
246
+ StorageProvider_1.Storage.removeItem(storageKey);
247
+ }
248
+ const isSuccess = yield this._sendEvents(events);
249
+ if (isSuccess && trigger === RetryFailedLogsTrigger.GainedFocus) {
250
+ StorageProvider_1.Storage.removeItem(storageKey);
251
+ }
231
252
  }))().catch(() => {
232
253
  Log_1.Log.warn('Failed to flush stored logs');
233
254
  });
@@ -279,7 +300,7 @@ class EventLogger {
279
300
  clearInterval(intervalId);
280
301
  }
281
302
  else {
282
- _safeFlushAndForget(this._sdkKey);
303
+ EventLogger._safeFlushAndForget(this._sdkKey);
283
304
  }
284
305
  }, flushInterval);
285
306
  this._flushIntervalId = intervalId;
@@ -20,6 +20,7 @@ export type InitializeResponseWithUpdates = SessionReplayFields & AutoCaptureFie
20
20
  hash_used: 'none' | 'sha256' | 'djb2';
21
21
  derived_fields?: Record<string, unknown>;
22
22
  user?: StatsigUser;
23
+ sdkInfo?: Record<string, string>;
23
24
  };
24
25
  export type InitializeResponse = InitializeResponseWithUpdates | {
25
26
  has_updates: false;
package/src/Log.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- export declare enum LogLevel {
2
- None = 0,
3
- Error = 1,
4
- Warn = 2,
5
- Info = 3,
6
- Debug = 4
7
- }
1
+ export declare const LogLevel: {
2
+ readonly None: 0;
3
+ readonly Error: 1;
4
+ readonly Warn: 2;
5
+ readonly Info: 3;
6
+ readonly Debug: 4;
7
+ };
8
+ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
8
9
  export declare abstract class Log {
9
10
  static level: LogLevel;
10
11
  static info(...args: unknown[]): void;
package/src/Log.js CHANGED
@@ -11,35 +11,34 @@ function addTag(args) {
11
11
  args.unshift('[Statsig]');
12
12
  return args; // ['[Statsig]', ...args];
13
13
  }
14
- var LogLevel;
15
- (function (LogLevel) {
16
- LogLevel[LogLevel["None"] = 0] = "None";
17
- LogLevel[LogLevel["Error"] = 1] = "Error";
18
- LogLevel[LogLevel["Warn"] = 2] = "Warn";
19
- LogLevel[LogLevel["Info"] = 3] = "Info";
20
- LogLevel[LogLevel["Debug"] = 4] = "Debug";
21
- })(LogLevel || (exports.LogLevel = LogLevel = {}));
14
+ exports.LogLevel = {
15
+ None: 0,
16
+ Error: 1,
17
+ Warn: 2,
18
+ Info: 3,
19
+ Debug: 4,
20
+ };
22
21
  class Log {
23
22
  static info(...args) {
24
- if (this.level >= LogLevel.Info) {
25
- console.info('\x1b[34m%s\x1b[0m', _INFO, ...addTag(args));
23
+ if (this.level >= exports.LogLevel.Info) {
24
+ console.info(_INFO, ...addTag(args));
26
25
  }
27
26
  }
28
27
  static debug(...args) {
29
- if (this.level >= LogLevel.Debug) {
30
- console.debug('\x1b[32m%s\x1b[0m', DEBUG, ...addTag(args));
28
+ if (this.level >= exports.LogLevel.Debug) {
29
+ console.debug(DEBUG, ...addTag(args));
31
30
  }
32
31
  }
33
32
  static warn(...args) {
34
- if (this.level >= LogLevel.Warn) {
35
- console.warn('\x1b[33m%s\x1b[0m', _WARN, ...addTag(args));
33
+ if (this.level >= exports.LogLevel.Warn) {
34
+ console.warn(_WARN, ...addTag(args));
36
35
  }
37
36
  }
38
37
  static error(...args) {
39
- if (this.level >= LogLevel.Error) {
40
- console.error('\x1b[31m%s\x1b[0m', ERROR, ...addTag(args));
38
+ if (this.level >= exports.LogLevel.Error) {
39
+ console.error(ERROR, ...addTag(args));
41
40
  }
42
41
  }
43
42
  }
44
43
  exports.Log = Log;
45
- Log.level = LogLevel.Warn;
44
+ Log.level = exports.LogLevel.Warn;
@@ -7,13 +7,14 @@ export type NetworkPriority = 'high' | 'low' | 'auto';
7
7
  export type NetworkArgs = RequestInit & {
8
8
  priority?: NetworkPriority;
9
9
  };
10
- export declare enum NetworkParam {
11
- EventCount = "ec",
12
- SdkKey = "k",
13
- SdkType = "st",
14
- SdkVersion = "sv",
15
- Time = "t",
16
- SessionID = "sid",
17
- StatsigEncoded = "se",
18
- IsGzipped = "gz"
19
- }
10
+ export declare const NetworkParam: {
11
+ readonly EventCount: "ec";
12
+ readonly SdkKey: "k";
13
+ readonly SdkType: "st";
14
+ readonly SdkVersion: "sv";
15
+ readonly Time: "t";
16
+ readonly SessionID: "sid";
17
+ readonly StatsigEncoded: "se";
18
+ readonly IsGzipped: "gz";
19
+ };
20
+ export type NetworkParam = (typeof NetworkParam)[keyof typeof NetworkParam];
@@ -6,14 +6,13 @@ exports.NetworkDefault = {
6
6
  initializeApi: 'https://featureassets.org/v1',
7
7
  specsApi: 'https://assetsconfigcdn.org/v1',
8
8
  };
9
- var NetworkParam;
10
- (function (NetworkParam) {
11
- NetworkParam["EventCount"] = "ec";
12
- NetworkParam["SdkKey"] = "k";
13
- NetworkParam["SdkType"] = "st";
14
- NetworkParam["SdkVersion"] = "sv";
15
- NetworkParam["Time"] = "t";
16
- NetworkParam["SessionID"] = "sid";
17
- NetworkParam["StatsigEncoded"] = "se";
18
- NetworkParam["IsGzipped"] = "gz";
19
- })(NetworkParam || (exports.NetworkParam = NetworkParam = {}));
9
+ exports.NetworkParam = {
10
+ EventCount: 'ec',
11
+ SdkKey: 'k',
12
+ SdkType: 'st',
13
+ SdkVersion: 'sv',
14
+ Time: 't',
15
+ SessionID: 'sid',
16
+ StatsigEncoded: 'se',
17
+ IsGzipped: 'gz',
18
+ };
@@ -25,6 +25,7 @@ export declare abstract class StatsigClientBase<TAdapter extends EvaluationsData
25
25
  protected readonly _options: AnyStatsigOptions;
26
26
  protected readonly _errorBoundary: ErrorBoundary;
27
27
  protected readonly _logger: EventLogger;
28
+ protected _initializePromise: Promise<void> | null;
28
29
  private _listeners;
29
30
  constructor(sdkKey: string, adapter: TAdapter, network: NetworkCore, options: AnyStatsigOptions | null);
30
31
  /**
@@ -20,8 +20,9 @@ const SessionID_1 = require("./SessionID");
20
20
  const StorageProvider_1 = require("./StorageProvider");
21
21
  class StatsigClientBase {
22
22
  constructor(sdkKey, adapter, network, options) {
23
- var _a, _b;
23
+ var _a;
24
24
  this.loadingStatus = 'Uninitialized';
25
+ this._initializePromise = null;
25
26
  this._listeners = {};
26
27
  const emitter = this.$emt.bind(this);
27
28
  (options === null || options === void 0 ? void 0 : options.logLevel) != null && (Log_1.Log.level = options.logLevel);
@@ -38,22 +39,11 @@ class StatsigClientBase {
38
39
  this._errorBoundary.wrap(network);
39
40
  this._errorBoundary.wrap(adapter);
40
41
  this._errorBoundary.wrap(this._logger);
41
- if (!(0, SafeJs_1._isServerEnv)()) {
42
- const statsigGlobal = (0, __StatsigGlobal_1._getStatsigGlobal)();
43
- const instances = (_b = statsigGlobal.instances) !== null && _b !== void 0 ? _b : {};
44
- const inst = this;
45
- if (instances[sdkKey] != null) {
46
- Log_1.Log.warn('Creating multiple Statsig clients with the same SDK key can lead to unexpected behavior. Multi-instance support requires different SDK keys.');
47
- }
48
- instances[sdkKey] = inst;
49
- statsigGlobal.lastInstance = inst;
50
- statsigGlobal.instances = instances;
51
- __STATSIG__ = statsigGlobal;
52
- }
53
42
  this.dataAdapter = adapter;
54
43
  this.dataAdapter.attach(sdkKey, options);
55
44
  this.storageProvider = StorageProvider_1.Storage;
56
45
  this._primeReadyRipcord();
46
+ _assignGlobalInstance(sdkKey, this);
57
47
  }
58
48
  /**
59
49
  * Updates runtime configuration options for the SDK, allowing toggling of certain behaviors such as logging and storage to comply with user preferences or regulations such as GDPR.
@@ -85,6 +75,8 @@ class StatsigClientBase {
85
75
  shutdown() {
86
76
  return __awaiter(this, void 0, void 0, function* () {
87
77
  this.$emt({ name: 'pre_shutdown' });
78
+ this._setStatus('Uninitialized', null);
79
+ this._initializePromise = null;
88
80
  yield this._logger.stop();
89
81
  });
90
82
  }
@@ -153,3 +145,19 @@ class StatsigClientBase {
153
145
  }
154
146
  }
155
147
  exports.StatsigClientBase = StatsigClientBase;
148
+ function _assignGlobalInstance(sdkKey, client) {
149
+ var _a;
150
+ if ((0, SafeJs_1._isServerEnv)()) {
151
+ return;
152
+ }
153
+ const statsigGlobal = (0, __StatsigGlobal_1._getStatsigGlobal)();
154
+ const instances = (_a = statsigGlobal.instances) !== null && _a !== void 0 ? _a : {};
155
+ const inst = client;
156
+ if (instances[sdkKey] != null) {
157
+ Log_1.Log.warn('Creating multiple Statsig clients with the same SDK key can lead to unexpected behavior. Multi-instance support requires different SDK keys.');
158
+ }
159
+ instances[sdkKey] = inst;
160
+ statsigGlobal.lastInstance = inst;
161
+ statsigGlobal.instances = instances;
162
+ __STATSIG__ = statsigGlobal;
163
+ }
@@ -15,6 +15,7 @@ type EventNameToEventDataMap = {
15
15
  events: Record<string, unknown>[];
16
16
  };
17
17
  pre_shutdown: object;
18
+ initialization_failure: object;
18
19
  gate_evaluation: {
19
20
  gate: FeatureGate;
20
21
  };
@@ -29,9 +29,10 @@ export type DataAdapterAsyncOptions = {
29
29
  export type DataAdapterSyncOptions = {
30
30
  /**
31
31
  * The flag to disable background cache refresh.
32
- * If set to true, the cache will not be updated in the background and will only use the data adatpter values.
32
+ * If set to true, a network request to update the cache for future sessions will not fired.
33
+ * if "explicitly" set to false, a network request to update the cache for future sessions will be fired, even in bootstrap cases.
33
34
  *
34
- * default: false
35
+ * default: implicit false
35
36
  */
36
37
  readonly disableBackgroundCacheRefresh?: boolean;
37
38
  };
@@ -8,6 +8,11 @@ export type StatsigEvent = {
8
8
  [key: string]: string | undefined;
9
9
  } | null;
10
10
  };
11
+ export type BootstrapMetadata = {
12
+ generatorSDKInfo?: Record<string, string>;
13
+ lcut?: number;
14
+ user?: Record<string, unknown>;
15
+ };
11
16
  export type StatsigEventInternal = Omit<StatsigEvent, 'metadata'> & {
12
17
  user: StatsigUserInternal | null;
13
18
  time: number;
@@ -5,6 +5,9 @@ const CONFIG_EXPOSURE_NAME = 'statsig::config_exposure';
5
5
  const GATE_EXPOSURE_NAME = 'statsig::gate_exposure';
6
6
  const LAYER_EXPOSURE_NAME = 'statsig::layer_exposure';
7
7
  const _createExposure = (eventName, user, details, metadata, secondaryExposures) => {
8
+ if (details.bootstrapMetadata) {
9
+ metadata['bootstrapMetadata'] = details.bootstrapMetadata;
10
+ }
8
11
  return {
9
12
  eventName,
10
13
  user,
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "3.0.0";
1
+ export declare const SDK_VERSION = "3.2.0-beta.1";
2
2
  export type StatsigMetadata = {
3
3
  readonly [key: string]: string | undefined;
4
4
  readonly appVersion?: string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StatsigMetadataProvider = exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = '3.0.0';
4
+ exports.SDK_VERSION = '3.2.0-beta.1';
5
5
  let metadata = {
6
6
  sdkVersion: exports.SDK_VERSION,
7
7
  sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
@@ -0,0 +1,5 @@
1
+ import { StatsigClientInterface } from './ClientInterfaces';
2
+ export type StatsigPlugin<T extends StatsigClientInterface> = {
3
+ readonly __plugin: 'session-replay' | 'auto-capture';
4
+ bind: (client: T) => void;
5
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/src/index.d.ts CHANGED
@@ -33,6 +33,7 @@ export * from './StatsigOptionsCommon';
33
33
  export * from './StatsigTypeFactories';
34
34
  export * from './StatsigTypes';
35
35
  export * from './StatsigUser';
36
+ export * from './StatsigPlugin';
36
37
  export * from './StorageProvider';
37
38
  export * from './TypedJsonParse';
38
39
  export * from './TypingUtils';
package/src/index.js CHANGED
@@ -55,6 +55,7 @@ __exportStar(require("./StatsigOptionsCommon"), exports);
55
55
  __exportStar(require("./StatsigTypeFactories"), exports);
56
56
  __exportStar(require("./StatsigTypes"), exports);
57
57
  __exportStar(require("./StatsigUser"), exports);
58
+ __exportStar(require("./StatsigPlugin"), exports);
58
59
  __exportStar(require("./StorageProvider"), exports);
59
60
  __exportStar(require("./TypedJsonParse"), exports);
60
61
  __exportStar(require("./TypingUtils"), exports);