@jitsu/js 1.9.7-canary.903.20240731114701 → 1.9.8
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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-clean.log +1 -1
- package/.turbo/turbo-test.log +2129 -436
- package/__tests__/node/nodejs.test.ts +213 -78
- package/__tests__/playwright/cases/disable-user-ids.html +23 -0
- package/__tests__/playwright/cases/dont-send.html +22 -0
- package/__tests__/playwright/cases/ip-policy.html +22 -0
- package/__tests__/playwright/integration.test.ts +170 -2
- package/dist/analytics-plugin.d.ts +3 -5
- package/dist/browser.d.ts +5 -3
- package/dist/index.d.ts +2 -2
- package/dist/jitsu.cjs.js +165 -39
- package/dist/jitsu.d.ts +6 -1
- package/dist/jitsu.es.js +165 -40
- package/dist/web/p.js.txt +195 -44
- package/package.json +4 -3
- package/src/analytics-plugin.ts +146 -49
- package/src/browser.ts +45 -16
- package/src/destination-plugins/index.ts +0 -1
- package/src/index.ts +57 -28
- package/src/jitsu.ts +0 -93
package/src/analytics-plugin.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
/* global analytics */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
DynamicJitsuOptions,
|
|
5
|
+
JitsuOptions,
|
|
6
|
+
PersistentStorage,
|
|
7
|
+
RuntimeFacade,
|
|
8
|
+
AnalyticsClientEvent,
|
|
9
|
+
Callback,
|
|
10
|
+
ID,
|
|
11
|
+
JSONObject,
|
|
12
|
+
Options,
|
|
13
|
+
} from "@jitsu/protocols/analytics";
|
|
5
14
|
import parse from "./index";
|
|
6
15
|
|
|
7
16
|
import { AnalyticsInstance, AnalyticsPlugin } from "analytics";
|
|
@@ -26,6 +35,39 @@ const defaultConfig: Required<JitsuOptions> = {
|
|
|
26
35
|
fetchTimeoutMs: undefined,
|
|
27
36
|
s2s: undefined,
|
|
28
37
|
idEndpoint: undefined,
|
|
38
|
+
privacy: {
|
|
39
|
+
dontSend: false,
|
|
40
|
+
disableUserIds: false,
|
|
41
|
+
ipPolicy: "keep",
|
|
42
|
+
consentCategories: undefined,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// mergeConfig merges newConfig into currentConfig also making sure that all undefined values are replaced with defaultConfig values
|
|
47
|
+
const mergeConfig = (current: JitsuOptions, newConfig: JitsuOptions): void => {
|
|
48
|
+
for (const key of Object.keys(defaultConfig)) {
|
|
49
|
+
const value = newConfig[key];
|
|
50
|
+
if (key === "privacy") {
|
|
51
|
+
if (typeof value === "object") {
|
|
52
|
+
current.privacy = {
|
|
53
|
+
...defaultConfig.privacy,
|
|
54
|
+
...current.privacy,
|
|
55
|
+
...value,
|
|
56
|
+
};
|
|
57
|
+
} else if (newConfig.hasOwnProperty("privacy") && typeof value === "undefined") {
|
|
58
|
+
// explicitly set to undefined - reset to default
|
|
59
|
+
current.privacy = { ...defaultConfig.privacy };
|
|
60
|
+
}
|
|
61
|
+
} else if (typeof value === "undefined") {
|
|
62
|
+
if (newConfig.hasOwnProperty(key) || !current.hasOwnProperty(key)) {
|
|
63
|
+
// explicitly set to undefined - reset to default
|
|
64
|
+
// or was not set at all - set to default
|
|
65
|
+
current[key] = defaultConfig[key];
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
current[key] = value;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
29
71
|
};
|
|
30
72
|
|
|
31
73
|
export const parseQuery = (qs?: string): Record<string, string> => {
|
|
@@ -154,11 +196,20 @@ const defaultCookie2Key = {
|
|
|
154
196
|
__anon_id: "__eventn_id",
|
|
155
197
|
__user_traits: "__eventn_id_usr",
|
|
156
198
|
__user_id: "__eventn_uid",
|
|
199
|
+
__group_id: "__group_id",
|
|
200
|
+
__group_traits: "__group_traits",
|
|
157
201
|
};
|
|
158
202
|
|
|
159
203
|
const cookieStorage: StorageFactory = (cookieDomain, key2cookie) => {
|
|
160
204
|
return {
|
|
161
205
|
setItem(key: string, val: any) {
|
|
206
|
+
if (typeof val === "undefined") {
|
|
207
|
+
removeCookie(key2cookie[key] || key, {
|
|
208
|
+
domain: cookieDomain,
|
|
209
|
+
secure: window.location.protocol === "https:",
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
162
213
|
const strVal = typeof val === "object" && val !== null ? encodeURIComponent(JSON.stringify(val)) : val;
|
|
163
214
|
const cookieName = key2cookie[key] || key;
|
|
164
215
|
setCookie(cookieName, strVal, {
|
|
@@ -270,7 +321,11 @@ export const emptyRuntime = (config: JitsuOptions): RuntimeFacade => ({
|
|
|
270
321
|
if (config.debug) {
|
|
271
322
|
console.log(`[JITSU EMPTY RUNTIME] Set storage item ${key}=${JSON.stringify(val)}`);
|
|
272
323
|
}
|
|
273
|
-
|
|
324
|
+
if (typeof val === "undefined") {
|
|
325
|
+
delete storage[key];
|
|
326
|
+
} else {
|
|
327
|
+
storage[key] = val;
|
|
328
|
+
}
|
|
274
329
|
},
|
|
275
330
|
getItem(key: string) {
|
|
276
331
|
const val = storage[key];
|
|
@@ -372,8 +427,13 @@ function adjustPayload(
|
|
|
372
427
|
library: {
|
|
373
428
|
name: jitsuLibraryName,
|
|
374
429
|
version: jitsuVersion,
|
|
375
|
-
env:
|
|
430
|
+
env: isInBrowser() ? "browser" : "node",
|
|
376
431
|
},
|
|
432
|
+
consent: config.privacy?.consentCategories
|
|
433
|
+
? {
|
|
434
|
+
categoryPreferences: config.privacy.consentCategories,
|
|
435
|
+
}
|
|
436
|
+
: undefined,
|
|
377
437
|
userAgent: runtime.userAgent(),
|
|
378
438
|
locale: runtime.language(),
|
|
379
439
|
screen: runtime.screen(),
|
|
@@ -388,11 +448,13 @@ function adjustPayload(
|
|
|
388
448
|
url: properties.url || url,
|
|
389
449
|
encoding: properties.encoding || runtime.documentEncoding(),
|
|
390
450
|
},
|
|
391
|
-
clientIds:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
451
|
+
clientIds: !config.privacy?.disableUserIds
|
|
452
|
+
? {
|
|
453
|
+
fbc: runtime.getCookie("_fbc"),
|
|
454
|
+
fbp: runtime.getCookie("_fbp"),
|
|
455
|
+
...getGa4Ids(runtime),
|
|
456
|
+
}
|
|
457
|
+
: undefined,
|
|
396
458
|
campaign: parseUtms(query),
|
|
397
459
|
};
|
|
398
460
|
const withContext = {
|
|
@@ -406,6 +468,12 @@ function adjustPayload(
|
|
|
406
468
|
};
|
|
407
469
|
delete withContext.meta;
|
|
408
470
|
delete withContext.options;
|
|
471
|
+
if (config.privacy?.disableUserIds) {
|
|
472
|
+
delete withContext.userId;
|
|
473
|
+
delete withContext.anonymousId;
|
|
474
|
+
delete withContext.context.traits;
|
|
475
|
+
delete withContext.groupId;
|
|
476
|
+
}
|
|
409
477
|
return withContext;
|
|
410
478
|
}
|
|
411
479
|
|
|
@@ -562,7 +630,7 @@ function maskWriteKey(writeKey?: string): string | undefined {
|
|
|
562
630
|
async function send(
|
|
563
631
|
method,
|
|
564
632
|
payload,
|
|
565
|
-
jitsuConfig:
|
|
633
|
+
jitsuConfig: JitsuOptions,
|
|
566
634
|
instance: AnalyticsInstance,
|
|
567
635
|
store: PersistentStorage
|
|
568
636
|
): Promise<any> {
|
|
@@ -570,7 +638,7 @@ async function send(
|
|
|
570
638
|
console.log(`[JITSU DEBUG] sending '${method}' event:`, payload);
|
|
571
639
|
return;
|
|
572
640
|
}
|
|
573
|
-
const s2s = jitsuConfig.s2s
|
|
641
|
+
const s2s = !!jitsuConfig.s2s;
|
|
574
642
|
const url = s2s ? `${jitsuConfig.host}/api/s/s2s/${method}` : `${jitsuConfig.host}/api/s/${method}`;
|
|
575
643
|
const fetch = jitsuConfig.fetch || globalThis.fetch;
|
|
576
644
|
if (!fetch) {
|
|
@@ -592,15 +660,19 @@ async function send(
|
|
|
592
660
|
: undefined;
|
|
593
661
|
|
|
594
662
|
const authHeader = jitsuConfig.writeKey ? { "X-Write-Key": jitsuConfig.writeKey } : {};
|
|
663
|
+
const ipHeader =
|
|
664
|
+
typeof jitsuConfig.privacy?.ipPolicy === "undefined" || jitsuConfig.privacy?.ipPolicy === "keep"
|
|
665
|
+
? {}
|
|
666
|
+
: { "X-IP-Policy": jitsuConfig.privacy.ipPolicy };
|
|
595
667
|
let fetchResult;
|
|
596
668
|
try {
|
|
597
669
|
fetchResult = await fetch(url, {
|
|
598
670
|
method: "POST",
|
|
599
671
|
headers: {
|
|
600
672
|
"Content-Type": "application/json",
|
|
601
|
-
|
|
602
673
|
...authHeader,
|
|
603
674
|
...debugHeader,
|
|
675
|
+
...ipHeader,
|
|
604
676
|
},
|
|
605
677
|
body: JSON.stringify(adjustedPayload),
|
|
606
678
|
signal: abortController?.signal,
|
|
@@ -658,17 +730,12 @@ async function send(
|
|
|
658
730
|
return adjustedPayload;
|
|
659
731
|
}
|
|
660
732
|
|
|
661
|
-
export
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
export const jitsuAnalyticsPlugin = (pluginConfig: JitsuPluginConfig = {}): AnalyticsPlugin => {
|
|
665
|
-
const instanceConfig = {
|
|
666
|
-
...defaultConfig,
|
|
667
|
-
...pluginConfig,
|
|
668
|
-
};
|
|
733
|
+
export const jitsuAnalyticsPlugin = (jitsuOptions: JitsuOptions = {}, storage: PersistentStorage): AnalyticsPlugin => {
|
|
734
|
+
// just to make sure that all undefined values are replaced with defaultConfig values
|
|
735
|
+
mergeConfig(jitsuOptions, jitsuOptions);
|
|
669
736
|
return {
|
|
670
737
|
name: "jitsu",
|
|
671
|
-
config:
|
|
738
|
+
config: jitsuOptions,
|
|
672
739
|
|
|
673
740
|
initialize: async args => {
|
|
674
741
|
const { config } = args;
|
|
@@ -706,28 +773,24 @@ export const jitsuAnalyticsPlugin = (pluginConfig: JitsuPluginConfig = {}): Anal
|
|
|
706
773
|
},
|
|
707
774
|
page: args => {
|
|
708
775
|
const { payload, config, instance } = args;
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
instance,
|
|
714
|
-
pluginConfig.storageWrapper ? pluginConfig.storageWrapper(instance.storage) : instance.storage
|
|
715
|
-
);
|
|
776
|
+
if (config.privacy?.dontSend) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
return send("page", payload, config, instance, storage);
|
|
716
780
|
},
|
|
717
781
|
track: args => {
|
|
718
782
|
const { payload, config, instance } = args;
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
instance,
|
|
724
|
-
pluginConfig.storageWrapper ? pluginConfig.storageWrapper(instance.storage) : instance.storage
|
|
725
|
-
);
|
|
783
|
+
if (config.privacy?.dontSend) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
return send("track", payload, config, instance, storage);
|
|
726
787
|
},
|
|
727
788
|
identify: args => {
|
|
728
789
|
const { payload, config, instance } = args;
|
|
790
|
+
if (config.privacy?.dontSend || config.privacy?.disableUserIds) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
729
793
|
// Store traits in cache to be able to use them in page and track events that run asynchronously with current identify.
|
|
730
|
-
const storage = pluginConfig.storageWrapper ? pluginConfig.storageWrapper(instance.storage) : instance.storage;
|
|
731
794
|
storage.setItem("__user_id", payload.userId);
|
|
732
795
|
if (payload.traits && typeof payload.traits === "object") {
|
|
733
796
|
storage.setItem("__user_traits", payload.traits);
|
|
@@ -736,37 +799,54 @@ export const jitsuAnalyticsPlugin = (pluginConfig: JitsuPluginConfig = {}): Anal
|
|
|
736
799
|
},
|
|
737
800
|
reset: args => {
|
|
738
801
|
const { config, instance } = args;
|
|
739
|
-
|
|
740
|
-
storage?.reset();
|
|
802
|
+
storage.reset();
|
|
741
803
|
if (config.debug) {
|
|
742
804
|
console.log("[JITSU DEBUG] Resetting Jitsu plugin storage");
|
|
743
805
|
}
|
|
744
806
|
},
|
|
745
807
|
methods: {
|
|
746
808
|
//analytics doesn't support group as a base method, so we need to add it manually
|
|
809
|
+
configure(newOptions: DynamicJitsuOptions) {
|
|
810
|
+
const idsWasDisabled = jitsuOptions.privacy?.disableUserIds || jitsuOptions.privacy?.dontSend;
|
|
811
|
+
mergeConfig(jitsuOptions, newOptions);
|
|
812
|
+
const idsDisabledNow = jitsuOptions.privacy?.disableUserIds || jitsuOptions.privacy?.dontSend;
|
|
813
|
+
if (!idsDisabledNow && idsWasDisabled) {
|
|
814
|
+
if (jitsuOptions.debug) {
|
|
815
|
+
console.log("[JITSU] Enabling Anonymous ID. Generating new Id.");
|
|
816
|
+
}
|
|
817
|
+
const instance = (this as any).instance;
|
|
818
|
+
const newAnonymousId = uuid();
|
|
819
|
+
const userState = instance.user();
|
|
820
|
+
if (userState) {
|
|
821
|
+
userState.anonymousId = newAnonymousId;
|
|
822
|
+
}
|
|
823
|
+
storage.setItem("__anon_id", newAnonymousId);
|
|
824
|
+
instance.setAnonymousId(newAnonymousId);
|
|
825
|
+
}
|
|
826
|
+
},
|
|
747
827
|
group(groupId?: ID, traits?: JSONObject | null, options?: Options, callback?: Callback) {
|
|
828
|
+
if (jitsuOptions.privacy?.dontSend || jitsuOptions.privacy?.disableUserIds) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
748
831
|
if (typeof groupId === "number") {
|
|
749
832
|
//fix potential issues with group id being used incorrectly
|
|
750
833
|
groupId = groupId + "";
|
|
751
834
|
}
|
|
752
835
|
|
|
753
|
-
const
|
|
754
|
-
const
|
|
755
|
-
? pluginConfig.storageWrapper(analyticsInstance.storage)
|
|
756
|
-
: analyticsInstance.storage;
|
|
757
|
-
const user = analyticsInstance.user();
|
|
836
|
+
const instance = (this as any).instance;
|
|
837
|
+
const user = instance.user();
|
|
758
838
|
const userId = options?.userId || user?.userId;
|
|
759
|
-
const anonymousId = options?.anonymousId || user?.anonymousId ||
|
|
760
|
-
|
|
839
|
+
const anonymousId = options?.anonymousId || user?.anonymousId || storage.getItem("__anon_id");
|
|
840
|
+
storage.setItem("__group_id", groupId);
|
|
761
841
|
if (traits && typeof traits === "object") {
|
|
762
|
-
|
|
842
|
+
storage.setItem("__group_traits", traits);
|
|
763
843
|
}
|
|
764
844
|
return send(
|
|
765
845
|
"group",
|
|
766
846
|
{ type: "group", groupId, traits, ...(anonymousId ? { anonymousId } : {}), ...(userId ? { userId } : {}) },
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
847
|
+
jitsuOptions,
|
|
848
|
+
instance,
|
|
849
|
+
storage
|
|
770
850
|
);
|
|
771
851
|
},
|
|
772
852
|
},
|
|
@@ -786,6 +866,23 @@ export function randomId(hashString: string | undefined = ""): string {
|
|
|
786
866
|
);
|
|
787
867
|
}
|
|
788
868
|
|
|
869
|
+
export function uuid() {
|
|
870
|
+
var u = "",
|
|
871
|
+
m = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx",
|
|
872
|
+
i = 0,
|
|
873
|
+
rb = (Math.random() * 0xffffffff) | 0;
|
|
874
|
+
|
|
875
|
+
while (i++ < 36) {
|
|
876
|
+
var c = m[i - 1],
|
|
877
|
+
r = rb & 0xf,
|
|
878
|
+
v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
879
|
+
|
|
880
|
+
u += c == "-" || c == "4" ? c : v.toString(16);
|
|
881
|
+
rb = i % 8 == 0 ? (Math.random() * 0xffffffff) | 0 : rb >> 4;
|
|
882
|
+
}
|
|
883
|
+
return u;
|
|
884
|
+
}
|
|
885
|
+
|
|
789
886
|
function hash(str: string, seed: number = 0): number {
|
|
790
887
|
let h1 = 0xdeadbeef ^ seed,
|
|
791
888
|
h2 = 0x41c6ce57 ^ seed;
|
package/src/browser.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { AnalyticsInterface, JitsuOptions } from "
|
|
1
|
+
import type { AnalyticsInterface, JitsuOptions } from "@jitsu/protocols/analytics";
|
|
2
2
|
import { jitsuAnalytics } from "./index";
|
|
3
3
|
|
|
4
4
|
export type JitsuBrowserOptions = {
|
|
5
5
|
namespace?: string;
|
|
6
|
-
userId?: string;
|
|
7
6
|
onload?: string;
|
|
8
7
|
initOnly?: boolean;
|
|
9
8
|
} & JitsuOptions;
|
|
@@ -14,16 +13,47 @@ function snakeToCamel(s: string) {
|
|
|
14
13
|
});
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
export type Parser =
|
|
18
|
-
|
|
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
|
+
});
|
|
19
34
|
|
|
20
|
-
const parsers: Partial<Record<
|
|
21
|
-
debug: booleanParser,
|
|
22
|
-
|
|
35
|
+
const parsers: Partial<Record<string, Parser>> = {
|
|
36
|
+
debug: booleanParser(),
|
|
37
|
+
"privacy-disable-user-ids": booleanParser(["privacy"]),
|
|
38
|
+
"privacy-dont-send": booleanParser(["privacy"]),
|
|
39
|
+
"privacy-ip-policy": defaultParser(["privacy"]),
|
|
40
|
+
"echo-events": booleanParser(),
|
|
41
|
+
"init-only": booleanParser(),
|
|
23
42
|
};
|
|
24
43
|
|
|
25
|
-
function getParser(name:
|
|
26
|
-
return parsers[name] || (
|
|
44
|
+
function getParser(name: string): Parser {
|
|
45
|
+
return parsers[name] || defaultParser();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function setPath(obj: any, path: string[], value: any) {
|
|
49
|
+
let current = obj;
|
|
50
|
+
let i = 0;
|
|
51
|
+
for (; i < path.length - 1; i++) {
|
|
52
|
+
const key = path[i];
|
|
53
|
+
current[key] = current[key] || {};
|
|
54
|
+
current = current[key];
|
|
55
|
+
}
|
|
56
|
+
current[path[i]] = value;
|
|
27
57
|
}
|
|
28
58
|
|
|
29
59
|
function getScriptAttributes(scriptElement: HTMLScriptElement) {
|
|
@@ -31,13 +61,12 @@ function getScriptAttributes(scriptElement: HTMLScriptElement) {
|
|
|
31
61
|
.getAttributeNames()
|
|
32
62
|
.filter(name => name.indexOf("data-") === 0)
|
|
33
63
|
.map(name => name.substring("data-".length))
|
|
34
|
-
.reduce(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
64
|
+
.reduce((res, name) => {
|
|
65
|
+
const parser = getParser(name);
|
|
66
|
+
const path = parser.path(name);
|
|
67
|
+
setPath(res, path, parser.parse(scriptElement.getAttribute(`data-${name}`)));
|
|
68
|
+
return res;
|
|
69
|
+
}, {});
|
|
41
70
|
}
|
|
42
71
|
|
|
43
72
|
function runCallback(callback: any, jitsu: AnalyticsInterface) {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import Analytics from "analytics";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { jitsuAnalyticsPlugin, emptyRuntime, isInBrowser, windowRuntime, uuid } from "./analytics-plugin";
|
|
3
|
+
import {
|
|
4
|
+
Callback,
|
|
5
|
+
DispatchedEvent,
|
|
6
|
+
ID,
|
|
7
|
+
JSONObject,
|
|
8
|
+
Options,
|
|
9
|
+
AnalyticsInterface,
|
|
10
|
+
JitsuOptions,
|
|
11
|
+
PersistentStorage,
|
|
12
|
+
RuntimeFacade,
|
|
13
|
+
DynamicJitsuOptions,
|
|
14
|
+
} from "@jitsu/protocols/analytics";
|
|
5
15
|
|
|
6
16
|
export default function parse(input) {
|
|
7
17
|
let value = input;
|
|
@@ -31,6 +41,7 @@ export const emptyAnalytics: AnalyticsInterface = {
|
|
|
31
41
|
identify: () => Promise.resolve({}),
|
|
32
42
|
group: () => Promise.resolve({}),
|
|
33
43
|
reset: () => Promise.resolve({}),
|
|
44
|
+
configure: () => {},
|
|
34
45
|
};
|
|
35
46
|
|
|
36
47
|
function createUnderlyingAnalyticsInstance(
|
|
@@ -38,8 +49,6 @@ function createUnderlyingAnalyticsInstance(
|
|
|
38
49
|
rt: RuntimeFacade,
|
|
39
50
|
plugins: any[] = []
|
|
40
51
|
): AnalyticsInterface {
|
|
41
|
-
const storage = rt.store();
|
|
42
|
-
|
|
43
52
|
const storageCache: any = {};
|
|
44
53
|
|
|
45
54
|
// AnalyticsInstance's storage is async somewhere inside. So if we make 'page' call right after 'identify' call
|
|
@@ -47,6 +56,9 @@ function createUnderlyingAnalyticsInstance(
|
|
|
47
56
|
// to avoid that we use in-memory cache for storage
|
|
48
57
|
const cachingStorageWrapper = (persistentStorage: PersistentStorage) => ({
|
|
49
58
|
setItem(key: string, val: any) {
|
|
59
|
+
if (opts.privacy?.dontSend || opts.privacy?.disableUserIds) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
50
62
|
if (opts.debug) {
|
|
51
63
|
console.log(`[JITSU DEBUG] Caching storage setItem: ${key}=${val}`);
|
|
52
64
|
}
|
|
@@ -54,6 +66,9 @@ function createUnderlyingAnalyticsInstance(
|
|
|
54
66
|
persistentStorage.setItem(key, val);
|
|
55
67
|
},
|
|
56
68
|
getItem(key: string) {
|
|
69
|
+
if (opts.privacy?.dontSend || opts.privacy?.disableUserIds) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
57
72
|
const value = storageCache[key] || persistentStorage.getItem(key);
|
|
58
73
|
if (opts.debug) {
|
|
59
74
|
console.log(
|
|
@@ -66,7 +81,7 @@ function createUnderlyingAnalyticsInstance(
|
|
|
66
81
|
for (const key of [...Object.keys(storageCache)]) {
|
|
67
82
|
delete storageCache[key];
|
|
68
83
|
}
|
|
69
|
-
|
|
84
|
+
persistentStorage.reset();
|
|
70
85
|
},
|
|
71
86
|
removeItem(key: string) {
|
|
72
87
|
if (opts.debug) {
|
|
@@ -76,14 +91,15 @@ function createUnderlyingAnalyticsInstance(
|
|
|
76
91
|
persistentStorage.removeItem(key);
|
|
77
92
|
},
|
|
78
93
|
});
|
|
94
|
+
const storage = cachingStorageWrapper(rt.store());
|
|
79
95
|
|
|
80
96
|
const analytics = Analytics({
|
|
81
97
|
debug: !!opts.debug,
|
|
82
98
|
storage,
|
|
83
|
-
plugins: [jitsuAnalyticsPlugin(
|
|
99
|
+
plugins: [jitsuAnalyticsPlugin(opts, storage), ...plugins],
|
|
84
100
|
} as any);
|
|
85
101
|
|
|
86
|
-
|
|
102
|
+
const a = {
|
|
87
103
|
...analytics,
|
|
88
104
|
page: (...args) => {
|
|
89
105
|
if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") {
|
|
@@ -124,7 +140,7 @@ function createUnderlyingAnalyticsInstance(
|
|
|
124
140
|
if (opts.debug) {
|
|
125
141
|
console.log("[JITSU DEBUG] Setting anonymous id to " + id);
|
|
126
142
|
}
|
|
127
|
-
//Workaround for analytics.js bug. Underlying setAnonymousId doesn't
|
|
143
|
+
//Workaround for analytics.js bug. Underlying setAnonymousId doesn't set the id immediately,
|
|
128
144
|
//so we got to it manually here. See https://github.com/jitsucom/jitsu/issues/1060
|
|
129
145
|
storage.setItem("__anon_id", id);
|
|
130
146
|
const userState = analytics.user();
|
|
@@ -144,6 +160,19 @@ function createUnderlyingAnalyticsInstance(
|
|
|
144
160
|
console.log("[JITSU DEBUG] User state after reset", JSON.stringify(analytics.user()));
|
|
145
161
|
}
|
|
146
162
|
},
|
|
163
|
+
async configure(options: DynamicJitsuOptions) {
|
|
164
|
+
if (opts.debug) {
|
|
165
|
+
console.log("[JITSU DEBUG] Update Jitsu config with", JSON.stringify(options));
|
|
166
|
+
}
|
|
167
|
+
if (options.privacy?.disableUserIds || options.privacy?.dontSend) {
|
|
168
|
+
storage.reset();
|
|
169
|
+
}
|
|
170
|
+
for (const plugin of Object.values(analytics.plugins)) {
|
|
171
|
+
if (typeof plugin["configure"] === "function") {
|
|
172
|
+
plugin["configure"](options);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
147
176
|
async group(
|
|
148
177
|
groupId?: ID,
|
|
149
178
|
traits?: JSONObject | null,
|
|
@@ -161,6 +190,10 @@ function createUnderlyingAnalyticsInstance(
|
|
|
161
190
|
return results[0];
|
|
162
191
|
},
|
|
163
192
|
} as AnalyticsInterface;
|
|
193
|
+
if (opts.privacy?.disableUserIds || opts.privacy?.dontSend) {
|
|
194
|
+
storage.reset();
|
|
195
|
+
}
|
|
196
|
+
return a;
|
|
164
197
|
}
|
|
165
198
|
|
|
166
199
|
/**
|
|
@@ -171,7 +204,9 @@ function fixOptions(opts: JitsuOptions): JitsuOptions {
|
|
|
171
204
|
return {
|
|
172
205
|
...opts,
|
|
173
206
|
host:
|
|
174
|
-
opts.host.indexOf("https://") !== 0 && opts.host.indexOf("http://") !== 0
|
|
207
|
+
(opts.host ?? "").indexOf("https://") !== 0 && (opts.host ?? "").indexOf("http://") !== 0
|
|
208
|
+
? `https://${opts.host}`
|
|
209
|
+
: opts.host,
|
|
175
210
|
};
|
|
176
211
|
}
|
|
177
212
|
|
|
@@ -205,22 +240,16 @@ export function jitsuAnalytics(_opts: JitsuOptions): AnalyticsInterface {
|
|
|
205
240
|
// }
|
|
206
241
|
}
|
|
207
242
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
rb = i % 8 == 0 ? (Math.random() * 0xffffffff) | 0 : rb >> 4;
|
|
221
|
-
}
|
|
222
|
-
return u;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export * from "./jitsu";
|
|
243
|
+
export {
|
|
244
|
+
Callback,
|
|
245
|
+
DispatchedEvent,
|
|
246
|
+
ID,
|
|
247
|
+
JSONObject,
|
|
248
|
+
Options,
|
|
249
|
+
AnalyticsInterface,
|
|
250
|
+
JitsuOptions,
|
|
251
|
+
PersistentStorage,
|
|
252
|
+
RuntimeFacade,
|
|
253
|
+
DynamicJitsuOptions,
|
|
254
|
+
};
|
|
226
255
|
export * from "./analytics-plugin";
|
package/src/jitsu.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { AnalyticsInterface } from "@jitsu/protocols/analytics";
|
|
2
|
-
|
|
3
|
-
type JitsuOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* API Key. Optional. If not set, Jitsu will send event to the server without auth, and server
|
|
6
|
-
* will link the call to configured source by domain name
|
|
7
|
-
*/
|
|
8
|
-
writeKey?: string;
|
|
9
|
-
/**
|
|
10
|
-
* API Host. Default value: same host as script origin
|
|
11
|
-
*/
|
|
12
|
-
host?: string;
|
|
13
|
-
/**
|
|
14
|
-
* To enable debug logging
|
|
15
|
-
*/
|
|
16
|
-
debug?: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Explicitly specify cookie domain. If not set, cookie domain will be set to top level
|
|
19
|
-
* of the current domain. Example: if JS lives on "app.example.com", cookie domain will be
|
|
20
|
-
* set to ".example.com". If it lives on "example.com", cookie domain will be set to ".example.com" too
|
|
21
|
-
*/
|
|
22
|
-
cookieDomain?: string;
|
|
23
|
-
/**
|
|
24
|
-
* Provide fetch implementation. It is required if you want to use Jitsu in NodeJS
|
|
25
|
-
*/
|
|
26
|
-
fetch?: typeof fetch;
|
|
27
|
-
/**
|
|
28
|
-
* Which runtime to use. Runtime is used for obtaining context of the event: cookes,
|
|
29
|
-
* url, etc. At the moment, Jitsu supports browser runtime and NodeJS runtime, but you
|
|
30
|
-
* can provide your own implementation.
|
|
31
|
-
*
|
|
32
|
-
* If it's not set, the runtime will be detected automatically by presense of `window` object
|
|
33
|
-
*/
|
|
34
|
-
runtime?: RuntimeFacade;
|
|
35
|
-
/**
|
|
36
|
-
* If set to true, jitsu will output events in console. In this case you don't need to set
|
|
37
|
-
* writeKey / host. It's useful for debugging development environment
|
|
38
|
-
*/
|
|
39
|
-
echoEvents?: boolean;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* If true, events will go to s2s endpoints like ${host}/api/s/s2s/{type}. Otherwise they'll go to ${host}/api/s/{type}.
|
|
43
|
-
*
|
|
44
|
-
* If not set at all, it will be detected automatically by presence of `window` object
|
|
45
|
-
*/
|
|
46
|
-
s2s?: boolean;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Timeout for fetch requests. Default value: 5000
|
|
50
|
-
*/
|
|
51
|
-
fetchTimeoutMs?: number;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Endpoint that makes sure that Jitsu anonymousId cookie is set as server (httpOnly) cookie.
|
|
55
|
-
* Endpoint must be hosted on the same domain as the site where Jitsu code is installed.
|
|
56
|
-
* Required to overcome Safari ITP restrictions.
|
|
57
|
-
*/
|
|
58
|
-
idEndpoint?: string;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
type PersistentStorage = {
|
|
62
|
-
getItem: (key: string, options?: any) => any;
|
|
63
|
-
setItem: (key: string, value: any, options?: any) => void;
|
|
64
|
-
removeItem: (key: string, options?: any) => void;
|
|
65
|
-
reset: () => void;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
type RuntimeFacade = {
|
|
69
|
-
store(): PersistentStorage;
|
|
70
|
-
userAgent(): string | undefined;
|
|
71
|
-
language(): string | undefined;
|
|
72
|
-
pageUrl(): string | undefined;
|
|
73
|
-
documentEncoding(): string | undefined;
|
|
74
|
-
getCookie(name: string): string | undefined;
|
|
75
|
-
getCookies(): Record<string, string>;
|
|
76
|
-
|
|
77
|
-
timezoneOffset(): number | undefined;
|
|
78
|
-
screen():
|
|
79
|
-
| {
|
|
80
|
-
width: number;
|
|
81
|
-
height: number;
|
|
82
|
-
innerWidth: number;
|
|
83
|
-
innerHeight: number;
|
|
84
|
-
density: number;
|
|
85
|
-
}
|
|
86
|
-
| undefined;
|
|
87
|
-
referrer(): string | undefined;
|
|
88
|
-
pageTitle(): string | undefined;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
export declare function jitsuAnalytics(opts: JitsuOptions): AnalyticsInterface;
|
|
92
|
-
|
|
93
|
-
export { AnalyticsInterface, JitsuOptions, PersistentStorage, RuntimeFacade };
|