@statsig/client-core 3.30.2 → 3.31.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.30.2",
3
+ "version": "3.31.1",
4
4
  "license": "ISC",
5
5
  "homepage": "https://github.com/statsig-io/js-client-monorepo",
6
6
  "repository": {
@@ -2,7 +2,7 @@ import { DownloadConfigSpecsResponse } from './DownloadConfigSpecsResponse';
2
2
  import { ErrorBoundary } from './ErrorBoundary';
3
3
  import { DynamicConfigEvaluationOptions, ExperimentEvaluationOptions, FeatureGateEvaluationOptions, LayerEvaluationOptions, ParameterStoreEvaluationOptions } from './EvaluationOptions';
4
4
  import { AnyInitializeResponse, ClientInitializeResponseOptions } from './InitializeResponse';
5
- import { StatsigSession } from './SessionID';
5
+ import { StatsigSession, StatsigSession as StatsigSessionType } from './SessionID';
6
6
  import { StatsigClientEventEmitterInterface } from './StatsigClientEventEmitter';
7
7
  import { EvaluationsDataAdapter, SpecsDataAdapter } from './StatsigDataAdapter';
8
8
  import { StatsigEvent } from './StatsigEvent';
@@ -48,6 +48,7 @@ export type PrecomputedEvaluationsContext = Flatten<CommonContext & {
48
48
  export interface PrecomputedEvaluationsInterface extends StatsigClientCommonInterface {
49
49
  readonly dataAdapter: EvaluationsDataAdapter;
50
50
  getContext(): PrecomputedEvaluationsContext;
51
+ getContextHandle(): PrecomputedEvaluationsContextHandle;
51
52
  updateUserSync(user: StatsigUser): StatsigUpdateDetails;
52
53
  updateUserAsync(user: StatsigUser): Promise<StatsigUpdateDetails>;
53
54
  checkGate(name: string, options?: FeatureGateEvaluationOptions): boolean;
@@ -60,3 +61,35 @@ export interface PrecomputedEvaluationsInterface extends StatsigClientCommonInte
60
61
  logEvent(eventName: string, value?: string | number, metadata?: Record<string, string>): void;
61
62
  }
62
63
  export type StatsigClientInterface = OnDeviceEvaluationsInterface | PrecomputedEvaluationsInterface;
64
+ /**
65
+ * A handle to the PrecomputedEvaluationsContext that computes fields lazily on access.
66
+ * This avoids unnecessary computation (e.g., cloning the user) when only certain fields are needed.
67
+ * The handle is created once and reused; individual getters fetch current values on each access.
68
+ */
69
+ export declare class PrecomputedEvaluationsContextHandle {
70
+ private _sdkKey;
71
+ private _getOptions;
72
+ private _getErrorBoundary;
73
+ private _getValues;
74
+ private _getUser;
75
+ private _getSdkInstanceID;
76
+ constructor(sdkKey: string, getOptions: () => AnyStatsigOptions, getErrorBoundary: () => ErrorBoundary, getValues: () => AnyInitializeResponse | null, getUser: () => StatsigUser, getSdkInstanceID: () => string);
77
+ get sdkKey(): string;
78
+ get options(): AnyStatsigOptions;
79
+ get errorBoundary(): ErrorBoundary;
80
+ get values(): AnyInitializeResponse | null;
81
+ get user(): StatsigUser;
82
+ /**
83
+ * Gets the current session.
84
+ * @param {boolean} [bumpSession=true] - Whether to bump/update the session timing. Set to false to read without affecting session state.
85
+ */
86
+ getSession(bumpSession?: boolean): StatsigSessionType;
87
+ get stableID(): string | null;
88
+ get sdkInstanceID(): string;
89
+ /**
90
+ * Returns the full PrecomputedEvaluationsContext object.
91
+ * Use this when you need all fields at once.
92
+ * @param {boolean} [bumpSession=true] - Whether to bump the session when building the context.
93
+ */
94
+ toContext(bumpSession?: boolean): PrecomputedEvaluationsContext;
95
+ }
@@ -1,2 +1,73 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrecomputedEvaluationsContextHandle = void 0;
4
+ const Log_1 = require("./Log");
5
+ const SafeJs_1 = require("./SafeJs");
6
+ const SessionID_1 = require("./SessionID");
7
+ const StableID_1 = require("./StableID");
8
+ /**
9
+ * A handle to the PrecomputedEvaluationsContext that computes fields lazily on access.
10
+ * This avoids unnecessary computation (e.g., cloning the user) when only certain fields are needed.
11
+ * The handle is created once and reused; individual getters fetch current values on each access.
12
+ */
13
+ class PrecomputedEvaluationsContextHandle {
14
+ constructor(sdkKey, getOptions, getErrorBoundary, getValues, getUser, getSdkInstanceID) {
15
+ this._sdkKey = sdkKey;
16
+ this._getOptions = getOptions;
17
+ this._getErrorBoundary = getErrorBoundary;
18
+ this._getValues = getValues;
19
+ this._getUser = getUser;
20
+ this._getSdkInstanceID = getSdkInstanceID;
21
+ }
22
+ get sdkKey() {
23
+ return this._sdkKey;
24
+ }
25
+ get options() {
26
+ return this._getOptions();
27
+ }
28
+ get errorBoundary() {
29
+ return this._getErrorBoundary();
30
+ }
31
+ get values() {
32
+ return this._getValues();
33
+ }
34
+ get user() {
35
+ let user = (0, SafeJs_1._cloneObject)('StatsigUser', this._getUser());
36
+ if (user == null) {
37
+ Log_1.Log.error('Failed to clone user');
38
+ user = {};
39
+ }
40
+ return user;
41
+ }
42
+ /**
43
+ * Gets the current session.
44
+ * @param {boolean} [bumpSession=true] - Whether to bump/update the session timing. Set to false to read without affecting session state.
45
+ */
46
+ getSession(bumpSession = true) {
47
+ return SessionID_1.StatsigSession.get(this._sdkKey, bumpSession);
48
+ }
49
+ get stableID() {
50
+ return StableID_1.StableID.get(this._sdkKey);
51
+ }
52
+ get sdkInstanceID() {
53
+ return this._getSdkInstanceID();
54
+ }
55
+ /**
56
+ * Returns the full PrecomputedEvaluationsContext object.
57
+ * Use this when you need all fields at once.
58
+ * @param {boolean} [bumpSession=true] - Whether to bump the session when building the context.
59
+ */
60
+ toContext(bumpSession = true) {
61
+ return {
62
+ sdkKey: this.sdkKey,
63
+ options: this.options,
64
+ values: this.values,
65
+ user: this.user,
66
+ errorBoundary: this.errorBoundary,
67
+ session: this.getSession(bumpSession),
68
+ stableID: this.stableID,
69
+ sdkInstanceID: this.sdkInstanceID,
70
+ };
71
+ }
72
+ }
73
+ exports.PrecomputedEvaluationsContextHandle = PrecomputedEvaluationsContextHandle;
@@ -0,0 +1,8 @@
1
+ import { StatsigEventInternal } from './StatsigEvent';
2
+ export declare class EventBatch {
3
+ attempts: number;
4
+ readonly events: StatsigEventInternal[];
5
+ readonly createdAt: number;
6
+ constructor(events: StatsigEventInternal[]);
7
+ incrementAttempts(): void;
8
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventBatch = void 0;
4
+ class EventBatch {
5
+ constructor(events) {
6
+ this.attempts = 0;
7
+ this.createdAt = Date.now();
8
+ this.events = events;
9
+ }
10
+ incrementAttempts() {
11
+ this.attempts++;
12
+ }
13
+ }
14
+ exports.EventBatch = EventBatch;
@@ -0,0 +1,7 @@
1
+ export declare const EventRetryConstants: {
2
+ readonly MAX_RETRY_ATTEMPTS: 5;
3
+ readonly DEFAULT_BATCH_SIZE: 100;
4
+ readonly MAX_PENDING_BATCHES: 10;
5
+ readonly TICK_INTERVAL_MS: 1000;
6
+ readonly MAX_QUEUED_EVENTS: number;
7
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventRetryConstants = void 0;
4
+ exports.EventRetryConstants = {
5
+ MAX_RETRY_ATTEMPTS: 5,
6
+ DEFAULT_BATCH_SIZE: 100,
7
+ MAX_PENDING_BATCHES: 10,
8
+ TICK_INTERVAL_MS: 1000,
9
+ get MAX_QUEUED_EVENTS() {
10
+ return this.DEFAULT_BATCH_SIZE * this.MAX_PENDING_BATCHES;
11
+ },
12
+ };
@@ -9,11 +9,18 @@ type SessionReplayFields = {
9
9
  passes_session_recording_targeting?: boolean;
10
10
  session_recording_event_triggers?: Record<string, SessionReplayTrigger>;
11
11
  session_recording_exposure_triggers?: Record<string, SessionReplayTrigger>;
12
+ session_recording_privacy_settings?: SessionReplayPrivacySettings;
12
13
  };
13
14
  type SessionReplayTrigger = {
14
15
  values?: string[];
15
16
  passes_sampling?: boolean;
16
17
  };
18
+ export type SessionReplayPrivacySettings = {
19
+ privacy_mode?: 'min' | 'input' | 'max';
20
+ unmasked_elements?: string[];
21
+ masked_elements?: string[];
22
+ blocked_elements?: string[];
23
+ };
17
24
  type AutoCaptureFields = {
18
25
  auto_capture_settings?: {
19
26
  disabled_events: Record<string, boolean>;
package/src/SafeJs.d.ts CHANGED
@@ -5,3 +5,4 @@ export declare const _addWindowEventListenerSafe: (key: string, listener: () =>
5
5
  export declare const _addDocumentEventListenerSafe: (key: string, listener: () => void) => void;
6
6
  export declare const _getCurrentPageUrlSafe: () => string | undefined;
7
7
  export declare const _getUnloadEvent: () => string;
8
+ export declare const _cloneObject: <T>(tag: string, obj: T) => T | null;
package/src/SafeJs.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._getUnloadEvent = exports._getCurrentPageUrlSafe = exports._addDocumentEventListenerSafe = exports._addWindowEventListenerSafe = exports._isServerEnv = exports._getDocumentSafe = exports._getWindowSafe = void 0;
3
+ exports._cloneObject = exports._getUnloadEvent = exports._getCurrentPageUrlSafe = exports._addDocumentEventListenerSafe = exports._addWindowEventListenerSafe = exports._isServerEnv = exports._getDocumentSafe = exports._getWindowSafe = void 0;
4
+ const Log_1 = require("./Log");
4
5
  const _getWindowSafe = () => {
5
6
  return typeof window !== 'undefined' ? window : null;
6
7
  };
@@ -55,3 +56,13 @@ const _getUnloadEvent = () => {
55
56
  return eventType;
56
57
  };
57
58
  exports._getUnloadEvent = _getUnloadEvent;
59
+ const _cloneObject = (tag, obj) => {
60
+ try {
61
+ return JSON.parse(JSON.stringify(obj));
62
+ }
63
+ catch (error) {
64
+ Log_1.Log.error(`Failed to clone object ${tag}`);
65
+ return null;
66
+ }
67
+ };
68
+ exports._cloneObject = _cloneObject;
@@ -14,7 +14,7 @@ export declare const SessionID: {
14
14
  get: (sdkKey: string) => string;
15
15
  };
16
16
  export declare const StatsigSession: {
17
- get: (sdkKey: string) => StatsigSession;
17
+ get: (sdkKey: string, bumpSession?: boolean) => StatsigSession;
18
18
  overrideInitialSessionID: (override: string, sdkKey: string) => void;
19
19
  };
20
20
  export {};
package/src/SessionID.js CHANGED
@@ -15,12 +15,12 @@ exports.SessionID = {
15
15
  },
16
16
  };
17
17
  exports.StatsigSession = {
18
- get: (sdkKey) => {
18
+ get: (sdkKey, bumpSession = true) => {
19
19
  if (PROMISE_MAP[sdkKey] == null) {
20
20
  PROMISE_MAP[sdkKey] = _loadSession(sdkKey);
21
21
  }
22
22
  const session = PROMISE_MAP[sdkKey];
23
- return _bumpSession(session);
23
+ return bumpSession ? _bumpSession(session) : session;
24
24
  },
25
25
  overrideInitialSessionID: (override, sdkKey) => {
26
26
  PROMISE_MAP[sdkKey] = _overrideSessionId(override, sdkKey);
@@ -53,16 +53,14 @@ function _overrideSessionId(override, sdkKey) {
53
53
  };
54
54
  }
55
55
  function _bumpSession(session) {
56
+ var _a;
56
57
  const now = Date.now();
57
58
  const data = session.data;
58
59
  const sdkKey = session.sdkKey;
59
- if (_isIdle(data) || _hasRunTooLong(data)) {
60
+ const sessionExpired = _isIdle(data) || _hasRunTooLong(data);
61
+ if (sessionExpired) {
60
62
  data.sessionID = (0, UUID_1.getUUID)();
61
63
  data.startTime = now;
62
- const client = __STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__.instance(sdkKey);
63
- if (client) {
64
- client.$emt({ name: 'session_expired' });
65
- }
66
64
  }
67
65
  data.lastUpdate = now;
68
66
  _persistToStorage(data, session.sdkKey);
@@ -71,6 +69,9 @@ function _bumpSession(session) {
71
69
  const lifetime = now - data.startTime;
72
70
  session.idleTimeoutID = _createSessionTimeout(sdkKey, MAX_SESSION_IDLE_TIME);
73
71
  session.ageTimeoutID = _createSessionTimeout(sdkKey, MAX_SESSION_AGE - lifetime);
72
+ if (sessionExpired) {
73
+ (_a = __STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__.instance(sdkKey)) === null || _a === void 0 ? void 0 : _a.$emt({ name: 'session_expired' });
74
+ }
74
75
  return session;
75
76
  }
76
77
  function _createSessionTimeout(sdkKey, duration) {
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "3.30.2";
1
+ export declare const SDK_VERSION = "3.31.1";
2
2
  export type StatsigMetadata = {
3
3
  readonly [key: string]: string | undefined | null;
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.30.2';
4
+ exports.SDK_VERSION = '3.31.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
@@ -21,4 +21,5 @@ export type StatsigUserInternal = StatsigUser & {
21
21
  };
22
22
  export declare function _normalizeUser(original: StatsigUser, options?: AnyStatsigOptions | null, fallbackEnvironment?: string | null): StatsigUserInternal;
23
23
  export declare function _getFullUserHash(user: StatsigUser | undefined): string | null;
24
+ export declare function _getUnitIDFromUser(user: StatsigUser, idType: string): string | undefined;
24
25
  export {};
@@ -1,26 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._getFullUserHash = exports._normalizeUser = void 0;
3
+ exports._getUnitIDFromUser = exports._getFullUserHash = exports._normalizeUser = void 0;
4
4
  const Hashing_1 = require("./Hashing");
5
5
  const Log_1 = require("./Log");
6
+ const SafeJs_1 = require("./SafeJs");
6
7
  function _normalizeUser(original, options, fallbackEnvironment) {
7
- try {
8
- const copy = JSON.parse(JSON.stringify(original));
9
- if (options != null && options.environment != null) {
10
- copy.statsigEnvironment = options.environment;
11
- }
12
- else if (fallbackEnvironment != null) {
13
- copy.statsigEnvironment = { tier: fallbackEnvironment };
14
- }
15
- return copy;
16
- }
17
- catch (error) {
18
- Log_1.Log.error('Failed to JSON.stringify user');
8
+ const copy = (0, SafeJs_1._cloneObject)('StatsigUser', original);
9
+ if (copy == null) {
10
+ Log_1.Log.error('Failed to clone user');
19
11
  return { statsigEnvironment: undefined };
20
12
  }
13
+ if (options != null && options.environment != null) {
14
+ copy.statsigEnvironment = options.environment;
15
+ }
16
+ else if (fallbackEnvironment != null) {
17
+ copy.statsigEnvironment = { tier: fallbackEnvironment };
18
+ }
19
+ return copy;
21
20
  }
22
21
  exports._normalizeUser = _normalizeUser;
23
22
  function _getFullUserHash(user) {
24
23
  return user ? (0, Hashing_1._DJB2Object)(user) : null;
25
24
  }
26
25
  exports._getFullUserHash = _getFullUserHash;
26
+ function _getUnitIDFromUser(user, idType) {
27
+ var _a, _b, _c;
28
+ if (typeof idType !== 'string') {
29
+ return user.userID;
30
+ }
31
+ const lowered = idType.toLowerCase();
32
+ if (lowered !== 'userid') {
33
+ return (_b = (_a = user.customIDs) === null || _a === void 0 ? void 0 : _a[idType]) !== null && _b !== void 0 ? _b : (_c = user.customIDs) === null || _c === void 0 ? void 0 : _c[lowered];
34
+ }
35
+ return user.userID;
36
+ }
37
+ exports._getUnitIDFromUser = _getUnitIDFromUser;