@jitsu/js 1.1.0-canary.344.20230430234929 → 1.1.0-canary.391.20230523080206

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/web/p.js.txt CHANGED
@@ -68,7 +68,50 @@
68
68
  return ke(_objectSpread2(_objectSpread2({}, defaultSettings), opts));
69
69
  }
70
70
 
71
- var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
71
+ function findScript(src) {
72
+ const scripts = Array.prototype.slice.call(window.document.querySelectorAll("script"));
73
+ return scripts.find(s => s.src === src);
74
+ }
75
+ function loadScript(src, attributes) {
76
+ const found = findScript(src);
77
+ if (found !== undefined) {
78
+ const status = found === null || found === void 0 ? void 0 : found.getAttribute("status");
79
+ if (status === "loaded") {
80
+ return Promise.resolve(found);
81
+ }
82
+ if (status === "loading") {
83
+ return new Promise((resolve, reject) => {
84
+ found.addEventListener("load", () => resolve(found));
85
+ found.addEventListener("error", err => reject(err));
86
+ });
87
+ }
88
+ }
89
+ return new Promise((resolve, reject) => {
90
+ var _a;
91
+ const script = window.document.createElement("script");
92
+ script.type = "text/javascript";
93
+ script.src = src;
94
+ script.async = true;
95
+ script.setAttribute("status", "loading");
96
+ for (const [k, v] of Object.entries(attributes !== null && attributes !== void 0 ? attributes : {})) {
97
+ script.setAttribute(k, v);
98
+ }
99
+ script.onload = () => {
100
+ script.onerror = script.onload = null;
101
+ script.setAttribute("status", "loaded");
102
+ resolve(script);
103
+ };
104
+ script.onerror = () => {
105
+ script.onerror = script.onload = null;
106
+ script.setAttribute("status", "error");
107
+ reject(new Error(`Failed to load ${src}`));
108
+ };
109
+ const tag = window.document.getElementsByTagName("script")[0];
110
+ (_a = tag.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(script, tag);
111
+ });
112
+ }
113
+
114
+ var __awaiter$3 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
72
115
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
73
116
  return new (P || (P = Promise))(function (resolve, reject) {
74
117
  function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
@@ -77,18 +120,10 @@
77
120
  step((generator = generator.apply(thisArg, _arguments || [])).next());
78
121
  });
79
122
  };
