@statsig/client-core 3.12.0 → 3.12.2

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.12.0",
3
+ "version": "3.12.2",
4
4
  "dependencies": {},
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -14,7 +14,7 @@ export declare abstract class DataAdapterCore {
14
14
  setData(data: string, user?: StatsigUser): void;
15
15
  protected _getDataAsyncImpl(current: DataAdapterResult | null, user?: StatsigUserInternal, options?: DataAdapterAsyncOptions): Promise<DataAdapterResult | null>;
16
16
  protected _prefetchDataImpl(user?: StatsigUser, options?: DataAdapterAsyncOptions): Promise<void>;
17
- protected abstract _fetchFromNetwork(current: string | null, user?: StatsigUser, options?: DataAdapterAsyncOptions): Promise<string | null>;
17
+ protected abstract _fetchFromNetwork(current: string | null, user?: StatsigUser, options?: DataAdapterAsyncOptions, isCacheValidFor204?: boolean): Promise<string | null>;
18
18
  protected abstract _getCacheKey(user?: StatsigUserInternal): string;
19
19
  protected abstract _isCachedResultValidFor204(result: DataAdapterResult, user: StatsigUser | undefined): boolean;
20
20
  private _fetchAndPrepFromNetwork;
@@ -75,12 +75,12 @@ class DataAdapterCore {
75
75
  });
76
76
  }
