@jitsu/js 1.9.11 → 1.9.13-canary.1167.20250215132227

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.
@@ -214,6 +214,33 @@ describe("Test Jitsu NodeJS client", () => {
214
214
  expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0);
215
215
  });
216
216
 
217
+ test("test defaultPayloadContext", async () => {
218
+ const config = {
219
+ host: server.baseUrl,
220
+ writeKey: "key:secret",
221
+ debug: true,
222
+ defaultPayloadContext: {
223
+ awesomeIdentifier: "awesome-identifier",
224
+ awesome: {
225
+ nestedKey: "awesome-key",
226
+ },
227
+ },
228
+ };
229
+ const client = jitsuAnalytics(config);
230
+ expect(requestLog.length).toBe(0);
231
+ await client.identify("myUserId", { email: "myUserId@example.com" });
232
+ await client.group("myGroupId", { name: "myGroupId" });
233
+ await client.track("myEvent", { prop1: "value1" });
234
+ await new Promise(resolve => setTimeout(resolve, 1000));
235
+ expect(requestLog.length).toBe(3);
236
+ expect(requestLog[0].body.context.awesomeIdentifier).toBe("awesome-identifier");
237
+ expect(requestLog[0].body.context.awesome.nestedKey).toBe("awesome-key");
238
+ expect(requestLog[1].body.context.awesomeIdentifier).toBe("awesome-identifier");
239
+ expect(requestLog[1].body.context.awesome.nestedKey).toBe("awesome-key");
240
+ expect(requestLog[2].body.context.awesomeIdentifier).toBe("awesome-identifier");
241
+ expect(requestLog[2].body.context.awesome.nestedKey).toBe("awesome-key");
242
+ });
243
+
217
244
  test("node js", async () => {
218
245
  const jitsu: AnalyticsInterface = jitsuAnalytics({
219
246
  writeKey: "key:secret",
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+
8
+ <title>Tracking page</title>
9
+ <script>
10
+ window.testOnload = async j => {
11
+ j.track("pageLoaded");
12
+ };
13
+ </script>
14
+ <script
15
+ type="text/javascript"
16
+ src="<%=trackingBase%>/p.js"
17
+ data-onload="testOnload"
18
+ data-debug="true"
19
+ defer
20
+ ></script>
21
+ </head>
22
+
23
+ <body>
24
+ <h1>Test</h1>
25
+ </body>
26
+ </html>
@@ -7,6 +7,11 @@
7
7
 
8
8
  <title>Tracking page</title>
9
9
  <script>
10
+ window.jitsuConfig = {
11
+ cookieCapture: {
12
+ ttp: "_ttp",
13
+ },
14
+ };
10
15
  window.testOnload = async j => {
11
16
  j.identify("john-doe-id-1", { email: "john.doe@gmail.com" });
12
17
  j.track("pageLoaded", { trackParam: "trackValue" });
@@ -450,9 +450,46 @@ test("disable-user-ids-then-consent", async ({ browser }) => {
450
450
  expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0);
451
451
  });
452
452
 
453
+ test("anonymous-id-bug", async ({ browser }) => {
454
+ clearRequestLog();
455
+ const anonymousId = "1724633695283.638279";
456
+ const browserContext = await browser.newContext();
457
+ await browserContext.addCookies([{ name: "__eventn_id", value: anonymousId, url: server.baseUrl }]);
458
+ const { page, uncaughtErrors } = await createLoggingPage(browserContext);
459
+ const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/anonymous-id-bug.html`)]);
460
+ await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
461
+ timeout: 1000,
462
+ polling: 100,
463
+ });
464
+ expect(pageResult.status()).toBe(200);
465
+ const cookies = (await browserContext.cookies()).reduce(
466
+ (res, cookie) => ({
467
+ ...res,
468
+ [cookie.name]: cookie.value,
469
+ }),
470
+ {}
471
+ );
472
+ console.log("🍪 Jitsu Cookies", cookies);
473
+ //wait for some time since the server has an artificial latency of 30ms
474
+ await new Promise(resolve => setTimeout(resolve, 1000));
475
+ expect(uncaughtErrors.length).toEqual(0);
476
+ console.log(
477
+ `📝 Request log size of ${requestLog.length}`,
478
+ requestLog.map(x => describeEvent(x.type, x.body))
479
+ );
480
+ const p = requestLog[0];
481
+ console.log(chalk.bold("📝 Checking page event"), JSON.stringify(p, null, 3));
482
+ expect(p.body.anonymousId).toEqual(anonymousId);
483
+ });
484
+
453
485
  test("basic", async ({ browser }) => {
454
486
  clearRequestLog();
455
487
  const browserContext = await browser.newContext();
488
+ await browserContext.addCookies([
489
+ { name: "_fbc", value: "fbc-id", url: server.baseUrl },
490
+ { name: "_fbp", value: "fbp-id", url: server.baseUrl },
491
+ { name: "_ttp", value: "ttp-id", url: server.baseUrl },
492
+ ]);
456
493
 
457
494
  const { page: firstPage, uncaughtErrors: firstPageErrors } = await createLoggingPage(browserContext);
458
495
  const [pageResult] = await Promise.all([
@@ -498,6 +535,9 @@ test("basic", async ({ browser }) => {
498
535
  console.log(chalk.bold("📝 Checking track event"), JSON.stringify(track, null, 3));
499
536
  expect(track.properties.trackParam).toEqual("trackValue");
500
537
  expect(track.type).toEqual("track");
538
+ expect(track.context.clientIds).toHaveProperty("fbc", "fbc-id");
539
+ expect(track.context.clientIds).toHaveProperty("fbp", "fbp-id");
540
+ expect(track.context.clientIds).toHaveProperty("ttp", "ttp-id");
501
541
  expect(track.context.traits.email).toEqual("john.doe@gmail.com");
502
542
  expect(track.userId).toEqual("john-doe-id-1");
503
543
  expect(track.event).toEqual("pageLoaded");
@@ -509,6 +549,9 @@ test("basic", async ({ browser }) => {
509
549
 
510
550
  console.log(chalk.bold("📝 Checking page event"), JSON.stringify(page, null, 3));
511
551
  expect(page.anonymousId).toEqual(anonymousId);
552
+ expect(page.context.clientIds).toHaveProperty("fbc", "fbc-id");
553
+ expect(page.context.clientIds).toHaveProperty("fbp", "fbp-id");
554
+ expect(page.context.clientIds).toHaveProperty("ttp", "ttp-id");
512
555
  expect(page.context.traits.email).toEqual("john.doe@gmail.com");
513
556
  expect(page.userId).toEqual("john-doe-id-1");
514
557
 
package/dist/jitsu.cjs.js CHANGED
@@ -1061,11 +1061,13 @@ const defaultConfig = {
1061
1061
  fetch: null,
1062
1062
  echoEvents: false,
1063
1063
  cookieDomain: undefined,
1064
+ cookieCapture: {},
1064
1065
  runtime: undefined,
1065
1066
  fetchTimeoutMs: undefined,
1066
1067
  s2s: undefined,
1067
1068
  idEndpoint: undefined,
1068
1069
  errorPolicy: "log",
1070
+ defaultPayloadContext: {},
1069
1071
  privacy: {
1070
1072
  dontSend: false,
1071
1073
  disableUserIds: false,
@@ -1150,6 +1152,14 @@ function getCookie(name) {
1150
1152
  const parts = value.split(`; ${name}=`);
1151
1153
  return parts.length === 2 ? parts.pop().split(";").shift() : undefined;
1152
1154
  }
1155
+ function getClientIds(runtime, customCookieCapture) {
1156
+ const cookieCapture = Object.assign({ fbc: "_fbc", fbp: "_fbp" }, customCookieCapture);
1157
+ const clientIds = Object.entries(cookieCapture).reduce((acc, [key, cookieName]) => {
1158
+ acc[key] = runtime.getCookie(cookieName);
1159
+ return acc;
1160
+ }, {});
1161
+ return Object.assign(Object.assign({}, clientIds), getGa4Ids(runtime));
1162
+ }
1153
1163
  function getGa4Ids(runtime) {
1154
1164
  var _a;
1155
1165
  const allCookies = runtime.getCookies();
@@ -1227,6 +1237,10 @@ const cookieStorage = (cookieDomain, key2cookie) => {
1227
1237
  getItem(key) {
1228
1238
  const cookieName = key2cookie[key] || key;
1229
1239
  const result = getCookie(cookieName);
1240
+ if (key === "__anon_id") {
1241
+ //anonymous id must always be a string, so we don't parse it to preserve its exact value
1242
+ return result;
1243
+ }
1230
1244
  if (typeof result === "undefined" && key === "__user_id") {
1231
1245
  //backward compatibility with old jitsu cookie. get user id if from traits
1232
1246
  const traits = parse(getCookie("__eventn_id_usr")) || {};
@@ -1413,7 +1427,7 @@ function adjustPayload(payload, config, storage, s2s) {
1413
1427
  properties.url = targetUrl.replace(hashRegex, "");
1414
1428
  properties.path = fixPath(urlPath(targetUrl));
1415
1429
  }
1416
- const customContext = ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || ((_b = payload.options) === null || _b === void 0 ? void 0 : _b.context) || {};
1430
+ const customContext = deepMerge(config.defaultPayloadContext, ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || ((_b = payload.options) === null || _b === void 0 ? void 0 : _b.context) || {});
1417
1431
  (_c = payload.properties) === null || _c === void 0 ? true : delete _c.context;
1418
1432
  const referrer = runtime.referrer();
1419
1433
  const context = {
@@ -1441,8 +1455,7 @@ function adjustPayload(payload, config, storage, s2s) {
1441
1455
  url: properties.url || url,
1442
1456
  encoding: properties.encoding || runtime.documentEncoding(),
1443
1457
  },
1444
- clientIds: !((_e = config.privacy) === null || _e === void 0 ? void 0 : _e.disableUserIds)
1445
- ? Object.assign({ fbc: runtime.getCookie("_fbc"), fbp: runtime.getCookie("_fbp") }, getGa4Ids(runtime)) : undefined,
1458
+ clientIds: !((_e = config.privacy) === null || _e === void 0 ? void 0 : _e.disableUserIds) ? getClientIds(runtime, config.cookieCapture) : undefined,
1446
1459
  campaign: parseUtms(query),
1447
1460
  };
1448
1461
  const withContext = Object.assign(Object.assign({}, payload), { timestamp: new Date().toISOString(), sentAt: new Date().toISOString(), messageId: randomId(properties.path || (parsedUrl && parsedUrl.pathname)), writeKey: maskWriteKey(config.writeKey), groupId: storage.getItem("__group_id"), context: deepMerge(context, customContext) });
@@ -1630,7 +1643,8 @@ function send(method, payload, jitsuConfig, instance, store) {
1630
1643
  console.log(`[JITSU DEBUG] ${url} replied ${fetchResult.status}: ${responseText}. Original payload:\n${JSON.stringify(adjustedPayload, null, 2)}`);
1631
1644
  }
1632
1645
  if (!fetchResult.ok) {
1633
- throw new Error(`Jitsu ${url} replied ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
1646
+ getErrorHandler(jitsuConfig)(`Call to ${url} failed with error: ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
1647
+ return Promise.resolve();
1634
1648
  }
1635
1649
  let responseJson;
1636
1650
  try {
package/dist/jitsu.es.js CHANGED
@@ -1059,11 +1059,13 @@ const defaultConfig = {
1059
1059
  fetch: null,
1060
1060
  echoEvents: false,
1061
1061
  cookieDomain: undefined,
1062
+ cookieCapture: {},
1062
1063
  runtime: undefined,
1063
1064
  fetchTimeoutMs: undefined,
1064
1065
  s2s: undefined,
1065
1066
  idEndpoint: undefined,
1066
1067
  errorPolicy: "log",
1068
+ defaultPayloadContext: {},
1067
1069
  privacy: {
1068
1070
  dontSend: false,
1069
1071
  disableUserIds: false,
@@ -1148,6 +1150,14 @@ function getCookie(name) {
1148
1150
  const parts = value.split(`; ${name}=`);
1149
1151
  return parts.length === 2 ? parts.pop().split(";").shift() : undefined;
1150
1152
  }
1153
+ function getClientIds(runtime, customCookieCapture) {
1154
+ const cookieCapture = Object.assign({ fbc: "_fbc", fbp: "_fbp" }, customCookieCapture);
1155
+ const clientIds = Object.entries(cookieCapture).reduce((acc, [key, cookieName]) => {
1156
+ acc[key] = runtime.getCookie(cookieName);
1157
+ return acc;
1158
+ }, {});
1159
+ return Object.assign(Object.assign({}, clientIds), getGa4Ids(runtime));
1160
+ }
1151
1161
  function getGa4Ids(runtime) {
1152
1162
  var _a;
1153
1163
  const allCookies = runtime.getCookies();
@@ -1225,6 +1235,10 @@ const cookieStorage = (cookieDomain, key2cookie) => {
1225
1235
  getItem(key) {
1226
1236
  const cookieName = key2cookie[key] || key;
1227
1237
  const result = getCookie(cookieName);
1238
+ if (key === "__anon_id") {
1239
+ //anonymous id must always be a string, so we don't parse it to preserve its exact value
1240
+ return result;
1241
+ }
1228
1242
  if (typeof result === "undefined" && key === "__user_id") {
1229
1243
  //backward compatibility with old jitsu cookie. get user id if from traits
1230
1244
  const traits = parse(getCookie("__eventn_id_usr")) || {};
@@ -1411,7 +1425,7 @@ function adjustPayload(payload, config, storage, s2s) {
1411
1425
  properties.url = targetUrl.replace(hashRegex, "");
1412
1426
  properties.path = fixPath(urlPath(targetUrl));
1413
1427
  }
1414
- const customContext = ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || ((_b = payload.options) === null || _b === void 0 ? void 0 : _b.context) || {};
1428
+ const customContext = deepMerge(config.defaultPayloadContext, ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || ((_b = payload.options) === null || _b === void 0 ? void 0 : _b.context) || {});
1415
1429
  (_c = payload.properties) === null || _c === void 0 ? true : delete _c.context;
1416
1430
  const referrer = runtime.referrer();
1417
1431
  const context = {
@@ -1439,8 +1453,7 @@ function adjustPayload(payload, config, storage, s2s) {
1439
1453
  url: properties.url || url,
1440
1454
  encoding: properties.encoding || runtime.documentEncoding(),
1441
1455
  },
1442
- clientIds: !((_e = config.privacy) === null || _e === void 0 ? void 0 : _e.disableUserIds)
1443
- ? Object.assign({ fbc: runtime.getCookie("_fbc"), fbp: runtime.getCookie("_fbp") }, getGa4Ids(runtime)) : undefined,
1456
+ clientIds: !((_e = config.privacy) === null || _e === void 0 ? void 0 : _e.disableUserIds) ? getClientIds(runtime, config.cookieCapture) : undefined,
1444
1457
  campaign: parseUtms(query),
1445
1458
  };
1446
1459
  const withContext = Object.assign(Object.assign({}, payload), { timestamp: new Date().toISOString(), sentAt: new Date().toISOString(), messageId: randomId(properties.path || (parsedUrl && parsedUrl.pathname)), writeKey: maskWriteKey(config.writeKey), groupId: storage.getItem("__group_id"), context: deepMerge(context, customContext) });
@@ -1628,7 +1641,8 @@ function send(method, payload, jitsuConfig, instance, store) {
1628
1641
  console.log(`[JITSU DEBUG] ${url} replied ${fetchResult.status}: ${responseText}. Original payload:\n${JSON.stringify(adjustedPayload, null, 2)}`);
1629
1642
  }
1630
1643
  if (!fetchResult.ok) {
1631
- throw new Error(`Jitsu ${url} replied ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
1644
+ getErrorHandler(jitsuConfig)(`Call to ${url} failed with error: ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
1645
+ return Promise.resolve();
1632
1646
  }
1633
1647
  let responseJson;
1634
1648
  try {
package/dist/web/p.js.txt CHANGED
@@ -1062,11 +1062,13 @@
1062
1062
  fetch: null,
1063
1063
  echoEvents: false,
1064
1064
  cookieDomain: undefined,
1065
+ cookieCapture: {},
1065
1066
  runtime: undefined,
1066
1067
  fetchTimeoutMs: undefined,
1067
1068
  s2s: undefined,
1068
1069
  idEndpoint: undefined,
1069
1070
  errorPolicy: "log",
1071
+ defaultPayloadContext: {},
1070
1072
  privacy: {
1071
1073
  dontSend: false,
1072
1074
  disableUserIds: false,
@@ -1151,6 +1153,14 @@
1151
1153
  const parts = value.split(`; ${name}=`);
1152
1154
  return parts.length === 2 ? parts.pop().split(";").shift() : undefined;
1153
1155
  }
1156
+ function getClientIds(runtime, customCookieCapture) {
1157
+ const cookieCapture = Object.assign({ fbc: "_fbc", fbp: "_fbp" }, customCookieCapture);
1158
+ const clientIds = Object.entries(cookieCapture).reduce((acc, [key, cookieName]) => {
1159
+ acc[key] = runtime.getCookie(cookieName);
1160
+ return acc;
1161
+ }, {});
1162
+ return Object.assign(Object.assign({}, clientIds), getGa4Ids(runtime));
1163
+ }
1154
1164
  function getGa4Ids(runtime) {
1155
1165
  var _a;
1156
1166
  const allCookies = runtime.getCookies();
@@ -1228,6 +1238,10 @@
1228
1238
  getItem(key) {
1229
1239
  const cookieName = key2cookie[key] || key;
1230
1240
  const result = getCookie(cookieName);
1241
+ if (key === "__anon_id") {
1242
+ //anonymous id must always be a string, so we don't parse it to preserve its exact value
1243
+ return result;
1244
+ }
1231
1245
  if (typeof result === "undefined" && key === "__user_id") {
1232
1246
  //backward compatibility with old jitsu cookie. get user id if from traits
1233
1247
  const traits = parse(getCookie("__eventn_id_usr")) || {};
@@ -1414,7 +1428,7 @@
1414
1428
  properties.url = targetUrl.replace(hashRegex, "");
1415
1429
  properties.path = fixPath(urlPath(targetUrl));
1416
1430
  }
1417
- const customContext = ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || ((_b = payload.options) === null || _b === void 0 ? void 0 : _b.context) || {};
1431
+ const customContext = deepMerge(config.defaultPayloadContext, ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || ((_b = payload.options) === null || _b === void 0 ? void 0 : _b.context) || {});
1418
1432
  (_c = payload.properties) === null || _c === void 0 ? true : delete _c.context;
1419
1433
  const referrer = runtime.referrer();
1420
1434
  const context = {
@@ -1442,8 +1456,7 @@
1442
1456
  url: properties.url || url,
1443
1457
  encoding: properties.encoding || runtime.documentEncoding(),
1444
1458
  },
1445
- clientIds: !((_e = config.privacy) === null || _e === void 0 ? void 0 : _e.disableUserIds)
1446
- ? Object.assign({ fbc: runtime.getCookie("_fbc"), fbp: runtime.getCookie("_fbp") }, getGa4Ids(runtime)) : undefined,
1459
+ clientIds: !((_e = config.privacy) === null || _e === void 0 ? void 0 : _e.disableUserIds) ? getClientIds(runtime, config.cookieCapture) : undefined,
1447
1460
  campaign: parseUtms(query),
1448
1461
  };
1449
1462
  const withContext = Object.assign(Object.assign({}, payload), { timestamp: new Date().toISOString(), sentAt: new Date().toISOString(), messageId: randomId(properties.path || (parsedUrl && parsedUrl.pathname)), writeKey: maskWriteKey(config.writeKey), groupId: storage.getItem("__group_id"), context: deepMerge(context, customContext) });
@@ -1631,7 +1644,8 @@
1631
1644
  console.log(`[JITSU DEBUG] ${url} replied ${fetchResult.status}: ${responseText}. Original payload:\n${JSON.stringify(adjustedPayload, null, 2)}`);
1632
1645
  }
1633
1646
  if (!fetchResult.ok) {
1634
- throw new Error(`Jitsu ${url} replied ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
1647
+ getErrorHandler(jitsuConfig)(`Call to ${url} failed with error: ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
1648
+ return Promise.resolve();
1635
1649
  }
1636
1650
  let responseJson;
1637
1651
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jitsu/js",
3
- "version": "1.9.11",
3
+ "version": "1.9.13-canary.1167.20250215132227",
4
4
  "description": "",
5
5
  "author": "Jitsu Dev Team <dev@jitsu.com>",
6
6
  "main": "dist/jitsu.cjs.js",
@@ -14,10 +14,10 @@
14
14
  "devDependencies": {
15
15
  "tslib": "^2.6.3",
16
16
  "@playwright/test": "1.39.0",
17
- "@rollup/plugin-commonjs": "^23.0.2",
17
+ "@rollup/plugin-commonjs": "^28.0.2",
18
18
  "@rollup/plugin-json": "^5.0.1",
19
19
  "@rollup/plugin-multi-entry": "^6.0.0",
20
- "@rollup/plugin-node-resolve": "^15.0.1",
20
+ "@rollup/plugin-node-resolve": "^16.0.0",
21
21
  "@rollup/plugin-replace": "^5.0.1",
22
22
  "@rollup/plugin-terser": "^0.1.0",
23
23
  "@segment/analytics-next": "^1.75.0",
@@ -27,7 +27,7 @@
27
27
  "chalk": "^4.1.2",
28
28
  "cookie-parser": "^1.4.7",
29
29
  "ejs": "^3.1.8",
30
- "express": "^4.21.1",
30
+ "express": "^4.21.2",
31
31
  "jest": "^29.2.2",
32
32
  "node-fetch-commonjs": "^3.3.2",
33
33
  "node-forge": "^1.3.1",
@@ -35,8 +35,8 @@
35
35
  "rollup": "^3.29.5",
36
36
  "ts-jest": "29.0.5",
37
37
  "typescript": "^5.6.3",
38
- "@jitsu/protocols": "1.9.11",
39
- "jsondiffpatch": "1.9.11"
38
+ "@jitsu/protocols": "1.9.13-canary.1167.20250215132227",
39
+ "jsondiffpatch": "1.9.13-canary.1167.20250215132227"
40
40
  },
41
41
  "dependencies": {
42
42
  "analytics": "0.8.9"
@@ -33,11 +33,13 @@ const defaultConfig: Required<JitsuOptions> = {
33
33
  fetch: null,
34
34
  echoEvents: false,
35
35
  cookieDomain: undefined,
36
+ cookieCapture: {},
36
37
  runtime: undefined,
37
38
  fetchTimeoutMs: undefined,
38
39
  s2s: undefined,
39
40
  idEndpoint: undefined,
40
41
  errorPolicy: "log",
42
+ defaultPayloadContext: {},
41
43
  privacy: {
42
44
  dontSend: false,
43
45
  disableUserIds: false,
@@ -141,6 +143,22 @@ function getCookie(name: string) {
141
143
  return parts.length === 2 ? parts.pop().split(";").shift() : undefined;
142
144
  }
143
145
 
146
+ function getClientIds(runtime: RuntimeFacade, customCookieCapture: Record<string, string>) {
147
+ const cookieCapture = {
148
+ fbc: "_fbc",
149
+ fbp: "_fbp",
150
+ ...customCookieCapture,
151
+ };
152
+ const clientIds = Object.entries(cookieCapture).reduce((acc, [key, cookieName]) => {
153
+ acc[key] = runtime.getCookie(cookieName);
154
+ return acc;
155
+ }, {});
156
+ return {
157
+ ...clientIds,
158
+ ...getGa4Ids(runtime),
159
+ };
160
+ }
161
+
144
162
  function getGa4Ids(runtime: RuntimeFacade) {
145
163
  const allCookies = runtime.getCookies();
146
164
  const clientId = allCookies["_ga"]?.split(".").slice(-2).join(".");
@@ -223,6 +241,10 @@ const cookieStorage: StorageFactory = (cookieDomain, key2cookie) => {
223
241
  getItem(key: string) {
224
242
  const cookieName = key2cookie[key] || key;
225
243
  const result = getCookie(cookieName);
244
+ if (key === "__anon_id") {
245
+ //anonymous id must always be a string, so we don't parse it to preserve its exact value
246
+ return result;
247
+ }
226
248
  if (typeof result === "undefined" && key === "__user_id") {
227
249
  //backward compatibility with old jitsu cookie. get user id if from traits
228
250
  const traits = parse(getCookie("__eventn_id_usr")) || {};
@@ -424,7 +446,10 @@ function adjustPayload(
424
446
  properties.path = fixPath(urlPath(targetUrl));
425
447
  }
426
448
 
427
- const customContext = payload.properties?.context || payload.options?.context || {};
449
+ const customContext = deepMerge(
450
+ config.defaultPayloadContext,
451
+ payload.properties?.context || payload.options?.context || {}
452
+ );
428
453
  delete payload.properties?.context;
429
454
  const referrer = runtime.referrer();
430
455
  const context: AnalyticsClientEvent["context"] = {
@@ -452,13 +477,7 @@ function adjustPayload(
452
477
  url: properties.url || url,
453
478
  encoding: properties.encoding || runtime.documentEncoding(),
454
479
  },
455
- clientIds: !config.privacy?.disableUserIds
456
- ? {
457
- fbc: runtime.getCookie("_fbc"),
458
- fbp: runtime.getCookie("_fbp"),
459
- ...getGa4Ids(runtime),
460
- }
461
- : undefined,
480
+ clientIds: !config.privacy?.disableUserIds ? getClientIds(runtime, config.cookieCapture) : undefined,
462
481
  campaign: parseUtms(query),
463
482
  };
464
483
  const withContext = {
@@ -723,7 +742,10 @@ async function send(
723
742
  );
724
743
  }
725
744
  if (!fetchResult.ok) {
726
- throw new Error(`Jitsu ${url} replied ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`);
745
+ getErrorHandler(jitsuConfig)(
746
+ `Call to ${url} failed with error: ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}`
747
+ );
748
+ return Promise.resolve();
727
749
  }
728
750
 
729
751
  let responseJson: any;