@tell-rs/browser 0.2.3 → 0.2.5
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/{chunk-HYQWDHQT.js → chunk-VENRJ443.js} +19 -28
- package/dist/chunk-VENRJ443.js.map +1 -0
- package/dist/events.js +1 -1
- package/dist/index.cjs +36 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +19 -16
- package/dist/index.js.map +1 -1
- package/dist/tell.global.js +7 -0
- package/dist/tell.global.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-HYQWDHQT.js.map +0 -1
|
@@ -244,18 +244,6 @@ function stripUrlParams(url, params) {
|
|
|
244
244
|
return url;
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
|
-
function stripParamsInProperties(props, params) {
|
|
248
|
-
if (!props) return props;
|
|
249
|
-
const out = {};
|
|
250
|
-
for (const [k, v] of Object.entries(props)) {
|
|
251
|
-
if (typeof v === "string" && v.startsWith("http")) {
|
|
252
|
-
out[k] = stripUrlParams(v, params);
|
|
253
|
-
} else {
|
|
254
|
-
out[k] = v;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return out;
|
|
258
|
-
}
|
|
259
247
|
function redactKeysInProperties(props, keys) {
|
|
260
248
|
if (!props) return props;
|
|
261
249
|
const out = {};
|
|
@@ -267,33 +255,36 @@ function redactKeysInProperties(props, keys) {
|
|
|
267
255
|
function redact(options) {
|
|
268
256
|
const { stripParams, redactKeys, dropRoutes } = options;
|
|
269
257
|
return (event) => {
|
|
270
|
-
if (dropRoutes && dropRoutes.length > 0 && event.
|
|
258
|
+
if (dropRoutes && dropRoutes.length > 0 && event.url) {
|
|
271
259
|
try {
|
|
272
|
-
const pathname = new URL(String(event.
|
|
260
|
+
const pathname = new URL(String(event.url)).pathname;
|
|
273
261
|
for (const prefix of dropRoutes) {
|
|
274
262
|
if (pathname.startsWith(prefix)) return null;
|
|
275
263
|
}
|
|
276
264
|
} catch {
|
|
277
265
|
}
|
|
278
266
|
}
|
|
279
|
-
let
|
|
280
|
-
let props = event.properties;
|
|
281
|
-
let traits = event.traits;
|
|
267
|
+
let result = event;
|
|
282
268
|
if (stripParams && stripParams.length > 0) {
|
|
283
|
-
|
|
284
|
-
|
|
269
|
+
for (const [k, v] of Object.entries(event)) {
|
|
270
|
+
if (typeof v === "string" && v.startsWith("http")) {
|
|
271
|
+
const stripped = stripUrlParams(v, stripParams);
|
|
272
|
+
if (stripped !== v) {
|
|
273
|
+
if (result === event) result = { ...event };
|
|
274
|
+
result[k] = stripped;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
285
277
|
}
|
|
286
|
-
props = stripParamsInProperties(props, stripParams);
|
|
287
|
-
traits = stripParamsInProperties(traits, stripParams);
|
|
288
278
|
}
|
|
289
279
|
if (redactKeys && redactKeys.length > 0) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
280
|
+
for (const [k, v] of Object.entries(event)) {
|
|
281
|
+
if (redactKeys.includes(k)) {
|
|
282
|
+
if (result === event) result = { ...event };
|
|
283
|
+
result[k] = "[REDACTED]";
|
|
284
|
+
}
|
|
285
|
+
}
|
|
295
286
|
}
|
|
296
|
-
return
|
|
287
|
+
return result;
|
|
297
288
|
};
|
|
298
289
|
}
|
|
299
290
|
function redactLog(options) {
|
|
@@ -327,4 +318,4 @@ export {
|
|
|
327
318
|
redact,
|
|
328
319
|
redactLog
|
|
329
320
|
};
|
|
330
|
-
//# sourceMappingURL=chunk-
|
|
321
|
+
//# sourceMappingURL=chunk-VENRJ443.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../core/src/constants.ts","../../core/src/errors.ts","../../core/src/validation.ts","../../core/src/batcher.ts","../../core/src/before-send.ts","../../core/src/redact.ts"],"sourcesContent":["// Standard event names — typed constants matching the spec appendix A\n\nexport const Events = {\n // User Lifecycle\n UserSignedUp: \"User Signed Up\",\n UserSignedIn: \"User Signed In\",\n UserSignedOut: \"User Signed Out\",\n UserInvited: \"User Invited\",\n UserOnboarded: \"User Onboarded\",\n AuthenticationFailed: \"Authentication Failed\",\n PasswordReset: \"Password Reset\",\n TwoFactorEnabled: \"Two Factor Enabled\",\n TwoFactorDisabled: \"Two Factor Disabled\",\n\n // Revenue & Billing\n OrderCompleted: \"Order Completed\",\n OrderRefunded: \"Order Refunded\",\n OrderCanceled: \"Order Canceled\",\n PaymentFailed: \"Payment Failed\",\n PaymentMethodAdded: \"Payment Method Added\",\n PaymentMethodUpdated: \"Payment Method Updated\",\n PaymentMethodRemoved: \"Payment Method Removed\",\n\n // Subscription\n SubscriptionStarted: \"Subscription Started\",\n SubscriptionRenewed: \"Subscription Renewed\",\n SubscriptionPaused: \"Subscription Paused\",\n SubscriptionResumed: \"Subscription Resumed\",\n SubscriptionChanged: \"Subscription Changed\",\n SubscriptionCanceled: \"Subscription Canceled\",\n\n // Trial\n TrialStarted: \"Trial Started\",\n TrialEndingSoon: \"Trial Ending Soon\",\n TrialEnded: \"Trial Ended\",\n TrialConverted: \"Trial Converted\",\n\n // Shopping\n CartViewed: \"Cart Viewed\",\n CartUpdated: \"Cart Updated\",\n CartAbandoned: \"Cart Abandoned\",\n CheckoutStarted: \"Checkout Started\",\n CheckoutCompleted: \"Checkout Completed\",\n\n // Engagement\n PageViewed: \"Page Viewed\",\n FeatureUsed: \"Feature Used\",\n SearchPerformed: \"Search Performed\",\n FileUploaded: \"File Uploaded\",\n NotificationSent: \"Notification Sent\",\n NotificationClicked: \"Notification Clicked\",\n\n // Communication\n EmailSent: \"Email Sent\",\n EmailOpened: \"Email Opened\",\n EmailClicked: \"Email Clicked\",\n EmailBounced: \"Email Bounced\",\n EmailUnsubscribed: \"Email Unsubscribed\",\n SupportTicketCreated: \"Support Ticket Created\",\n SupportTicketResolved: \"Support Ticket Resolved\",\n} as const;\n\nexport type EventName = (typeof Events)[keyof typeof Events];\n","export class TellError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TellError\";\n }\n}\n\nexport class ConfigurationError extends TellError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\nexport class ValidationError extends TellError {\n public readonly field: string;\n\n constructor(field: string, message: string) {\n super(`${field}: ${message}`);\n this.name = \"ValidationError\";\n this.field = field;\n }\n}\n\nexport class NetworkError extends TellError {\n public readonly statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"NetworkError\";\n this.statusCode = statusCode;\n }\n}\n\nexport class ClosedError extends TellError {\n constructor() {\n super(\"Client is closed\");\n this.name = \"ClosedError\";\n }\n}\n\nexport class SerializationError extends TellError {\n constructor(message: string) {\n super(message);\n this.name = \"SerializationError\";\n }\n}\n","import { ConfigurationError, ValidationError } from \"./errors.js\";\n\nconst HEX_RE = /^[0-9a-fA-F]{32}$/;\nconst MAX_EVENT_NAME = 256;\nconst MAX_LOG_MESSAGE = 65_536;\n\nexport function validateApiKey(key: string): void {\n if (!key) {\n throw new ConfigurationError(\"apiKey is required\");\n }\n if (!HEX_RE.test(key)) {\n throw new ConfigurationError(\n \"apiKey must be exactly 32 hex characters\"\n );\n }\n}\n\nexport function validateEventName(name: unknown): void {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new ValidationError(\"eventName\", \"must be a non-empty string\");\n }\n if (name.length > MAX_EVENT_NAME) {\n throw new ValidationError(\n \"eventName\",\n `must be at most ${MAX_EVENT_NAME} characters, got ${name.length}`\n );\n }\n}\n\nexport function validateLogMessage(message: unknown): void {\n if (typeof message !== \"string\" || message.length === 0) {\n throw new ValidationError(\"message\", \"must be a non-empty string\");\n }\n if (message.length > MAX_LOG_MESSAGE) {\n throw new ValidationError(\n \"message\",\n `must be at most ${MAX_LOG_MESSAGE} characters, got ${message.length}`\n );\n }\n}\n\nexport function validateUserId(id: unknown): void {\n if (typeof id !== \"string\" || id.length === 0) {\n throw new ValidationError(\"userId\", \"must be a non-empty string\");\n }\n}\n","export interface BatcherConfig<T> {\n size: number;\n interval: number; // ms\n maxQueueSize: number;\n send: (items: T[]) => Promise<void>;\n onOverflow?: () => void;\n}\n\nexport class Batcher<T> {\n private queue: T[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private closed = false;\n private flushing: Promise<void> | null = null;\n private config: BatcherConfig<T>;\n\n constructor(config: BatcherConfig<T>) {\n this.config = config;\n this.timer = setInterval(() => {\n if (this.queue.length > 0) {\n this.flush().catch(() => {});\n }\n }, config.interval);\n // Don't keep Node.js process alive just for flush timer\n if (this.timer && typeof (this.timer as any).unref === \"function\") {\n (this.timer as any).unref();\n }\n }\n\n add(item: T): void {\n if (this.closed) return;\n\n if (this.queue.length >= this.config.maxQueueSize) {\n this.queue.shift(); // drop oldest\n if (this.config.onOverflow) {\n this.config.onOverflow();\n }\n }\n\n this.queue.push(item);\n\n if (this.queue.length >= this.config.size) {\n this.flush().catch(() => {});\n }\n }\n\n async flush(): Promise<void> {\n // Prevent concurrent flushes\n if (this.flushing) {\n return this.flushing;\n }\n this.flushing = this.doFlush();\n try {\n await this.flushing;\n } finally {\n this.flushing = null;\n }\n }\n\n async close(): Promise<void> {\n this.closed = true;\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n get pending(): number {\n return this.queue.length;\n }\n\n drain(): T[] {\n const items = this.queue;\n this.queue = [];\n return items;\n }\n\n halveBatchSize(): void {\n this.config.size = Math.max(1, Math.floor(this.config.size / 2));\n }\n\n private async doFlush(): Promise<void> {\n while (this.queue.length > 0) {\n const batch = this.queue.slice(0, this.config.size);\n try {\n await this.config.send(batch);\n this.queue.splice(0, batch.length); // remove only on success\n } catch {\n return; // items stay in queue (e.g. 413 — batch size already halved)\n }\n }\n }\n}\n","/**\n * A function that transforms or drops an item before it is queued.\n * Return the (possibly modified) item, or null to drop it.\n */\nexport type BeforeSendFn<T> = (item: T) => T | null;\n\n/**\n * Run an item through a pipeline of beforeSend functions.\n * Returns the transformed item, or null if any function in the chain drops it.\n */\nexport function runBeforeSend<T>(\n item: T,\n fns: BeforeSendFn<T> | BeforeSendFn<T>[]\n): T | null {\n const pipeline = Array.isArray(fns) ? fns : [fns];\n let current: T | null = item;\n\n for (const fn of pipeline) {\n if (current === null) return null;\n current = fn(current);\n }\n\n return current;\n}\n","import type { BeforeSendFn } from \"./before-send.js\";\nimport type { JsonEvent, JsonLog, Properties } from \"./types.js\";\n\n/**\n * Common query-parameter names that often carry secrets or tokens.\n * Pass these (or a subset) to `stripParams` for quick sanitization.\n */\nexport const SENSITIVE_PARAMS: readonly string[] = [\n \"token\",\n \"api_key\",\n \"key\",\n \"secret\",\n \"password\",\n \"access_token\",\n \"refresh_token\",\n \"authorization\",\n] as const;\n\nexport interface RedactOptions {\n /** Query-parameter names to strip from URL-shaped values. */\n stripParams?: string[];\n /** Keys whose values should be replaced with \"[REDACTED]\". */\n redactKeys?: string[];\n /** URL pathname prefixes — events whose url matches are dropped entirely. */\n dropRoutes?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction stripUrlParams(url: string, params: string[]): string {\n try {\n const u = new URL(url);\n for (const p of params) u.searchParams.delete(p);\n return u.toString();\n } catch {\n return url; // not a valid URL — leave as-is\n }\n}\n\nfunction redactKeysInProperties(\n props: Properties | undefined,\n keys: string[],\n): Properties | undefined {\n if (!props) return props;\n const out: Properties = {};\n for (const [k, v] of Object.entries(props)) {\n out[k] = keys.includes(k) ? \"[REDACTED]\" : v;\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Factory that returns a `beforeSend` hook for events.\n *\n * - `dropRoutes` — drops events whose `url` pathname starts with a prefix.\n * - `stripParams` — removes query params from URL-shaped string values.\n * - `redactKeys` — replaces matching keys with `\"[REDACTED]\"`.\n *\n * The returned function never mutates the input event.\n */\nexport function redact(options: RedactOptions): BeforeSendFn<JsonEvent> {\n const { stripParams, redactKeys, dropRoutes } = options;\n\n return (event: JsonEvent): JsonEvent | null => {\n // --- dropRoutes ---\n if (dropRoutes && dropRoutes.length > 0 && event.url) {\n try {\n const pathname = new URL(String(event.url)).pathname;\n for (const prefix of dropRoutes) {\n if (pathname.startsWith(prefix)) return null;\n }\n } catch {\n // not a valid URL — skip drop check\n }\n }\n\n let result: JsonEvent = event;\n\n // --- stripParams ---\n if (stripParams && stripParams.length > 0) {\n for (const [k, v] of Object.entries(event)) {\n if (typeof v === \"string\" && v.startsWith(\"http\")) {\n const stripped = stripUrlParams(v, stripParams);\n if (stripped !== v) {\n if (result === event) result = { ...event };\n result[k] = stripped;\n }\n }\n }\n }\n\n // --- redactKeys ---\n if (redactKeys && redactKeys.length > 0) {\n for (const [k, v] of Object.entries(event)) {\n if (redactKeys.includes(k)) {\n if (result === event) result = { ...event };\n result[k] = \"[REDACTED]\";\n }\n }\n }\n\n return result;\n };\n}\n\n/**\n * Factory that returns a `beforeSend` hook for log entries.\n *\n * - `redactKeys` — replaces matching keys in `log.data` with `\"[REDACTED]\"`.\n *\n * The returned function never mutates the input log.\n */\nexport function redactLog(\n options: Pick<RedactOptions, \"redactKeys\">,\n): BeforeSendFn<JsonLog> {\n const { redactKeys } = options;\n\n return (log: JsonLog): JsonLog | null => {\n if (redactKeys && redactKeys.length > 0 && log.data) {\n const data = redactKeysInProperties(log.data, redactKeys);\n if (data !== log.data) {\n return { ...log, data };\n }\n }\n return log;\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/events.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -283,18 +283,6 @@ function stripUrlParams(url, params) {
|
|
|
283
283
|
return url;
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
-
function stripParamsInProperties(props, params) {
|
|
287
|
-
if (!props) return props;
|
|
288
|
-
const out = {};
|
|
289
|
-
for (const [k, v] of Object.entries(props)) {
|
|
290
|
-
if (typeof v === "string" && v.startsWith("http")) {
|
|
291
|
-
out[k] = stripUrlParams(v, params);
|
|
292
|
-
} else {
|
|
293
|
-
out[k] = v;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return out;
|
|
297
|
-
}
|
|
298
286
|
function redactKeysInProperties(props, keys) {
|
|
299
287
|
if (!props) return props;
|
|
300
288
|
const out = {};
|
|
@@ -306,33 +294,36 @@ function redactKeysInProperties(props, keys) {
|
|
|
306
294
|
function redact(options) {
|
|
307
295
|
const { stripParams, redactKeys, dropRoutes } = options;
|
|
308
296
|
return (event) => {
|
|
309
|
-
if (dropRoutes && dropRoutes.length > 0 && event.
|
|
297
|
+
if (dropRoutes && dropRoutes.length > 0 && event.url) {
|
|
310
298
|
try {
|
|
311
|
-
const pathname = new URL(String(event.
|
|
299
|
+
const pathname = new URL(String(event.url)).pathname;
|
|
312
300
|
for (const prefix of dropRoutes) {
|
|
313
301
|
if (pathname.startsWith(prefix)) return null;
|
|
314
302
|
}
|
|
315
303
|
} catch {
|
|
316
304
|
}
|
|
317
305
|
}
|
|
318
|
-
let
|
|
319
|
-
let props = event.properties;
|
|
320
|
-
let traits = event.traits;
|
|
306
|
+
let result = event;
|
|
321
307
|
if (stripParams && stripParams.length > 0) {
|
|
322
|
-
|
|
323
|
-
|
|
308
|
+
for (const [k, v] of Object.entries(event)) {
|
|
309
|
+
if (typeof v === "string" && v.startsWith("http")) {
|
|
310
|
+
const stripped = stripUrlParams(v, stripParams);
|
|
311
|
+
if (stripped !== v) {
|
|
312
|
+
if (result === event) result = { ...event };
|
|
313
|
+
result[k] = stripped;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
324
316
|
}
|
|
325
|
-
props = stripParamsInProperties(props, stripParams);
|
|
326
|
-
traits = stripParamsInProperties(traits, stripParams);
|
|
327
317
|
}
|
|
328
318
|
if (redactKeys && redactKeys.length > 0) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
319
|
+
for (const [k, v] of Object.entries(event)) {
|
|
320
|
+
if (redactKeys.includes(k)) {
|
|
321
|
+
if (result === event) result = { ...event };
|
|
322
|
+
result[k] = "[REDACTED]";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
334
325
|
}
|
|
335
|
-
return
|
|
326
|
+
return result;
|
|
336
327
|
};
|
|
337
328
|
}
|
|
338
329
|
function redactLog(options) {
|
|
@@ -476,7 +467,6 @@ function captureContext() {
|
|
|
476
467
|
ctx.browser_version = parsed.browserVersion;
|
|
477
468
|
ctx.os_name = parsed.os;
|
|
478
469
|
ctx.os_version = parsed.osVersion;
|
|
479
|
-
ctx.device_type = inferDeviceType(parsed.os);
|
|
480
470
|
ctx.locale = navigator.language;
|
|
481
471
|
if ("hardwareConcurrency" in navigator) {
|
|
482
472
|
ctx.cpu_cores = navigator.hardwareConcurrency;
|
|
@@ -487,6 +477,7 @@ function captureContext() {
|
|
|
487
477
|
if ("maxTouchPoints" in navigator) {
|
|
488
478
|
ctx.touch = navigator.maxTouchPoints > 0;
|
|
489
479
|
}
|
|
480
|
+
ctx.device_type = inferDeviceType(parsed.os, ua, ctx.touch);
|
|
490
481
|
const conn = navigator.connection;
|
|
491
482
|
if (conn?.effectiveType) {
|
|
492
483
|
ctx.connection_type = conn.effectiveType;
|
|
@@ -522,8 +513,10 @@ function captureContext() {
|
|
|
522
513
|
}
|
|
523
514
|
return ctx;
|
|
524
515
|
}
|
|
525
|
-
function inferDeviceType(os) {
|
|
526
|
-
if (os === "
|
|
516
|
+
function inferDeviceType(os, ua, touch) {
|
|
517
|
+
if (os === "macOS" && touch) return "tablet";
|
|
518
|
+
if (os === "Android") return ua && !/Mobile/.test(ua) ? "tablet" : "mobile";
|
|
519
|
+
if (os === "iOS") return "mobile";
|
|
527
520
|
return "desktop";
|
|
528
521
|
}
|
|
529
522
|
function parseUA(ua) {
|
|
@@ -889,7 +882,8 @@ function onNewSession(reason, sessionId) {
|
|
|
889
882
|
session_id: sessionId,
|
|
890
883
|
user_id: userId,
|
|
891
884
|
timestamp: Date.now(),
|
|
892
|
-
|
|
885
|
+
reason,
|
|
886
|
+
...ctx
|
|
893
887
|
};
|
|
894
888
|
eventBatcher.add(event);
|
|
895
889
|
}
|
|
@@ -1034,7 +1028,8 @@ var tell = {
|
|
|
1034
1028
|
session_id: sessionManager.sessionId,
|
|
1035
1029
|
user_id: userId,
|
|
1036
1030
|
timestamp: Date.now(),
|
|
1037
|
-
|
|
1031
|
+
...superProperties,
|
|
1032
|
+
...properties
|
|
1038
1033
|
};
|
|
1039
1034
|
if (beforeSend) {
|
|
1040
1035
|
event = runBeforeSend(event, beforeSend);
|
|
@@ -1067,7 +1062,7 @@ var tell = {
|
|
|
1067
1062
|
session_id: sessionManager.sessionId,
|
|
1068
1063
|
user_id: userId,
|
|
1069
1064
|
timestamp: Date.now(),
|
|
1070
|
-
traits
|
|
1065
|
+
...traits
|
|
1071
1066
|
};
|
|
1072
1067
|
if (beforeSend) {
|
|
1073
1068
|
event = runBeforeSend(event, beforeSend);
|
|
@@ -1099,7 +1094,8 @@ var tell = {
|
|
|
1099
1094
|
user_id: userId,
|
|
1100
1095
|
group_id: groupId,
|
|
1101
1096
|
timestamp: Date.now(),
|
|
1102
|
-
|
|
1097
|
+
...superProperties,
|
|
1098
|
+
...properties
|
|
1103
1099
|
};
|
|
1104
1100
|
if (beforeSend) {
|
|
1105
1101
|
event = runBeforeSend(event, beforeSend);
|
|
@@ -1133,13 +1129,11 @@ var tell = {
|
|
|
1133
1129
|
session_id: sessionManager.sessionId,
|
|
1134
1130
|
user_id: userId,
|
|
1135
1131
|
timestamp: Date.now(),
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
currency
|
|
1142
|
-
}
|
|
1132
|
+
...superProperties,
|
|
1133
|
+
...properties,
|
|
1134
|
+
order_id: orderId,
|
|
1135
|
+
amount,
|
|
1136
|
+
currency
|
|
1143
1137
|
};
|
|
1144
1138
|
if (beforeSend) {
|
|
1145
1139
|
event = runBeforeSend(event, beforeSend);
|
|
@@ -1172,7 +1166,7 @@ var tell = {
|
|
|
1172
1166
|
session_id: sessionManager.sessionId,
|
|
1173
1167
|
user_id: newUserId,
|
|
1174
1168
|
timestamp: Date.now(),
|
|
1175
|
-
|
|
1169
|
+
previous_id: previousId
|
|
1176
1170
|
};
|
|
1177
1171
|
if (beforeSend) {
|
|
1178
1172
|
event = runBeforeSend(event, beforeSend);
|