humanbehavior-js 0.0.5 → 0.0.7

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/dist/esm/index.js CHANGED
@@ -15,17 +15,6 @@ PERFORMANCE OF THIS SOFTWARE.
15
15
  /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
16
16
 
17
17
 
18
- var __assign = function() {
19
- __assign = Object.assign || function __assign(t) {
20
- for (var s, i = 1, n = arguments.length; i < n; i++) {
21
- s = arguments[i];
22
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
-
29
18
  function __awaiter$1(thisArg, _arguments, P, generator) {
30
19
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
31
20
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -36,44 +25,6 @@ function __awaiter$1(thisArg, _arguments, P, generator) {
36
25
  });
37
26
  }
38
27
 
39
- function __generator(thisArg, body) {
40
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
41
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
42
- function verb(n) { return function (v) { return step([n, v]); }; }
43
- function step(op) {
44
- if (f) throw new TypeError("Generator is already executing.");
45
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
46
- 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;
47
- if (y = 0, t) op = [op[0] & 2, t.value];
48
- switch (op[0]) {
49
- case 0: case 1: t = op; break;
50
- case 4: _.label++; return { value: op[1], done: false };
51
- case 5: _.label++; y = op[1]; op = [0]; continue;
52
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
53
- default:
54
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
55
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
56
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
57
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
58
- if (t[2]) _.ops.pop();
59
- _.trys.pop(); continue;
60
- }
61
- op = body.call(thisArg, _);
62
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
63
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
64
- }
65
- }
66
-
67
- function __spreadArray(to, from, pack) {
68
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
69
- if (ar || !(i in from)) {
70
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
71
- ar[i] = from[i];
72
- }
73
- }
74
- return to.concat(ar || Array.prototype.slice.call(from));
75
- }
76
-
77
28
  typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
78
29
  var e = new Error(message);
79
30
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
@@ -4098,336 +4049,271 @@ function v1Bytes(rnds, msecs, nsecs, clockseq, node, buf, offset = 0) {
4098
4049
  return buf;
4099
4050
  }
4100
4051
 
4101
- var MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
4052
+ const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
4102
4053
  function isChunkSizeExceeded(currentChunk, newEvent, sessionId) {
4103
- var nextChunkSize = new TextEncoder().encode(JSON.stringify({
4104
- sessionId: sessionId,
4105
- events: __spreadArray(__spreadArray([], currentChunk, true), [newEvent], false)
4054
+ const nextChunkSize = new TextEncoder().encode(JSON.stringify({
4055
+ sessionId,
4056
+ events: [...currentChunk, newEvent]
4106
4057
  })).length;
4107
4058
  return nextChunkSize > MAX_CHUNK_SIZE_BYTES;
4108
4059
  }
4109
4060
  function validateSingleEventSize(event, sessionId) {
4110
- var singleEventSize = new TextEncoder().encode(JSON.stringify({
4111
- sessionId: sessionId,
4061
+ const singleEventSize = new TextEncoder().encode(JSON.stringify({
4062
+ sessionId,
4112
4063
  events: [event]
4113
4064
  })).length;
4114
4065
  if (singleEventSize > MAX_CHUNK_SIZE_BYTES) {
4115
- throw new Error("Single event size (".concat(singleEventSize, " bytes) exceeds maximum chunk size (").concat(MAX_CHUNK_SIZE_BYTES, " bytes)"));
4066
+ throw new Error(`Single event size (${singleEventSize} bytes) exceeds maximum chunk size (${MAX_CHUNK_SIZE_BYTES} bytes)`);
4116
4067
  }
4117
4068
  }
4118
- var HumanBehaviorAPI = /** @class */ (function () {
4119
- function HumanBehaviorAPI(_a) {
4120
- var apiKey = _a.apiKey, ingestionUrl = _a.ingestionUrl;
4069
+ class HumanBehaviorAPI {
4070
+ constructor({ apiKey, ingestionUrl }) {
4121
4071
  this.apiKey = apiKey;
4122
4072
  this.baseUrl = ingestionUrl;
4123
4073
  }
4124
- HumanBehaviorAPI.prototype.init = function (sessionId, userId) {
4125
- return __awaiter$1(this, void 0, void 0, function () {
4126
- var response, responseJson;
4127
- return __generator(this, function (_a) {
4128
- switch (_a.label) {
4129
- case 0: return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/init"), {
4130
- method: 'POST',
4131
- headers: {
4132
- 'Content-Type': 'application/json',
4133
- 'Authorization': "Bearer ".concat(this.apiKey)
4134
- },
4135
- body: JSON.stringify({
4136
- sessionId: sessionId,
4137
- endUserId: userId
4138
- })
4139
- })];
4140
- case 1:
4141
- response = _a.sent();
4142
- if (!response.ok) {
4143
- throw new Error("Failed to initialize ingestion: ".concat(response.statusText));
4144
- }
4145
- return [4 /*yield*/, response.json()];
4146
- case 2:
4147
- responseJson = _a.sent();
4148
- return [2 /*return*/, {
4149
- sessionId: responseJson.sessionId,
4150
- endUserId: responseJson.endUserId
4151
- }];
4152
- }
4074
+ init(sessionId, userId) {
4075
+ return __awaiter$1(this, void 0, void 0, function* () {
4076
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/init`, {
4077
+ method: 'POST',
4078
+ headers: {
4079
+ 'Content-Type': 'application/json',
4080
+ 'Authorization': `Bearer ${this.apiKey}`
4081
+ },
4082
+ body: JSON.stringify({
4083
+ sessionId: sessionId,
4084
+ endUserId: userId
4085
+ })
4153
4086
  });
4087
+ if (!response.ok) {
4088
+ throw new Error(`Failed to initialize ingestion: ${response.statusText}`);
4089
+ }
4090
+ const responseJson = yield response.json();
4091
+ return {
4092
+ sessionId: responseJson.sessionId,
4093
+ endUserId: responseJson.endUserId
4094
+ };
4154
4095
  });
4155
- };
4156
- HumanBehaviorAPI.prototype.sendEvents = function (events, sessionId, userId) {
4157
- return __awaiter$1(this, void 0, void 0, function () {
4158
- var response;
4159
- return __generator(this, function (_a) {
4160
- switch (_a.label) {
4161
- case 0: return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/events"), {
4162
- method: 'POST',
4163
- headers: {
4164
- 'Content-Type': 'application/json',
4165
- 'Authorization': "Bearer ".concat(this.apiKey)
4166
- },
4167
- body: JSON.stringify({
4168
- sessionId: sessionId,
4169
- events: events,
4170
- endUserId: userId
4171
- })
4172
- })];
4173
- case 1:
4174
- response = _a.sent();
4175
- if (!response.ok) {
4176
- throw new Error("Failed to send events: ".concat(response.statusText));
4177
- }
4178
- return [2 /*return*/];
4179
- }
4096
+ }
4097
+ sendEvents(events, sessionId, userId) {
4098
+ return __awaiter$1(this, void 0, void 0, function* () {
4099
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/events`, {
4100
+ method: 'POST',
4101
+ headers: {
4102
+ 'Content-Type': 'application/json',
4103
+ 'Authorization': `Bearer ${this.apiKey}`
4104
+ },
4105
+ body: JSON.stringify({
4106
+ sessionId,
4107
+ events: events,
4108
+ endUserId: userId
4109
+ })
4180
4110
  });
4111
+ if (!response.ok) {
4112
+ throw new Error(`Failed to send events: ${response.statusText}`);
4113
+ }
4181
4114
  });
4182
- };
4183
- HumanBehaviorAPI.prototype.sendEventsChunked = function (events, sessionId) {
4184
- return __awaiter$1(this, void 0, void 0, function () {
4185
- var results, currentChunk, _i, events_1, event_1, response, _a, _b, response, _c, _d, error_1;
4186
- return __generator(this, function (_e) {
4187
- switch (_e.label) {
4188
- case 0:
4189
- _e.trys.push([0, 11, , 12]);
4190
- results = [];
4191
- currentChunk = [];
4192
- _i = 0, events_1 = events;
4193
- _e.label = 1;
4194
- case 1:
4195
- if (!(_i < events_1.length)) return [3 /*break*/, 7];
4196
- event_1 = events_1[_i];
4197
- if (!isChunkSizeExceeded(currentChunk, event_1, sessionId)) return [3 /*break*/, 5];
4198
- if (!(currentChunk.length > 0)) return [3 /*break*/, 4];
4199
- return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/events"), {
4115
+ }
4116
+ sendEventsChunked(events, sessionId) {
4117
+ return __awaiter$1(this, void 0, void 0, function* () {
4118
+ try {
4119
+ const results = [];
4120
+ let currentChunk = [];
4121
+ for (const event of events) {
4122
+ if (isChunkSizeExceeded(currentChunk, event, sessionId)) {
4123
+ // If current chunk is not empty, send it first
4124
+ if (currentChunk.length > 0) {
4125
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/events`, {
4200
4126
  method: 'POST',
4201
4127
  headers: {
4202
4128
  'Content-Type': 'application/json',
4203
- 'Authorization': "Bearer ".concat(this.apiKey)
4129
+ 'Authorization': `Bearer ${this.apiKey}`
4204
4130
  },
4205
4131
  body: JSON.stringify({
4206
- sessionId: sessionId,
4132
+ sessionId,
4207
4133
  events: currentChunk
4208
4134
  })
4209
- })];
4210
- case 2:
4211
- response = _e.sent();
4212
- if (!response.ok) {
4213
- throw new Error("Failed to send events: ".concat(response.statusText));
4135
+ });
4136
+ if (!response.ok) {
4137
+ throw new Error(`Failed to send events: ${response.statusText}`);
4138
+ }
4139
+ results.push(yield response.json());
4140
+ currentChunk = [];
4214
4141
  }
4215
- _b = (_a = results).push;
4216
- return [4 /*yield*/, response.json()];
4217
- case 3:
4218
- _b.apply(_a, [_e.sent()]);
4219
- currentChunk = [];
4220
- _e.label = 4;
4221
- case 4:
4222
4142
  // Validate single event size
4223
- validateSingleEventSize(event_1, sessionId);
4143
+ validateSingleEventSize(event, sessionId);
4224
4144
  // Start new chunk with this event
4225
- currentChunk = [event_1];
4226
- return [3 /*break*/, 6];
4227
- case 5:
4145
+ currentChunk = [event];
4146
+ }
4147
+ else {
4228
4148
  // Add event to current chunk
4229
- currentChunk.push(event_1);
4230
- _e.label = 6;
4231
- case 6:
4232
- _i++;
4233
- return [3 /*break*/, 1];
4234
- case 7:
4235
- if (!(currentChunk.length > 0)) return [3 /*break*/, 10];
4236
- return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/events"), {
4237
- method: 'POST',
4238
- headers: {
4239
- 'Content-Type': 'application/json',
4240
- 'Authorization': "Bearer ".concat(this.apiKey)
4241
- },
4242
- body: JSON.stringify({
4243
- sessionId: sessionId,
4244
- events: currentChunk
4245
- })
4246
- })];
4247
- case 8:
4248
- response = _e.sent();
4249
- if (!response.ok) {
4250
- throw new Error("Failed to send events: ".concat(response.statusText));
4251
- }
4252
- _d = (_c = results).push;
4253
- return [4 /*yield*/, response.json()];
4254
- case 9:
4255
- _d.apply(_c, [_e.sent()]);
4256
- _e.label = 10;
4257
- case 10: return [2 /*return*/, results.flat()];
4258
- case 11:
4259
- error_1 = _e.sent();
4260
- console.error('Error sending events:', error_1);
4261
- throw error_1;
4262
- case 12: return [2 /*return*/];
4149
+ currentChunk.push(event);
4150
+ }
4263
4151
  }
4264
- });
4152
+ // Send any remaining events
4153
+ if (currentChunk.length > 0) {
4154
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/events`, {
4155
+ method: 'POST',
4156
+ headers: {
4157
+ 'Content-Type': 'application/json',
4158
+ 'Authorization': `Bearer ${this.apiKey}`
4159
+ },
4160
+ body: JSON.stringify({
4161
+ sessionId,
4162
+ events: currentChunk
4163
+ })
4164
+ });
4165
+ if (!response.ok) {
4166
+ throw new Error(`Failed to send events: ${response.statusText}`);
4167
+ }
4168
+ results.push(yield response.json());
4169
+ }
4170
+ return results.flat();
4171
+ }
4172
+ catch (error) {
4173
+ console.error('Error sending events:', error);
4174
+ throw error;
4175
+ }
4265
4176
  });
4266
- };
4267
- HumanBehaviorAPI.prototype.sendUserData = function (userId, userData, sessionId) {
4268
- return __awaiter$1(this, void 0, void 0, function () {
4269
- var response, error_2;
4270
- return __generator(this, function (_a) {
4271
- switch (_a.label) {
4272
- case 0:
4273
- _a.trys.push([0, 3, , 4]);
4274
- return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/user"), {
4275
- method: 'POST',
4276
- headers: {
4277
- 'Content-Type': 'application/json',
4278
- 'Authorization': "Bearer ".concat(this.apiKey)
4279
- },
4280
- body: JSON.stringify({
4281
- userId: userId,
4282
- userAttributes: userData,
4283
- sessionId: sessionId
4284
- })
4285
- })];
4286
- case 1:
4287
- response = _a.sent();
4288
- if (!response.ok) {
4289
- throw new Error("Failed to send user data: ".concat(response.statusText, " with API key: ").concat(this.apiKey));
4290
- }
4291
- return [4 /*yield*/, response.json()];
4292
- case 2: return [2 /*return*/, _a.sent()];
4293
- case 3:
4294
- error_2 = _a.sent();
4295
- console.error('Error sending user data:', error_2);
4296
- throw error_2;
4297
- case 4: return [2 /*return*/];
4177
+ }
4178
+ sendUserData(userId, userData, sessionId) {
4179
+ return __awaiter$1(this, void 0, void 0, function* () {
4180
+ try {
4181
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/user`, {
4182
+ method: 'POST',
4183
+ headers: {
4184
+ 'Content-Type': 'application/json',
4185
+ 'Authorization': `Bearer ${this.apiKey}`
4186
+ },
4187
+ body: JSON.stringify({
4188
+ userId: userId,
4189
+ userAttributes: userData,
4190
+ sessionId: sessionId
4191
+ })
4192
+ });
4193
+ if (!response.ok) {
4194
+ throw new Error(`Failed to send user data: ${response.statusText} with API key: ${this.apiKey}`);
4298
4195
  }
4299
- });
4196
+ return yield response.json();
4197
+ }
4198
+ catch (error) {
4199
+ console.error('Error sending user data:', error);
4200
+ throw error;
4201
+ }
4300
4202
  });
4301
- };
4302
- HumanBehaviorAPI.prototype.sendSessionComplete = function (sessionId) {
4303
- return __awaiter$1(this, void 0, void 0, function () {
4304
- var response;
4305
- return __generator(this, function (_a) {
4306
- switch (_a.label) {
4307
- case 0: return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/sessionComplete"), {
4308
- method: 'POST',
4309
- headers: {
4310
- 'Content-Type': 'application/json',
4311
- 'Authorization': "Bearer ".concat(this.apiKey)
4312
- },
4313
- body: JSON.stringify({ sessionId: sessionId })
4314
- })];
4315
- case 1:
4316
- response = _a.sent();
4317
- if (!response.ok) {
4318
- throw new Error("Failed to send session complete: ".concat(response.statusText));
4319
- }
4320
- return [2 /*return*/];
4203
+ }
4204
+ sendUserAuth(userId, userData, sessionId, authFields) {
4205
+ return __awaiter$1(this, void 0, void 0, function* () {
4206
+ try {
4207
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/user/auth`, {
4208
+ method: 'POST',
4209
+ headers: {
4210
+ 'Content-Type': 'application/json',
4211
+ 'Authorization': `Bearer ${this.apiKey}`
4212
+ },
4213
+ body: JSON.stringify({
4214
+ userId: userId,
4215
+ userAttributes: userData,
4216
+ sessionId: sessionId,
4217
+ authFields: authFields
4218
+ })
4219
+ });
4220
+ if (!response.ok) {
4221
+ throw new Error(`Failed to authenticate user: ${response.statusText} with API key: ${this.apiKey}`);
4321
4222
  }
4223
+ return yield response.json();
4224
+ }
4225
+ catch (error) {
4226
+ console.error('Error authenticating user:', error);
4227
+ throw error;
4228
+ }
4229
+ });
4230
+ }
4231
+ sendSessionComplete(sessionId) {
4232
+ return __awaiter$1(this, void 0, void 0, function* () {
4233
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
4234
+ method: 'POST',
4235
+ headers: {
4236
+ 'Content-Type': 'application/json',
4237
+ 'Authorization': `Bearer ${this.apiKey}`
4238
+ },
4239
+ body: JSON.stringify({ sessionId })
4322
4240
  });
4241
+ if (!response.ok) {
4242
+ throw new Error(`Failed to send session complete: ${response.statusText}`);
4243
+ }
4323
4244
  });
4324
- };
4325
- HumanBehaviorAPI.prototype.sendCustomEvent = function (eventName, eventProperties, sessionId) {
4326
- return __awaiter$1(this, void 0, void 0, function () {
4327
- var maxRetries, retryCount, response, error_3;
4328
- return __generator(this, function (_a) {
4329
- switch (_a.label) {
4330
- case 0:
4331
- maxRetries = 3;
4332
- retryCount = 0;
4333
- _a.label = 1;
4334
- case 1:
4335
- if (!(retryCount < maxRetries)) return [3 /*break*/, 8];
4336
- _a.label = 2;
4337
- case 2:
4338
- _a.trys.push([2, 5, , 7]);
4339
- return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/customEvent"), {
4340
- method: 'POST',
4341
- headers: {
4342
- 'Content-Type': 'application/json',
4343
- 'Authorization': "Bearer ".concat(this.apiKey)
4344
- },
4345
- body: JSON.stringify({
4346
- name: eventName,
4347
- properties: eventProperties,
4348
- sessionId: sessionId,
4349
- timestamp: new Date().toISOString()
4350
- })
4351
- })];
4352
- case 3:
4353
- response = _a.sent();
4354
- if (!response.ok) {
4355
- throw new Error("Failed to send custom event: ".concat(response.statusText));
4356
- }
4357
- return [4 /*yield*/, response.json()];
4358
- case 4: return [2 /*return*/, _a.sent()];
4359
- case 5:
4360
- error_3 = _a.sent();
4361
- retryCount++;
4362
- if (retryCount === maxRetries) {
4363
- console.error('Error sending custom event after max retries:', error_3);
4364
- throw error_3;
4365
- }
4366
- // Exponential backoff
4367
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, Math.pow(2, retryCount) * 1000); })];
4368
- case 6:
4369
- // Exponential backoff
4370
- _a.sent();
4371
- return [3 /*break*/, 7];
4372
- case 7: return [3 /*break*/, 1];
4373
- case 8: return [2 /*return*/];
4245
+ }
4246
+ sendCustomEvent(eventName, eventProperties, sessionId) {
4247
+ return __awaiter$1(this, void 0, void 0, function* () {
4248
+ const maxRetries = 3;
4249
+ let retryCount = 0;
4250
+ while (retryCount < maxRetries) {
4251
+ try {
4252
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
4253
+ method: 'POST',
4254
+ headers: {
4255
+ 'Content-Type': 'application/json',
4256
+ 'Authorization': `Bearer ${this.apiKey}`
4257
+ },
4258
+ body: JSON.stringify({
4259
+ name: eventName,
4260
+ properties: eventProperties,
4261
+ sessionId: sessionId,
4262
+ timestamp: new Date().toISOString()
4263
+ })
4264
+ });
4265
+ if (!response.ok) {
4266
+ throw new Error(`Failed to send custom event: ${response.statusText}`);
4267
+ }
4268
+ return yield response.json();
4374
4269
  }
4375
- });
4270
+ catch (error) {
4271
+ retryCount++;
4272
+ if (retryCount === maxRetries) {
4273
+ console.error('Error sending custom event after max retries:', error);
4274
+ throw error;
4275
+ }
4276
+ // Exponential backoff
4277
+ yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
4278
+ }
4279
+ }
4376
4280
  });
4377
- };
4378
- HumanBehaviorAPI.prototype.sendCustomEvents = function (events, sessionId) {
4379
- return __awaiter$1(this, void 0, void 0, function () {
4380
- var maxRetries, retryCount, response, error_4;
4381
- return __generator(this, function (_a) {
4382
- switch (_a.label) {
4383
- case 0:
4384
- maxRetries = 3;
4385
- retryCount = 0;
4386
- _a.label = 1;
4387
- case 1:
4388
- if (!(retryCount < maxRetries)) return [3 /*break*/, 8];
4389
- _a.label = 2;
4390
- case 2:
4391
- _a.trys.push([2, 5, , 7]);
4392
- return [4 /*yield*/, fetch("".concat(this.baseUrl, "/api/ingestion/customEvent/batch"), {
4393
- method: 'POST',
4394
- headers: {
4395
- 'Content-Type': 'application/json',
4396
- 'Authorization': "Bearer ".concat(this.apiKey)
4397
- },
4398
- body: JSON.stringify({
4399
- events: events.map(function (event) { return (__assign(__assign({}, event), { sessionId: sessionId })); })
4400
- })
4401
- })];
4402
- case 3:
4403
- response = _a.sent();
4404
- if (!response.ok) {
4405
- throw new Error("Failed to send custom events: ".concat(response.statusText));
4406
- }
4407
- return [4 /*yield*/, response.json()];
4408
- case 4: return [2 /*return*/, _a.sent()];
4409
- case 5:
4410
- error_4 = _a.sent();
4411
- retryCount++;
4412
- if (retryCount === maxRetries) {
4413
- console.error('Error sending custom events after max retries:', error_4);
4414
- throw error_4;
4415
- }
4416
- // Exponential backoff
4417
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, Math.pow(2, retryCount) * 1000); })];
4418
- case 6:
4419
- // Exponential backoff
4420
- _a.sent();
4421
- return [3 /*break*/, 7];
4422
- case 7: return [3 /*break*/, 1];
4423
- case 8: return [2 /*return*/];
4281
+ }
4282
+ sendCustomEvents(events, sessionId) {
4283
+ return __awaiter$1(this, void 0, void 0, function* () {
4284
+ const maxRetries = 3;
4285
+ let retryCount = 0;
4286
+ while (retryCount < maxRetries) {
4287
+ try {
4288
+ const response = yield fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
4289
+ method: 'POST',
4290
+ headers: {
4291
+ 'Content-Type': 'application/json',
4292
+ 'Authorization': `Bearer ${this.apiKey}`
4293
+ },
4294
+ body: JSON.stringify({
4295
+ events: events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId })))
4296
+ })
4297
+ });
4298
+ if (!response.ok) {
4299
+ throw new Error(`Failed to send custom events: ${response.statusText}`);
4300
+ }
4301
+ return yield response.json();
4424
4302
  }
4425
- });
4303
+ catch (error) {
4304
+ retryCount++;
4305
+ if (retryCount === maxRetries) {
4306
+ console.error('Error sending custom events after max retries:', error);
4307
+ throw error;
4308
+ }
4309
+ // Exponential backoff
4310
+ yield new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
4311
+ }
4312
+ }
4426
4313
  });
4427
- };
4428
- HumanBehaviorAPI.prototype.sendBeaconEvents = function (events, sessionId, isSessionComplete) {
4429
- if (isSessionComplete === void 0) { isSessionComplete = false; }
4430
- var data = new URLSearchParams();
4314
+ }
4315
+ sendBeaconEvents(events, sessionId, isSessionComplete = false) {
4316
+ const data = new URLSearchParams();
4431
4317
  data.append('events', encodeURIComponent(JSON.stringify(events)));
4432
4318
  data.append('sessionId', encodeURIComponent(sessionId));
4433
4319
  data.append('timestamp', encodeURIComponent(Date.now().toString()));
@@ -4437,41 +4323,453 @@ var HumanBehaviorAPI = /** @class */ (function () {
4437
4323
  localStorage.setItem('koalaware_session_complete', Date.now().toString());
4438
4324
  data.append('sessionComplete', encodeURIComponent('true'));
4439
4325
  }
4440
- navigator.sendBeacon("".concat(this.baseUrl, "/api/ingestion/events"), data);
4326
+ navigator.sendBeacon(`${this.baseUrl}/api/ingestion/events`, data);
4441
4327
  // KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
4442
4328
  // KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
4443
- };
4444
- HumanBehaviorAPI.prototype.sendBeaconSessionComplete = function (sessionId) {
4445
- var data = new URLSearchParams();
4329
+ }
4330
+ sendBeaconSessionComplete(sessionId) {
4331
+ const data = new URLSearchParams();
4446
4332
  data.append('sessionId', sessionId);
4447
4333
  data.append('apiKey', this.apiKey);
4448
4334
  data.append('sessionComplete', 'true');
4449
- navigator.sendBeacon("".concat(this.baseUrl, "/api/ingestion/sessionComplete"), data);
4335
+ navigator.sendBeacon(`${this.baseUrl}/api/ingestion/sessionComplete`, data);
4450
4336
  // KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
4451
4337
  // KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
4452
- };
4453
- HumanBehaviorAPI.prototype.sendBeaconCustomEvent = function (eventName, eventProperties, sessionId) {
4454
- var data = new URLSearchParams();
4338
+ }
4339
+ sendBeaconCustomEvent(eventName, eventProperties, sessionId) {
4340
+ const data = new URLSearchParams();
4455
4341
  data.append('name', encodeURIComponent(eventName));
4456
4342
  data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)));
4457
4343
  data.append('sessionId', encodeURIComponent(sessionId));
4458
4344
  data.append('timestamp', encodeURIComponent(new Date().toISOString()));
4459
4345
  data.append('apiKey', encodeURIComponent(this.apiKey));
4460
- return navigator.sendBeacon("".concat(this.baseUrl, "/api/ingestion/customEvent"), data);
4461
- };
4462
- HumanBehaviorAPI.prototype.sendBeaconCustomEvents = function (events, sessionId) {
4463
- var data = new URLSearchParams();
4464
- data.append('events', encodeURIComponent(JSON.stringify(events.map(function (event) { return (__assign(__assign({}, event), { sessionId: sessionId })); }))));
4346
+ return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent`, data);
4347
+ }
4348
+ sendBeaconCustomEvents(events, sessionId) {
4349
+ const data = new URLSearchParams();
4350
+ data.append('events', encodeURIComponent(JSON.stringify(events.map(event => (Object.assign(Object.assign({}, event), { sessionId: sessionId }))))));
4465
4351
  data.append('apiKey', encodeURIComponent(this.apiKey));
4466
- return navigator.sendBeacon("".concat(this.baseUrl, "/api/ingestion/customEvent/batch"), data);
4467
- };
4468
- return HumanBehaviorAPI;
4469
- }());
4352
+ return navigator.sendBeacon(`${this.baseUrl}/api/ingestion/customEvent/batch`, data);
4353
+ }
4354
+ }
4355
+
4356
+ // Redaction functionality for sensitive input fields
4357
+ // This module provides methods to redact sensitive input fields in event recordings
4358
+ // Check if we're in a browser environment
4359
+ const isBrowser$1 = typeof window !== 'undefined';
4360
+ class RedactionManager {
4361
+ constructor(options) {
4362
+ this.redactedText = '[REDACTED]';
4363
+ this.userSelectedFields = new Set(); // User-selected fields to redact
4364
+ this.excludeSelectors = [
4365
+ '[data-no-redact="true"]',
4366
+ '.human-behavior-no-redact'
4367
+ ];
4368
+ if (options === null || options === void 0 ? void 0 : options.redactedText) {
4369
+ this.redactedText = options.redactedText;
4370
+ }
4371
+ if (options === null || options === void 0 ? void 0 : options.excludeSelectors) {
4372
+ this.excludeSelectors = [...this.excludeSelectors, ...options.excludeSelectors];
4373
+ }
4374
+ if (options === null || options === void 0 ? void 0 : options.userFields) {
4375
+ this.setFieldsToRedact(options.userFields);
4376
+ }
4377
+ }
4378
+ /**
4379
+ * Set specific fields to be redacted
4380
+ * @param fields Array of CSS selectors for fields to redact
4381
+ */
4382
+ setFieldsToRedact(fields) {
4383
+ this.userSelectedFields.clear();
4384
+ fields.forEach(field => this.userSelectedFields.add(field));
4385
+ if (fields.length > 0) {
4386
+ console.log(`Redaction: Active for ${fields.length} field(s):`, fields);
4387
+ // Debug: Check if elements exist
4388
+ fields.forEach(selector => {
4389
+ const elements = document.querySelectorAll(selector);
4390
+ console.log(`Redaction: Found ${elements.length} element(s) for selector '${selector}'`);
4391
+ elements.forEach((el, index) => {
4392
+ console.log(`Redaction: Element ${index} for '${selector}':`, el);
4393
+ });
4394
+ });
4395
+ }
4396
+ else {
4397
+ console.log('Redaction: Disabled - no fields selected');
4398
+ }
4399
+ }
4400
+ /**
4401
+ * Check if redaction is currently active (has fields selected)
4402
+ */
4403
+ isActive() {
4404
+ return this.userSelectedFields.size > 0;
4405
+ }
4406
+ /**
4407
+ * Get the currently selected fields for redaction
4408
+ */
4409
+ getSelectedFields() {
4410
+ return Array.from(this.userSelectedFields);
4411
+ }
4412
+ /**
4413
+ * Process an event and redact sensitive data if needed
4414
+ */
4415
+ processEvent(event) {
4416
+ // Only process if we have fields selected for redaction
4417
+ if (this.userSelectedFields.size === 0) {
4418
+ return event;
4419
+ }
4420
+ // Clone the event to avoid modifying the original
4421
+ const processedEvent = JSON.parse(JSON.stringify(event));
4422
+ // Handle different event types
4423
+ if (processedEvent.type === 3) { // IncrementalSnapshot
4424
+ if (processedEvent.data.source === 5) { // Input event
4425
+ const shouldRedact = this.isFieldSelected(processedEvent.data);
4426
+ if (shouldRedact) {
4427
+ console.log('Redaction: Processing input event for redaction');
4428
+ this.redactInputEvent(processedEvent.data);
4429
+ }
4430
+ }
4431
+ // Also check for other sources that might contain text changes
4432
+ else if (processedEvent.data.source === 0) { // DOM mutations
4433
+ this.redactDOMEvent(processedEvent.data);
4434
+ }
4435
+ // Handle other sources that might contain text
4436
+ else if (processedEvent.data.source === 2) { // Mouse/Touch interaction
4437
+ this.redactMouseEvent(processedEvent.data);
4438
+ }
4439
+ }
4440
+ else if (processedEvent.type === 2) { // FullSnapshot
4441
+ this.redactFullSnapshot(processedEvent.data);
4442
+ }
4443
+ return processedEvent;
4444
+ }
4445
+ /**
4446
+ * Redact sensitive data in input events
4447
+ */
4448
+ redactInputEvent(inputData) {
4449
+ // Check if this input event is from a field we want to redact
4450
+ if (!this.isFieldSelected(inputData)) {
4451
+ return;
4452
+ }
4453
+ console.log('Redaction: Redacting input event with text:', inputData.text);
4454
+ // Redact all text-related properties that could contain input data
4455
+ const textProperties = ['text', 'value', 'content', 'data', 'input', 'textContent'];
4456
+ textProperties.forEach(prop => {
4457
+ if (inputData[prop] !== undefined && typeof inputData[prop] === 'string') {
4458
+ inputData[prop] = this.redactedText;
4459
+ console.log(`Redaction: Redacted property '${prop}'`);
4460
+ }
4461
+ });
4462
+ // Also check for any other string properties that might contain input data
4463
+ Object.keys(inputData).forEach(key => {
4464
+ if (typeof inputData[key] === 'string' && inputData[key].length > 0) {
4465
+ inputData[key] = this.redactedText;
4466
+ console.log(`Redaction: Redacted additional property '${key}'`);
4467
+ }
4468
+ });
4469
+ // Handle nested objects that might contain text data
4470
+ if (inputData.attributes && typeof inputData.attributes === 'object') {
4471
+ if (inputData.attributes.value && typeof inputData.attributes.value === 'string') {
4472
+ inputData.attributes.value = this.redactedText;
4473
+ console.log('Redaction: Redacted nested value attribute');
4474
+ }
4475
+ }
4476
+ console.log('Redaction: Input event redaction complete');
4477
+ }
4478
+ /**
4479
+ * Redact sensitive data in DOM mutation events
4480
+ */
4481
+ redactDOMEvent(domData) {
4482
+ // Check for text changes in DOM mutations
4483
+ if (domData.texts && Array.isArray(domData.texts)) {
4484
+ domData.texts.forEach((textChange) => {
4485
+ if (textChange.text && typeof textChange.text === 'string' &&
4486
+ this.shouldRedactDOMChange(textChange)) {
4487
+ textChange.text = this.redactedText;
4488
+ }
4489
+ });
4490
+ }
4491
+ // Also check for attribute changes that might contain input data
4492
+ if (domData.attributes && Array.isArray(domData.attributes)) {
4493
+ domData.attributes.forEach((attrChange) => {
4494
+ if (attrChange.attributes && attrChange.attributes.value &&
4495
+ typeof attrChange.attributes.value === 'string' &&
4496
+ this.shouldRedactDOMChange(attrChange)) {
4497
+ attrChange.attributes.value = this.redactedText;
4498
+ }
4499
+ });
4500
+ }
4501
+ // Check for any other properties that might contain text data
4502
+ if (domData.adds && Array.isArray(domData.adds)) {
4503
+ domData.adds.forEach((add) => {
4504
+ if (add.node && add.node.textContent && typeof add.node.textContent === 'string' &&
4505
+ this.shouldRedactDOMChange(add)) {
4506
+ add.node.textContent = this.redactedText;
4507
+ }
4508
+ });
4509
+ }
4510
+ }
4511
+ /**
4512
+ * Check if a DOM change should be redacted based on its ID
4513
+ */
4514
+ shouldRedactDOMChange(changeData) {
4515
+ if (!isBrowser$1)
4516
+ return false;
4517
+ try {
4518
+ // Check if this change has an ID that we can use to find the element
4519
+ const elementId = changeData.id;
4520
+ if (elementId !== undefined) {
4521
+ // Try to find the element by data-rrweb-id attribute
4522
+ let element = document.querySelector(`[data-rrweb-id="${elementId}"]`);
4523
+ if (element) {
4524
+ return this.shouldRedactElement(element);
4525
+ }
4526
+ }
4527
+ // Also check for nodeId which is another way rrweb identifies elements
4528
+ const nodeId = changeData.nodeId;
4529
+ if (nodeId !== undefined) {
4530
+ const element = document.querySelector(`[data-rrweb-id="${nodeId}"]`);
4531
+ if (element) {
4532
+ return this.shouldRedactElement(element);
4533
+ }
4534
+ }
4535
+ return false;
4536
+ }
4537
+ catch (e) {
4538
+ console.warn('Error checking if DOM change should be redacted:', e);
4539
+ return false;
4540
+ }
4541
+ }
4542
+ /**
4543
+ * Redact sensitive data in mouse/touch interaction events
4544
+ */
4545
+ redactMouseEvent(mouseData) {
4546
+ // Mouse events typically don't contain text data, but check for any text properties
4547
+ if (mouseData.text && typeof mouseData.text === 'string' &&
4548
+ this.isFieldSelected(mouseData)) {
4549
+ mouseData.text = this.redactedText;
4550
+ }
4551
+ }
4552
+ /**
4553
+ * Redact sensitive data in full snapshot events
4554
+ */
4555
+ redactFullSnapshot(snapshotData) {
4556
+ if (snapshotData.node && snapshotData.node.type === 2) { // Element node
4557
+ this.redactNode(snapshotData.node);
4558
+ }
4559
+ }
4560
+ /**
4561
+ * Recursively redact sensitive data in DOM nodes
4562
+ */
4563
+ redactNode(node) {
4564
+ if (!node)
4565
+ return;
4566
+ // Check if this node should be redacted
4567
+ if (node.type === 2 && node.tagName &&
4568
+ (node.tagName.toLowerCase() === 'input' || node.tagName.toLowerCase() === 'textarea')) {
4569
+ // Check if this input/textarea should be redacted
4570
+ if (this.shouldRedactNode(node)) {
4571
+ // Redact value attribute
4572
+ if (node.attributes && node.attributes.value) {
4573
+ node.attributes.value = this.redactedText;
4574
+ }
4575
+ // Redact text content
4576
+ if (node.textContent) {
4577
+ node.textContent = this.redactedText;
4578
+ }
4579
+ }
4580
+ }
4581
+ // Recursively process child nodes
4582
+ if (node.childNodes && Array.isArray(node.childNodes)) {
4583
+ node.childNodes.forEach((childNode) => {
4584
+ this.redactNode(childNode);
4585
+ });
4586
+ }
4587
+ }
4588
+ /**
4589
+ * Check if a node should be redacted based on its attributes
4590
+ */
4591
+ shouldRedactNode(node) {
4592
+ if (!node.attributes)
4593
+ return false;
4594
+ // Check if any of our selectors would match this node
4595
+ for (const selector of this.userSelectedFields) {
4596
+ if (this.selectorMatchesNode(selector, node)) {
4597
+ return true;
4598
+ }
4599
+ }
4600
+ return false;
4601
+ }
4602
+ /**
4603
+ * Check if a CSS selector would match a node based on its attributes
4604
+ */
4605
+ selectorMatchesNode(selector, node) {
4606
+ if (!node.attributes)
4607
+ return false;
4608
+ // Create a temporary element to test the selector
4609
+ try {
4610
+ const tempElement = document.createElement(node.tagName || 'div');
4611
+ // Copy attributes from the node to the temp element
4612
+ if (node.attributes) {
4613
+ Object.keys(node.attributes).forEach(key => {
4614
+ tempElement.setAttribute(key, node.attributes[key]);
4615
+ });
4616
+ }
4617
+ // Test if the selector matches this element
4618
+ return tempElement.matches(selector);
4619
+ }
4620
+ catch (e) {
4621
+ // If matches() is not supported or fails, fall back to basic attribute checking
4622
+ return this.basicSelectorMatch(selector, node);
4623
+ }
4624
+ }
4625
+ /**
4626
+ * Basic selector matching for environments where matches() is not available
4627
+ */
4628
+ basicSelectorMatch(selector, node) {
4629
+ if (!node.attributes)
4630
+ return false;
4631
+ // Handle simple selectors like 'input[type="password"]'
4632
+ if (selector.includes('input[type=')) {
4633
+ const typeMatch = selector.match(/input\[type="([^"]+)"\]/);
4634
+ if (typeMatch && node.tagName === 'input' && node.attributes.type === typeMatch[1]) {
4635
+ return true;
4636
+ }
4637
+ }
4638
+ // Handle ID selectors like '#email'
4639
+ if (selector.startsWith('#')) {
4640
+ const id = selector.substring(1);
4641
+ return node.attributes.id === id;
4642
+ }
4643
+ // Handle class selectors like '.sensitive-field'
4644
+ if (selector.startsWith('.')) {
4645
+ const className = selector.substring(1);
4646
+ return node.attributes.class && node.attributes.class.includes(className);
4647
+ }
4648
+ // Handle tag selectors like 'input'
4649
+ if (!selector.includes('[') && !selector.includes('.')) {
4650
+ return node.tagName && node.tagName.toLowerCase() === selector.toLowerCase();
4651
+ }
4652
+ return false;
4653
+ }
4654
+ /**
4655
+ * Check if an event is from a field that should be redacted
4656
+ */
4657
+ isFieldSelected(eventData) {
4658
+ if (!isBrowser$1)
4659
+ return false;
4660
+ try {
4661
+ // For input events (source 5), we need to determine if this is a sensitive field
4662
+ if (eventData.source === 5) { // Input event
4663
+ const elementId = eventData.id;
4664
+ if (elementId !== undefined) {
4665
+ // Try to find the element by data-rrweb-id attribute
4666
+ let element = document.querySelector(`[data-rrweb-id="${elementId}"]`);
4667
+ if (element) {
4668
+ return this.shouldRedactElement(element);
4669
+ }
4670
+ // Fallback: Try to find by nodeId if available
4671
+ if (eventData.nodeId !== undefined) {
4672
+ element = document.querySelector(`[data-rrweb-id="${eventData.nodeId}"]`);
4673
+ if (element) {
4674
+ return this.shouldRedactElement(element);
4675
+ }
4676
+ }
4677
+ // More aggressive approach: Check all elements that match our selectors
4678
+ // and see if any of them are currently focused or have the same ID
4679
+ for (const selector of this.userSelectedFields) {
4680
+ const matchingElements = document.querySelectorAll(selector);
4681
+ if (matchingElements.length > 0) {
4682
+ // Check if any of these elements are currently focused
4683
+ for (const el of matchingElements) {
4684
+ if (el === document.activeElement) {
4685
+ console.log('Redaction: Found focused element matching selector:', selector);
4686
+ return true;
4687
+ }
4688
+ }
4689
+ }
4690
+ }
4691
+ // If we still can't find it, try a more direct approach
4692
+ // Look for any input element that might be the active one
4693
+ const activeElement = document.activeElement;
4694
+ if (activeElement && this.shouldRedactElement(activeElement)) {
4695
+ console.log('Redaction: Active element should be redacted');
4696
+ return true;
4697
+ }
4698
+ return false;
4699
+ }
4700
+ }
4701
+ // For other event types, try to find the element
4702
+ const elementId = eventData.id;
4703
+ if (elementId !== undefined) {
4704
+ // First try to find by data-rrweb-id attribute
4705
+ let element = document.querySelector(`[data-rrweb-id="${elementId}"]`);
4706
+ if (element) {
4707
+ return this.shouldRedactElement(element);
4708
+ }
4709
+ }
4710
+ // Also check for nodeId which is another way rrweb identifies elements
4711
+ const nodeId = eventData.nodeId;
4712
+ if (nodeId !== undefined) {
4713
+ const element = document.querySelector(`[data-rrweb-id="${nodeId}"]`);
4714
+ if (element) {
4715
+ return this.shouldRedactElement(element);
4716
+ }
4717
+ }
4718
+ // For DOM mutations, check if the target element should be redacted
4719
+ if (eventData.target && eventData.target.id) {
4720
+ const element = document.querySelector(`[data-rrweb-id="${eventData.target.id}"]`);
4721
+ if (element) {
4722
+ return this.shouldRedactElement(element);
4723
+ }
4724
+ }
4725
+ return false;
4726
+ }
4727
+ catch (e) {
4728
+ console.warn('Error checking if field should be redacted:', e);
4729
+ return false;
4730
+ }
4731
+ }
4732
+ /**
4733
+ * Check if an element should be redacted based on user-selected fields
4734
+ */
4735
+ shouldRedactElement(element) {
4736
+ // Check if element is excluded from redaction
4737
+ for (const excludeSelector of this.excludeSelectors) {
4738
+ if (element.matches(excludeSelector) || element.closest(excludeSelector)) {
4739
+ return false;
4740
+ }
4741
+ }
4742
+ // Check if element matches any of the user-selected fields
4743
+ for (const selector of this.userSelectedFields) {
4744
+ if (element.matches(selector)) {
4745
+ return true;
4746
+ }
4747
+ }
4748
+ return false;
4749
+ }
4750
+ /**
4751
+ * Get the original value of a redacted element (for debugging)
4752
+ */
4753
+ getOriginalValue(element) {
4754
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
4755
+ return element.value;
4756
+ }
4757
+ return undefined;
4758
+ }
4759
+ /**
4760
+ * Check if an element is currently being redacted
4761
+ */
4762
+ isElementRedacted(element) {
4763
+ return this.shouldRedactElement(element);
4764
+ }
4765
+ }
4766
+ // Export a default instance
4767
+ const redactionManager = new RedactionManager();
4470
4768
 
4471
4769
  // Check if we're in a browser environment
4472
- var isBrowser = typeof window !== 'undefined';
4473
- var HumanBehaviorTracker = /** @class */ (function () {
4474
- function HumanBehaviorTracker(apiKey, ingestionUrl) {
4770
+ const isBrowser = typeof window !== 'undefined';
4771
+ class HumanBehaviorTracker {
4772
+ constructor(apiKey, ingestionUrl) {
4475
4773
  this.eventIngestionQueue = [];
4476
4774
  this.queueSizeBytes = 0;
4477
4775
  this.rejectedEvents = [];
@@ -4494,12 +4792,13 @@ var HumanBehaviorTracker = /** @class */ (function () {
4494
4792
  ingestionUrl: ingestionUrl
4495
4793
  });
4496
4794
  this.apiKey = apiKey;
4795
+ this.redactionManager = new RedactionManager();
4497
4796
  // Check for existing session ID and last activity time in localStorage
4498
- var existingSessionId = isBrowser ? localStorage.getItem('human_behavior_session_id') : null;
4499
- var lastActivity = isBrowser ? localStorage.getItem('human_behavior_last_activity') : null;
4797
+ const existingSessionId = isBrowser ? localStorage.getItem('human_behavior_session_id') : null;
4798
+ const lastActivity = isBrowser ? localStorage.getItem('human_behavior_last_activity') : null;
4500
4799
  // If we have a last activity time, check if it's within 30 minutes
4501
- var thirtyMinutesAgo = Date.now() - (30 * 60 * 1000);
4502
- var shouldUseExistingSession = lastActivity && parseInt(lastActivity) > thirtyMinutesAgo;
4800
+ const thirtyMinutesAgo = Date.now() - (30 * 60 * 1000);
4801
+ const shouldUseExistingSession = lastActivity && parseInt(lastActivity) > thirtyMinutesAgo;
4503
4802
  this.sessionId = (existingSessionId && shouldUseExistingSession) ? existingSessionId : v1();
4504
4803
  // Store the session ID if it's new
4505
4804
  if ((!existingSessionId || !shouldUseExistingSession) && isBrowser) {
@@ -4508,300 +4807,298 @@ var HumanBehaviorTracker = /** @class */ (function () {
4508
4807
  // Start initialization immediately
4509
4808
  this.initializationPromise = this.init();
4510
4809
  }
4511
- HumanBehaviorTracker.prototype.init = function () {
4512
- return __awaiter$1(this, void 0, void 0, function () {
4513
- var userId, _a, sessionId, endUserId, error_1;
4514
- return __generator(this, function (_b) {
4515
- switch (_b.label) {
4516
- case 0:
4517
- _b.trys.push([0, 2, , 3]);
4518
- userId = this.getCookie("human_behavior_end_user_id_".concat(this.apiKey));
4519
- return [4 /*yield*/, this.api.init(this.sessionId, userId)];
4520
- case 1:
4521
- _a = _b.sent(), sessionId = _a.sessionId, endUserId = _a.endUserId;
4522
- this.sessionId = sessionId;
4523
- this.endUserId = endUserId;
4524
- this.setCookie("human_behavior_end_user_id_".concat(this.apiKey), endUserId, 365);
4525
- // Only setup browser-specific handlers when in browser environment
4526
- if (isBrowser) {
4527
- this.setupPageUnloadHandler();
4528
- this.start();
4529
- this.processRejectedEvents();
4530
- }
4531
- else {
4532
- console.warn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
4533
- }
4534
- this.initialized = true;
4535
- console.log('HumanBehaviorTracker initialized');
4536
- return [3 /*break*/, 3];
4537
- case 2:
4538
- error_1 = _b.sent();
4539
- console.error('Failed to initialize HumanBehaviorTracker:', error_1);
4540
- throw error_1;
4541
- case 3: return [2 /*return*/];
4810
+ init() {
4811
+ return __awaiter$1(this, void 0, void 0, function* () {
4812
+ try {
4813
+ const userId = this.getCookie(`human_behavior_end_user_id_${this.apiKey}`);
4814
+ const { sessionId, endUserId } = yield this.api.init(this.sessionId, userId);
4815
+ this.sessionId = sessionId;
4816
+ this.endUserId = endUserId;
4817
+ this.setCookie(`human_behavior_end_user_id_${this.apiKey}`, endUserId, 365);
4818
+ // Only setup browser-specific handlers when in browser environment
4819
+ if (isBrowser) {
4820
+ this.setupPageUnloadHandler();
4821
+ this.start();
4822
+ this.processRejectedEvents();
4542
4823
  }
4543
- });
4544
- });
4545
- };
4546
- HumanBehaviorTracker.prototype.ensureInitialized = function () {
4547
- return __awaiter$1(this, void 0, void 0, function () {
4548
- return __generator(this, function (_a) {
4549
- switch (_a.label) {
4550
- case 0:
4551
- if (!this.initializationPromise) {
4552
- throw new Error('HumanBehaviorTracker initialization failed');
4553
- }
4554
- return [4 /*yield*/, this.initializationPromise];
4555
- case 1:
4556
- _a.sent();
4557
- return [2 /*return*/];
4824
+ else {
4825
+ console.warn('HumanBehaviorTracker initialized in a non-browser environment. Session tracking is disabled.');
4558
4826
  }
4559
- });
4827
+ this.initialized = true;
4828
+ console.log('HumanBehaviorTracker initialized');
4829
+ }
4830
+ catch (error) {
4831
+ console.error('Failed to initialize HumanBehaviorTracker:', error);
4832
+ throw error;
4833
+ }
4560
4834
  });
4561
- };
4562
- HumanBehaviorTracker.logToStorage = function (message) {
4835
+ }
4836
+ ensureInitialized() {
4837
+ return __awaiter$1(this, void 0, void 0, function* () {
4838
+ if (!this.initializationPromise) {
4839
+ throw new Error('HumanBehaviorTracker initialization failed');
4840
+ }
4841
+ yield this.initializationPromise;
4842
+ });
4843
+ }
4844
+ static logToStorage(message) {
4563
4845
  try {
4564
- var logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
4565
- logs.push("".concat(new Date().toISOString(), ": ").concat(message));
4846
+ const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
4847
+ logs.push(`${new Date().toISOString()}: ${message}`);
4566
4848
  localStorage.setItem('human_behavior_logs', JSON.stringify(logs));
4567
4849
  }
4568
4850
  catch (e) {
4569
4851
  console.error('Failed to log to storage:', e);
4570
4852
  }
4571
- };
4572
- HumanBehaviorTracker.prototype.setupPageUnloadHandler = function () {
4573
- var _this = this;
4853
+ }
4854
+ setupPageUnloadHandler() {
4574
4855
  if (!isBrowser)
4575
4856
  return;
4576
4857
  console.log('Setting up page unload handler');
4577
4858
  // Handle visibility changes for sending events
4578
- window.addEventListener('visibilitychange', function () {
4859
+ window.addEventListener('visibilitychange', () => {
4579
4860
  // Only send events when page becomes hidden
4580
4861
  if (document.visibilityState === 'hidden') {
4581
4862
  console.log('Page hidden - sending pending events');
4582
- _this.api.sendBeaconEvents(_this.eventIngestionQueue, _this.sessionId);
4863
+ this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
4583
4864
  }
4584
4865
  });
4585
4866
  // Handle actual page unload/close
4586
- window.addEventListener('beforeunload', function () {
4867
+ window.addEventListener('beforeunload', () => {
4587
4868
  // Update last activity time
4588
4869
  localStorage.setItem('human_behavior_last_activity', Date.now().toString());
4589
4870
  // Send final events
4590
- _this.api.sendBeaconEvents(_this.eventIngestionQueue, _this.sessionId);
4871
+ this.api.sendBeaconEvents(this.eventIngestionQueue, this.sessionId);
4591
4872
  });
4592
4873
  // Update activity timestamp periodically
4593
- setInterval(function () {
4874
+ setInterval(() => {
4594
4875
  localStorage.setItem('human_behavior_last_activity', Date.now().toString());
4595
4876
  }, 60000); // Update every minute
4596
- };
4597
- HumanBehaviorTracker.prototype.viewLogs = function () {
4877
+ }
4878
+ viewLogs() {
4598
4879
  try {
4599
- var logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
4880
+ const logs = JSON.parse(localStorage.getItem('human_behavior_logs') || '[]');
4600
4881
  console.log('HumanBehavior Logs:', logs);
4601
4882
  localStorage.removeItem('human_behavior_logs'); // Clear logs after viewing
4602
4883
  }
4603
4884
  catch (e) {
4604
4885
  console.error('Failed to read logs:', e);
4605
4886
  }
4606
- };
4607
- HumanBehaviorTracker.prototype.identifyUser = function (userProperties) {
4608
- return __awaiter$1(this, void 0, void 0, function () {
4609
- return __generator(this, function (_a) {
4610
- switch (_a.label) {
4611
- case 0: return [4 /*yield*/, this.ensureInitialized()];
4612
- case 1:
4613
- _a.sent();
4614
- this.userProperties = userProperties;
4615
- return [4 /*yield*/, this.api.sendUserData(this.endUserId, userProperties, this.sessionId)];
4616
- case 2:
4617
- _a.sent();
4618
- return [2 /*return*/];
4619
- }
4620
- });
4887
+ }
4888
+ addUserInfo(userProperties) {
4889
+ return __awaiter$1(this, void 0, void 0, function* () {
4890
+ yield this.ensureInitialized();
4891
+ if (!this.endUserId) {
4892
+ throw new Error('Cannot add user info before tracker initialization');
4893
+ }
4894
+ this.userProperties = userProperties;
4895
+ yield this.api.sendUserData(this.endUserId, userProperties, this.sessionId);
4621
4896
  });
4622
- };
4623
- HumanBehaviorTracker.prototype.customEvent = function (eventName_1) {
4624
- return __awaiter$1(this, arguments, void 0, function (eventName, eventProperties) {
4625
- if (eventProperties === void 0) { eventProperties = {}; }
4626
- return __generator(this, function (_a) {
4627
- switch (_a.label) {
4628
- case 0: return [4 /*yield*/, this.ensureInitialized()];
4629
- case 1:
4630
- _a.sent();
4631
- this.api.sendBeaconCustomEvent(eventName, eventProperties, this.sessionId);
4632
- return [2 /*return*/];
4633
- }
4634
- });
4897
+ }
4898
+ /**
4899
+ * Authenticate user using existing userInfo data
4900
+ * @param authFields Array of field names to check for existing users (e.g., ['email', 'phoneNumber'])
4901
+ */
4902
+ auth(authFields) {
4903
+ return __awaiter$1(this, void 0, void 0, function* () {
4904
+ yield this.ensureInitialized();
4905
+ if (!this.endUserId) {
4906
+ throw new Error('Cannot authenticate before tracker initialization');
4907
+ }
4908
+ if (!this.userProperties || Object.keys(this.userProperties).length === 0) {
4909
+ throw new Error('No user info available. Call addUserInfo() first.');
4910
+ }
4911
+ yield this.api.sendUserAuth(this.endUserId, this.userProperties, this.sessionId, authFields);
4635
4912
  });
4636
- };
4637
- HumanBehaviorTracker.prototype.start = function () {
4638
- return __awaiter$1(this, void 0, void 0, function () {
4639
- var _this = this;
4640
- return __generator(this, function (_a) {
4641
- switch (_a.label) {
4642
- case 0: return [4 /*yield*/, this.ensureInitialized()];
4643
- case 1:
4644
- _a.sent();
4645
- if (!isBrowser)
4646
- return [2 /*return*/];
4647
- // Start periodic flushing
4648
- this.flushInterval = window.setInterval(function () {
4649
- _this.flush();
4650
- }, this.FLUSH_INTERVAL_MS);
4651
- // Start recording
4652
- record({
4653
- emit: function (event) {
4654
- _this.addEvent(event);
4655
- },
4656
- });
4657
- return [2 /*return*/];
4658
- }
4659
- });
4913
+ }
4914
+ customEvent(eventName_1) {
4915
+ return __awaiter$1(this, arguments, void 0, function* (eventName, eventProperties = {}) {
4916
+ yield this.ensureInitialized();
4917
+ this.api.sendBeaconCustomEvent(eventName, eventProperties, this.sessionId);
4660
4918
  });
4661
- };
4662
- HumanBehaviorTracker.prototype.stop = function () {
4663
- return __awaiter$1(this, void 0, void 0, function () {
4664
- return __generator(this, function (_a) {
4665
- switch (_a.label) {
4666
- case 0: return [4 /*yield*/, this.ensureInitialized()];
4667
- case 1:
4668
- _a.sent();
4669
- if (!isBrowser)
4670
- return [2 /*return*/];
4671
- if (this.flushInterval) {
4672
- clearInterval(this.flushInterval);
4673
- this.flushInterval = null;
4674
- }
4675
- return [2 /*return*/];
4676
- }
4919
+ }
4920
+ start() {
4921
+ return __awaiter$1(this, void 0, void 0, function* () {
4922
+ yield this.ensureInitialized();
4923
+ if (!isBrowser)
4924
+ return;
4925
+ // Start periodic flushing
4926
+ this.flushInterval = window.setInterval(() => {
4927
+ this.flush();
4928
+ }, this.FLUSH_INTERVAL_MS);
4929
+ // Start recording with redaction enabled
4930
+ record({
4931
+ emit: (event) => {
4932
+ this.addEvent(event);
4933
+ },
4934
+ inlineStylesheet: true,
4935
+ recordCanvas: true,
4936
+ collectFonts: true,
4937
+ blockClass: 'rr-block',
4938
+ ignoreClass: 'rr-ignore',
4939
+ maskTextClass: 'rr-ignore'
4677
4940
  });
4678
4941
  });
4679
- };
4680
- HumanBehaviorTracker.prototype.addEvent = function (event) {
4681
- return __awaiter$1(this, void 0, void 0, function () {
4682
- var eventSize;
4683
- return __generator(this, function (_a) {
4684
- switch (_a.label) {
4685
- case 0: return [4 /*yield*/, this.ensureInitialized()];
4686
- case 1:
4687
- _a.sent();
4688
- eventSize = new TextEncoder().encode(JSON.stringify(event)).length;
4689
- this.eventIngestionQueue.push(event);
4690
- this.queueSizeBytes += eventSize;
4691
- return [2 /*return*/];
4692
- }
4693
- });
4942
+ }
4943
+ stop() {
4944
+ return __awaiter$1(this, void 0, void 0, function* () {
4945
+ yield this.ensureInitialized();
4946
+ if (!isBrowser)
4947
+ return;
4948
+ if (this.flushInterval) {
4949
+ clearInterval(this.flushInterval);
4950
+ this.flushInterval = null;
4951
+ }
4694
4952
  });
4695
- };
4696
- HumanBehaviorTracker.prototype.processRejectedEvents = function () {
4697
- return __awaiter$1(this, void 0, void 0, function () {
4698
- var newSessionId;
4699
- return __generator(this, function (_a) {
4700
- if (this.isProcessingRejectedEvents || this.rejectedEvents.length === 0)
4701
- return [2 /*return*/];
4702
- this.isProcessingRejectedEvents = true;
4703
- try {
4704
- newSessionId = v1();
4705
- if (isBrowser) {
4706
- localStorage.setItem('human_behavior_session_id', newSessionId);
4707
- }
4708
- // Try to send rejected events with new session ID using beacon
4709
- // sendBeacon returns true if the request was queued successfully
4710
- this.api.sendBeaconEvents(this.rejectedEvents, newSessionId);
4711
- // Clear rejected events and update session ID
4712
- // Note: We can't verify if the beacon data was actually sent,
4713
- // but we clear the events to prevent duplicate sending attempts
4714
- this.rejectedEvents = [];
4715
- this.sessionId = newSessionId;
4716
- }
4717
- catch (error) {
4718
- console.error('Failed to process rejected events:', error);
4719
- }
4720
- finally {
4721
- this.isProcessingRejectedEvents = false;
4953
+ }
4954
+ addEvent(event) {
4955
+ return __awaiter$1(this, void 0, void 0, function* () {
4956
+ yield this.ensureInitialized();
4957
+ // Process event through redaction manager if active
4958
+ const processedEvent = this.redactionManager.processEvent(event);
4959
+ const eventSize = new TextEncoder().encode(JSON.stringify(processedEvent)).length;
4960
+ this.eventIngestionQueue.push(processedEvent);
4961
+ this.queueSizeBytes += eventSize;
4962
+ });
4963
+ }
4964
+ processRejectedEvents() {
4965
+ return __awaiter$1(this, void 0, void 0, function* () {
4966
+ if (this.isProcessingRejectedEvents || this.rejectedEvents.length === 0)
4967
+ return;
4968
+ this.isProcessingRejectedEvents = true;
4969
+ try {
4970
+ // Create a new session ID for rejected events
4971
+ const newSessionId = v1();
4972
+ if (isBrowser) {
4973
+ localStorage.setItem('human_behavior_session_id', newSessionId);
4722
4974
  }
4723
- return [2 /*return*/];
4724
- });
4975
+ // Try to send rejected events with new session ID using beacon
4976
+ // sendBeacon returns true if the request was queued successfully
4977
+ this.api.sendBeaconEvents(this.rejectedEvents, newSessionId);
4978
+ // Clear rejected events and update session ID
4979
+ // Note: We can't verify if the beacon data was actually sent,
4980
+ // but we clear the events to prevent duplicate sending attempts
4981
+ this.rejectedEvents = [];
4982
+ this.sessionId = newSessionId;
4983
+ }
4984
+ catch (error) {
4985
+ console.error('Failed to process rejected events:', error);
4986
+ }
4987
+ finally {
4988
+ this.isProcessingRejectedEvents = false;
4989
+ }
4725
4990
  });
4726
- };
4727
- HumanBehaviorTracker.prototype.flush = function () {
4728
- return __awaiter$1(this, void 0, void 0, function () {
4729
- var eventsToProcess, error_2;
4991
+ }
4992
+ flush() {
4993
+ return __awaiter$1(this, void 0, void 0, function* () {
4730
4994
  var _a;
4731
- var _b;
4732
- return __generator(this, function (_c) {
4733
- switch (_c.label) {
4734
- case 0:
4735
- // Prevent concurrent flushes
4736
- if (this.isProcessing || !this.initialized) {
4737
- return [2 /*return*/];
4738
- }
4739
- this.isProcessing = true;
4740
- _c.label = 1;
4741
- case 1:
4742
- _c.trys.push([1, , 6, 7]);
4743
- eventsToProcess = this.eventIngestionQueue;
4744
- this.eventIngestionQueue = [];
4745
- this.queueSizeBytes = 0;
4746
- if (!(eventsToProcess.length > 0)) return [3 /*break*/, 5];
4747
- console.log('Flushing events:', eventsToProcess);
4748
- _c.label = 2;
4749
- case 2:
4750
- _c.trys.push([2, 4, , 5]);
4751
- return [4 /*yield*/, this.api.sendEvents(eventsToProcess, this.sessionId, this.endUserId)];
4752
- case 3:
4753
- _c.sent();
4754
- return [3 /*break*/, 5];
4755
- case 4:
4756
- error_2 = _c.sent();
4995
+ // Prevent concurrent flushes
4996
+ if (this.isProcessing || !this.initialized) {
4997
+ return;
4998
+ }
4999
+ this.isProcessing = true;
5000
+ try {
5001
+ // Swap the current queue with an empty one atomically
5002
+ const eventsToProcess = this.eventIngestionQueue;
5003
+ this.eventIngestionQueue = [];
5004
+ this.queueSizeBytes = 0;
5005
+ if (eventsToProcess.length > 0) {
5006
+ console.log('Flushing events:', eventsToProcess);
5007
+ try {
5008
+ yield this.api.sendEvents(eventsToProcess, this.sessionId, this.endUserId);
5009
+ }
5010
+ catch (error) {
4757
5011
  // If we get a 400 error, store events for retry
4758
- if ((_b = error_2.message) === null || _b === void 0 ? void 0 : _b.includes('ERROR: Session already completed')) {
5012
+ if ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ERROR: Session already completed')) {
4759
5013
  console.log('Session expired, storing events for retry');
4760
- (_a = this.rejectedEvents).push.apply(_a, eventsToProcess);
5014
+ this.rejectedEvents.push(...eventsToProcess);
4761
5015
  this.processRejectedEvents();
4762
5016
  }
4763
5017
  else {
4764
- throw error_2;
5018
+ throw error;
4765
5019
  }
4766
- return [3 /*break*/, 5];
4767
- case 5: return [3 /*break*/, 7];
4768
- case 6:
4769
- this.isProcessing = false;
4770
- return [7 /*endfinally*/];
4771
- case 7: return [2 /*return*/];
5020
+ }
4772
5021
  }
4773
- });
5022
+ }
5023
+ finally {
5024
+ this.isProcessing = false;
5025
+ }
4774
5026
  });
4775
- };
5027
+ }
4776
5028
  // Add helper methods for cookie management
4777
- HumanBehaviorTracker.prototype.setCookie = function (name, value, daysToExpire) {
5029
+ setCookie(name, value, daysToExpire) {
4778
5030
  if (!isBrowser)
4779
5031
  return;
4780
- var date = new Date();
5032
+ const date = new Date();
4781
5033
  date.setTime(date.getTime() + (daysToExpire * 24 * 60 * 60 * 1000));
4782
- var expires = "expires=".concat(date.toUTCString());
4783
- document.cookie = "".concat(name, "=").concat(value, ";").concat(expires, ";path=/;SameSite=Lax");
4784
- };
4785
- HumanBehaviorTracker.prototype.getCookie = function (name) {
5034
+ const expires = `expires=${date.toUTCString()}`;
5035
+ document.cookie = `${name}=${value};${expires};path=/;SameSite=Lax`;
5036
+ }
5037
+ getCookie(name) {
4786
5038
  if (!isBrowser)
4787
5039
  return null;
4788
- var nameEQ = name + "=";
4789
- var ca = document.cookie.split(';');
4790
- for (var i = 0; i < ca.length; i++) {
4791
- var c = ca[i];
5040
+ const nameEQ = name + "=";
5041
+ const ca = document.cookie.split(';');
5042
+ for (let i = 0; i < ca.length; i++) {
5043
+ let c = ca[i];
4792
5044
  while (c.charAt(0) === ' ')
4793
5045
  c = c.substring(1, c.length);
4794
5046
  if (c.indexOf(nameEQ) === 0)
4795
5047
  return c.substring(nameEQ.length, c.length);
4796
5048
  }
4797
5049
  return null;
4798
- };
4799
- return HumanBehaviorTracker;
4800
- }());
5050
+ }
5051
+ /**
5052
+ * Start redaction functionality for sensitive input fields
5053
+ * @param options Optional configuration for redaction behavior
5054
+ */
5055
+ redact(options) {
5056
+ return __awaiter$1(this, void 0, void 0, function* () {
5057
+ yield this.ensureInitialized();
5058
+ if (!isBrowser) {
5059
+ console.warn('Redaction is only available in browser environments');
5060
+ return;
5061
+ }
5062
+ // Create a new redaction manager with the provided options
5063
+ this.redactionManager = new RedactionManager(options);
5064
+ });
5065
+ }
5066
+ /**
5067
+ * Set specific fields to be redacted during session recording
5068
+ * @param fields Array of CSS selectors for fields to redact (e.g., ['input[type="password"]', '#email-field'])
5069
+ */
5070
+ setRedactedFields(fields) {
5071
+ if (!isBrowser) {
5072
+ console.warn('Redaction is only available in browser environments');
5073
+ return;
5074
+ }
5075
+ this.redactionManager.setFieldsToRedact(fields);
5076
+ }
5077
+ /**
5078
+ * Check if redaction is currently active
5079
+ */
5080
+ isRedactionActive() {
5081
+ return this.redactionManager.isActive();
5082
+ }
5083
+ /**
5084
+ * Get the currently selected fields for redaction
5085
+ */
5086
+ getRedactedFields() {
5087
+ return this.redactionManager.getSelectedFields();
5088
+ }
5089
+ }
4801
5090
  // Only expose to window object in browser environments
4802
5091
  if (isBrowser) {
4803
5092
  window.HumanBehaviorTracker = HumanBehaviorTracker;
4804
5093
  }
4805
5094
 
4806
- export { HumanBehaviorAPI, HumanBehaviorTracker, MAX_CHUNK_SIZE_BYTES, HumanBehaviorTracker as default, isChunkSizeExceeded, validateSingleEventSize };
5095
+ /**
5096
+ * Main entry point for the HumanBehavior SDK
5097
+ */
5098
+ // For UMD builds, expose the main class globally
5099
+ if (typeof window !== 'undefined') {
5100
+ window.HumanBehaviorTracker = HumanBehaviorTracker;
5101
+ }
5102
+
5103
+ export { HumanBehaviorAPI, HumanBehaviorTracker, MAX_CHUNK_SIZE_BYTES, RedactionManager, HumanBehaviorTracker as default, isChunkSizeExceeded, redactionManager, validateSingleEventSize };
4807
5104
  //# sourceMappingURL=index.js.map