77
77
  _fetchAndPrepFromNetwork(cachedResult, user, options) {
78
+ var _a;
78
79
  return __awaiter(this, void 0, void 0, function* () {
79
- let cachedData = null;
80
- if (cachedResult && this._isCachedResultValidFor204(cachedResult, user)) {
81
- cachedData = cachedResult.data;
82
- }
83
- const latest = yield this._fetchFromNetwork(cachedData, user, options);
80
+ const cachedData = (_a = cachedResult === null || cachedResult === void 0 ? void 0 : cachedResult.data) !== null && _a !== void 0 ? _a : null;
81
+ const isCacheValidFor204 = cachedResult != null &&
82
+ this._isCachedResultValidFor204(cachedResult, user);
83
+ const latest = yield this._fetchFromNetwork(cachedData, user, options, isCacheValidFor204);
84
84
  if (!latest) {
85
85
  Log_1.Log.debug('No response returned for latest value');
86
86
  return null;
@@ -1,3 +1,4 @@
1
+ import { ParamStoreConfig } from './ParamStoreTypes';
1
2
  import { StatsigUser } from './StatsigUser';
2
3
  export type SpecCondition = {
3
4
  type: string;
@@ -34,10 +35,15 @@ export type Spec = {
34
35
  targetAppIDs?: string[];
35
36
  version?: number;
36
37
  };
38
+ export type ParamStore = {
39
+ targetAppIDs?: string[];
40
+ parameters: ParamStoreConfig;
41
+ };
37
42
  export type DownloadConfigSpecsResponse = {
38
43
  feature_gates: Spec[];
39
44
  dynamic_configs: Spec[];
40
45
  layer_configs: Spec[];
46
+ param_stores?: Record<string, ParamStore>;
41
47
  time: number;
42
48
  has_updates: boolean;
43
49
  sdkInfo?: Record<string, string>;
@@ -5,7 +5,7 @@ type EvaluationBase<T> = {
5
5
  id_type: string;
6
6
  name: string;
7
7
  rule_id: string;
8
- secondary_exposures: SecondaryExposure[];
8
+ secondary_exposures: SecondaryExposure[] | string[];
9
9
  value: T;
10
10
  version?: string;
11
11
  };
@@ -28,7 +28,7 @@ export type DynamicConfigEvaluation = ExperimentEvaluation;
28
28
  export type LayerEvaluation = Flatten<Omit<ExperimentEvaluation, 'id_type'> & {
29
29
  allocated_experiment_name: string;
30
30
  explicit_parameters: string[];
31
- undelegated_secondary_exposures?: SecondaryExposure[];
31
+ undelegated_secondary_exposures?: SecondaryExposure[] | string[];
32
32
  }>;
33
33
  export type AnyEvaluation = GateEvaluation | ExperimentEvaluation | DynamicConfigEvaluation | LayerEvaluation;
34
34
  export type EvaluationDetails = {
@@ -1,4 +1,4 @@
1
- import { DynamicConfigEvaluation, GateEvaluation, LayerEvaluation } from './EvaluationTypes';
1
+ import { DynamicConfigEvaluation, GateEvaluation, LayerEvaluation, SecondaryExposure } from './EvaluationTypes';
2
2
  import { ParamStoreConfig } from './ParamStoreTypes';
3
3
  import { StatsigUser } from './StatsigUser';
4
4
  type SessionReplayFields = {
@@ -23,6 +23,7 @@ export type InitializeResponseWithUpdates = SessionReplayFields & AutoCaptureFie
23
23
  sdkInfo?: Record<string, string>;
24
24
  sdk_flags?: Record<string, boolean>;
25
25
  full_checksum?: string;
26
+ exposures?: Record<string, SecondaryExposure>;
26
27
  };
27
28
  export type InitializeResponse = InitializeResponseWithUpdates | {
28
29
  has_updates: false;
@@ -14,11 +14,13 @@ type RequestArgs = {
14
14
  params?: Record<string, string>;
15
15
  headers?: Record<string, string>;
16
16
  };
17
- export type RequestArgsWithData = Flatten<RequestArgs & {
18
- data: Record<string, unknown>;
17
+ type DataFlags = {
19
18
  isStatsigEncodable?: boolean;
20
19
  isCompressable?: boolean;
21
- }>;
20
+ };
21
+ export type RequestArgsWithData = Flatten<RequestArgs & {
22
+ data: Record<string, unknown>;
23
+ } & DataFlags>;
22
24
  type BeaconRequestArgs = Pick<RequestArgsWithData, 'data' | 'sdkKey' | 'urlConfig' | 'params' | 'isCompressable' | 'attempt'>;
23
25
  type NetworkResponse = {
24
26
  body: string | null;
@@ -43,8 +45,8 @@ export declare class NetworkCore {
43
45
  private _sendRequest;
44
46
  private _isRateLimited;
45
47
  private _getPopulatedURL;
46
- private _getPopulatedBody;
47
- private _attemptToEncodeString;
48
+ private _tryEncodeBody;
49
+ private _tryToCompressBody;
48
50
  private _getInternalRequestArgs;
49
51
  }
50
52
  export {};
@@ -16,6 +16,7 @@ const Diagnostics_1 = require("./Diagnostics");
16
16
  const Log_1 = require("./Log");
17
17
  const NetworkConfig_1 = require("./NetworkConfig");
18
18
  const NetworkFallbackResolver_1 = require("./NetworkFallbackResolver");
19
+ const SDKFlags_1 = require("./SDKFlags");
19
20
  const SDKType_1 = require("./SDKType");
20
21
  const SafeJs_1 = require("./SafeJs");
21
22
  const SessionID_1 = require("./SessionID");
@@ -71,19 +72,17 @@ class NetworkCore {
71
72
  return false;
72
73
  }
73
74
  const argsInternal = this._getInternalRequestArgs('POST', args);
74
- const body = yield this._getPopulatedBody(argsInternal, args.data);
75
+ yield this._tryToCompressBody(argsInternal);
75
76
  const url = yield this._getPopulatedURL(argsInternal);
76
77
  const nav = navigator;
77
- return nav.sendBeacon.bind(nav)(url, body);
78
+ return nav.sendBeacon.bind(nav)(url, argsInternal.body);
78
79
  });
79
80
  }
80
81
  post(args) {
81
82
  return __awaiter(this, void 0, void 0, function* () {
82
83
  const argsInternal = this._getInternalRequestArgs('POST', args);
83
- argsInternal.body = yield this._getPopulatedBody(argsInternal, args.data);
84
- if (args.isStatsigEncodable) {
85
- argsInternal.body = this._attemptToEncodeString(argsInternal, argsInternal.body);
86
- }
84
+ this._tryEncodeBody(argsInternal);
85
+ yield this._tryToCompressBody(argsInternal);
87
86
  return this._sendRequest(argsInternal);
88
87
  });
89
88
  }
@@ -208,40 +207,74 @@ class NetworkCore {
208
207
  return `${url}${query ? `?${query}` : ''}`;
209
208
  });
210
209
  }
211
- _getPopulatedBody(args, data) {
212
- return __awaiter(this, void 0, void 0, function* () {
213
- const { sdkKey, fallbackUrl } = args;
214
- const stableID = StableID_1.StableID.get(sdkKey);
215
- const sessionID = SessionID_1.SessionID.get(sdkKey);
216
- const sdkType = SDKType_1.SDKType._get(sdkKey);
217
- return JSON.stringify(Object.assign(Object.assign({}, data), { statsigMetadata: Object.assign(Object.assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID,
218
- sessionID,
219
- sdkType,
220
- fallbackUrl }) }));
221
- });
222
- }
223
- _attemptToEncodeString(args, input) {
224
- var _a, _b;
210
+ _tryEncodeBody(args) {
211
+ var _a;
225
212
  const win = (0, SafeJs_1._getWindowSafe)();
226
- if (this._options.disableStatsigEncoding ||
213
+ const body = args.body;
214
+ if (!args.isStatsigEncodable ||
215
+ this._options.disableStatsigEncoding ||
216
+ typeof body !== 'string' ||
227
217
  (0, __StatsigGlobal_1._getStatsigGlobalFlag)('no-encode') != null ||
228
218
  !(win === null || win === void 0 ? void 0 : win.btoa)) {
229
- return input;
219
+ return;
230
220
  }
231
221
  try {
232
- const result = (_a = win.btoa(input).split('').reverse().join('')) !== null && _a !== void 0 ? _a : input;
233
- args.params = Object.assign(Object.assign({}, ((_b = args.params) !== null && _b !== void 0 ? _b : {})), { [NetworkConfig_1.NetworkParam.StatsigEncoded]: '1' });
234
- return result;
222
+ args.body = win.btoa(body).split('').reverse().join('');
223
+ args.params = Object.assign(Object.assign({}, ((_a = args.params) !== null && _a !== void 0 ? _a : {})), { [NetworkConfig_1.NetworkParam.StatsigEncoded]: '1' });
235
224
  }
236
- catch (_c) {
237
- Log_1.Log.warn(`Request encoding failed for ${args.urlConfig.getUrl()}`);
238
- return input;
225
+ catch (e) {
226
+ Log_1.Log.warn(`Request encoding failed for ${args.urlConfig.getUrl()}`, e);
239
227
  }
240
228
  }
229
+ _tryToCompressBody(args) {
230
+ var _a;
231
+ return __awaiter(this, void 0, void 0, function* () {
232
+ const body = args.body;
233
+ if (!args.isCompressable ||
234
+ this._options.disableCompression ||
235
+ typeof body !== 'string' ||
236
+ SDKFlags_1.SDKFlags.get(args.sdkKey, 'enable_log_event_compression') !== true ||
237
+ (0, __StatsigGlobal_1._getStatsigGlobalFlag)('no-compress') != null ||
238
+ typeof CompressionStream === 'undefined' ||
239
+ typeof TextEncoder === 'undefined') {
240
+ return;
241
+ }
242
+ try {
243
+ const bytes = new TextEncoder().encode(body);
244
+ const stream = new CompressionStream('gzip');
245
+ const writer = stream.writable.getWriter();
246
+ writer.write(bytes).catch(Log_1.Log.error);
247
+ writer.close().catch(Log_1.Log.error);
248
+ const reader = stream.readable.getReader();
249
+ const chunks = [];
250
+ let result;
251
+ // eslint-disable-next-line no-await-in-loop
252
+ while (!(result = yield reader.read()).done) {
253
+ chunks.push(result.value);
254
+ }
255
+ const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
256
+ const combined = new Uint8Array(totalLength);
257
+ let offset = 0;
258
+ for (const chunk of chunks) {
259
+ combined.set(chunk, offset);
260
+ offset += chunk.length;
261
+ }
262
+ args.body = combined;
263
+ args.params = Object.assign(Object.assign({}, ((_a = args.params) !== null && _a !== void 0 ? _a : {})), { [NetworkConfig_1.NetworkParam.IsGzipped]: '1' });
264
+ }
265
+ catch (e) {
266
+ Log_1.Log.warn(`Request compression failed for ${args.urlConfig.getUrl()}`, e);
267
+ }
268
+ });
269
+ }
241
270
  _getInternalRequestArgs(method, args) {
242
271
  const fallbackUrl = this._fallbackResolver.getActiveFallbackUrl(args.sdkKey, args.urlConfig);
243
- return Object.assign(Object.assign({}, args), { method,
272
+ const result = Object.assign(Object.assign({}, args), { method,
244
273
  fallbackUrl });
274
+ if ('data' in args) {
275
+ _populateRequestBody(result, args.data);
276
+ }
277
+ return result;
245
278
  }
246
279
  }
247
280
  exports.NetworkCore = NetworkCore;
@@ -252,6 +285,16 @@ const _ensureValidSdkKey = (args) => {
252
285
  }
253
286
  return true;
254
287
  };
288
+ const _populateRequestBody = (args, data) => {
289
+ const { sdkKey, fallbackUrl } = args;
290
+ const stableID = StableID_1.StableID.get(sdkKey);
291
+ const sessionID = SessionID_1.SessionID.get(sdkKey);
292
+ const sdkType = SDKType_1.SDKType._get(sdkKey);
293
+ args.body = JSON.stringify(Object.assign(Object.assign({}, data), { statsigMetadata: Object.assign(Object.assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID,
294
+ sessionID,
295
+ sdkType,
296
+ fallbackUrl }) }));
297
+ };
255
298
  function _getErrorMessage(controller, error) {
256
299
  if ((controller === null || controller === void 0 ? void 0 : controller.signal.aborted) &&
257
300
  typeof controller.signal.reason === 'string') {
@@ -1,9 +1,15 @@
1
- import { DynamicConfigEvaluationOptions, ExperimentEvaluationOptions, FeatureGateEvaluationOptions, LayerEvaluationOptions } from './EvaluationOptions';
2
- import { DynamicConfig, Experiment, FeatureGate, Layer } from './StatsigTypes';
1
+ import { DynamicConfigEvaluationOptions, ExperimentEvaluationOptions, FeatureGateEvaluationOptions, LayerEvaluationOptions, ParameterStoreEvaluationOptions } from './EvaluationOptions';
2
+ import { EvaluationDetails } from './EvaluationTypes';
3
+ import { ParamStoreConfig } from './ParamStoreTypes';
4
+ import { DynamicConfig, Experiment, FeatureGate, Layer, ParameterStore } from './StatsigTypes';
3
5
  import { StatsigUser } from './StatsigUser';
4
6
  export type OverrideAdapter = {
5
7
  getGateOverride?(current: FeatureGate, user: StatsigUser, options?: FeatureGateEvaluationOptions): FeatureGate | null;
6
8
  getDynamicConfigOverride?(current: DynamicConfig, user: StatsigUser, options?: DynamicConfigEvaluationOptions): DynamicConfig | null;
7
9
  getExperimentOverride?(current: Experiment, user: StatsigUser, options?: ExperimentEvaluationOptions): Experiment | null;
8
10
  getLayerOverride?(current: Layer, user: StatsigUser, options?: LayerEvaluationOptions): Layer | null;
11
+ getParamStoreOverride?(current: ParameterStore, options?: ParameterStoreEvaluationOptions): {
12
+ config: ParamStoreConfig;
13
+ details: EvaluationDetails;
14
+ } | null;
9
15
  };
@@ -58,7 +58,7 @@ export type ExperimentParam = Param<'experiment', ParamType> & {
58
58
  experiment_name: string;
59
59
  param_name: string;
60
60
  };
61
- type AnyParam = GateParam | StaticParam | LayerParam | DynamicConfigParam | ExperimentParam;
61
+ export type AnyParam = GateParam | StaticParam | LayerParam | DynamicConfigParam | ExperimentParam;
62
62
  export type ParamStoreConfig = {
63
63
  [param_name: string]: AnyParam | undefined;
64
64
  };
@@ -22,6 +22,7 @@ export type StatsigEventInternal = Omit<StatsigEvent, 'metadata'> & {
22
22
  secondaryExposures?: SecondaryExposure[];
23
23
  };
24
24
  export declare const _isExposureEvent: ({ eventName, }: StatsigEventInternal) => boolean;
25
- export declare const _createGateExposure: (user: StatsigUserInternal, gate: FeatureGate) => StatsigEventInternal;
26
- export declare const _createConfigExposure: (user: StatsigUserInternal, config: DynamicConfig) => StatsigEventInternal;
27
- export declare const _createLayerParameterExposure: (user: StatsigUserInternal, layer: Layer, parameterName: string) => StatsigEventInternal;
25
+ export declare const _createGateExposure: (user: StatsigUserInternal, gate: FeatureGate, exposureMapping?: Record<string, SecondaryExposure>) => StatsigEventInternal;
26
+ export declare function _mapExposures(exposures: SecondaryExposure[] | string[], exposureMapping?: Record<string, SecondaryExposure>): SecondaryExposure[];
27
+ export declare const _createConfigExposure: (user: StatsigUserInternal, config: DynamicConfig, exposureMapping?: Record<string, SecondaryExposure>) => StatsigEventInternal;
28
+ export declare const _createLayerParameterExposure: (user: StatsigUserInternal, layer: Layer, parameterName: string, exposureMapping?: Record<string, SecondaryExposure>) => StatsigEventInternal;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._createLayerParameterExposure = exports._createConfigExposure = exports._createGateExposure = exports._isExposureEvent = void 0;
3
+ exports._createLayerParameterExposure = exports._createConfigExposure = exports._mapExposures = exports._createGateExposure = exports._isExposureEvent = void 0;
4
4
  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';
@@ -23,7 +23,7 @@ const _isExposureEvent = ({ eventName, }) => {
23
23
  eventName === LAYER_EXPOSURE_NAME);
24
24
  };
25
25
  exports._isExposureEvent = _isExposureEvent;
26
- const _createGateExposure = (user, gate) => {
26
+ const _createGateExposure = (user, gate, exposureMapping) => {
27
27
  var _a, _b, _c;
28
28
  const metadata = {
29
29
  gate: gate.name,
@@ -33,10 +33,21 @@ const _createGateExposure = (user, gate) => {
33
33
  if (((_a = gate.__evaluation) === null || _a === void 0 ? void 0 : _a.version) != null) {
34
34
  metadata['configVersion'] = gate.__evaluation.version;
35
35
  }
36
- return _createExposure(GATE_EXPOSURE_NAME, user, gate.details, metadata, (_c = (_b = gate.__evaluation) === null || _b === void 0 ? void 0 : _b.secondary_exposures) !== null && _c !== void 0 ? _c : []);
36
+ return _createExposure(GATE_EXPOSURE_NAME, user, gate.details, metadata, _mapExposures((_c = (_b = gate.__evaluation) === null || _b === void 0 ? void 0 : _b.secondary_exposures) !== null && _c !== void 0 ? _c : [], exposureMapping));
37
37
  };
38
38
  exports._createGateExposure = _createGateExposure;
39
- const _createConfigExposure = (user, config) => {
39
+ function _mapExposures(exposures, exposureMapping) {
40
+ return exposures
41
+ .map((exposure) => {
42
+ if (typeof exposure === 'string') {
43
+ return (exposureMapping !== null && exposureMapping !== void 0 ? exposureMapping : {})[exposure];
44
+ }
45
+ return exposure;
46
+ })
47
+ .filter((exposure) => exposure != null);
48
+ }
49
+ exports._mapExposures = _mapExposures;
50
+ const _createConfigExposure = (user, config, exposureMapping) => {
40
51
  var _a, _b, _c, _d;
41
52
  const metadata = {
42
53
  config: config.name,
@@ -48,10 +59,10 @@ const _createConfigExposure = (user, config) => {
48
59
  if (((_b = config.__evaluation) === null || _b === void 0 ? void 0 : _b.passed) != null) {
49
60
  metadata['rulePassed'] = String(config.__evaluation.passed);
50
61
  }
51
- return _createExposure(CONFIG_EXPOSURE_NAME, user, config.details, metadata, (_d = (_c = config.__evaluation) === null || _c === void 0 ? void 0 : _c.secondary_exposures) !== null && _d !== void 0 ? _d : []);
62
+ return _createExposure(CONFIG_EXPOSURE_NAME, user, config.details, metadata, _mapExposures((_d = (_c = config.__evaluation) === null || _c === void 0 ? void 0 : _c.secondary_exposures) !== null && _d !== void 0 ? _d : [], exposureMapping));
52
63
  };
53
64
  exports._createConfigExposure = _createConfigExposure;
54
- const _createLayerParameterExposure = (user, layer, parameterName) => {
65
+ const _createLayerParameterExposure = (user, layer, parameterName, exposureMapping) => {
55
66
  var _a, _b, _c, _d;
56
67
  const evaluation = layer.__evaluation;
57
68
  const isExplicit = ((_a = evaluation === null || evaluation === void 0 ? void 0 : evaluation.explicit_parameters) === null || _a === void 0 ? void 0 : _a.includes(parameterName)) === true;
@@ -71,7 +82,7 @@ const _createLayerParameterExposure = (user, layer, parameterName) => {
71
82
  if (((_d = layer.__evaluation) === null || _d === void 0 ? void 0 : _d.version) != null) {
72
83
  metadata['configVersion'] = layer.__evaluation.version;
73
84
  }
74
- return _createExposure(LAYER_EXPOSURE_NAME, user, layer.details, metadata, secondaryExposures);
85
+ return _createExposure(LAYER_EXPOSURE_NAME, user, layer.details, metadata, _mapExposures(secondaryExposures, exposureMapping));
75
86
  };
76
87
  exports._createLayerParameterExposure = _createLayerParameterExposure;
77
88
  const _addEvaluationDetailsToMetadata = (details, metadata) => {
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "3.12.0";
1
+ export declare const SDK_VERSION = "3.12.2";
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.12.0';
4
+ exports.SDK_VERSION = '3.12.2';
5
5
  let metadata = {
6
6
  sdkVersion: exports.SDK_VERSION,
7
7
  sdkType: 'js-mono', // js-mono is overwritten by Precomp and OnDevice clients
@@ -14,6 +14,12 @@ export type StatsigRuntimeMutableOptions = {
14
14
  * Note: caching will not work if storage is disabled
15
15
  */
16
16
  disableStorage?: boolean;
17
+ /**
18
+ * Whether or not Statsig should compress JSON bodies for network requests where possible.
19
+ *
20
+ * default: `false`
21
+ */
22
+ disableCompression?: boolean;
17
23
  };
18
24
  export type NetworkConfigCommon = {
19
25
  /**