@statsig/client-core 0.0.1-beta.4 → 0.0.1-beta.40

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 (73) hide show
  1. package/README.md +1 -4
  2. package/package.json +1 -1
  3. package/src/$_StatsigGlobal.d.ts +8 -1
  4. package/src/$_StatsigGlobal.js +22 -12
  5. package/src/ClientInterfaces.d.ts +50 -14
  6. package/src/DataAdapterCore.d.ts +30 -0
  7. package/src/DataAdapterCore.js +169 -0
  8. package/src/Diagnostics.d.ts +1 -3
  9. package/src/Diagnostics.js +4 -49
  10. package/src/DownloadConfigSpecsResponse.d.ts +41 -0
  11. package/src/ErrorBoundary.d.ts +7 -2
  12. package/src/ErrorBoundary.js +79 -87
  13. package/src/EvaluationOptions.d.ts +20 -0
  14. package/src/EvaluationOptions.js +2 -0
  15. package/src/EvaluationTypes.d.ts +39 -0
  16. package/src/EvaluationTypes.js +2 -0
  17. package/src/EventLogger.d.ts +20 -9
  18. package/src/EventLogger.js +216 -232
  19. package/src/Hashing.d.ts +2 -1
  20. package/src/Hashing.js +25 -6
  21. package/src/InitializeResponse.d.ts +18 -0
  22. package/src/InitializeResponse.js +2 -0
  23. package/src/Log.js +15 -34
  24. package/src/NetworkConfig.d.ts +18 -0
  25. package/src/NetworkConfig.js +18 -0
  26. package/src/NetworkCore.d.ts +19 -6
  27. package/src/NetworkCore.js +137 -167
  28. package/src/OverrideAdapter.d.ts +9 -0
  29. package/src/OverrideAdapter.js +2 -0
  30. package/src/SDKType.d.ts +8 -0
  31. package/src/SDKType.js +19 -0
  32. package/src/SafeJs.d.ts +6 -0
  33. package/src/SafeJs.js +41 -0
  34. package/src/SessionID.d.ts +17 -1
  35. package/src/SessionID.js +89 -8
  36. package/src/StableID.js +24 -53
  37. package/src/StatsigClientBase.d.ts +57 -28
  38. package/src/StatsigClientBase.js +115 -238
  39. package/src/StatsigClientEventEmitter.d.ts +69 -28
  40. package/src/StatsigDataAdapter.d.ts +107 -0
  41. package/src/StatsigDataAdapter.js +4 -0
  42. package/src/StatsigEvent.d.ts +10 -19
  43. package/src/StatsigEvent.js +50 -41
  44. package/src/StatsigMetadata.d.ts +1 -1
  45. package/src/StatsigMetadata.js +7 -18
  46. package/src/StatsigOptionsCommon.d.ts +85 -17
  47. package/src/StatsigTypeFactories.d.ts +6 -0
  48. package/src/StatsigTypeFactories.js +50 -0
  49. package/src/StatsigTypes.d.ts +29 -18
  50. package/src/StatsigTypes.js +0 -29
  51. package/src/StatsigUser.d.ts +2 -5
  52. package/src/StatsigUser.js +10 -18
  53. package/src/StorageProvider.d.ts +13 -8
  54. package/src/StorageProvider.js +77 -73
  55. package/src/TypedJsonParse.d.ts +8 -0
  56. package/src/TypedJsonParse.js +27 -0
  57. package/src/UUID.js +9 -5
  58. package/src/UrlOverrides.d.ts +1 -0
  59. package/src/UrlOverrides.js +15 -0
  60. package/src/UtitlityTypes.d.ts +3 -0
  61. package/src/UtitlityTypes.js +2 -0
  62. package/src/VisibilityObserving.d.ts +8 -0
  63. package/src/VisibilityObserving.js +30 -0
  64. package/src/index.d.ts +20 -5
  65. package/src/index.js +30 -18
  66. package/src/Monitoring.d.ts +0 -3
  67. package/src/Monitoring.js +0 -33
  68. package/src/StatsigDataProvider.d.ts +0 -9
  69. package/src/VisibilityChangeObserver.d.ts +0 -13
  70. package/src/VisibilityChangeObserver.js +0 -48
  71. package/src/__tests__/MockLocalStorage.d.ts +0 -9
  72. package/src/__tests__/MockLocalStorage.js +0 -37
  73. /package/src/{StatsigDataProvider.js → DownloadConfigSpecsResponse.js} +0 -0
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkParam = exports.NetworkDefault = void 0;
4
+ exports.NetworkDefault = {
5
+ eventsApi: 'https://prodregistryv2.org/v1',
6
+ initializeApi: 'https://featureassets.org/v1',
7
+ specsApi: 'https://assetsconfigcdn.org/v1',
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 || (exports.NetworkParam = NetworkParam = {}));
@@ -1,22 +1,35 @@
1
- import { StatsigOptionsCommon } from './StatsigOptionsCommon';
1
+ import './$_StatsigGlobal';
2
+ import { NetworkPriority } from './NetworkConfig';
3
+ import { StatsigClientEmitEventFunc } from './StatsigClientBase';
4
+ import { AnyStatsigOptions } from './StatsigOptionsCommon';
2
5
  type RequestArgs = {
3
6
  sdkKey: string;
4
7
  url: string;
5
- timeoutMs?: number;
8
+ priority?: NetworkPriority;
6
9
  retries?: number;
10
+ params?: Record<string, string>;
7
11
  headers?: Record<string, string>;
8
12
  };
