@jitsu/js 1.9.3 → 1.9.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.
@@ -0,0 +1,31 @@
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
+ await j.setAnonymousId("john-doe-id-1");
12
+ await j.identify("john-nondoe", { email: "john@example.com" });
13
+ await j.track("pageLoaded", { trackParam: "trackValue" });
14
+ await j.reset();
15
+ await j.track("pageLoaded", { trackParam: "trackValue" });
16
+ };
17
+ </script>
18
+ <script
19
+ type="text/javascript"
20
+ src="<%=trackingBase%>/p.js"
21
+ data-onload="testOnload"
22
+ data-debug="true"
23
+ data-init-only="true"
24
+ defer
25
+ ></script>
26
+ </head>
27
+
28
+ <body>
29
+ <h1>Test</h1>
30
+ </body>
31
+ </html>
@@ -214,8 +214,8 @@ test("jitsu-queue-callbacks", async ({ browser }) => {
214
214
  expect(requestLog.length).toBe(3);
215
215
  });
216
216
 
217
- test("url-bug", async ({ browser }) => {
218
- clearRequestLog();
217
+ // Skip this test because jitsu-js no longer relies on canonical URL
218
+ test.skip("url-bug", async ({ browser, context }) => {
219
219
  //tests a bug in getanalytics.io where the url without slash provided by
220
220
  //<link rel="canonical" ../> causes incorrect page path
221
221
  const browserContext = await browser.newContext();
@@ -244,6 +244,41 @@ test("url-bug", async ({ browser }) => {
244
244
  expect(pagePath).toEqual("/");
245
245
  });
246
246
 
247
+ test("reset", async ({ browser }) => {
248
+ clearRequestLog();
249
+ const browserContext = await browser.newContext();
250
+ const { page, uncaughtErrors } = await createLoggingPage(browserContext);
251
+ const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/reset.html`)]);
252
+ await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
253
+ timeout: 1000,
254
+ polling: 100,
255
+ });
256
+ expect(pageResult.status()).toBe(200);
257
+ //wait for some time since the server has an artificial latency of 30ms
258
+ await new Promise(resolve => setTimeout(resolve, 1000));
259
+ expect(uncaughtErrors.length).toEqual(0);
260
+ expect(requestLog.length).toBe(3);
261
+ console.log(
262
+ `📝 Request log size of ${requestLog.length}`,
263
+ requestLog.map(x => describeEvent(x.type, x.body))
264
+ );
265
+ const [identifyEvent, firstTrack, secondTrack] = requestLog;
266
+ expect(firstTrack.body.anonymousId).toEqual("john-doe-id-1");
267
+
268
+ const cookies = await browserContext.cookies();
269
+ // all cookies should be cleared by .reset()
270
+ // but new cookie for new anonymousId should be set
271
+ expect(cookies.length).toBe(1);
272
+ expect(cookies[0].name).toEqual("__eventn_id");
273
+ const newAnonymousId = cookies[0].value;
274
+ console.log(`🍪Cookies`, cookies);
275
+
276
+ expect(secondTrack.body.anonymousId).not.toBeNull();
277
+ expect(secondTrack.body.anonymousId).toBeDefined();
278
+ expect(secondTrack.body.anonymousId).toEqual(newAnonymousId);
279
+ expect(secondTrack.body.anonymousId).not.toEqual("john-doe-id-1");
280
+ });
281
+
247
282
  test("basic", async ({ browser }) => {
248
283
  clearRequestLog();
249
284
  const browserContext = await browser.newContext();
@@ -24,10 +24,7 @@ export type InternalPluginDescriptor = {
24
24
  };
25
25
  export type DeviceOptions = AnalyticsPluginDescriptor | InternalPluginDescriptor;
26
26
  export type JitsuPluginConfig = JitsuOptions & {
27
- storageWrapper?: (persistentStorage: PersistentStorage) => PersistentStorage & {
28
- reset: () => void;
29
- };
27
+ storageWrapper?: (persistentStorage: PersistentStorage) => PersistentStorage;
30
28
  };
31
- declare const jitsuAnalyticsPlugin: (pluginConfig?: JitsuPluginConfig) => AnalyticsPlugin;
29
+ export declare const jitsuAnalyticsPlugin: (pluginConfig?: JitsuPluginConfig) => AnalyticsPlugin;
32
30
  export declare function randomId(hashString?: string | undefined): string;
33
- export default jitsuAnalyticsPlugin;
package/dist/jitsu.cjs.js CHANGED
@@ -71,7 +71,23 @@ function findScript(src) {
71
71
  const scripts = Array.prototype.slice.call(window.document.querySelectorAll("script"));
72
72
  return scripts.find(s => s.src === src);
73
73
  }
74
- function loadScript(src, attributes) {
74
+ function buildScriptSrc(src, options) {
75
+ let result = src;
76
+ if (!result.startsWith("http")) {
77
+ result = `https://${(options === null || options === void 0 ? void 0 : options.www) ? "www." : ""}${result}`;
78
+ }
79
+ if (options === null || options === void 0 ? void 0 : options.min) {
80
+ result = result + ".min.js";
81
+ }
82
+ else if (options === null || options === void 0 ? void 0 : options.js) {
83
+ result = result + ".js";
84
+ }
85
+ if (options === null || options === void 0 ? void 0 : options.query) {
86
+ result += "?" + options.query;
87
+ }
88
+ return result;
89
+ }
90
+ function loadScript(src, options) {
75
91
  const found = findScript(src);
76
92
  if (found !== undefined) {
77
93
  const status = found === null || found === void 0 ? void 0 : found.getAttribute("status");
@@ -86,13 +102,13 @@ function loadScript(src, attributes) {
86
102
  }
87
103
  }
88
104
  return new Promise((resolve, reject) => {
89
- var _a;
105
+ var _a, _b;
90
106
  const script = window.document.createElement("script");
91
107
  script.type = "text/javascript";
92
- script.src = src;
108
+ script.src = buildScriptSrc(src, options);
93
109
  script.async = true;
94
110
  script.setAttribute("status", "loading");
95
- for (const [k, v] of Object.entries(attributes !== null && attributes !== void 0 ? attributes : {})) {
111
+ for (const [k, v] of Object.entries((_a = options === null || options === void 0 ? void 0 : options.attributes) !== null && _a !== void 0 ? _a : {})) {
96
112
  script.setAttribute(k, v);
97
113
  }
98
114
  script.onload = () => {
@@ -106,7 +122,7 @@ function loadScript(src, attributes) {
106
122
  reject(new Error(`Failed to load ${src}`));
107
123
  };
108
124
  const tag = window.document.getElementsByTagName("script")[0];
109
- (_a = tag.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(script, tag);
125
+ (_b = tag.parentElement) === null || _b === void 0 ? void 0 : _b.insertBefore(script, tag);
110
126
  });
111
127
  }
112
128
 
@@ -206,6 +222,7 @@ var __awaiter$4 = (undefined && undefined.__awaiter) || function (thisArg, _argu
206
222
  step((generator = generator.apply(thisArg, _arguments || [])).next());
207
223
  });
208
224
  };
225
+ const cdn = "cdn.lr-ingest.io/";
209
226
  const logrocketPlugin = {
210
227
  id: "logrocket",
211
228
  handle(config, payload) {
@@ -256,7 +273,7 @@ function initLogrocketIfNeeded(appId) {
256
273
  return;
257
274
  }
258
275
  setLogRocketState("loading");
259
- loadScript(`https://cdn.lr-ingest.io/LogRocket.min.js`, { crossOrigin: "anonymous" })
276
+ loadScript(`${cdn}LogRocket`, { min: true, attributes: { crossOrigin: "anonymous" } })
260
277
  .then(() => {
261
278
  if (window["LogRocket"]) {
262
279
  try {
@@ -379,8 +396,8 @@ function initGtmIfNeeded(config, payload) {
379
396
  event: "gtm.js",
380
397
  });
381
398
  const dl = l != "dataLayer" ? "&l=" + l : "";
382
- const scriptSrc = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
383
- loadScript(scriptSrc)
399
+ const scriptSrc = "googletagmanager.com/gtm";
400
+ loadScript(scriptSrc, { www: true, js: true, query: "id=" + i + dl })
384
401
  .then(() => {
385
402
  setGtmState("loaded");
386
403
  })
@@ -401,7 +418,7 @@ var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _argu
401
418
  step((generator = generator.apply(thisArg, _arguments || [])).next());
402
419
  });
403
420
  };
404
- const defaultScriptSrc = "https://www.googletagmanager.com/gtag/js";
421
+ const defaultScriptSrc = "googletagmanager.com/gtag/js";
405
422
  const ga4Plugin = {
406
423
  id: "ga4-tag",
407
424
  handle(config, payload) {
@@ -463,7 +480,6 @@ function initGa4IfNeeded(config, payload) {
463
480
  const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : "";
464
481
  // to work with both GA4 and GTM
465
482
  const tagId = config.measurementIds;
466
- const scriptSrc = `${defaultScriptSrc}?id=${tagId}${dlParam}`;
467
483
  window[dlName] = window[dlName] || [];
468
484
  const gtag = function () {
469
485
  window[dlName].push(arguments);
@@ -473,7 +489,7 @@ function initGa4IfNeeded(config, payload) {
473
489
  gtag(
474
490
  // @ts-ignore
475
491
  "config", tagId, Object.assign(Object.assign({}, (payload.userId ? { user_id: payload.userId } : {})), (!config.autoPageView ? { send_page_view: false } : {})));
476
- loadScript(scriptSrc)
492
+ loadScript(defaultScriptSrc, { query: `id=${tagId}${dlParam}`, www: true })
477
493
  .then(() => {
478
494
  setGa4State("loaded");
479
495
  })
@@ -1112,8 +1128,15 @@ function getGa4Ids(runtime) {
1112
1128
  return undefined;
1113
1129
  }
1114
1130
  }
1115
- function removeCookie(name) {
1116
- document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
1131
+ function removeCookie(name, { domain, secure }) {
1132
+ document.cookie =
1133
+ name +
1134
+ "=;domain=" +
1135
+ domain +
1136
+ ";path=/" +
1137
+ ";expires=Thu, 01 Jan 1970 00:00:01 GMT;SameSite=" +
1138
+ (secure ? "None" : "Lax") +
1139
+ (secure ? ";secure" : "");
1117
1140
  }
1118
1141
  function setCookie(name, val, { domain, secure }) {
1119
1142
  document.cookie =
@@ -1155,7 +1178,18 @@ const cookieStorage = (cookieDomain, key2cookie) => {
1155
1178
  return parse(result);
1156
1179
  },
1157
1180
  removeItem(key) {
1158
- removeCookie(key2cookie[key] || key);
1181
+ removeCookie(key2cookie[key] || key, {
1182
+ domain: cookieDomain,
1183
+ secure: window.location.protocol === "https:",
1184
+ });
1185
+ },
1186
+ reset() {
1187
+ for (const v of Object.values(key2cookie)) {
1188
+ removeCookie(v, {
1189
+ domain: cookieDomain,
1190
+ secure: window.location.protocol === "https:",
1191
+ });
1192
+ }
1159
1193
  },
1160
1194
  };
1161
1195
  };
@@ -1226,6 +1260,9 @@ const emptyRuntime = (config) => ({
1226
1260
  store() {
1227
1261
  const storage = {};
1228
1262
  return {
1263
+ reset() {
1264
+ Object.keys(storage).forEach(key => delete storage[key]);
1265
+ },
1229
1266
  setItem(key, val) {
1230
1267
  if (config.debug) {
1231
1268
  console.log(`[JITSU EMPTY RUNTIME] Set storage item ${key}=${JSON.stringify(val)}`);
@@ -1291,6 +1328,17 @@ function fixPath(path) {
1291
1328
  }
1292
1329
  return path;
1293
1330
  }
1331
+ const hashRegex = /#.*$/;
1332
+ /**
1333
+ * for compatibility with path produced by analytics.js
1334
+ * @param url
1335
+ */
1336
+ function urlPath(url) {
1337
+ const regex = /(http[s]?:\/\/)?([^\/\s]+\/)(.*)/g;
1338
+ const matches = regex.exec(url);
1339
+ const pathMatch = matches && matches[3] ? matches[3].split("?")[0].replace(hashRegex, "") : "";
1340
+ return "/" + pathMatch;
1341
+ }
1294
1342
  function adjustPayload(payload, config, storage, s2s) {
1295
1343
  var _a, _b;
1296
1344
  const runtime = config.runtime || (isInBrowser() ? windowRuntime(config) : emptyRuntime(config));
@@ -1298,8 +1346,9 @@ function adjustPayload(payload, config, storage, s2s) {
1298
1346
  const parsedUrl = safeCall(() => new URL(url), undefined);
1299
1347
  const query = parsedUrl ? parseQuery(parsedUrl.search) : {};
1300
1348
  const properties = payload.properties || {};
1301
- if (properties.path) {
1302
- properties.path = fixPath(properties.path);
1349
+ if (payload.type === "page" && url) {
1350
+ properties.url = url.replace(hashRegex, "");
1351
+ properties.path = fixPath(urlPath(url));
1303
1352
  }
1304
1353
  const customContext = ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || {};
1305
1354
  (_b = payload.properties) === null || _b === void 0 ? true : delete _b.context;
@@ -1497,10 +1546,13 @@ function send(method, payload, jitsuConfig, instance, store) {
1497
1546
  console.warn(`[JITSU] ${payload.type} responded with list of ${responseJson.destinations.length} destinations. However, this code is running in server-to-server mode, so destinations will be ignored`, jitsuConfig.debug ? JSON.stringify(responseJson.destinations, null, 2) : undefined);
1498
1547
  }
1499
1548
  else {
1500
- if (jitsuConfig.debug) {
1501
- console.log(`[JITSU] Processing device destinations: `, JSON.stringify(responseJson.destinations, null, 2));
1549
+ //double protection, ingest should not return destinations in s2s mode
1550
+ if (isInBrowser()) {
1551
+ if (jitsuConfig.debug) {
1552
+ console.log(`[JITSU] Processing device destinations: `, JSON.stringify(responseJson.destinations, null, 2));
1553
+ }
1554
+ return processDestinations(responseJson.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
1502
1555
  }
1503
- return processDestinations(responseJson.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
1504
1556
  }
1505
1557
  }
1506
1558
  return adjustedPayload;
@@ -1540,9 +1592,11 @@ const jitsuAnalyticsPlugin = (pluginConfig = {}) => {
1540
1592
  return send("identify", payload, config, instance, storage);
1541
1593
  },
1542
1594
  reset: args => {
1543
- //clear storage cache
1544
- if (pluginConfig.storageWrapper) {
1545
- pluginConfig.storageWrapper(args.instance.storage).reset();
1595
+ const { config, instance } = args;
1596
+ const storage = pluginConfig.storageWrapper ? pluginConfig.storageWrapper(instance.storage) : instance.storage;
1597
+ storage === null || storage === void 0 ? void 0 : storage.reset();
1598
+ if (config.debug) {
1599
+ console.log("[JITSU DEBUG] Resetting Jitsu plugin storage");
1546
1600
  }
1547
1601
  },
1548
1602
  methods: {
@@ -1654,9 +1708,9 @@ function createUnderlyingAnalyticsInstance(opts, rt, plugins = []) {
1654
1708
  },
1655
1709
  reset() {
1656
1710
  for (const key of [...Object.keys(storageCache)]) {
1657
- storage.removeItem(key);
1658
1711
  delete storageCache[key];
1659
1712
  }
1713
+ storage.reset();
1660
1714
  },
1661
1715
  removeItem(key) {
1662
1716
  if (opts.debug) {
@@ -1713,7 +1767,20 @@ function createUnderlyingAnalyticsInstance(opts, rt, plugins = []) {
1713
1767
  userState.anonymousId = id;
1714
1768
  }
1715
1769
  analytics.setAnonymousId(id);
1716
- }, group(groupId, traits, options, callback) {
1770
+ }, reset() {
1771
+ return __awaiter(this, void 0, void 0, function* () {
1772
+ if (opts.debug) {
1773
+ console.log("[JITSU DEBUG] Called reset(). Storage state", JSON.stringify(analytics.user()));
1774
+ }
1775
+ storage.reset();
1776
+ yield analytics.reset();
1777
+ this.setAnonymousId(uuid());
1778
+ if (opts.debug) {
1779
+ console.log("[JITSU DEBUG] User state after reset", JSON.stringify(analytics.user()));
1780
+ }
1781
+ });
1782
+ },
1783
+ group(groupId, traits, options, callback) {
1717
1784
  return __awaiter(this, void 0, void 0, function* () {
1718
1785
  const results = [];
1719
1786
  for (const plugin of Object.values(analytics.plugins)) {
@@ -1762,11 +1829,21 @@ function jitsuAnalytics(_opts) {
1762
1829
  // result.loaded(createUnderlyingAnalyticsInstance(opts, rt));
1763
1830
  // }
1764
1831
  }
1832
+ function uuid() {
1833
+ var u = "", m = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", i = 0, rb = (Math.random() * 0xffffffff) | 0;
1834
+ while (i++ < 36) {
1835
+ var c = m[i - 1], r = rb & 0xf, v = c == "x" ? r : (r & 0x3) | 0x8;
1836
+ u += c == "-" || c == "4" ? c : v.toString(16);
1837
+ rb = i % 8 == 0 ? (Math.random() * 0xffffffff) | 0 : rb >> 4;
1838
+ }
1839
+ return u;
1840
+ }
1765
1841
 
1766
1842
  exports.emptyAnalytics = emptyAnalytics;
1767
1843
  exports.emptyRuntime = emptyRuntime;
1768
1844
  exports.isInBrowser = isInBrowser;
1769
1845
  exports.jitsuAnalytics = jitsuAnalytics;
1846
+ exports.jitsuAnalyticsPlugin = jitsuAnalyticsPlugin;
1770
1847
  exports.parseQuery = parseQuery;
1771
1848
  exports.randomId = randomId;
1772
1849
  exports.windowRuntime = windowRuntime;
package/dist/jitsu.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { AnalyticsInterface } from "@jitsu/protocols/analytics";
2
- import type { AnalyticsPlugin } from "analytics";
3
2
  type JitsuOptions = {
4
3
  /**
5
4
  * API Key. Optional. If not set, Jitsu will send event to the server without auth, and server
@@ -52,6 +51,7 @@ type PersistentStorage = {
52
51
  getItem: (key: string, options?: any) => any;
53
52
  setItem: (key: string, value: any, options?: any) => void;
54
53
  removeItem: (key: string, options?: any) => void;
54
+ reset: () => void;
55
55
  };
56
56
  type RuntimeFacade = {
57
57
  store(): PersistentStorage;
@@ -73,5 +73,4 @@ type RuntimeFacade = {
73
73
  pageTitle(): string | undefined;
74
74
  };
75
75
  export declare function jitsuAnalytics(opts: JitsuOptions): AnalyticsInterface;
76
- export declare const jitsuAnalyticsPlugin: AnalyticsPlugin;
77
76
  export { AnalyticsInterface, JitsuOptions, PersistentStorage, RuntimeFacade };
package/dist/jitsu.es.js CHANGED
@@ -69,7 +69,23 @@ function findScript(src) {
69
69
  const scripts = Array.prototype.slice.call(window.document.querySelectorAll("script"));
70
70
  return scripts.find(s => s.src === src);
71
71
  }
72
- function loadScript(src, attributes) {
72
+ function buildScriptSrc(src, options) {
73
+ let result = src;
74
+ if (!result.startsWith("http")) {
75
+ result = `https://${(options === null || options === void 0 ? void 0 : options.www) ? "www." : ""}${result}`;
76
+ }
77
+ if (options === null || options === void 0 ? void 0 : options.min) {
78
+ result = result + ".min.js";
79
+ }
80
+ else if (options === null || options === void 0 ? void 0 : options.js) {
81
+ result = result + ".js";
82
+ }
83
+ if (options === null || options === void 0 ? void 0 : options.query) {
84
+ result += "?" + options.query;
85
+ }
86
+ return result;
87
+ }
88
+ function loadScript(src, options) {
73
89
  const found = findScript(src);
74
90
  if (found !== undefined) {
75
91
  const status = found === null || found === void 0 ? void 0 : found.getAttribute("status");
@@ -84,13 +100,13 @@ function loadScript(src, attributes) {
84
100
  }
85
101
  }
86
102
  return new Promise((resolve, reject) => {
87
- var _a;
103
+ var _a, _b;
88
104
  const script = window.document.createElement("script");
89
105
  script.type = "text/javascript";
90
- script.src = src;
106
+ script.src = buildScriptSrc(src, options);
91
107
  script.async = true;
92
108
  script.setAttribute("status", "loading");
93
- for (const [k, v] of Object.entries(attributes !== null && attributes !== void 0 ? attributes : {})) {
109
+ for (const [k, v] of Object.entries((_a = options === null || options === void 0 ? void 0 : options.attributes) !== null && _a !== void 0 ? _a : {})) {
94
110
  script.setAttribute(k, v);
95
111
  }
96
112
  script.onload = () => {
@@ -104,7 +120,7 @@ function loadScript(src, attributes) {
104
120
  reject(new Error(`Failed to load ${src}`));
105
121
  };
106
122
  const tag = window.document.getElementsByTagName("script")[0];
107
- (_a = tag.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(script, tag);
123
+ (_b = tag.parentElement) === null || _b === void 0 ? void 0 : _b.insertBefore(script, tag);
108
124
  });
109
125
  }
110
126
 
@@ -204,6 +220,7 @@ var __awaiter$4 = (undefined && undefined.__awaiter) || function (thisArg, _argu
204
220
  step((generator = generator.apply(thisArg, _arguments || [])).next());
205
221
  });
206
222
  };
223
+ const cdn = "cdn.lr-ingest.io/";
207
224
  const logrocketPlugin = {
208
225
  id: "logrocket",
209
226
  handle(config, payload) {
@@ -254,7 +271,7 @@ function initLogrocketIfNeeded(appId) {
254
271
  return;
255
272
  }
256
273
  setLogRocketState("loading");
257
- loadScript(`https://cdn.lr-ingest.io/LogRocket.min.js`, { crossOrigin: "anonymous" })
274
+ loadScript(`${cdn}LogRocket`, { min: true, attributes: { crossOrigin: "anonymous" } })
258
275
  .then(() => {
259
276
  if (window["LogRocket"]) {
260
277
  try {
@@ -377,8 +394,8 @@ function initGtmIfNeeded(config, payload) {
377
394
  event: "gtm.js",
378
395
  });
379
396
  const dl = l != "dataLayer" ? "&l=" + l : "";
380
- const scriptSrc = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
381
- loadScript(scriptSrc)
397
+ const scriptSrc = "googletagmanager.com/gtm";
398
+ loadScript(scriptSrc, { www: true, js: true, query: "id=" + i + dl })
382
399
  .then(() => {
383
400
  setGtmState("loaded");
384
401
  })
@@ -399,7 +416,7 @@ var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _argu
399
416
  step((generator = generator.apply(thisArg, _arguments || [])).next());
400
417
  });
401
418
  };
402
- const defaultScriptSrc = "https://www.googletagmanager.com/gtag/js";
419
+ const defaultScriptSrc = "googletagmanager.com/gtag/js";
403
420
  const ga4Plugin = {
404
421
  id: "ga4-tag",
405
422
  handle(config, payload) {
@@ -461,7 +478,6 @@ function initGa4IfNeeded(config, payload) {
461
478
  const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : "";
462
479
  // to work with both GA4 and GTM
463
480
  const tagId = config.measurementIds;
464
- const scriptSrc = `${defaultScriptSrc}?id=${tagId}${dlParam}`;
465
481
  window[dlName] = window[dlName] || [];
466
482
  const gtag = function () {
467
483
  window[dlName].push(arguments);
@@ -471,7 +487,7 @@ function initGa4IfNeeded(config, payload) {
471
487
  gtag(
472
488
  // @ts-ignore
473
489
  "config", tagId, Object.assign(Object.assign({}, (payload.userId ? { user_id: payload.userId } : {})), (!config.autoPageView ? { send_page_view: false } : {})));
474
- loadScript(scriptSrc)
490
+ loadScript(defaultScriptSrc, { query: `id=${tagId}${dlParam}`, www: true })
475
491
  .then(() => {
476
492
  setGa4State("loaded");
477
493
  })
@@ -1110,8 +1126,15 @@ function getGa4Ids(runtime) {
1110
1126
  return undefined;
1111
1127
  }
1112
1128
  }
1113
- function removeCookie(name) {
1114
- document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
1129
+ function removeCookie(name, { domain, secure }) {
1130
+ document.cookie =
1131
+ name +
1132
+ "=;domain=" +
1133
+ domain +
1134
+ ";path=/" +
1135
+ ";expires=Thu, 01 Jan 1970 00:00:01 GMT;SameSite=" +
1136
+ (secure ? "None" : "Lax") +
1137
+ (secure ? ";secure" : "");
1115
1138
  }
1116
1139
  function setCookie(name, val, { domain, secure }) {
1117
1140
  document.cookie =
@@ -1153,7 +1176,18 @@ const cookieStorage = (cookieDomain, key2cookie) => {
1153
1176
  return parse(result);
1154
1177
  },
1155
1178
  removeItem(key) {
1156
- removeCookie(key2cookie[key] || key);
1179
+ removeCookie(key2cookie[key] || key, {
1180
+ domain: cookieDomain,
1181
+ secure: window.location.protocol === "https:",
1182
+ });
1183
+ },
1184
+ reset() {
1185
+ for (const v of Object.values(key2cookie)) {
1186
+ removeCookie(v, {
1187
+ domain: cookieDomain,
1188
+ secure: window.location.protocol === "https:",
1189
+ });
1190
+ }
1157
1191
  },
1158
1192
  };
1159
1193
  };
@@ -1224,6 +1258,9 @@ const emptyRuntime = (config) => ({
1224
1258
  store() {
1225
1259
  const storage = {};
1226
1260
  return {
1261
+ reset() {
1262
+ Object.keys(storage).forEach(key => delete storage[key]);
1263
+ },
1227
1264
  setItem(key, val) {
1228
1265
  if (config.debug) {
1229
1266
  console.log(`[JITSU EMPTY RUNTIME] Set storage item ${key}=${JSON.stringify(val)}`);
@@ -1289,6 +1326,17 @@ function fixPath(path) {
1289
1326
  }
1290
1327
  return path;
1291
1328
  }
1329
+ const hashRegex = /#.*$/;
1330
+ /**
1331
+ * for compatibility with path produced by analytics.js
1332
+ * @param url
1333
+ */
1334
+ function urlPath(url) {
1335
+ const regex = /(http[s]?:\/\/)?([^\/\s]+\/)(.*)/g;
1336
+ const matches = regex.exec(url);
1337
+ const pathMatch = matches && matches[3] ? matches[3].split("?")[0].replace(hashRegex, "") : "";
1338
+ return "/" + pathMatch;
1339
+ }
1292
1340
  function adjustPayload(payload, config, storage, s2s) {
1293
1341
  var _a, _b;
1294
1342
  const runtime = config.runtime || (isInBrowser() ? windowRuntime(config) : emptyRuntime(config));
@@ -1296,8 +1344,9 @@ function adjustPayload(payload, config, storage, s2s) {
1296
1344
  const parsedUrl = safeCall(() => new URL(url), undefined);
1297
1345
  const query = parsedUrl ? parseQuery(parsedUrl.search) : {};
1298
1346
  const properties = payload.properties || {};
1299
- if (properties.path) {
1300
- properties.path = fixPath(properties.path);
1347
+ if (payload.type === "page" && url) {
1348
+ properties.url = url.replace(hashRegex, "");
1349
+ properties.path = fixPath(urlPath(url));
1301
1350
  }
1302
1351
  const customContext = ((_a = payload.properties) === null || _a === void 0 ? void 0 : _a.context) || {};
1303
1352
  (_b = payload.properties) === null || _b === void 0 ? true : delete _b.context;
@@ -1495,10 +1544,13 @@ function send(method, payload, jitsuConfig, instance, store) {
1495
1544
  console.warn(`[JITSU] ${payload.type} responded with list of ${responseJson.destinations.length} destinations. However, this code is running in server-to-server mode, so destinations will be ignored`, jitsuConfig.debug ? JSON.stringify(responseJson.destinations, null, 2) : undefined);
1496
1545
  }
1497
1546
  else {
1498
- if (jitsuConfig.debug) {
1499
- console.log(`[JITSU] Processing device destinations: `, JSON.stringify(responseJson.destinations, null, 2));
1547
+ //double protection, ingest should not return destinations in s2s mode
1548
+ if (isInBrowser()) {
1549
+ if (jitsuConfig.debug) {
1550
+ console.log(`[JITSU] Processing device destinations: `, JSON.stringify(responseJson.destinations, null, 2));
1551
+ }
1552
+ return processDestinations(responseJson.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
1500
1553
  }
1501
- return processDestinations(responseJson.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
1502
1554
  }
1503
1555
  }
1504
1556
  return adjustedPayload;
@@ -1538,9 +1590,11 @@ const jitsuAnalyticsPlugin = (pluginConfig = {}) => {
1538
1590
  return send("identify", payload, config, instance, storage);
1539
1591
  },
1540
1592
  reset: args => {
1541
- //clear storage cache
1542
- if (pluginConfig.storageWrapper) {
1543
- pluginConfig.storageWrapper(args.instance.storage).reset();
1593
+ const { config, instance } = args;
1594
+ const storage = pluginConfig.storageWrapper ? pluginConfig.storageWrapper(instance.storage) : instance.storage;
1595
+ storage === null || storage === void 0 ? void 0 : storage.reset();
1596
+ if (config.debug) {
1597
+ console.log("[JITSU DEBUG] Resetting Jitsu plugin storage");
1544
1598
  }
1545
1599
  },
1546
1600
  methods: {
@@ -1652,9 +1706,9 @@ function createUnderlyingAnalyticsInstance(opts, rt, plugins = []) {
1652
1706
  },
1653
1707
  reset() {
1654
1708
  for (const key of [...Object.keys(storageCache)]) {
1655
- storage.removeItem(key);
1656
1709
  delete storageCache[key];
1657
1710
  }
1711
+ storage.reset();
1658
1712
  },
1659
1713
  removeItem(key) {
1660
1714
  if (opts.debug) {
@@ -1711,7 +1765,20 @@ function createUnderlyingAnalyticsInstance(opts, rt, plugins = []) {
1711
1765
  userState.anonymousId = id;
1712
1766
  }
1713
1767
  analytics.setAnonymousId(id);
1714
- }, group(groupId, traits, options, callback) {
1768
+ }, reset() {
1769
+ return __awaiter(this, void 0, void 0, function* () {
1770
+ if (opts.debug) {
1771
+ console.log("[JITSU DEBUG] Called reset(). Storage state", JSON.stringify(analytics.user()));
1772
+ }
1773
+ storage.reset();
1774
+ yield analytics.reset();
1775
+ this.setAnonymousId(uuid());
1776
+ if (opts.debug) {
1777
+ console.log("[JITSU DEBUG] User state after reset", JSON.stringify(analytics.user()));
1778
+ }
1779
+ });
1780
+ },
1781
+ group(groupId, traits, options, callback) {
1715
1782
  return __awaiter(this, void 0, void 0, function* () {
1716
1783
  const results = [];
1717
1784
  for (const plugin of Object.values(analytics.plugins)) {
@@ -1760,5 +1827,14 @@ function jitsuAnalytics(_opts) {
1760
1827
  // result.loaded(createUnderlyingAnalyticsInstance(opts, rt));
1761
1828
  // }
1762
1829
  }
1830
+ function uuid() {
1831
+ var u = "", m = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", i = 0, rb = (Math.random() * 0xffffffff) | 0;
1832
+ while (i++ < 36) {
1833
+ var c = m[i - 1], r = rb & 0xf, v = c == "x" ? r : (r & 0x3) | 0x8;
1834
+ u += c == "-" || c == "4" ? c : v.toString(16);
1835
+ rb = i % 8 == 0 ? (Math.random() * 0xffffffff) | 0 : rb >> 4;
1836
+ }
1837
+ return u;
1838
+ }
1763
1839
 
1764
- export { emptyAnalytics, emptyRuntime, isInBrowser, jitsuAnalytics, parseQuery, randomId, windowRuntime };
1840
+ export { emptyAnalytics, emptyRuntime, isInBrowser, jitsuAnalytics, jitsuAnalyticsPlugin, parseQuery, randomId, windowRuntime };
@@ -1 +1,8 @@
1
- export declare function loadScript(src: string, attributes?: Record<string, string>): Promise<HTMLScriptElement>;
1
+ export type ScriptOptions = {
2
+ attributes?: Record<string, string>;
3
+ www?: boolean;
4
+ js?: boolean;
5
+ min?: boolean;
6
+ query?: string;
7
+ };
8
+ export declare function loadScript(src: string, options?: ScriptOptions): Promise<HTMLScriptElement>;