@jitsu/js 1.9.15 → 1.9.18-canary.1269.20250403142744
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/jitsu.cjs.js +6 -2
- package/dist/jitsu.d.ts +1 -12
- package/dist/jitsu.es.js +6 -2
- package/dist/web/p.js.txt +6 -2
- package/package.json +9 -3
- package/.turbo/turbo-build.log +0 -67
- package/.turbo/turbo-clean.log +0 -5
- package/.turbo/turbo-test.log +0 -4671
- package/__tests__/node/method-queue.test.ts +0 -66
- package/__tests__/node/nodejs.test.ts +0 -306
- package/__tests__/playwright/cases/anonymous-id-bug.html +0 -26
- package/__tests__/playwright/cases/basic.html +0 -32
- package/__tests__/playwright/cases/callbacks.html +0 -21
- package/__tests__/playwright/cases/cookie-names.html +0 -22
- package/__tests__/playwright/cases/disable-user-ids.html +0 -23
- package/__tests__/playwright/cases/dont-send.html +0 -22
- package/__tests__/playwright/cases/ip-policy.html +0 -22
- package/__tests__/playwright/cases/reset.html +0 -32
- package/__tests__/playwright/cases/segment-reference.html +0 -64
- package/__tests__/playwright/cases/url-bug.html +0 -20
- package/__tests__/playwright/integration.test.ts +0 -640
- package/__tests__/simple-syrup.ts +0 -136
- package/jest.config.js +0 -13
- package/playwrite.config.ts +0 -91
- package/rollup.config.js +0 -32
- package/src/analytics-plugin.ts +0 -988
- package/src/browser.ts +0 -163
- package/src/destination-plugins/ga4.ts +0 -138
- package/src/destination-plugins/gtm.ts +0 -142
- package/src/destination-plugins/index.ts +0 -61
- package/src/destination-plugins/logrocket.ts +0 -85
- package/src/destination-plugins/tag.ts +0 -85
- package/src/index.ts +0 -255
- package/src/method-queue.ts +0 -70
- package/src/script-loader.ts +0 -76
- package/src/tlds.ts +0 -27
- package/src/version.ts +0 -6
- package/tsconfig.json +0 -23
- package/tsconfig.test.json +0 -15
package/src/browser.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import type { AnalyticsInterface, JitsuOptions } from "@jitsu/protocols/analytics";
|
|
2
|
-
import { jitsuAnalytics } from "./index";
|
|
3
|
-
|
|
4
|
-
export type JitsuBrowserOptions = {
|
|
5
|
-
namespace?: string;
|
|
6
|
-
onload?: string;
|
|
7
|
-
initOnly?: boolean;
|
|
8
|
-
} & JitsuOptions;
|
|
9
|
-
|
|
10
|
-
function snakeToCamel(s: string) {
|
|
11
|
-
return s.replace(/([-_][a-z])/gi, $1 => {
|
|
12
|
-
return $1.toUpperCase().replace("-", "").replace("_", "");
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type Parser = {
|
|
17
|
-
path?: (name: string) => string[];
|
|
18
|
-
parse: (arg: string) => any;
|
|
19
|
-
};
|
|
20
|
-
const trimPrefix = (s: string, prefix: string) => s.replace(new RegExp(`^${prefix}`), "");
|
|
21
|
-
|
|
22
|
-
const defaultParser = (nestedPath: string[] = []) => ({
|
|
23
|
-
path: (name: string) => [
|
|
24
|
-
...nestedPath,
|
|
25
|
-
snakeToCamel(nestedPath.length > 0 ? trimPrefix(name, nestedPath.join("-") + "-") : name),
|
|
26
|
-
],
|
|
27
|
-
parse: (arg: string) => arg,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const booleanParser = (nestedPath: string[] = []) => ({
|
|
31
|
-
...defaultParser(nestedPath),
|
|
32
|
-
parse: (arg: string) => arg === "true" || arg === "1" || arg === "yes",
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const jsonParser = (nestedPath: string[] = []) => ({
|
|
36
|
-
...defaultParser(nestedPath),
|
|
37
|
-
parse: (arg: string) => {
|
|
38
|
-
try {
|
|
39
|
-
return JSON.parse(arg);
|
|
40
|
-
} catch (e) {
|
|
41
|
-
console.error(`Can't parse Jitsu Option as JSON: ${arg}`);
|
|
42
|
-
return {};
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const parsers: Partial<Record<string, Parser>> = {
|
|
48
|
-
debug: booleanParser(),
|
|
49
|
-
"privacy-disable-user-ids": booleanParser(["privacy"]),
|
|
50
|
-
"privacy-dont-send": booleanParser(["privacy"]),
|
|
51
|
-
"privacy-ip-policy": defaultParser(["privacy"]),
|
|
52
|
-
"echo-events": booleanParser(),
|
|
53
|
-
"init-only": booleanParser(),
|
|
54
|
-
"cookie-names": jsonParser(),
|
|
55
|
-
"cookie-capture": jsonParser(),
|
|
56
|
-
"default-payload-context": jsonParser(),
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
function getParser(name: string): Parser {
|
|
60
|
-
return parsers[name] || defaultParser();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function setPath(obj: any, path: string[], value: any) {
|
|
64
|
-
let current = obj;
|
|
65
|
-
let i = 0;
|
|
66
|
-
for (; i < path.length - 1; i++) {
|
|
67
|
-
const key = path[i];
|
|
68
|
-
current[key] = current[key] || {};
|
|
69
|
-
current = current[key];
|
|
70
|
-
}
|
|
71
|
-
current[path[i]] = value;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function getScriptAttributes(scriptElement: HTMLScriptElement) {
|
|
75
|
-
return scriptElement
|
|
76
|
-
.getAttributeNames()
|
|
77
|
-
.filter(name => name.indexOf("data-") === 0)
|
|
78
|
-
.map(name => name.substring("data-".length))
|
|
79
|
-
.reduce((res, name) => {
|
|
80
|
-
const parser = getParser(name);
|
|
81
|
-
const path = parser.path(name);
|
|
82
|
-
setPath(res, path, parser.parse(scriptElement.getAttribute(`data-${name}`)));
|
|
83
|
-
return res;
|
|
84
|
-
}, {});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function runCallback(callback: any, jitsu: AnalyticsInterface) {
|
|
88
|
-
if (typeof callback === "function") {
|
|
89
|
-
callback(jitsu);
|
|
90
|
-
} else if (Array.isArray(callback) && typeof callback[0] === "string") {
|
|
91
|
-
const [method, ...args] = callback;
|
|
92
|
-
if (typeof jitsu[method] === "function") {
|
|
93
|
-
jitsu[method](...args);
|
|
94
|
-
} else {
|
|
95
|
-
console.warn(`Method ${method} is not supported, ignoring callback`);
|
|
96
|
-
}
|
|
97
|
-
} else {
|
|
98
|
-
console.warn(`Invalid jitsu queue callback`, callback);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
(function () {
|
|
103
|
-
function readJitsuOptions(): JitsuBrowserOptions {
|
|
104
|
-
const scriptElement = window.document.currentScript as HTMLScriptElement;
|
|
105
|
-
if (!scriptElement) {
|
|
106
|
-
throw new Error(`Can't find script element`);
|
|
107
|
-
}
|
|
108
|
-
const host = new URL(scriptElement.src).origin;
|
|
109
|
-
|
|
110
|
-
return { ...((window as any)?.jitsuConfig || {}), ...getScriptAttributes(scriptElement), host };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const options = readJitsuOptions();
|
|
114
|
-
const JITSU_V2_ID: string = options.namespace || "jitsu";
|
|
115
|
-
if (options.debug) {
|
|
116
|
-
console.log(`Jitsu options: `, JSON.stringify(options));
|
|
117
|
-
}
|
|
118
|
-
const jitsu = jitsuAnalytics(options);
|
|
119
|
-
|
|
120
|
-
if (options.onload) {
|
|
121
|
-
const onloadFunction = window[options.onload] as any;
|
|
122
|
-
if (!onloadFunction) {
|
|
123
|
-
console.warn(`onload function ${options.onload} is not found in window`);
|
|
124
|
-
}
|
|
125
|
-
if (typeof onloadFunction === "function") {
|
|
126
|
-
onloadFunction(jitsu);
|
|
127
|
-
} else {
|
|
128
|
-
console.warn(`onload function ${options.onload} is not callable: ${typeof onloadFunction}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
window[JITSU_V2_ID] = jitsu;
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* New callback based queue, see below
|
|
135
|
-
*/
|
|
136
|
-
//make a copy of the queue
|
|
137
|
-
const callbackQueue =
|
|
138
|
-
window[JITSU_V2_ID + "Q"] && window[JITSU_V2_ID + "Q"].length ? [...window[JITSU_V2_ID + "Q"]] : [];
|
|
139
|
-
//replace push with a function that calls callback immediately
|
|
140
|
-
window[JITSU_V2_ID + "Q"] = {
|
|
141
|
-
push: (callback: any) => {
|
|
142
|
-
if (typeof callback === "function") {
|
|
143
|
-
callback(jitsu);
|
|
144
|
-
} else {
|
|
145
|
-
console.warn(`${JITSU_V2_ID}Q.push() accepts only function, ${typeof callback} given`);
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
if (options.debug) {
|
|
151
|
-
console.log(`[JITSU DEBUG] Jitsu callback queue size: ${callbackQueue.length}`, callbackQueue);
|
|
152
|
-
}
|
|
153
|
-
callbackQueue.forEach((callback: any) => {
|
|
154
|
-
try {
|
|
155
|
-
runCallback(callback, jitsu);
|
|
156
|
-
} catch (e: any) {
|
|
157
|
-
console.warn(`Error processing callback from Jitsu queue`, e);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
if (!options.initOnly) {
|
|
161
|
-
jitsu.page();
|
|
162
|
-
}
|
|
163
|
-
})();
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { loadScript } from "../script-loader";
|
|
2
|
-
import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
|
|
3
|
-
import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index";
|
|
4
|
-
|
|
5
|
-
const urlData = [
|
|
6
|
-
new Uint8Array([109, 111, 99, 46, 114, 101, 103, 97, 110, 97, 109, 103, 97, 116, 101, 108, 103, 111, 111, 103]),
|
|
7
|
-
new Uint8Array([103, 97, 116, 103]),
|
|
8
|
-
new Uint8Array([115, 106]),
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
function byteArrayToString(byteArray: Uint8Array): string {
|
|
12
|
-
const decoder = new TextDecoder();
|
|
13
|
-
return decoder.decode(byteArray);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function buildTagUrl() {
|
|
17
|
-
return urlData
|
|
18
|
-
.map(d => d.reverse())
|
|
19
|
-
.map(byteArrayToString)
|
|
20
|
-
.join("/");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type Ga4DestinationCredentials = {
|
|
24
|
-
debug?: boolean;
|
|
25
|
-
measurementIds?: string;
|
|
26
|
-
autoPageView?: boolean;
|
|
27
|
-
dataLayerName?: string;
|
|
28
|
-
} & CommonDestinationCredentials;
|
|
29
|
-
|
|
30
|
-
export const ga4Plugin: InternalPlugin<Ga4DestinationCredentials> = {
|
|
31
|
-
id: "ga4-tag",
|
|
32
|
-
async handle(config, payload: AnalyticsClientEvent) {
|
|
33
|
-
if (!applyFilters(payload, config)) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
await initGa4IfNeeded(config, payload);
|
|
37
|
-
|
|
38
|
-
const dataLayer = window[config.dataLayerName || "dataLayer"];
|
|
39
|
-
const gtag = function () {
|
|
40
|
-
dataLayer.push(arguments);
|
|
41
|
-
};
|
|
42
|
-
const ids = {
|
|
43
|
-
...(payload.userId ? { user_id: payload.userId, userId: payload.userId } : {}),
|
|
44
|
-
...(payload.anonymousId ? { anonymousId: payload.anonymousId } : {}),
|
|
45
|
-
};
|
|
46
|
-
if (payload.userId) {
|
|
47
|
-
// @ts-ignore
|
|
48
|
-
gtag("set", { user_id: payload.userId });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
switch (payload.type) {
|
|
52
|
-
case "page":
|
|
53
|
-
if (config.autoPageView) {
|
|
54
|
-
console.log("autoPageView");
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
const { properties: pageProperties, context } = payload;
|
|
58
|
-
const pageEvent = {
|
|
59
|
-
page_location: pageProperties.url,
|
|
60
|
-
page_title: pageProperties.title,
|
|
61
|
-
page_path: pageProperties.path,
|
|
62
|
-
page_hash: pageProperties.hash,
|
|
63
|
-
page_search: pageProperties.search,
|
|
64
|
-
page_referrer: context?.page?.referrer ?? "",
|
|
65
|
-
...ids,
|
|
66
|
-
};
|
|
67
|
-
// @ts-ignore
|
|
68
|
-
gtag("event", "page_view", pageEvent);
|
|
69
|
-
break;
|
|
70
|
-
case "track":
|
|
71
|
-
const { properties: trackProperties } = payload;
|
|
72
|
-
const trackEvent: any = {
|
|
73
|
-
...trackProperties,
|
|
74
|
-
...ids,
|
|
75
|
-
};
|
|
76
|
-
// @ts-ignore
|
|
77
|
-
gtag("event", payload.event, trackEvent);
|
|
78
|
-
break;
|
|
79
|
-
case "identify":
|
|
80
|
-
const { traits } = payload;
|
|
81
|
-
const identifyEvent: any = {
|
|
82
|
-
...traits,
|
|
83
|
-
...ids,
|
|
84
|
-
};
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
gtag("event", "identify", identifyEvent);
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
type GtmState = "fresh" | "loading" | "loaded" | "failed";
|
|
93
|
-
|
|
94
|
-
function getGa4State(): GtmState {
|
|
95
|
-
return window["__jitsuGa4State"] || "fresh";
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function setGa4State(s: GtmState) {
|
|
99
|
-
window["__jitsuGa4State"] = s;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function initGa4IfNeeded(config: Ga4DestinationCredentials, payload: AnalyticsClientEvent) {
|
|
103
|
-
if (getGa4State() !== "fresh") {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
setGa4State("loading");
|
|
107
|
-
|
|
108
|
-
const dlName = config.dataLayerName || "dataLayer";
|
|
109
|
-
const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : "";
|
|
110
|
-
|
|
111
|
-
// to work with both GA4 and GTM
|
|
112
|
-
const tagId = config.measurementIds;
|
|
113
|
-
|
|
114
|
-
window[dlName] = window[dlName] || [];
|
|
115
|
-
const gtag = function () {
|
|
116
|
-
window[dlName].push(arguments);
|
|
117
|
-
};
|
|
118
|
-
// @ts-ignore
|
|
119
|
-
gtag("js", new Date());
|
|
120
|
-
gtag(
|
|
121
|
-
// @ts-ignore
|
|
122
|
-
"config",
|
|
123
|
-
tagId,
|
|
124
|
-
{
|
|
125
|
-
...(payload.userId ? { user_id: payload.userId } : {}),
|
|
126
|
-
...(!config.autoPageView ? { send_page_view: false } : {}),
|
|
127
|
-
}
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
loadScript(buildTagUrl(), { query: `id=${tagId}${dlParam}`, www: true })
|
|
131
|
-
.then(() => {
|
|
132
|
-
setGa4State("loaded");
|
|
133
|
-
})
|
|
134
|
-
.catch(e => {
|
|
135
|
-
console.warn(`GA4 (containerId=${config.measurementIds}) init failed: ${e.message}`, e);
|
|
136
|
-
setGa4State("failed");
|
|
137
|
-
});
|
|
138
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { loadScript } from "../script-loader";
|
|
2
|
-
import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
|
|
3
|
-
import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index";
|
|
4
|
-
|
|
5
|
-
export type GtmDestinationCredentials = {
|
|
6
|
-
containerId?: string;
|
|
7
|
-
dataLayerName?: string;
|
|
8
|
-
} & CommonDestinationCredentials;
|
|
9
|
-
|
|
10
|
-
function omit(obj: any, ...keys: string[]) {
|
|
11
|
-
return Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const gtmPlugin: InternalPlugin<GtmDestinationCredentials> = {
|
|
15
|
-
id: "gtm",
|
|
16
|
-
async handle(config, payload: AnalyticsClientEvent) {
|
|
17
|
-
const debug = !!config.debug;
|
|
18
|
-
if (!applyFilters(payload, config)) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
await initGtmIfNeeded(config, payload);
|
|
22
|
-
|
|
23
|
-
const dataLayer = window[config.dataLayerName || "dataLayer"];
|
|
24
|
-
//traits could be in both nodes, context.traits takes precedence
|
|
25
|
-
const traits = {
|
|
26
|
-
...(payload?.traits || {}),
|
|
27
|
-
...(payload?.context?.traits || {}),
|
|
28
|
-
};
|
|
29
|
-
//remove properties that defined separately
|
|
30
|
-
const idsFromTraits = omit(traits, "id", "userId", "user_id", "anonymousId", "userId");
|
|
31
|
-
if (debug) {
|
|
32
|
-
console.debug("GTM plugin will be applied to following payload", payload);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// See https://developers.google.com/tag-platform/tag-manager/server-side/common-event-data
|
|
36
|
-
const userData = {
|
|
37
|
-
email_address: traits.email,
|
|
38
|
-
};
|
|
39
|
-
const ids = {
|
|
40
|
-
...(payload.userId ? { user_id: payload.userId, userId: payload.userId } : {}),
|
|
41
|
-
...(payload.anonymousId ? { anonymousId: payload.anonymousId } : {}),
|
|
42
|
-
...idsFromTraits,
|
|
43
|
-
user_data: Object.keys(userData).length > 0 ? userData : undefined,
|
|
44
|
-
};
|
|
45
|
-
if (debug) {
|
|
46
|
-
console.debug("GTM plugin will set following user-related data layer vars", ids);
|
|
47
|
-
}
|
|
48
|
-
const pageProperties = payload.properties || {};
|
|
49
|
-
const pageVariables = {
|
|
50
|
-
page_location: pageProperties.url || payload.context?.page?.url,
|
|
51
|
-
page_title: pageProperties.title || payload.context?.page?.title,
|
|
52
|
-
page_path: pageProperties.path || payload.context?.page?.path,
|
|
53
|
-
page_hash: pageProperties.hash || payload.context?.page?.hash,
|
|
54
|
-
page_search: pageProperties.search || payload.context?.page?.search,
|
|
55
|
-
page_referrer: payload?.context?.page?.referrer ?? "",
|
|
56
|
-
};
|
|
57
|
-
if (debug) {
|
|
58
|
-
console.debug("GTM plugin will set following context (page) related data layer vars", ids);
|
|
59
|
-
}
|
|
60
|
-
const pushToDataLayer = (data: any) => {
|
|
61
|
-
dataLayer.push(data);
|
|
62
|
-
if (debug) {
|
|
63
|
-
console.debug("GTM plugin will push following data to dataLayer", data);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
switch (payload.type) {
|
|
67
|
-
case "page":
|
|
68
|
-
const { properties: pageProperties, context } = payload;
|
|
69
|
-
const pageEvent = {
|
|
70
|
-
event: "page_view",
|
|
71
|
-
...pageVariables,
|
|
72
|
-
...ids,
|
|
73
|
-
};
|
|
74
|
-
pushToDataLayer(pageEvent);
|
|
75
|
-
break;
|
|
76
|
-
case "track":
|
|
77
|
-
const { properties: trackProperties } = payload;
|
|
78
|
-
const trackEvent: any = {
|
|
79
|
-
event: payload.event,
|
|
80
|
-
...pageVariables,
|
|
81
|
-
...trackProperties,
|
|
82
|
-
...ids,
|
|
83
|
-
};
|
|
84
|
-
pushToDataLayer(trackEvent);
|
|
85
|
-
break;
|
|
86
|
-
case "identify":
|
|
87
|
-
const { traits } = payload;
|
|
88
|
-
const identifyEvent: any = {
|
|
89
|
-
event: "identify",
|
|
90
|
-
...pageVariables,
|
|
91
|
-
...traits,
|
|
92
|
-
...ids,
|
|
93
|
-
};
|
|
94
|
-
pushToDataLayer(identifyEvent);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
dataLayer.push(function () {
|
|
98
|
-
this.reset();
|
|
99
|
-
});
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
type GtmState = "fresh" | "loading" | "loaded" | "failed";
|
|
104
|
-
|
|
105
|
-
function getGtmState(): GtmState {
|
|
106
|
-
return window["__jitsuGtmState"] || "fresh";
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function setGtmState(s: GtmState) {
|
|
110
|
-
window["__jitsuGtmState"] = s;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function initGtmIfNeeded(config: GtmDestinationCredentials, payload: AnalyticsClientEvent) {
|
|
114
|
-
if (getGtmState() !== "fresh") {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
setGtmState("loading");
|
|
118
|
-
|
|
119
|
-
const dlName = config.dataLayerName || "dataLayer";
|
|
120
|
-
const tagId = config.containerId;
|
|
121
|
-
|
|
122
|
-
(function (w, l, i) {
|
|
123
|
-
w[l] = w[l] || [];
|
|
124
|
-
w[l].push({
|
|
125
|
-
user_id: payload.userId,
|
|
126
|
-
});
|
|
127
|
-
w[l].push({
|
|
128
|
-
"gtm.start": new Date().getTime(),
|
|
129
|
-
event: "gtm.js",
|
|
130
|
-
});
|
|
131
|
-
const dl = l != "dataLayer" ? "&l=" + l : "";
|
|
132
|
-
const scriptSrc = "googletagmanager.com/gtm";
|
|
133
|
-
loadScript(scriptSrc, { www: true, js: true, query: "id=" + i + dl })
|
|
134
|
-
.then(() => {
|
|
135
|
-
setGtmState("loaded");
|
|
136
|
-
})
|
|
137
|
-
.catch(e => {
|
|
138
|
-
console.warn(`GTM (containerId=${tagId}) init failed: ${e.message}`, e);
|
|
139
|
-
setGtmState("failed");
|
|
140
|
-
});
|
|
141
|
-
})(window, dlName, tagId);
|
|
142
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
|
|
2
|
-
import { tagPlugin } from "./tag";
|
|
3
|
-
import { logrocketPlugin } from "./logrocket";
|
|
4
|
-
import { gtmPlugin } from "./gtm";
|
|
5
|
-
import { ga4Plugin } from "./ga4";
|
|
6
|
-
|
|
7
|
-
export type InternalPlugin<T> = {
|
|
8
|
-
id: string;
|
|
9
|
-
handle(config: T & { debug?: boolean }, payload: AnalyticsClientEvent): Promise<void>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type CommonDestinationCredentials = {
|
|
13
|
-
hosts?: string;
|
|
14
|
-
events?: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export function satisfyFilter(filter: string, subject: string | undefined): boolean {
|
|
18
|
-
return filter === "*" || filter.toLowerCase().trim() === (subject || "").trim().toLowerCase();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function satisfyDomainFilter(filter: string, subject: string | undefined): boolean {
|
|
22
|
-
if (filter === "*") {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
subject = subject || "";
|
|
26
|
-
|
|
27
|
-
if (filter.startsWith("*.")) {
|
|
28
|
-
return subject.endsWith(filter.substring(1));
|
|
29
|
-
} else {
|
|
30
|
-
return filter === subject;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function applyFilters(event: AnalyticsClientEvent, creds: CommonDestinationCredentials): boolean {
|
|
35
|
-
const { hosts = "*", events = "*" } = creds;
|
|
36
|
-
try {
|
|
37
|
-
const eventsArray = Array.isArray(events) ? events : events.split("\n");
|
|
38
|
-
const hostsArray = Array.isArray(hosts) ? hosts : hosts.split("\n");
|
|
39
|
-
return (
|
|
40
|
-
!!hostsArray.find(hostFilter => satisfyDomainFilter(hostFilter, event.context?.page?.host)) &&
|
|
41
|
-
(!!eventsArray.find(eventFilter => satisfyFilter(eventFilter, event.type)) ||
|
|
42
|
-
!!eventsArray.find(eventFilter => satisfyFilter(eventFilter, event.event)))
|
|
43
|
-
);
|
|
44
|
-
} catch (e) {
|
|
45
|
-
console.warn(
|
|
46
|
-
`Failed to apply filters: ${e.message}. Typeof events: ${typeof events}, typeof hosts: ${typeof hosts}. Values`,
|
|
47
|
-
events,
|
|
48
|
-
hosts
|
|
49
|
-
);
|
|
50
|
-
throw new Error(
|
|
51
|
-
`Failed to apply filters: ${e.message}. Typeof events: ${typeof events}, typeof hosts: ${typeof hosts}`
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export const internalDestinationPlugins: Record<string, InternalPlugin<any>> = {
|
|
57
|
-
[tagPlugin.id]: tagPlugin,
|
|
58
|
-
[gtmPlugin.id]: gtmPlugin,
|
|
59
|
-
[ga4Plugin.id]: ga4Plugin,
|
|
60
|
-
[logrocketPlugin.id]: logrocketPlugin,
|
|
61
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { loadScript } from "../script-loader";
|
|
2
|
-
import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
|
|
3
|
-
import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index";
|
|
4
|
-
|
|
5
|
-
export type LogRocketDestinationCredentials = {
|
|
6
|
-
appId: string;
|
|
7
|
-
} & CommonDestinationCredentials;
|
|
8
|
-
|
|
9
|
-
const cdn = "cdn.lr-ingest.io/";
|
|
10
|
-
|
|
11
|
-
export const logrocketPlugin: InternalPlugin<LogRocketDestinationCredentials> = {
|
|
12
|
-
id: "logrocket",
|
|
13
|
-
async handle(config, payload: AnalyticsClientEvent) {
|
|
14
|
-
if (!applyFilters(payload, config)) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
initLogrocketIfNeeded(config.appId);
|
|
18
|
-
|
|
19
|
-
const action = logRocket => {
|
|
20
|
-
if (payload.type === "identify" && payload.userId) {
|
|
21
|
-
logRocket.identify(payload.userId, payload.traits || {});
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
getLogRocketQueue().push(action);
|
|
25
|
-
if (getLogRocketState() === "loaded") {
|
|
26
|
-
flushLogRocketQueue(window["LogRocket"]);
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type LogRocketState = "fresh" | "loading" | "loaded" | "failed";
|
|
32
|
-
|
|
33
|
-
function getLogRocketState(): LogRocketState {
|
|
34
|
-
return window["__jitsuLrState"] || "fresh";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function setLogRocketState(s: LogRocketState) {
|
|
38
|
-
window["__jitsuLrState"] = s;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getLogRocketQueue(): ((lr: LogRocket) => void | Promise<void>)[] {
|
|
42
|
-
return window["__jitsuLrQueue"] || (window["__jitsuLrQueue"] = []);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export type LogRocket = any;
|
|
46
|
-
|
|
47
|
-
function flushLogRocketQueue(lr: LogRocket) {
|
|
48
|
-
const queue = getLogRocketQueue();
|
|
49
|
-
|
|
50
|
-
while (queue.length > 0) {
|
|
51
|
-
const method = queue.shift();
|
|
52
|
-
try {
|
|
53
|
-
const res = method(lr);
|
|
54
|
-
if (res) {
|
|
55
|
-
res.catch(e => console.warn(`Async LogRocket method failed: ${e.message}`, e));
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.warn(`LogRocket method failed: ${e.message}`, e);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function initLogrocketIfNeeded(appId: string) {
|
|
64
|
-
if (getLogRocketState() !== "fresh") {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
setLogRocketState("loading");
|
|
68
|
-
loadScript(`${cdn}LogRocket`, { min: true, attributes: { crossOrigin: "anonymous" } })
|
|
69
|
-
.then(() => {
|
|
70
|
-
if (window["LogRocket"]) {
|
|
71
|
-
try {
|
|
72
|
-
window["LogRocket"].init(appId);
|
|
73
|
-
} catch (e) {
|
|
74
|
-
console.warn(`LogRocket (id=${appId}) init failed: ${e.message}`, e);
|
|
75
|
-
setLogRocketState("failed");
|
|
76
|
-
}
|
|
77
|
-
setLogRocketState("loaded");
|
|
78
|
-
flushLogRocketQueue(window["LogRocket"]);
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
.catch(e => {
|
|
82
|
-
console.warn(`LogRocket (id=${appId}) init failed: ${e.message}`, e);
|
|
83
|
-
setLogRocketState("failed");
|
|
84
|
-
});
|
|
85
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { AnalyticsClientEvent } from "@jitsu/protocols/analytics";
|
|
2
|
-
import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index";
|
|
3
|
-
import { isInBrowser, randomId } from "../analytics-plugin";
|
|
4
|
-
|
|
5
|
-
export type TagDestinationCredentials = {
|
|
6
|
-
code: string;
|
|
7
|
-
} & CommonDestinationCredentials;
|
|
8
|
-
|
|
9
|
-
export const tagPlugin: InternalPlugin<TagDestinationCredentials> = {
|
|
10
|
-
id: "tag",
|
|
11
|
-
async handle(config, payload: AnalyticsClientEvent) {
|
|
12
|
-
if (!applyFilters(payload, config)) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
insertTags(config.code, payload);
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function insertTags(code, event: AnalyticsClientEvent, opts: { debug?: boolean } = {}) {
|
|
20
|
-
let tag;
|
|
21
|
-
try {
|
|
22
|
-
tag = JSON.parse(code);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
tag = { code, lang: "javascript" };
|
|
25
|
-
}
|
|
26
|
-
const debug = opts.debug || false;
|
|
27
|
-
if (isInBrowser()) {
|
|
28
|
-
if (tag.lang === "javascript") {
|
|
29
|
-
execJs(tag.code, event);
|
|
30
|
-
} else {
|
|
31
|
-
const codeHolder = document.createElement("span");
|
|
32
|
-
codeHolder.innerHTML = replaceMacro(tag.code, event);
|
|
33
|
-
document.body.insertAdjacentElement("beforeend", codeHolder);
|
|
34
|
-
const scripts = codeHolder.querySelectorAll("script");
|
|
35
|
-
scripts.forEach(script => {
|
|
36
|
-
const scriptClone = document.createElement("script");
|
|
37
|
-
scriptClone.type = scriptClone.type || "text/javascript";
|
|
38
|
-
if (script.hasAttribute("src")) {
|
|
39
|
-
scriptClone.src = script.src;
|
|
40
|
-
}
|
|
41
|
-
scriptClone.text = script.text;
|
|
42
|
-
if (debug) {
|
|
43
|
-
console.log(
|
|
44
|
-
`[JITSU] Executing script${script.hasAttribute("src") ? ` ${script.src}` : ""}`,
|
|
45
|
-
scriptClone.text
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
document.head.appendChild(scriptClone);
|
|
49
|
-
document.head.removeChild(scriptClone);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
if (debug) {
|
|
54
|
-
console.log(`[JITSU] insertTags(): cannot insert tags in non-browser environment`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
//This weird code is used to mask eval() usage.
|
|
60
|
-
//Although the code can be executed in the browser, some server side bundlers (like Vercel) fails
|
|
61
|
-
//the build if a direct reference to eval() is found.
|
|
62
|
-
let al = "al";
|
|
63
|
-
let ev = "ve".split("").reverse().join("");
|
|
64
|
-
const execF = globalThis[ev + al];
|
|
65
|
-
|
|
66
|
-
function execJs(code: string, event: any) {
|
|
67
|
-
const varName = `jitsu_event_${randomId()}`;
|
|
68
|
-
window[varName] = event;
|
|
69
|
-
const iif = `(function(){
|
|
70
|
-
const event = ${varName};
|
|
71
|
-
${code}
|
|
72
|
-
})()`;
|
|
73
|
-
try {
|
|
74
|
-
execF(iif);
|
|
75
|
-
} catch (e) {
|
|
76
|
-
console.error(`[JITSU] Error executing JS code: ${e.message}. Code: `, iif);
|
|
77
|
-
} finally {
|
|
78
|
-
delete window[varName];
|
|
79
|
-
}
|
|
80
|
-
return iif;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function replaceMacro(code, event) {
|
|
84
|
-
return code.replace(/{{\s*event\s*}}/g, JSON.stringify(event));
|
|
85
|
-
}
|