@statsig/client-core 0.0.1-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.
Files changed (50) hide show
  1. package/README.md +5 -0
  2. package/package.json +8 -0
  3. package/src/$_StatsigGlobal.d.ts +10 -0
  4. package/src/$_StatsigGlobal.js +14 -0
  5. package/src/ClientInterfaces.d.ts +28 -0
  6. package/src/ClientInterfaces.js +2 -0
  7. package/src/Diagnostics.d.ts +5 -0
  8. package/src/Diagnostics.js +54 -0
  9. package/src/ErrorBoundary.d.ts +9 -0
  10. package/src/ErrorBoundary.js +131 -0
  11. package/src/EventLogger.d.ts +31 -0
  12. package/src/EventLogger.js +285 -0
  13. package/src/Hashing.d.ts +1 -0
  14. package/src/Hashing.js +13 -0
  15. package/src/Log.d.ts +14 -0
  16. package/src/Log.js +59 -0
  17. package/src/Monitoring.d.ts +3 -0
  18. package/src/Monitoring.js +33 -0
  19. package/src/NetworkCore.d.ts +22 -0
  20. package/src/NetworkCore.js +197 -0
  21. package/src/SessionID.d.ts +3 -0
  22. package/src/SessionID.js +13 -0
  23. package/src/StableID.d.ts +4 -0
  24. package/src/StableID.js +78 -0
  25. package/src/StatsigClientBase.d.ts +37 -0
  26. package/src/StatsigClientBase.js +272 -0
  27. package/src/StatsigClientEventEmitter.d.ts +33 -0
  28. package/src/StatsigClientEventEmitter.js +2 -0
  29. package/src/StatsigDataProvider.d.ts +9 -0
  30. package/src/StatsigDataProvider.js +2 -0
  31. package/src/StatsigEvent.d.ts +31 -0
  32. package/src/StatsigEvent.js +57 -0
  33. package/src/StatsigMetadata.d.ts +18 -0
  34. package/src/StatsigMetadata.js +25 -0
  35. package/src/StatsigOptionsCommon.d.ts +48 -0
  36. package/src/StatsigOptionsCommon.js +2 -0
  37. package/src/StatsigTypes.d.ts +22 -0
  38. package/src/StatsigTypes.js +31 -0
  39. package/src/StatsigUser.d.ts +24 -0
  40. package/src/StatsigUser.js +37 -0
  41. package/src/StorageProvider.d.ts +11 -0
  42. package/src/StorageProvider.js +110 -0
  43. package/src/UUID.d.ts +1 -0
  44. package/src/UUID.js +24 -0
  45. package/src/VisibilityChangeObserver.d.ts +13 -0
  46. package/src/VisibilityChangeObserver.js +45 -0
  47. package/src/__tests__/MockLocalStorage.d.ts +9 -0
  48. package/src/__tests__/MockLocalStorage.js +37 -0
  49. package/src/index.d.ts +23 -0
  50. package/src/index.js +52 -0
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Statsig - Core
2
+
3
+ The package that contains all the common logic shared by the various Statsig Javascript packages.
4
+
5
+ Learn more by visiting: https://docs.statsig.com/client/jsClientSDK
package/package.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@statsig/client-core",
3
+ "version": "0.0.1-beta.1",
4
+ "dependencies": {},
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "typings": "./src/index.d.ts"
8
+ }
@@ -0,0 +1,10 @@
1
+ export type StatsigGlobal = {
2
+ [key: string]: unknown;
3
+ instances?: Set<unknown>;
4
+ };
5
+ declare global {
6
+ let __STATSIG__: StatsigGlobal | undefined;
7
+ interface Window {
8
+ __STATSIG__: StatsigGlobal | undefined;
9
+ }
10
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var statsigGlobal = {};
4
+ if (typeof window !== 'undefined') {
5
+ window.__STATSIG__ = statsigGlobal;
6
+ }
7
+ if (typeof global !== 'undefined') {
8
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
9
+ global.__STATSIG__ = statsigGlobal;
10
+ }
11
+ if (typeof globalThis !== 'undefined') {
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
13
+ globalThis.__STATSIG__ = statsigGlobal;
14
+ }
@@ -0,0 +1,28 @@
1
+ import { EvaluationOptions } from './StatsigClientBase';
2
+ import { StatsigClientEventEmitterInterface } from './StatsigClientEventEmitter';
3
+ import { StatsigEvent } from './StatsigEvent';
4
+ import { DynamicConfig, Experiment, FeatureGate, Layer } from './StatsigTypes';
5
+ import { StatsigUser } from './StatsigUser';
6
+ export interface StatsigClientCommonInterface extends StatsigClientEventEmitterInterface {
7
+ initialize(): Promise<void>;
8
+ shutdown(): Promise<void>;
9
+ }
10
+ export interface OnDeviceEvaluationsInterface extends StatsigClientCommonInterface {
11
+ checkGate(name: string, user: StatsigUser, options: EvaluationOptions): boolean;
12
+ getFeatureGate(name: string, user: StatsigUser, options: EvaluationOptions): FeatureGate;
13
+ getDynamicConfig(name: string, user: StatsigUser, options: EvaluationOptions): DynamicConfig;
14
+ getExperiment(name: string, user: StatsigUser, options: EvaluationOptions): Experiment;
15
+ getLayer(name: string, user: StatsigUser, options: EvaluationOptions): Layer;
16
+ logEvent(event: StatsigEvent, user: StatsigUser): void;
17
+ }
18
+ export interface PrecomputedEvaluationsInterface extends StatsigClientCommonInterface {
19
+ getCurrentUser(): StatsigUser;
20
+ updateUser(user: StatsigUser): Promise<void>;
21
+ checkGate(name: string, options: EvaluationOptions): boolean;
22
+ getFeatureGate(name: string, options: EvaluationOptions): FeatureGate;
23
+ getDynamicConfig(name: string, options: EvaluationOptions): DynamicConfig;
24
+ getExperiment(name: string, options: EvaluationOptions): Experiment;
25
+ getLayer(name: string, options: EvaluationOptions): Layer;
26
+ logEvent(event: StatsigEvent): void;
27
+ }
28
+ export type StatsigClientInterface = OnDeviceEvaluationsInterface | PrecomputedEvaluationsInterface;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export declare function captureDiagnostics(func: string, task: () => unknown): unknown;
2
+ export declare abstract class Diagnostics {
3
+ static mark(tag: string, metadata?: Record<string, unknown>): void;
4
+ static flush(): void;
5
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Diagnostics = exports.captureDiagnostics = void 0;
4
+ var Log_1 = require("./Log");
5
+ var SUPPORTS_PERFORMANCE_API = typeof performance !== 'undefined' && typeof performance.mark !== 'undefined';
6
+ var markers = [];
7
+ function captureDiagnostics(func, task) {
8
+ Diagnostics.mark("".concat(func, ":start"));
9
+ var result = task();
10
+ var markEnd = function () {
11
+ Diagnostics.mark("".concat(func, ":end"));
12
+ maybeFlush("".concat(func, ":end"));
13
+ };
14
+ if (result && result instanceof Promise) {
15
+ return result.finally(function () { return markEnd(); });
16
+ }
17
+ else {
18
+ markEnd();
19
+ }
20
+ return result;
21
+ }
22
+ exports.captureDiagnostics = captureDiagnostics;
23
+ var Diagnostics = /** @class */ (function () {
24
+ function Diagnostics() {
25
+ }
26
+ Diagnostics.mark = function (tag, metadata) {
27
+ if (!SUPPORTS_PERFORMANCE_API) {
28
+ return;
29
+ }
30
+ var marker = performance.mark(tag, { detail: metadata });
31
+ markers.push(marker);
32
+ };
33
+ Diagnostics.flush = function () {
34
+ var resources = performance
35
+ .getEntriesByType('resource')
36
+ .filter(function (resource) {
37
+ return resource.name.startsWith('https://api.statsig.com');
38
+ });
39
+ var payload = {
40
+ markers: markers,
41
+ resources: resources,
42
+ };
43
+ // TODO: Send as log to Statsig
44
+ Log_1.Log.debug('Diagnostics', payload, JSON.stringify(payload));
45
+ markers = [];
46
+ };
47
+ return Diagnostics;
48
+ }());
49
+ exports.Diagnostics = Diagnostics;
50
+ function maybeFlush(tag) {
51
+ if (tag.startsWith('initialize:')) {
52
+ Diagnostics.flush();
53
+ }
54
+ }
@@ -0,0 +1,9 @@
1
+ import { StatsigClientEmitEventFunc } from './StatsigClientBase';
2
+ export declare const EXCEPTION_ENDPOINT = "https://statsigapi.net/v1/sdk_exception";
3
+ export declare class ErrorBoundary {
4
+ private _sdkKey;
5
+ private _seen;
6
+ constructor(_sdkKey: string);
7
+ capture(tag: string, task: () => unknown, emitter?: StatsigClientEmitEventFunc): unknown;
8
+ private _onError;
9
+ }
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.ErrorBoundary = exports.EXCEPTION_ENDPOINT = void 0;
51
+ var Log_1 = require("./Log");
52
+ var StatsigMetadata_1 = require("./StatsigMetadata");
53
+ exports.EXCEPTION_ENDPOINT = 'https://statsigapi.net/v1/sdk_exception';
54
+ var ErrorBoundary = /** @class */ (function () {
55
+ function ErrorBoundary(_sdkKey) {
56
+ this._sdkKey = _sdkKey;
57
+ this._seen = new Set();
58
+ }
59
+ ErrorBoundary.prototype.capture = function (tag, task, emitter) {
60
+ var _this = this;
61
+ try {
62
+ var res = task();
63
+ if (res && res instanceof Promise) {
64
+ return res.catch(function (err) { return _this._onError(tag, err, emitter); });
65
+ }
66
+ return res;
67
+ }
68
+ catch (error) {
69
+ this._onError(tag, error, emitter);
70
+ return null;
71
+ }
72
+ };
73
+ ErrorBoundary.prototype._onError = function (tag, error, emitter) {
74
+ var _this = this;
75
+ try {
76
+ Log_1.Log.warn("Caught error in ".concat(tag), { error: error });
77
+ var impl = function () { return __awaiter(_this, void 0, void 0, function () {
78
+ var unwrapped, isError, name, statsigMetadata, info, body;
79
+ return __generator(this, function (_a) {
80
+ switch (_a.label) {
81
+ case 0:
82
+ unwrapped = (error !== null && error !== void 0 ? error : Error('[Statsig] Error was empty'));
83
+ isError = unwrapped instanceof Error;
84
+ name = isError ? unwrapped.name : 'No Name';
85
+ if (this._seen.has(name)) {
86
+ return [2 /*return*/];
87
+ }
88
+ this._seen.add(name);
89
+ statsigMetadata = StatsigMetadata_1.StatsigMetadataProvider.get();
90
+ info = isError ? unwrapped.stack : _getDescription(unwrapped);
91
+ body = JSON.stringify(__assign({ tag: tag, exception: name, info: info }, statsigMetadata));
92
+ return [4 /*yield*/, fetch(exports.EXCEPTION_ENDPOINT, {
93
+ method: 'POST',
94
+ headers: {
95
+ 'STATSIG-API-KEY': this._sdkKey,
96
+ 'STATSIG-SDK-TYPE': String(statsigMetadata.sdkType),
97
+ 'STATSIG-SDK-VERSION': String(statsigMetadata.sdkVersion),
98
+ 'Content-Type': 'application/json',
99
+ },
100
+ body: body,
101
+ })];
102
+ case 1:
103
+ _a.sent();
104
+ emitter === null || emitter === void 0 ? void 0 : emitter({ event: 'error', error: error });
105
+ return [2 /*return*/];
106
+ }
107
+ });
108
+ }); };
109
+ impl()
110
+ .then(function () {
111
+ /* noop */
112
+ })
113
+ .catch(function () {
114
+ /* noop */
115
+ });
116
+ }
117
+ catch (_error) {
118
+ /* noop */
119
+ }
120
+ };
121
+ return ErrorBoundary;
122
+ }());
123
+ exports.ErrorBoundary = ErrorBoundary;
124
+ function _getDescription(obj) {
125
+ try {
126
+ return JSON.stringify(obj);
127
+ }
128
+ catch (_a) {
129
+ return '[Statsig] Failed to get string for error.';
130
+ }
131
+ }
@@ -0,0 +1,31 @@
1
+ import { NetworkCore } from './NetworkCore';
2
+ import { StatsigClientEmitEventFunc } from './StatsigClientBase';
3
+ import { StatsigEventInternal } from './StatsigEvent';
4
+ import { StatsigOptionsCommon } from './StatsigOptionsCommon';
5
+ import { Visibility } from './VisibilityChangeObserver';
6
+ export declare class EventLogger {
7
+ private _sdkKey;
8
+ private _emitter;
9
+ private _network;
10
+ private _options;
11
+ private _queue;
12
+ private _flushTimer;
13
+ private _lastExposureMap;
14
+ private _maxQueueSize;
15
+ private _failedLogs;
16
+ constructor(_sdkKey: string, _emitter: StatsigClientEmitEventFunc, _network: NetworkCore, _options: StatsigOptionsCommon | null);
17
+ enqueue(event: StatsigEventInternal): void;
18
+ reset(): void;
19
+ onVisibilityChanged(visibility: Visibility): void;
20
+ shutdown(): Promise<void>;
21
+ private _shouldLogEvent;
22
+ private _flushAndForget;
23
+ private _flush;
24
+ private _sendEvents;
25
+ private _sendEventsViaPost;
26
+ private _sendEventsViaBeacon;
27
+ private _isBeaconSupported;
28
+ private _saveFailedLogsToStorage;
29
+ private _retryFailedLogs;
30
+ private _getStorageKey;
31
+ }
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.EventLogger = void 0;
51
+ var Hashing_1 = require("./Hashing");
52
+ var Log_1 = require("./Log");
53
+ var StatsigEvent_1 = require("./StatsigEvent");
54
+ var StatsigMetadata_1 = require("./StatsigMetadata");
55
+ var StorageProvider_1 = require("./StorageProvider");
56
+ var VisibilityChangeObserver_1 = require("./VisibilityChangeObserver");
57
+ var DEFAULT_QUEUE_SIZE = 50;
58
+ var DEFAULT_FLUSH_INTERVAL_MS = 10000;
59
+ var MAX_DEDUPER_KEYS = 1000;
60
+ var DEDUPER_WINDOW_DURATION_MS = 60000;
61
+ var MAX_FAILED_LOGS = 500;
62
+ var DEFAULT_API = 'https://api.statsig.com/v1';
63
+ var EventLogger = /** @class */ (function () {
64
+ function EventLogger(_sdkKey, _emitter, _network, _options) {
65
+ var _this = this;
66
+ var _a, _b;
67
+ this._sdkKey = _sdkKey;
68
+ this._emitter = _emitter;
69
+ this._network = _network;
70
+ this._options = _options;
71
+ this._queue = [];
72
+ this._lastExposureMap = {};
73
+ this._maxQueueSize = (_a = _options === null || _options === void 0 ? void 0 : _options.loggingBufferMaxSize) !== null && _a !== void 0 ? _a : DEFAULT_QUEUE_SIZE;
74
+ var flushInterval = (_b = _options === null || _options === void 0 ? void 0 : _options.loggingIntervalMs) !== null && _b !== void 0 ? _b : DEFAULT_FLUSH_INTERVAL_MS;
75
+ this._flushTimer = setInterval(function () { return _this._flushAndForget(); }, flushInterval);
76
+ VisibilityChangeObserver_1.VisibilityChangeObserver.add(this);
77
+ this._failedLogs = [];
78
+ this._retryFailedLogs();
79
+ }
80
+ EventLogger.prototype.enqueue = function (event) {
81
+ if (!this._shouldLogEvent(event)) {
82
+ return;
83
+ }
84
+ if (event.user) {
85
+ event.user = __assign({}, event.user);
86
+ delete event.user.privateAttributes;
87
+ }
88
+ var _a = StatsigMetadata_1.StatsigMetadataProvider.get(), sdkType = _a.sdkType, sdkVersion = _a.sdkVersion;
89
+ this._queue.push(__assign(__assign({}, event), { statsigMetadata: { sdkType: sdkType, sdkVersion: sdkVersion } }));
90
+ if (this._queue.length > this._maxQueueSize) {
91
+ this._flushAndForget();
92
+ }
93
+ };
94
+ EventLogger.prototype.reset = function () {
95
+ this._lastExposureMap = {};
96
+ };
97
+ EventLogger.prototype.onVisibilityChanged = function (visibility) {
98
+ if (visibility === 'background') {
99
+ this._flushAndForget();
100
+ }
101
+ };
102
+ EventLogger.prototype.shutdown = function () {
103
+ return __awaiter(this, void 0, void 0, function () {
104
+ return __generator(this, function (_a) {
105
+ switch (_a.label) {
106
+ case 0:
107
+ if (this._flushTimer) {
108
+ clearInterval(this._flushTimer);
109
+ this._flushTimer = null;
110
+ }
111
+ return [4 /*yield*/, this._flush()];
112
+ case 1:
113
+ _a.sent();
114
+ return [2 /*return*/];
115
+ }
116
+ });
117
+ });
118
+ };
119
+ EventLogger.prototype._shouldLogEvent = function (event) {
120
+ var _a, _b, _c, _d;
121
+ if (!(0, StatsigEvent_1.isExposureEvent)(event)) {
122
+ return true;
123
+ }
124
+ var key = [
125
+ event.eventName,
126
+ (_a = event.user) === null || _a === void 0 ? void 0 : _a.userID,
127
+ (_b = event.metadata) === null || _b === void 0 ? void 0 : _b['gate'],
128
+ (_c = event.metadata) === null || _c === void 0 ? void 0 : _c['config'],
129
+ (_d = event.metadata) === null || _d === void 0 ? void 0 : _d['ruleID'],
130
+ ].join('|');
131
+ var previous = this._lastExposureMap[key];
132
+ var now = Date.now();
133
+ if (previous && now - previous < DEDUPER_WINDOW_DURATION_MS) {
134
+ return false;
135
+ }
136
+ if (Object.keys(this._lastExposureMap).length > MAX_DEDUPER_KEYS) {
137
+ this._lastExposureMap = {};
138
+ }
139
+ this._lastExposureMap[key] = now;
140
+ return true;
141
+ };
142
+ EventLogger.prototype._flushAndForget = function () {
143
+ this._flush().catch(function () {
144
+ // noop
145
+ });
146
+ };
147
+ EventLogger.prototype._flush = function () {
148
+ return __awaiter(this, void 0, void 0, function () {
149
+ var events;
150
+ return __generator(this, function (_a) {
151
+ switch (_a.label) {
152
+ case 0:
153
+ if (this._queue.length === 0) {
154
+ return [2 /*return*/];
155
+ }
156
+ events = this._queue;
157
+ this._queue = [];
158
+ return [4 /*yield*/, this._sendEvents(events)];
159
+ case 1:
160
+ _a.sent();
161
+ return [2 /*return*/];
162
+ }
163
+ });
164
+ });
165
+ };
166
+ EventLogger.prototype._sendEvents = function (events) {
167
+ var _a, _b;
168
+ return __awaiter(this, void 0, void 0, function () {
169
+ var isInForeground, api, response, _c, _d;
170
+ return __generator(this, function (_e) {
171
+ switch (_e.label) {
172
+ case 0:
173
+ _e.trys.push([0, 4, , 5]);
174
+ isInForeground = VisibilityChangeObserver_1.VisibilityChangeObserver.isCurrentlyVisible();
175
+ api = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.api) !== null && _b !== void 0 ? _b : DEFAULT_API;
176
+ if (!(!isInForeground && this._isBeaconSupported())) return [3 /*break*/, 1];
177
+ _c = this._sendEventsViaBeacon(api, events);
178
+ return [3 /*break*/, 3];
179
+ case 1: return [4 /*yield*/, this._sendEventsViaPost(api, events)];
180
+ case 2:
181
+ _c = _e.sent();
182
+ _e.label = 3;
183
+ case 3:
184
+ response = _c;
185
+ if (response.success) {
186
+ this._emitter({
187
+ event: 'logs_flushed',
188
+ events: events,
189
+ });
190
+ }
191
+ else {
192
+ this._saveFailedLogsToStorage(events);
193
+ }
194
+ return [3 /*break*/, 5];
195
+ case 4:
196
+ _d = _e.sent();
197
+ Log_1.Log.warn('Failed to flush events.');
198
+ return [3 /*break*/, 5];
199
+ case 5: return [2 /*return*/];
200
+ }
201
+ });
202
+ });
203
+ };
204
+ EventLogger.prototype._sendEventsViaPost = function (api, events) {
205
+ return __awaiter(this, void 0, void 0, function () {
206
+ var result;
207
+ return __generator(this, function (_a) {
208
+ switch (_a.label) {
209
+ case 0: return [4 /*yield*/, this._network.post({
210
+ sdkKey: this._sdkKey,
211
+ data: {
212
+ events: events,
213
+ },
214
+ url: "".concat(api, "/rgstr"),
215
+ retries: 3,
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ 'STATSIG-EVENT-COUNT': String(events.length),
219
+ },
220
+ })];
221
+ case 1:
222
+ result = _a.sent();
223
+ if (result) {
224
+ return [2 /*return*/, JSON.parse(result)];
225
+ }
226
+ return [2 /*return*/, { success: false }];
227
+ }
228
+ });
229
+ });
230
+ };
231
+ EventLogger.prototype._sendEventsViaBeacon = function (api, events) {
232
+ return {
233
+ success: this._network.beacon({
234
+ sdkKey: this._sdkKey,
235
+ data: {
236
+ events: events,
237
+ },
238
+ url: "".concat(api, "/log_event_beacon"),
239
+ }),
240
+ };
241
+ };
242
+ EventLogger.prototype._isBeaconSupported = function () {
243
+ return (typeof navigator !== 'undefined' &&
244
+ typeof (navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === 'function');
245
+ };
246
+ EventLogger.prototype._saveFailedLogsToStorage = function (events) {
247
+ var _a;
248
+ (_a = this._failedLogs).push.apply(_a, events);
249
+ while (this._failedLogs.length > MAX_FAILED_LOGS) {
250
+ this._failedLogs.shift();
251
+ }
252
+ var storageKey = this._getStorageKey();
253
+ (0, StorageProvider_1.setObjectInStorage)(storageKey, this._failedLogs).catch(function () {
254
+ Log_1.Log.warn('Unable to save failed logs to storage');
255
+ });
256
+ };
257
+ EventLogger.prototype._retryFailedLogs = function () {
258
+ var _this = this;
259
+ var storageKey = this._getStorageKey();
260
+ (function () { return __awaiter(_this, void 0, void 0, function () {
261
+ var events;
262
+ return __generator(this, function (_a) {
263
+ switch (_a.label) {
264
+ case 0: return [4 /*yield*/, (0, StorageProvider_1.getObjectFromStorage)(storageKey)];
265
+ case 1:
266
+ events = _a.sent();
267
+ if (!events) {
268
+ return [2 /*return*/];
269
+ }
270
+ return [4 /*yield*/, this._sendEvents(events)];
271
+ case 2:
272
+ _a.sent();
273
+ return [2 /*return*/];
274
+ }
275
+ });
276
+ }); })().catch(function () {
277
+ Log_1.Log.warn('Unable to flush stored logs');
278
+ });
279
+ };
280
+ EventLogger.prototype._getStorageKey = function () {
281
+ return "STATSIG_FAILED_LOG:".concat((0, Hashing_1.DJB2)(this._sdkKey));
282
+ };
283
+ return EventLogger;
284
+ }());
285
+ exports.EventLogger = EventLogger;
@@ -0,0 +1 @@
1
+ export declare function DJB2(value: string): string;
package/src/Hashing.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DJB2 = void 0;
4
+ function DJB2(value) {
5
+ var hash = 0;
6
+ for (var i = 0; i < value.length; i++) {
7
+ var character = value.charCodeAt(i);
8
+ hash = (hash << 5) - hash + character;
9
+ hash = hash & hash; // Convert to 32bit integer
10
+ }
11
+ return String(hash >>> 0);
12
+ }
13
+ exports.DJB2 = DJB2;
package/src/Log.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare enum LogLevel {
2
+ None = 0,
3
+ Error = 1,
4
+ Warn = 2,
5
+ Info = 3,
6
+ Debug = 4
7
+ }
8
+ export declare abstract class Log {
9
+ static level: LogLevel;
10
+ static info(...args: unknown[]): void;
11
+ static debug(...args: unknown[]): void;
12
+ static warn(...args: unknown[]): void;
13
+ static error(...args: unknown[]): void;
14
+ }