9
13
  type RequestArgsWithData = RequestArgs & {
10
14
  data: Record<string, unknown>;
15
+ isStatsigEncodable?: boolean;
16
+ };
17
+ type NetworkResponse = {
18
+ body: string | null;
19
+ code: number;
11
20
  };
12
21
  export declare class NetworkCore {
13
22
  private _options;
23
+ private _emitter?;
14
24
  private readonly _timeout;
15
- constructor(_options: StatsigOptionsCommon | null);
16
- post(args: RequestArgsWithData): Promise<string | null>;
17
- get(args: RequestArgs): Promise<string | null>;
18
- beacon(args: RequestArgsWithData): boolean;
25
+ constructor(_options: AnyStatsigOptions | null, _emitter?: StatsigClientEmitEventFunc | undefined);
26
+ post(args: RequestArgsWithData): Promise<NetworkResponse | null>;
27
+ get(args: RequestArgs): Promise<NetworkResponse | null>;
28
+ isBeaconSupported(): boolean;
29
+ beacon(args: RequestArgsWithData): Promise<boolean>;
19
30
  private _sendRequest;
20
31
  private _getPopulatedURL;
32
+ private _getPopulatedBody;
33
+ private _attemptToEncodeString;
21
34
  }
22
35
  export {};
@@ -1,30 +1,4 @@
1
1
  "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- if (typeof b !== "function" && b !== null)
11
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __assign = (this && this.__assign) || function () {
18
- __assign = Object.assign || function(t) {
19
- for (var s, i = 1, n = arguments.length; i < n; i++) {
20
- s = arguments[i];
21
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
- t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
29
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -34,154 +8,150 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
34
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
35
9
  });
36
10
  };
37
- var __generator = (this && this.__generator) || function (thisArg, body) {
38
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
39
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
40
- function verb(n) { return function (v) { return step([n, v]); }; }
41
- function step(op) {
42
- if (f) throw new TypeError("Generator is already executing.");
43
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
44
- 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;
45
- if (y = 0, t) op = [op[0] & 2, t.value];
46
- switch (op[0]) {
47
- case 0: case 1: t = op; break;
48
- case 4: _.label++; return { value: op[1], done: false };
49
- case 5: _.label++; y = op[1]; op = [0]; continue;
50
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
51
- default:
52
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
53
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
54
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
55
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
56
- if (t[2]) _.ops.pop();
57
- _.trys.pop(); continue;
58
- }
59
- op = body.call(thisArg, _);
60
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
61
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
62
- }
63
- };
64
11
  Object.defineProperty(exports, "__esModule", { value: true });
