@statsig/client-core 3.31.0 → 3.31.1-beta.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.31.0",
3
+ "version": "3.31.1-beta.2",
4
4
  "license": "ISC",
5
5
  "homepage": "https://github.com/statsig-io/js-client-monorepo",
6
6
  "repository": {
@@ -0,0 +1,14 @@
1
+ import { EventBatch } from './EventBatch';
2
+ import { StatsigEventInternal } from './StatsigEvent';
3
+ export declare class BatchQueue {
4
+ private _batches;
5
+ private _batchSize;
6
+ constructor(batchSize?: number);
7
+ batchSize(): number;
8
+ requeueBatch(batch: EventBatch): number;
9
+ hasFullBatch(): boolean;
10
+ takeNextBatch(): EventBatch | null;
11
+ takeAllBatches(): EventBatch[];
12
+ createBatches(events: StatsigEventInternal[]): number;
13
+ private _enqueueBatch;
14
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatchQueue = void 0;
4
+ const EventBatch_1 = require("./EventBatch");
5
+ const EventRetryConstants_1 = require("./EventRetryConstants");
6
+ class BatchQueue {
7
+ constructor(batchSize = EventRetryConstants_1.EventRetryConstants.DEFAULT_BATCH_SIZE) {
8
+ this._batches = [];
9
+ this._batchSize = batchSize;
10
+ }
11
+ batchSize() {
12
+ return this._batchSize;
13
+ }
14
+ requeueBatch(batch) {
15
+ return this._enqueueBatch(batch);
16
+ }
17
+ hasFullBatch() {
18
+ return this._batches.some((batch) => batch.events.length >= this._batchSize);
19
+ }
20
+ takeNextBatch() {
21
+ var _a;
22
+ return (_a = this._batches.shift()) !== null && _a !== void 0 ? _a : null;
23
+ }
24
+ takeAllBatches() {
25
+ const batches = this._batches;
26
+ this._batches = [];
27
+ return batches;
28
+ }
29
+ createBatches(events) {
30
+ let i = 0;
31
+ let droppedCount = 0;
32
+ while (i < events.length) {
33
+ const chunk = events.slice(i, i + this._batchSize);
34
+ droppedCount += this._enqueueBatch(new EventBatch_1.EventBatch(chunk));
35
+ i += this._batchSize;
36
+ }
37
+ return droppedCount;
38
+ }
39
+ _enqueueBatch(batch) {
40
+ this._batches.push(batch);
41
+ let droppedEventCount = 0;
42
+ while (this._batches.length > EventRetryConstants_1.EventRetryConstants.MAX_PENDING_BATCHES) {
43
+ const dropped = this._batches.shift();
44
+ if (dropped) {
45
+ droppedEventCount += dropped.events.length;
46
+ }
47
+ }
48
+ return droppedEventCount;
49
+ }
50
+ }
51
+ exports.BatchQueue = BatchQueue;
@@ -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;
@@ -10,6 +10,8 @@ export declare class ErrorBoundary {
10
10
  constructor(_sdkKey: string, _options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined, _lastSeenError?: Error | undefined);
11
11
  wrap(instance: unknown, namePrefix?: string): void;
12
12
  logError(tag: string, error: unknown): void;
13
+ logDroppedEvents(count: number, reason: string, metadata?: Record<string, unknown>): void;
14
+ logEventRequestFailure(count: number, reason: string, flushType: string, statusCode: number): void;
13
15
  getLastSeenErrorAndReset(): Error | null;
14
16
  attachErrorIfNoneExists(error: unknown): void;
15
17
  private _capture;
@@ -44,6 +44,25 @@ class ErrorBoundary {
44
44
  logError(tag, error) {
45
45
  this._onError(tag, error);
46
46
  }
47
+ logDroppedEvents(count, reason, metadata) {
48
+ const extra = {
49
+ eventCount: String(count),
50
+ };
51
+ if (metadata) {
52
+ Object.entries(metadata).forEach(([key, value]) => {
53
+ extra[key] = String(value);
54
+ });
55
+ }
56
+ this._onError(`statsig::log_event_dropped_event_count`, new Error(reason), true, extra);
57
+ }
58
+ logEventRequestFailure(count, reason, flushType, statusCode) {
59
+ const extra = {
60
+ eventCount: String(count),
61
+ flushType: flushType,
62
+ statusCode: String(statusCode),
63
+ };
64
+ this._onError(`statsig::log_event_failed`, new Error(reason), true, extra);
65
+ }
47
66
  getLastSeenErrorAndReset() {
48
67
  const tempError = this._lastSeenError;
49
68
  this._lastSeenError = undefined;
@@ -68,7 +87,7 @@ class ErrorBoundary {
68
87
  return null;
69
88
  }
70
89
  }
71
- _onError(tag, error) {
90
+ _onError(tag, error, bypassDedupe = false, extra) {
72
91
  try {
73
92
  Log_1.Log.warn(`Caught error in ${tag}`, { error });
74
93
  const impl = () => __awaiter(this, void 0, void 0, function* () {
@@ -78,7 +97,7 @@ class ErrorBoundary {
78
97
  const name = isError ? unwrapped.name : 'No Name';
79
98
  const resolvedError = _resolveError(unwrapped);
80
99
  this._lastSeenError = resolvedError;
81
- if (this._seen.has(name)) {
100
+ if (!bypassDedupe && this._seen.has(name)) {
82
101
  return;
83
102
  }
84
103
  this._seen.add(name);
@@ -93,7 +112,8 @@ class ErrorBoundary {
93
112
  const sdkType = SDKType_1.SDKType._get(this._sdkKey);
94
113
  const statsigMetadata = StatsigMetadata_1.StatsigMetadataProvider.get();
95
114
  const info = isError ? unwrapped.stack : _getDescription(unwrapped);
96
- const body = Object.assign({ tag, exception: name, info, statsigOptions: _getStatsigOptionLoggingCopy(this._options) }, Object.assign(Object.assign({}, statsigMetadata), { sdkType }));
115
+ const body = Object.assign({ tag, exception: name, info,
116
+ extra, statsigOptions: _getStatsigOptionLoggingCopy(this._options) }, Object.assign(Object.assign({}, statsigMetadata), { sdkType }));
97
117
  const func = (_f = (_e = (_d = this._options) === null || _d === void 0 ? void 0 : _d.networkConfig) === null || _e === void 0 ? void 0 : _e.networkOverrideFunc) !== null && _f !== void 0 ? _f : fetch;
98
118
  yield func(exports.EXCEPTION_ENDPOINT, {
99
119
  method: 'POST',
@@ -1,3 +1,4 @@
1
+ import { ErrorBoundary } from './ErrorBoundary';
1
2
  import { NetworkCore } from './NetworkCore';
2
3
  import { StatsigClientEmitEventFunc } from './StatsigClientBase';
3
4
  import { StatsigEventInternal } from './StatsigEvent';
@@ -7,18 +8,18 @@ export declare class EventLogger {
7
8
  private _emitter;
8
9
  private _network;
9
10
  private _options;
10
- private _queue;
11
- private _flushIntervalId;
11
+ private _errorBoundary;
12
+ private _pendingEvents;
13
+ private _batchQueue;
14
+ private _flushCoordinator;
12
15
  private _lastExposureTimeMap;
13
16
  private _nonExposedChecks;
14
- private _maxQueueSize;
15
- private _hasRunQuickFlush;
16
- private _creationTime;
17
17
  private _loggingEnabled;
18
18
  private _logEventUrlConfig;
19
+ private _isShuttingDown;
20
+ private _storageKey;
19
21
  private static _safeFlushAndForget;
20
- private static _safeRetryFailedLogs;
21
- constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null);
22
+ constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon<NetworkConfigCommon> | null, _errorBoundary: ErrorBoundary);
22
23
  setLogEventCompressionMode(mode: LogEventCompressionMode): void;
23
24
  setLoggingEnabled(loggingEnabled: LoggingEnabledOption): void;
24
25
  enqueue(event: StatsigEventInternal): void;
@@ -27,22 +28,12 @@ export declare class EventLogger {
27
28
  start(): void;
28
29
  stop(): Promise<void>;
29
30
  flush(): Promise<void>;
30
- /**
31
- * We 'Quick Flush' following the very first event enqueued
32
- * within the quick flush window
33
- */
34
- private _quickFlushIfNeeded;
31
+ appendAndResetNonExposedChecks(): void;
35
32
  private _shouldLogEvent;
36
- private _sendEvents;
37
- private _sendEventsViaPost;
38
- private _sendEventsViaBeacon;
39
- private _getRequestData;
40
- private _saveFailedLogsToStorage;
41
- private _getFailedLogsFromStorage;
42
- private _retryFailedLogs;
43
- private _getStorageKey;
44
- private _normalizeAndAppendEvent;
45
- private _appendAndResetNonExposedChecks;
46
33
  private _getCurrentPageUrl;
47
- private _startBackgroundFlushInterval;
34
+ private _getStorageKey;
35
+ private _storeEventToStorage;
36
+ private _getEventsFromStorage;
37
+ private _loadStoredEvents;
38
+ private _normalizeEvent;
48
39
  }