80
- function satisfyFilter(filter, subject) {
81
- return filter === "*" || filter.toLowerCase().trim() === (subject || "").trim().toLowerCase();
82
- }
83
- function applyFilters(event, creds) {
84
- const { hosts = ["*"], events = ["*"] } = creds;
85
- return (!!hosts.find(hostFilter => { var _a; return satisfyFilter(hostFilter, (_a = event.context) === null || _a === void 0 ? void 0 : _a.host); }) &&
86
- !!events.find(eventFilter => satisfyFilter(eventFilter, event.type)));
87
- }
88
123
  const tagPlugin = {
89
124
  id: "tag",
90
125
  handle(config, payload) {
91
- return __awaiter$1(this, void 0, void 0, function* () {
126
+ return __awaiter$3(this, void 0, void 0, function* () {
92
127
  if (!applyFilters(payload, config)) {
93
128
  return;
94
129
  }
@@ -156,53 +191,206 @@
156
191
  function replaceMacro(code, event) {
157
192
  return code.replace(/{{\s*event\s*}}/g, JSON.stringify(event));
158
193
  }
159
- const internalDestinationPlugins = {
160
- [tagPlugin.id]: tagPlugin,
161
- };
162
194
 
163
- function findScript(src) {
164
- const scripts = Array.prototype.slice.call(window.document.querySelectorAll("script"));
165
- return scripts.find(s => s.src === src);
195
+ var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
196
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
197
+ return new (P || (P = Promise))(function (resolve, reject) {
198
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
199
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
200
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
201
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
202
+ });
203
+ };
204
+ const logrocketPlugin = {
205
+ id: "logrocket",
206
+ handle(config, payload) {
207
+ return __awaiter$2(this, void 0, void 0, function* () {
208
+ if (!applyFilters(payload, config)) {
209
+ return;
210
+ }
211
+ initLogrocketIfNeeded(config.appId);
212
+ const action = logRocket => {
213
+ if (payload.type === "identify" && payload.userId) {
214
+ logRocket.identify(payload.userId, payload.traits || {});
215
+ }
216
+ };
217
+ getLogRocketQueue().push(action);
218
+ if (getLogRocketState() === "loaded") {
219
+ flushLogRocketQueue(window["LogRocket"]);
220
+ }
221
+ });
222
+ },
223
+ };
224
+ function getLogRocketState() {
225
+ return window["__jitsuLrState"] || "fresh";
166
226
  }
167
- function loadScript(src, attributes) {
168
- const found = findScript(src);
169
- if (found !== undefined) {
170
- const status = found === null || found === void 0 ? void 0 : found.getAttribute("status");
171
- if (status === "loaded") {
172
- return Promise.resolve(found);
227
+ function setLogRocketState(s) {
228
+ window["__jitsuLrState"] = s;
229
+ }
230
+ function getLogRocketQueue() {
231
+ return window["__jitsuLrQueue"] || (window["__jitsuLrQueue"] = []);
232
+ }
233
+ function flushLogRocketQueue(lr) {
234
+ const queue = getLogRocketQueue();
235
+ while (queue.length > 0) {
236
+ const method = queue.shift();
237
+ try {
238
+ const res = method(lr);
239
+ if (res) {
240
+ res.catch(e => console.warn(`Async LogRocket method failed: ${e.message}`, e));
241
+ }
173
242
  }
174
- if (status === "loading") {
175
- return new Promise((resolve, reject) => {
176
- found.addEventListener("load", () => resolve(found));
177
- found.addEventListener("error", err => reject(err));
178
- });
243
+ catch (e) {
244
+ console.warn(`LogRocket method failed: ${e.message}`, e);
179
245
  }
180
246
  }
181
- return new Promise((resolve, reject) => {
182
- var _a;
183
- const script = window.document.createElement("script");
184
- script.type = "text/javascript";
185
- script.src = src;
186
- script.async = true;
187
- script.setAttribute("status", "loading");
188
- for (const [k, v] of Object.entries(attributes !== null && attributes !== void 0 ? attributes : {})) {
189
- script.setAttribute(k, v);
247
+ }
248
+ function initLogrocketIfNeeded(appId) {
249
+ return __awaiter$2(this, void 0, void 0, function* () {
250
+ if (getLogRocketState() !== "fresh") {
251
+ return;
190
252
  }
191
- script.onload = () => {
192
- script.onerror = script.onload = null;
193
- script.setAttribute("status", "loaded");
194
- resolve(script);
195
- };
196
- script.onerror = () => {
197
- script.onerror = script.onload = null;
198
- script.setAttribute("status", "error");
199
- reject(new Error(`Failed to load ${src}`));
253
+ setLogRocketState("loading");
254
+ loadScript(`https://cdn.lr-ingest.io/LogRocket.min.js`, { crossOrigin: "anonymous" })
255
+ .then(() => {
256
+ if (window["LogRocket"]) {
257
+ try {
258
+ window["LogRocket"].init(appId);
259
+ }
260
+ catch (e) {
261
+ console.warn(`LogRocket (id=${appId}) init failed: ${e.message}`, e);
262
+ setLogRocketState("failed");
263
+ }
264
+ setLogRocketState("loaded");
265
+ flushLogRocketQueue(window["LogRocket"]);
266
+ }
267
+ })
268
+ .catch(e => {
269
+ console.warn(`LogRocket (id=${appId}) init failed: ${e.message}`, e);
270
+ setLogRocketState("failed");
271
+ });
272
+ });
273
+ }
274
+
275
+ var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
276
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
277
+ return new (P || (P = Promise))(function (resolve, reject) {
278
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
279
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
280
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
281
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
282
+ });
283
+ };
284
+ const defaultScriptSrc = "https://www.googletagmanager.com/gtag/js";
285
+ const gtmPlugin = {
286
+ id: "gtm",
287
+ handle(config, payload) {
288
+ var _a, _b;
289
+ return __awaiter$1(this, void 0, void 0, function* () {
290
+ if (!applyFilters(payload, config)) {
291
+ return;
292
+ }
293
+ yield initGtmIfNeeded(config);
294
+ const dataLayer = window[config.dataLayerName || "dataLayer"];
295
+ switch (payload.type) {
296
+ case "page":
297
+ const { properties: pageProperties, context } = payload;
298
+ const pageEvent = {
299
+ event: "page_view",
300
+ url: pageProperties.url,
301
+ title: pageProperties.title,
302
+ referer: (_b = (_a = context === null || context === void 0 ? void 0 : context.page) === null || _a === void 0 ? void 0 : _a.referrer) !== null && _b !== void 0 ? _b : "",
303
+ };
304
+ if (config.debug) {
305
+ console.log("gtag push", pageEvent);
306
+ }
307
+ dataLayer.push(pageEvent);
308
+ break;
309
+ case "track":
310
+ const { properties: trackProperties } = payload;
311
+ const trackEvent = Object.assign({ event: payload.event }, trackProperties);
312
+ if (payload.userId) {
313
+ trackEvent.userId = payload.userId;
314
+ }
315
+ if (payload.anonymousId) {
316
+ trackEvent.anonymousId = payload.anonymousId;
317
+ }
318
+ if (config.debug) {
319
+ console.log("gtag push", trackEvent);
320
+ }
321
+ dataLayer.push(trackEvent);
322
+ break;
323
+ case "identify":
324
+ const { traits } = payload;
325
+ const identifyEvent = Object.assign({ event: "identify" }, traits);
326
+ if (payload.userId) {
327
+ identifyEvent.userId = payload.userId;
328
+ }
329
+ if (payload.anonymousId) {
330
+ identifyEvent.anonymousId = payload.anonymousId;
331
+ }
332
+ if (config.debug) {
333
+ console.log("gtag push", identifyEvent);
334
+ }
335
+ dataLayer.push(identifyEvent);
336
+ break;
337
+ }
338
+ });
339
+ },
340
+ };
341
+ function getGtmState() {
342
+ return window["__jitsuGtmState"] || "fresh";
343
+ }
344
+ function setGtmState(s) {
345
+ window["__jitsuGtmState"] = s;
346
+ }
347
+ function initGtmIfNeeded(config) {
348
+ return __awaiter$1(this, void 0, void 0, function* () {
349
+ if (getGtmState() !== "fresh") {
350
+ return;
351
+ }
352
+ setGtmState("loading");
353
+ const dlName = config.dataLayerName || "dataLayer";
354
+ const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : "";
355
+ const previewParams = config.preview
356
+ ? `&gtm_preview=${config.preview}&gtm_auth=${config.auth}&gtm_cookies_win=x`
357
+ : "";
358
+ const scriptSrc = `${config.customScriptSrc || defaultScriptSrc}?id=${config.containerId}${dlParam}${previewParams}`;
359
+ window[dlName] = window[dlName] || [];
360
+ const gtag = function () {
361
+ window[dlName].push(arguments);
200
362
  };
201
- const tag = window.document.getElementsByTagName("script")[0];
202
- (_a = tag.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(script, tag);
363
+ // @ts-ignore
364
+ gtag("js", new Date());
365
+ // @ts-ignore
366
+ gtag("config", config.containerId);
367
+ loadScript(scriptSrc)
368
+ .then(() => {
369
+ setGtmState("loaded");
370
+ })
371
+ .catch(e => {
372
+ console.warn(`GTM (containerId=${config.containerId}) init failed: ${e.message}`, e);
373
+ setGtmState("failed");
374
+ });
203
375
  });
204
376
  }
205
377
 
378
+ function satisfyFilter(filter, subject) {
379
+ return filter === "*" || filter.toLowerCase().trim() === (subject || "").trim().toLowerCase();
380
+ }
381
+ function applyFilters(event, creds) {
382
+ const { hosts = "*", events = "*" } = creds;
383
+ const eventsArray = events.split("\n");
384
+ return (!!hosts.split("\n").find(hostFilter => { var _a; return satisfyFilter(hostFilter, (_a = event.context) === null || _a === void 0 ? void 0 : _a.host); }) &&
385
+ (!!eventsArray.find(eventFilter => satisfyFilter(eventFilter, event.type)) ||
386
+ !!eventsArray.find(eventFilter => satisfyFilter(eventFilter, event.event))));
387
+ }
388
+ const internalDestinationPlugins = {
389
+ [tagPlugin.id]: tagPlugin,
390
+ [gtmPlugin.id]: gtmPlugin,
391
+ [logrocketPlugin.id]: logrocketPlugin,
392
+ };
393
+
206
394
  /* global analytics */
207
395
  var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
208
396
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -221,6 +409,8 @@
221
409
  debug: false,
222
410
  fetch: null,
223
411
  echoEvents: false,
412
+ cookieDomain: undefined,
413
+ runtime: undefined,
224
414
  };
225
415
  const parseQuery = (qs) => {
226
416
  if (!qs) {
@@ -444,7 +634,7 @@
444
634
  userAgent: runtime.userAgent(),
445
635
  locale: runtime.language(),
446
636
  screen: runtime.screen(),
447
- traits: payload.type != "identify" ? Object.assign({}, (restoreTraits(storage) || {})) : undefined,
637
+ traits: payload.type != "identify" && payload.type != "group" ? Object.assign({}, (restoreTraits(storage) || {})) : undefined,
448
638
  page: {
449
639
  path: properties.path || (parsedUrl && parsedUrl.pathname),
450
640
  referrer: referrer,
@@ -453,7 +643,7 @@
453
643
  search: properties.search || (parsedUrl && parsedUrl.search),
454
644
  title: properties.title || runtime.pageTitle(),
455
645
  url: properties.url || url,
456
- enconding: properties.enconding || runtime.documentEncoding(),
646
+ encoding: properties.encoding || runtime.documentEncoding(),
457
647
  },
458
648
  campaign: parseUtms(query),
459
649
  };
@@ -466,12 +656,12 @@
466
656
  return __awaiter(this, void 0, void 0, function* () {
467
657
  const promises = [];
468
658
  for (const destination of destinations) {
659
+ const credentials = Object.assign(Object.assign({}, destination.credentials), destination.options);
469
660
  if (destination.deviceOptions.type === "internal-plugin") {
470
661
  const plugin = internalDestinationPlugins[destination.deviceOptions.name];
471
662
  if (plugin) {
472
663
  try {
473
664
  //to support old versions, where credentials were stored in root
474
- const credentials = destination.credentials || destination;
475
665
  promises.push(plugin.handle(credentials, event));
476
666
  }
477
667
  catch (e) {
@@ -491,7 +681,7 @@
491
681
  else {
492
682
  let pluginInstance;
493
683
  try {
494
- pluginInstance = (typeof plugin === "function" ? plugin : plugin.init)(destination.credentials);
684
+ pluginInstance = (typeof plugin === "function" ? plugin : plugin.init)(credentials);
495
685
  }
496
686
  catch (e) {
497
687
  console.warn(`[JITSU] Error creating plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e === null || e === void 0 ? void 0 : e.message}`, e);
@@ -561,7 +751,7 @@
561
751
  }
562
752
  if (response.destinations) {
563
753
  if (jitsuConfig.debug) {
564
- console.log(`[JITSU] Processing device destianations: `, JSON.stringify(response.destinations, null, 2));
754
+ console.log(`[JITSU] Processing device destinations: `, JSON.stringify(response.destinations, null, 2));
565
755
  }
566
756
  return processDestinations(response.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
567
757
  }
@@ -590,9 +780,10 @@
590
780
  persistentStorage.removeItem(key);
591
781
  },
592
782
  });
783
+ const instanceConfig = Object.assign(Object.assign({}, config), pluginConfig);
593
784
  return {
594
785
  name: "jitsu",
595
- config: Object.assign(Object.assign({}, config), pluginConfig),
786
+ config: instanceConfig,
596
787
  initialize: args => {
597
788
  const { config } = args;
598
789
  if (config.debug) {
@@ -620,6 +811,16 @@
620
811
  //clear storage cache
621
812
  Object.keys(storageCache).forEach(key => delete storageCache[key]);
622
813
  },
814
+ methods: {
815
+ //analytics doesn't support group as a base method, so we need to add it manually
816
+ group(groupId, traits, options, callback) {
817
+ const analyticsInstance = this.instance;
818
+ const user = analyticsInstance.user();
819
+ const userId = (options === null || options === void 0 ? void 0 : options.userId) || (user === null || user === void 0 ? void 0 : user.userId);
820
+ const anonymousId = (options === null || options === void 0 ? void 0 : options.anonymousId) || (user === null || user === void 0 ? void 0 : user.anonymousId);
821
+ return send("group", Object.assign(Object.assign({ type: "group", groupId, traits }, (anonymousId ? { anonymousId } : {})), (userId ? { userId } : {})), instanceConfig, analyticsInstance, cachingStorageWrapper(analyticsInstance.storage));
822
+ },
823
+ },
623
824
  };
624
825
  };
625
826
  function getSeed() {
@@ -683,7 +884,14 @@
683
884
  return originalPage(...args);
684
885
  }
685
886
  };
686
- return analytics;
887
+ return Object.assign(Object.assign({}, analytics), { group(groupId, traits, options, callback) {
888
+ for (const plugin of Object.values(analytics.plugins)) {
889
+ if (plugin["group"]) {
890
+ plugin["group"](groupId, traits, options, callback);
891
+ }
892
+ }
893
+ return Promise.resolve({});
894
+ } });
687
895
  }
688
896
  function jitsuAnalytics(opts) {
689
897
  const inBrowser = isInBrowser();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jitsu/js",
3
- "version": "1.1.0-canary.344.20230430234929",
3
+ "version": "1.1.0-canary.391.20230523080206",
4
4
  "description": "",
5
5
  "author": "Jitsu Dev Team <dev@jitsu.com>",
6
6
  "main": "dist/jitsu.cjs.js",
@@ -34,7 +34,7 @@
34
34
  "rollup": "^3.2.5",
35
35
  "ts-jest": "29.0.5",
36
36
  "typescript": "^4.9.5",
37
- "@jitsu/protocols": "1.1.0-canary.344.20230430234929"
37
+ "@jitsu/protocols": "1.1.0-canary.391.20230523080206"
38
38
  },
39
39
  "dependencies": {
40
40
  "analytics": "^0.8.1"
@@ -1,13 +1,13 @@
1
1
  /* global analytics */
2
2
 
3
3
  import { JitsuOptions, PersistentStorage, RuntimeFacade } from "./jitsu";
4
- import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
4
+ import { AnalyticsClientEvent, Callback, ID, JSONObject, Options } from "@jitsu/protocols/analytics";
5
5
  import parse from "./index";
6
6
  import { AnalyticsInstance, AnalyticsPlugin } from "analytics";
7
- import { internalDestinationPlugins } from "./destination-plugins";
8
7
  import { loadScript } from "./script-loader";
8
+ import { internalDestinationPlugins } from "./destination-plugins";
9
9
 
10
- const config: JitsuOptions = {
10
+ const config: Required<JitsuOptions> = {
11
11
  /* Your segment writeKey */
12
12
  writeKey: null,
13
13
  /* Disable anonymous MTU */
@@ -15,6 +15,8 @@ const config: JitsuOptions = {
15
15
  debug: false,
16
16
  fetch: null,
17
17
  echoEvents: false,
18
+ cookieDomain: undefined,
19
+ runtime: undefined,
18
20
  };
19
21
 
20
22
  export const parseQuery = (qs?: string): Record<string, string> => {
@@ -261,7 +263,7 @@ function adjustPayload(payload: any, config: JitsuOptions, storage: PersistentSt
261
263
  userAgent: runtime.userAgent(),
262
264
  locale: runtime.language(),
263
265
  screen: runtime.screen(),
264
- traits: payload.type != "identify" ? { ...(restoreTraits(storage) || {}) } : undefined,
266
+ traits: payload.type != "identify" && payload.type != "group" ? { ...(restoreTraits(storage) || {}) } : undefined,
265
267
  page: {
266
268
  path: properties.path || (parsedUrl && parsedUrl.pathname),
267
269
  referrer: referrer,
@@ -270,7 +272,7 @@ function adjustPayload(payload: any, config: JitsuOptions, storage: PersistentSt
270
272
  search: properties.search || (parsedUrl && parsedUrl.search),
271
273
  title: properties.title || runtime.pageTitle(),
272
274
  url: properties.url || url,
273
- enconding: properties.enconding || runtime.documentEncoding(),
275
+ encoding: properties.encoding || runtime.documentEncoding(),
274
276
  },
275
277
  campaign: parseUtms(query),
276
278
  };
@@ -291,6 +293,7 @@ export type DestinationDescriptor = {
291
293
  id: string;
292
294
  destinationType: string;
293
295
  credentials: any;
296
+ options: any;
294
297
  deviceOptions: DeviceOptions;
295
298
  };
296
299
  export type AnalyticsPluginDescriptor = {
@@ -315,12 +318,12 @@ async function processDestinations(
315
318
  ) {
316
319
  const promises: Promise<any>[] = [];
317
320
  for (const destination of destinations) {
321
+ const credentials = { ...destination.credentials, ...destination.options };
322
+
318
323
  if (destination.deviceOptions.type === "internal-plugin") {
319
324
  const plugin = internalDestinationPlugins[destination.deviceOptions.name];
320
325
  if (plugin) {
321
326
  try {
322
- //to support old versions, where credentials were stored in root
323
- const credentials = destination.credentials || destination;
324
327
  promises.push(plugin.handle(credentials, event));
325
328
  } catch (e) {
326
329
  console.warn(
@@ -343,7 +346,7 @@ async function processDestinations(
343
346
  } else {
344
347
  let pluginInstance: any;
345
348
  try {
346
- pluginInstance = (typeof plugin === "function" ? plugin : plugin.init)(destination.credentials);
349
+ pluginInstance = (typeof plugin === "function" ? plugin : plugin.init)(credentials);
347
350
  } catch (e) {
348
351
  console.warn(
349
352
  `[JITSU] Error creating plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e?.message}`,
@@ -441,7 +444,7 @@ function send(
441
444
  }
442
445
  if (response.destinations) {
443
446
  if (jitsuConfig.debug) {
444
- console.log(`[JITSU] Processing device destianations: `, JSON.stringify(response.destinations, null, 2));
447
+ console.log(`[JITSU] Processing device destinations: `, JSON.stringify(response.destinations, null, 2));
445
448
  }
446
449
  return processDestinations(response.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance);
447
450
  }
@@ -471,12 +474,13 @@ const jitsuAnalyticsPlugin = (pluginConfig: JitsuOptions = {}): AnalyticsPlugin
471
474
  persistentStorage.removeItem(key);
472
475
  },
473
476
  });
477
+ const instanceConfig = {
478
+ ...config,
479
+ ...pluginConfig,
480
+ };
474
481
  return {
475
482
  name: "jitsu",
476
- config: {
477
- ...config,
478
- ...pluginConfig,
479
- },
483
+ config: instanceConfig,
480
484
  initialize: args => {
481
485
  const { config } = args;
482
486
  if (config.debug) {
@@ -504,6 +508,22 @@ const jitsuAnalyticsPlugin = (pluginConfig: JitsuOptions = {}): AnalyticsPlugin
504
508
  //clear storage cache
505
509
  Object.keys(storageCache).forEach(key => delete storageCache[key]);
506
510
  },
511
+ methods: {
512
+ //analytics doesn't support group as a base method, so we need to add it manually
513
+ group(groupId?: ID, traits?: JSONObject | null, options?: Options, callback?: Callback) {
514
+ const analyticsInstance = this.instance;
515
+ const user = analyticsInstance.user();
516
+ const userId = options?.userId || user?.userId;
517
+ const anonymousId = options?.anonymousId || user?.anonymousId;
518
+ return send(
519
+ "group",
520
+ { type: "group", groupId, traits, ...(anonymousId ? { anonymousId } : {}), ...(userId ? { userId } : {}) },
521
+ instanceConfig,
522
+ analyticsInstance,
523
+ cachingStorageWrapper(analyticsInstance.storage)
524
+ );
525
+ },
526
+ },
507
527
  };
508
528
  };
509
529
 
@@ -0,0 +1,112 @@
1
+ import { loadScript } from "../script-loader";
2
+ import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
3
+ import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index";
4
+
5
+ const defaultScriptSrc = "https://www.googletagmanager.com/gtag/js";
6
+
7
+ export type GtmDestinationCredentials = {
8
+ debug: boolean;
9
+ containerId: string;
10
+ dataLayerName: string;
11
+ preview: string;
12
+ auth: string;
13
+ customScriptSrc: string;
14
+ } & CommonDestinationCredentials;
15
+
16
+ export const gtmPlugin: InternalPlugin<GtmDestinationCredentials> = {
17
+ id: "gtm",
18
+ async handle(config, payload: AnalyticsClientEvent) {
19
+ if (!applyFilters(payload, config)) {
20
+ return;
21
+ }
22
+ await initGtmIfNeeded(config);
23
+
24
+ const dataLayer = window[config.dataLayerName || "dataLayer"];
25
+
26
+ switch (payload.type) {
27
+ case "page":
28
+ const { properties: pageProperties, context } = payload;
29
+ const pageEvent = {
30
+ event: "page_view",
31
+ url: pageProperties.url,
32
+ title: pageProperties.title,
33
+ referer: context?.page?.referrer ?? "",
34
+ };
35
+ if (config.debug) {
36
+ console.log("gtag push", pageEvent);
37
+ }
38
+ dataLayer.push(pageEvent);
39
+ break;
40
+ case "track":
41
+ const { properties: trackProperties } = payload;
42
+ const trackEvent: any = { event: payload.event, ...trackProperties };
43
+ if (payload.userId) {
44
+ trackEvent.userId = payload.userId;
45
+ }
46
+ if (payload.anonymousId) {
47
+ trackEvent.anonymousId = payload.anonymousId;
48
+ }
49
+ if (config.debug) {
50
+ console.log("gtag push", trackEvent);
51
+ }
52
+ dataLayer.push(trackEvent);
53
+ break;
54
+ case "identify":
55
+ const { traits } = payload;
56
+ const identifyEvent: any = { event: "identify", ...traits };
57
+ if (payload.userId) {
58
+ identifyEvent.userId = payload.userId;
59
+ }
60
+ if (payload.anonymousId) {
61
+ identifyEvent.anonymousId = payload.anonymousId;
62
+ }
63
+ if (config.debug) {
64
+ console.log("gtag push", identifyEvent);
65
+ }
66
+ dataLayer.push(identifyEvent);
67
+ break;
68
+ }
69
+ },
70
+ };
71
+
72
+ type GtmState = "fresh" | "loading" | "loaded" | "failed";
73
+
74
+ function getGtmState(): GtmState {
75
+ return window["__jitsuGtmState"] || "fresh";
76
+ }
77
+
78
+ function setGtmState(s: GtmState) {
79
+ window["__jitsuGtmState"] = s;
80
+ }
81
+
82
+ async function initGtmIfNeeded(config: GtmDestinationCredentials) {
83
+ if (getGtmState() !== "fresh") {
84
+ return;
85
+ }
86
+ setGtmState("loading");
87
+
88
+ const dlName = config.dataLayerName || "dataLayer";
89
+ const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : "";
90
+ const previewParams = config.preview
91
+ ? `&gtm_preview=${config.preview}&gtm_auth=${config.auth}&gtm_cookies_win=x`
92
+ : "";
93
+ const scriptSrc = `${config.customScriptSrc || defaultScriptSrc}?id=${config.containerId}${dlParam}${previewParams}`;
94
+
95
+ window[dlName] = window[dlName] || [];
96
+ const gtag = function () {
97
+ window[dlName].push(arguments);
98
+ };
99
+ // @ts-ignore
100
+ gtag("js", new Date());
101
+ // @ts-ignore
102
+ gtag("config", config.containerId);
103
+
104
+ loadScript(scriptSrc)
105
+ .then(() => {
106
+ setGtmState("loaded");
107
+ })
108
+ .catch(e => {
109
+ console.warn(`GTM (containerId=${config.containerId}) init failed: ${e.message}`, e);
110
+ setGtmState("failed");
111
+ });
112
+ }