65
12
  exports.NetworkCore = void 0;
66
- var Diagnostics_1 = require("./Diagnostics");
67
- var Log_1 = require("./Log");
68
- var SessionID_1 = require("./SessionID");
69
- var StableID_1 = require("./StableID");
70
- var StatsigMetadata_1 = require("./StatsigMetadata");
71
- var DEFAULT_TIMEOUT_MS = 10000;
72
- var NetworkError = /** @class */ (function (_super) {
73
- __extends(NetworkError, _super);
74
- function NetworkError(message, errorDescription) {
75
- var _this = _super.call(this, message) || this;
76
- _this.errorDescription = errorDescription;
77
- return _this;
78
- }
79
- return NetworkError;
80
- }(Error));
81
- var NetworkCore = /** @class */ (function () {
82
- function NetworkCore(_options) {
83
- var _a;
13
+ require("./$_StatsigGlobal");
14
+ const Diagnostics_1 = require("./Diagnostics");
15
+ const Log_1 = require("./Log");
16
+ const NetworkConfig_1 = require("./NetworkConfig");
17
+ const SDKType_1 = require("./SDKType");
18
+ const SafeJs_1 = require("./SafeJs");
19
+ const SessionID_1 = require("./SessionID");
20
+ const StableID_1 = require("./StableID");
21
+ const StatsigMetadata_1 = require("./StatsigMetadata");
22
+ const DEFAULT_TIMEOUT_MS = 10000;
23
+ class NetworkCore {
24
+ constructor(_options, _emitter) {
25
+ var _a, _b;
84
26
  this._options = _options;
85
- this._timeout = (_a = _options === null || _options === void 0 ? void 0 : _options.networkTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT_MS;
27
+ this._emitter = _emitter;
28
+ this._timeout =
29
+ (_b = (_a = _options === null || _options === void 0 ? void 0 : _options.networkConfig) === null || _a === void 0 ? void 0 : _a.networkTimeoutMs) !== null && _b !== void 0 ? _b : DEFAULT_TIMEOUT_MS;
86
30
  }
87
- NetworkCore.prototype.post = function (args) {
88
- return __awaiter(this, void 0, void 0, function () {
89
- var data, stableID, sessionID, body;
90
- return __generator(this, function (_a) {
91
- switch (_a.label) {
92
- case 0:
93
- data = args.data;
94
- return [4 /*yield*/, StableID_1.StableID.get(args.sdkKey)];
95
- case 1:
96
- stableID = _a.sent();
97
- sessionID = SessionID_1.SessionID.get(args.sdkKey);
98
- body = JSON.stringify(__assign(__assign({}, data), { statsigMetadata: __assign(__assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID: stableID, sessionID: sessionID }) }));
99
- return [2 /*return*/, this._sendRequest(__assign({ method: 'POST', body: body }, args))];
100
- }
101
- });
31
+ post(args) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const body = yield this._getPopulatedBody(args);
34
+ return this._sendRequest(Object.assign({ method: 'POST', body: this._attemptToEncodeString(args, body) }, args));
102
35
  });
103
- };
104
- NetworkCore.prototype.get = function (args) {
105
- return __awaiter(this, void 0, void 0, function () {
106
- return __generator(this, function (_a) {
107
- return [2 /*return*/, this._sendRequest(__assign({ method: 'GET' }, args))];
108
- });
36
+ }
37
+ get(args) {
38
+ return this._sendRequest(Object.assign({ method: 'GET' }, args));
39
+ }
40
+ isBeaconSupported() {
41
+ return (typeof navigator !== 'undefined' &&
42
+ typeof (navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === 'function');
43
+ }
44
+ beacon(args) {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ if (!_ensureValidSdkKey(args)) {
47
+ return false;
48
+ }
49
+ const url = yield this._getPopulatedURL(args);
50
+ const body = yield this._getPopulatedBody(args);
51
+ return navigator.sendBeacon(url, body);
109
52
  });
110
- };
111
- NetworkCore.prototype.beacon = function (args) {
112
- var url = new URL(args.url);
113
- url.searchParams.append('k', args.sdkKey);
114
- return navigator.sendBeacon(url, JSON.stringify(args.data));
115
- };
116
- NetworkCore.prototype._sendRequest = function (args) {
117
- return __awaiter(this, void 0, void 0, function () {
118
- var method, body, retries, controller, handle, response, fullUrl, text, error_1, errorMessage;
119
- var _this = this;
120
- return __generator(this, function (_a) {
121
- switch (_a.label) {
122
- case 0:
123
- method = args.method, body = args.body, retries = args.retries;
124
- controller = new AbortController();
125
- handle = setTimeout(function () { return controller.abort("Timeout of ".concat(_this._timeout, "ms expired.")); }, this._timeout);
126
- response = null;
127
- _a.label = 1;
128
- case 1:
129
- _a.trys.push([1, 4, , 5]);
130
- fullUrl = this._getPopulatedURL(args);
131
- return [4 /*yield*/, fetch(fullUrl, {
132
- method: method,
133
- body: body,
134
- headers: __assign({
135
- // Must set this content type to bypass cors
136
- // can override via headers if necessary (recommended for logevent)
137
- 'Content-Type': 'text/plain' }, args.headers),
138
- signal: controller.signal,
139
- })];
140
- case 2:
141
- response = _a.sent();
142
- clearTimeout(handle);
143
- return [4 /*yield*/, response.text()];
144
- case 3:
145
- text = _a.sent();
146
- if (!response.ok) {
147
- throw new NetworkError('Fetch Failure', text);
148
- }
149
- Diagnostics_1.Diagnostics.mark('_sendRequest:response-received', {
150
- status: response.status,
151
- contentLength: response.headers.get('content-length'),
152
- });
153
- return [2 /*return*/, text];
154
- case 4:
155
- error_1 = _a.sent();
156
- errorMessage = _getErrorMessage(controller, error_1);
157
- Diagnostics_1.Diagnostics.mark('_sendRequest:error', {
158
- error: errorMessage,
159
- status: response === null || response === void 0 ? void 0 : response.status,
160
- contentLength: response === null || response === void 0 ? void 0 : response.headers.get('content-length'),
161
- });
162
- if (!retries || retries <= 0) {
163
- Log_1.Log.error('A networking error occured.', errorMessage);
164
- return [2 /*return*/, null];
165
- }
166
- return [2 /*return*/, this._sendRequest(__assign(__assign({}, args), { retries: retries - 1 }))];
167
- case 5: return [2 /*return*/];
53
+ }
54
+ _sendRequest(args) {
55
+ var _a, _b, _c, _d, _e, _f;
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ if (!_ensureValidSdkKey(args)) {
58
+ return null;
59
+ }
60
+ if ((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.networkConfig) === null || _b === void 0 ? void 0 : _b.preventAllNetworkTraffic) {
61
+ return null;
62
+ }
63
+ const { method, body, retries } = args;
64
+ const controller = new AbortController();
65
+ const handle = setTimeout(() => controller.abort(`Timeout of ${this._timeout}ms expired.`), this._timeout);
66
+ const url = yield this._getPopulatedURL(args);
67
+ let response = null;
68
+ try {
69
+ const config = {
70
+ method,
71
+ body,
72
+ headers: Object.assign({}, args.headers),
73
+ signal: controller.signal,
74
+ priority: args.priority,
75
+ keepalive: true,
76
+ };
77
+ const func = (_e = (_d = (_c = this._options) === null || _c === void 0 ? void 0 : _c.networkConfig) === null || _d === void 0 ? void 0 : _d.networkOverrideFunc) !== null && _e !== void 0 ? _e : fetch;
78
+ response = yield func(url, config);
79
+ clearTimeout(handle);
80
+ if (!response.ok) {
81
+ const text = yield response.text().catch(() => 'No Text');
82
+ const err = new Error(`Failed to fetch: ${url} ${text}`);
83
+ err.name = 'NetworkError';
84
+ throw err;
168
85
  }
169
- });
86
+ const text = yield response.text();
87
+ Diagnostics_1.Diagnostics.mark();
88
+ return {
89
+ body: text,
90
+ code: response.status,
91
+ };
92
+ }
93
+ catch (error) {
94
+ const errorMessage = _getErrorMessage(controller, error);
95
+ Diagnostics_1.Diagnostics.mark();
96
+ if (!retries || retries <= 0) {
97
+ (_f = this._emitter) === null || _f === void 0 ? void 0 : _f.call(this, { name: 'error', error });
98
+ Log_1.Log.error(`A networking error occured during ${method} request to ${url}.`, errorMessage, error);
99
+ return null;
100
+ }
101
+ return this._sendRequest(Object.assign(Object.assign({}, args), { retries: retries - 1 }));
102
+ }
103
+ });
104
+ }
105
+ _getPopulatedURL(args) {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ const params = Object.assign({ [NetworkConfig_1.NetworkParam.SdkKey]: args.sdkKey, [NetworkConfig_1.NetworkParam.SdkType]: SDKType_1.SDKType._get(args.sdkKey), [NetworkConfig_1.NetworkParam.SdkVersion]: StatsigMetadata_1.SDK_VERSION, [NetworkConfig_1.NetworkParam.Time]: String(Date.now()), [NetworkConfig_1.NetworkParam.SessionID]: yield SessionID_1.SessionID.get(args.sdkKey) }, args.params);
108
+ const query = Object.entries(params)
109
+ .map(([key, value]) => {
110
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
111
+ })
112
+ .join('&');
113
+ return `${args.url}${query ? `?${query}` : ''}`;
170
114
  });
171
- };
172
- NetworkCore.prototype._getPopulatedURL = function (args) {
173
- var statsigMetadata = StatsigMetadata_1.StatsigMetadataProvider.get();
174
- var fullUrl = new URL(args.url);
175
- fullUrl.searchParams.append('k', args.sdkKey);
176
- fullUrl.searchParams.append('st', statsigMetadata.sdkType);
177
- fullUrl.searchParams.append('sv', statsigMetadata.sdkVersion);
178
- fullUrl.searchParams.append('t', String(Date.now()));
179
- fullUrl.searchParams.append('sid', SessionID_1.SessionID.get(args.sdkKey));
180
- return fullUrl.toString();
181
- };
182
- return NetworkCore;
183
- }());
115
+ }
116
+ _getPopulatedBody(args) {
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ const { data, sdkKey } = args;
119
+ const stableID = yield StableID_1.StableID.get(sdkKey);
120
+ const sessionID = yield SessionID_1.SessionID.get(sdkKey);
121
+ const sdkType = SDKType_1.SDKType._get(sdkKey);
122
+ return JSON.stringify(Object.assign(Object.assign({}, data), { statsigMetadata: Object.assign(Object.assign({}, StatsigMetadata_1.StatsigMetadataProvider.get()), { stableID,
123
+ sessionID,
124
+ sdkType }) }));
125
+ });
126
+ }
127
+ _attemptToEncodeString(args, input) {
128
+ var _a, _b, _c;
129
+ const win = (0, SafeJs_1._getWindowSafe)();
130
+ if (!(win === null || win === void 0 ? void 0 : win.btoa) ||
131
+ (__STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__['no-encode']) != null ||
132
+ ((_a = this._options) === null || _a === void 0 ? void 0 : _a.disableStatsigEncoding) ||
133
+ !args.isStatsigEncodable) {
134
+ return input;
135
+ }
136
+ try {
137
+ const result = (_b = win.btoa(input).split('').reverse().join('')) !== null && _b !== void 0 ? _b : input;
138
+ args.params = Object.assign(Object.assign({}, ((_c = args.params) !== null && _c !== void 0 ? _c : {})), { [NetworkConfig_1.NetworkParam.StatsigEncoded]: '1' });
139
+ return result;
140
+ }
141
+ catch (_d) {
142
+ Log_1.Log.warn('/initialize request encoding failed');
143
+ return input;
144
+ }
145
+ }
146
+ }
184
147
  exports.NetworkCore = NetworkCore;
148
+ const _ensureValidSdkKey = (args) => {
149
+ if (!args.sdkKey) {
150
+ Log_1.Log.warn('Unable to make request without an SDK key');
151
+ return false;
152
+ }
153
+ return true;
154
+ };
185
155
  function _getErrorMessage(controller, error) {
186
156
  if (controller.signal.aborted &&
187
157
  typeof controller.signal.reason === 'string') {
@@ -191,7 +161,7 @@ function _getErrorMessage(controller, error) {
191
161
  return error;
192
162
  }
193
163
  if (error instanceof Error) {
194
- return "".concat(error.name, ": ").concat(error.message);
164
+ return `${error.name}: ${error.message}`;
195
165
  }
196
- return null;
166
+ return 'Unknown Error';
197
167
  }
@@ -0,0 +1,9 @@
1
+ import { DynamicConfigEvaluationOptions, ExperimentEvaluationOptions, FeatureGateEvaluationOptions, LayerEvaluationOptions } from './EvaluationOptions';
2
+ import { DynamicConfig, Experiment, FeatureGate, Layer } from './StatsigTypes';
3
+ import { StatsigUser } from './StatsigUser';
4
+ export type OverrideAdapter = {
5
+ getGateOverride?(current: FeatureGate, user: StatsigUser, options?: FeatureGateEvaluationOptions): FeatureGate | null;
6
+ getDynamicConfigOverride?(current: DynamicConfig, user: StatsigUser, options?: DynamicConfigEvaluationOptions): DynamicConfig | null;
7
+ getExperimentOverride?(current: Experiment, user: StatsigUser, options?: ExperimentEvaluationOptions): Experiment | null;
8
+ getLayerOverride?(current: Layer, user: StatsigUser, options?: LayerEvaluationOptions): Layer | null;
9
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ type ClientType = 'javascript-client' | 'js-on-device-eval-client';
2
+ type BindingType = 'expo' | 'rn' | 'react';
3
+ export declare const SDKType: {
4
+ _get: (sdkKey: string) => string;
5
+ _setClientType(sdkKey: string, client: ClientType): void;
6
+ _setBindingType(binding: BindingType): void;
7
+ };
8
+ export {};
package/src/SDKType.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SDKType = void 0;
4
+ const SDK_CLIENT = {};
5
+ let suffix;
6
+ exports.SDKType = {
7
+ _get: (sdkKey) => {
8
+ var _a;
9
+ return ((_a = SDK_CLIENT[sdkKey]) !== null && _a !== void 0 ? _a : 'js-mono') + (suffix !== null && suffix !== void 0 ? suffix : '');
10
+ },
11
+ _setClientType(sdkKey, client) {
12
+ SDK_CLIENT[sdkKey] = client;
13
+ },
14
+ _setBindingType(binding) {
15
+ if (!suffix || suffix === '-react') {
16
+ suffix = '-' + binding;
17
+ }
18
+ },
19
+ };
@@ -0,0 +1,6 @@
1
+ export declare const _getWindowSafe: () => Window | null;
2
+ export declare const _getDocumentSafe: () => Document | null;
3
+ export declare const _isBrowserEnv: () => boolean;
4
+ export declare const _addWindowEventListenerSafe: (key: string, listener: () => void) => void;
5
+ export declare const _addDocumentEventListenerSafe: (key: string, listener: () => void) => void;
6
+ export declare const _getCurrentPageUrlSafe: () => string | undefined;
package/src/SafeJs.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._getCurrentPageUrlSafe = exports._addDocumentEventListenerSafe = exports._addWindowEventListenerSafe = exports._isBrowserEnv = exports._getDocumentSafe = exports._getWindowSafe = void 0;
4
+ const _getWindowSafe = () => {
5
+ return typeof window !== 'undefined' ? window : null;
6
+ };
7
+ exports._getWindowSafe = _getWindowSafe;
8
+ const _getDocumentSafe = () => {
9
+ var _a;
10
+ const win = (0, exports._getWindowSafe)();
11
+ return (_a = win === null || win === void 0 ? void 0 : win.document) !== null && _a !== void 0 ? _a : null;
12
+ };
13
+ exports._getDocumentSafe = _getDocumentSafe;
14
+ const _isBrowserEnv = () => {
15
+ return (0, exports._getDocumentSafe)() != null;
16
+ };
17
+ exports._isBrowserEnv = _isBrowserEnv;
18
+ const _addWindowEventListenerSafe = (key, listener) => {
19
+ const win = (0, exports._getWindowSafe)();
20
+ if (typeof (win === null || win === void 0 ? void 0 : win.addEventListener) === 'function') {
21
+ win.addEventListener(key, listener);
22
+ }
23
+ };
24
+ exports._addWindowEventListenerSafe = _addWindowEventListenerSafe;
25
+ const _addDocumentEventListenerSafe = (key, listener) => {
26
+ const doc = (0, exports._getDocumentSafe)();
27
+ if (typeof (doc === null || doc === void 0 ? void 0 : doc.addEventListener) === 'function') {
28
+ doc.addEventListener(key, listener);
29
+ }
30
+ };
31
+ exports._addDocumentEventListenerSafe = _addDocumentEventListenerSafe;
32
+ const _getCurrentPageUrlSafe = () => {
33
+ var _a;
34
+ try {
35
+ return (_a = (0, exports._getWindowSafe)()) === null || _a === void 0 ? void 0 : _a.location.href.split(/[?#]/)[0];
36
+ }
37
+ catch (_b) {
38
+ return;
39
+ }
40
+ };
41
+ exports._getCurrentPageUrlSafe = _getCurrentPageUrlSafe;
@@ -1,3 +1,19 @@
1
+ type SessionTimeoutID = ReturnType<typeof setTimeout>;
2
+ type SessionData = {
3
+ sessionID: string;
4
+ startTime: number;
5
+ lastUpdate: number;
6
+ };
7
+ export type StatsigSession = {
8
+ data: SessionData;
9
+ sdkKey: string;
10
+ ageTimeoutID?: SessionTimeoutID;
11
+ idleTimeoutID?: SessionTimeoutID;
12
+ };
1
13
  export declare const SessionID: {
2
- get: (sdkKey: string) => string;
14
+ get: (sdkKey: string) => Promise<string>;
15
+ };
16
+ export declare const StatsigSession: {
17
+ get: (sdkKey: string) => Promise<StatsigSession>;
3
18
  };
19
+ export {};
package/src/SessionID.js CHANGED
@@ -1,13 +1,94 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SessionID = void 0;
4
- var UUID_1 = require("./UUID");
5
- var SESSION_ID_MAP = {};
12
+ exports.StatsigSession = exports.SessionID = void 0;
13
+ const Hashing_1 = require("./Hashing");
14
+ const Log_1 = require("./Log");
15
+ const StorageProvider_1 = require("./StorageProvider");
16
+ const UUID_1 = require("./UUID");
17
+ const MAX_SESSION_IDLE_TIME = 30 * 60 * 1000; // 30 minutes
18
+ const MAX_SESSION_AGE = 4 * 60 * 60 * 1000; // 4 hours
19
+ const PROMISE_MAP = {};
6
20
  exports.SessionID = {
7
- get: function (sdkKey) {
8
- if (SESSION_ID_MAP[sdkKey] == null) {
9
- SESSION_ID_MAP[sdkKey] = (0, UUID_1.getUUID)();
21
+ get: (sdkKey) => __awaiter(void 0, void 0, void 0, function* () {
22
+ return exports.StatsigSession.get(sdkKey).then((x) => x.data.sessionID);
23
+ }),
24
+ };
25
+ exports.StatsigSession = {
26
+ get: (sdkKey) => __awaiter(void 0, void 0, void 0, function* () {
27
+ if (PROMISE_MAP[sdkKey] == null) {
28
+ PROMISE_MAP[sdkKey] = _loadSession(sdkKey);
10
29
  }
11
- return SESSION_ID_MAP[sdkKey];
12
- },
30
+ const session = yield PROMISE_MAP[sdkKey];
31
+ return _bumpSession(session);
32
+ }),
13
33
  };
34
+ function _loadSession(sdkKey) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ let data = yield _loadFromStorage(sdkKey);
37
+ const now = Date.now();
38
+ if (!data) {
39
+ data = {
40
+ sessionID: (0, UUID_1.getUUID)(),
41
+ startTime: now,
42
+ lastUpdate: now,
43
+ };
44
+ }
45
+ return {
46
+ data,
47
+ sdkKey,
48
+ };
49
+ });
50
+ }
51
+ function _bumpSession(session) {
52
+ const now = Date.now();
53
+ const data = session.data;
54
+ if (_isIdle(data) || _hasRunTooLong(data)) {
55
+ data.sessionID = (0, UUID_1.getUUID)();
56
+ data.startTime = now;
57
+ }
58
+ data.lastUpdate = now;
59
+ _persistToStorage(data, session.sdkKey);
60
+ clearTimeout(session.idleTimeoutID);
61
+ clearTimeout(session.ageTimeoutID);
62
+ const lifetime = now - data.startTime;
63
+ const sdkKey = session.sdkKey;
64
+ session.idleTimeoutID = _createSessionTimeout(sdkKey, MAX_SESSION_IDLE_TIME);
65
+ session.ageTimeoutID = _createSessionTimeout(sdkKey, MAX_SESSION_AGE - lifetime);
66
+ return session;
67
+ }
68
+ function _createSessionTimeout(sdkKey, duration) {
69
+ return setTimeout(() => {
70
+ const client = __STATSIG__ === null || __STATSIG__ === void 0 ? void 0 : __STATSIG__.instance(sdkKey);
71
+ if (client) {
72
+ client.$emt({ name: 'session_expired' });
73
+ }
74
+ }, duration);
75
+ }
76
+ function _isIdle({ lastUpdate }) {
77
+ return Date.now() - lastUpdate > MAX_SESSION_IDLE_TIME;
78
+ }
79
+ function _hasRunTooLong({ startTime }) {
80
+ return Date.now() - startTime > MAX_SESSION_AGE;
81
+ }
82
+ function _getSessionIDStorageKey(sdkKey) {
83
+ return `statsig.session_id.${(0, Hashing_1.DJB2)(sdkKey)}`;
84
+ }
85
+ function _persistToStorage(session, sdkKey) {
86
+ const storageKey = _getSessionIDStorageKey(sdkKey);
87
+ (0, StorageProvider_1._setObjectInStorage)(storageKey, session).catch(() => {
88
+ Log_1.Log.warn('Failed to save SessionID');
89
+ });
90
+ }
91
+ function _loadFromStorage(sdkKey) {
92
+ const storageKey = _getSessionIDStorageKey(sdkKey);
93
+ return (0, StorageProvider_1._getObjectFromStorage)(storageKey);
94
+ }