@workleap/mixpanel 6.0.3 → 6.1.0
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/CHANGELOG.md +6 -0
- package/dist/js/MixpanelClient.d.ts +4 -0
- package/dist/js/MixpanelClient.js +9 -2
- package/dist/js/MixpanelClient.js.map +1 -1
- package/dist/js/initializeMixpanel.d.ts +5 -0
- package/dist/js/initializeMixpanel.js +3 -2
- package/dist/js/initializeMixpanel.js.map +1 -1
- package/dist/js/properties.d.ts +1 -1
- package/dist/js/properties.js +5 -4
- package/dist/js/properties.js.map +1 -1
- package/dist/js/utils.d.ts +1 -0
- package/dist/js/utils.js +13 -1
- package/dist/js/utils.js.map +1 -1
- package/package.json +3 -4
- package/src/js/MixpanelClient.ts +10 -2
- package/src/js/initializeMixpanel.ts +8 -2
- package/src/js/properties.ts +5 -4
- package/src/js/utils.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @workleap/mixpanel
|
|
2
2
|
|
|
3
|
+
## 6.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#281](https://github.com/workleap/wl-telemetry/pull/281) [`7bc53df`](https://github.com/workleap/wl-telemetry/commit/7bc53df81efadfc0bd87f475995ee92916b5802e) Thanks [@patricklafrance](https://github.com/patricklafrance)! - Redact the private query parameters from the `$current_url` and `$referrer` properties sent with every Mixpanel event, and remove the fragment from `$current_url`. The `token`, `email` and `password` query parameters are redacted by default, additional names can be provided with the new `privateQueryParameterNames` option. An application using hash routing will no longer get the route in `$current_url`.
|
|
8
|
+
|
|
3
9
|
## 6.0.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -25,6 +25,10 @@ export interface MixpanelClientOptions {
|
|
|
25
25
|
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
26
26
|
*/
|
|
27
27
|
productId?: string;
|
|
28
|
+
/**
|
|
29
|
+
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
30
|
+
*/
|
|
31
|
+
privateQueryParameterNames?: string[];
|
|
28
32
|
}
|
|
29
33
|
export declare class MixpanelClientImpl implements MixpanelClient {
|
|
30
34
|
#private;
|
|
@@ -2,16 +2,23 @@ import { getBaseProperties } from "./properties.js";
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
const DefaultPrivateQueryParameterNames = [
|
|
6
|
+
"token",
|
|
7
|
+
"email",
|
|
8
|
+
"password"
|
|
9
|
+
];
|
|
5
10
|
class MixpanelClientImpl {
|
|
6
11
|
#globalEventProperties = new Map();
|
|
7
12
|
#endpoint;
|
|
8
13
|
#logger;
|
|
9
14
|
#productId;
|
|
15
|
+
#privateQueryParameterNames;
|
|
10
16
|
constructor(endpoint, logger, options = {}){
|
|
11
|
-
const { productId } = options;
|
|
17
|
+
const { productId, privateQueryParameterNames = [] } = options;
|
|
12
18
|
this.#endpoint = endpoint;
|
|
13
19
|
this.#logger = logger;
|
|
14
20
|
this.#productId = productId;
|
|
21
|
+
this.#privateQueryParameterNames = DefaultPrivateQueryParameterNames.concat(privateQueryParameterNames);
|
|
15
22
|
}
|
|
16
23
|
// IMPORTANT: If you update this method, make sure to update the MixpanelPartialClient
|
|
17
24
|
// interface as well in @workleap-telemetry/core.
|
|
@@ -20,7 +27,7 @@ class MixpanelClientImpl {
|
|
|
20
27
|
const trackFunction = async (eventName, properties, _options = {})=>{
|
|
21
28
|
try {
|
|
22
29
|
const { keepAlive = false } = _options;
|
|
23
|
-
const baseProperties = getBaseProperties();
|
|
30
|
+
const baseProperties = getBaseProperties(this.#privateQueryParameterNames);
|
|
24
31
|
const allProperties = {
|
|
25
32
|
...baseProperties,
|
|
26
33
|
...Object.fromEntries(this.#globalEventProperties),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"js/MixpanelClient.js","sources":["../../src/js/MixpanelClient.ts"],"sourcesContent":["import type { CreateMixpanelTrackingFunctionOptions, MixpanelTrackingFunction } from \"@workleap-telemetry/core\";\nimport type { Logger } from \"@workleap/logging\";\nimport { getBaseProperties } from \"./properties.ts\";\n\n/**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport interface MixpanelClient {\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n createTrackingFunction: (options?: CreateMixpanelTrackingFunctionOptions) => MixpanelTrackingFunction;\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n setGlobalEventProperty: (key: string, value: unknown) => void;\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n setGlobalEventProperties: (values: Record<string, unknown>) => void;\n}\n\n/**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport interface MixpanelClientOptions {\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n productId?: string;\n}\n\nexport class MixpanelClientImpl implements MixpanelClient {\n readonly #globalEventProperties = new Map<string, unknown>();\n readonly #endpoint: string;\n readonly #logger: Logger;\n readonly #productId?: string;\n\n constructor(endpoint: string, logger: Logger, options: MixpanelClientOptions = {}) {\n const { productId } = options;\n\n this.#endpoint = endpoint;\n this.#logger = logger;\n this.#productId = productId;\n }\n\n // IMPORTANT: If you update this method, make sure to update the MixpanelPartialClient\n // interface as well in @workleap-telemetry/core.\n createTrackingFunction(options: CreateMixpanelTrackingFunctionOptions = {}) {\n const { productId, targetProductId, additionalProperties = {} } = options;\n\n const trackFunction: MixpanelTrackingFunction = async (eventName, properties, _options = {}) => {\n try {\n const { keepAlive = false } = _options;\n\n const baseProperties = getBaseProperties();\n\n const allProperties = {\n ...baseProperties,\n ...Object.fromEntries(this.#globalEventProperties),\n ...additionalProperties,\n ...properties\n };\n\n await fetch(this.#endpoint, {\n method: \"POST\",\n credentials: \"include\",\n keepalive: keepAlive,\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n eventName,\n productIdentifier: productId ?? this.#productId,\n // Not sure why, but it seems important to send \"null\" if not target product identifier\n // are provided.\n targetProductIdentifier: targetProductId ?? null,\n properties: allProperties\n })\n });\n } catch (error: unknown) {\n this.#logger\n .withText(`[mixpanel] An error occurred while sending a tracking event to \"${this.#endpoint}\":`)\n .withError(error as Error)\n .error();\n }\n };\n\n return trackFunction;\n }\n\n setGlobalEventProperty(key: string, value: unknown) {\n this.#globalEventProperties.set(key, value);\n }\n\n setGlobalEventProperties(values: Record<string, unknown>) {\n Object.keys(values).forEach(x => {\n this.#globalEventProperties.set(x, values[x]);\n });\n }\n}\n"],"names":["getBaseProperties","MixpanelClientImpl","Map","endpoint","logger","options","productId","targetProductId","additionalProperties","trackFunction","eventName","properties","_options","keepAlive","baseProperties","allProperties","Object","fetch","JSON","error","key","value","values","x"],"mappings":";;;AAEoD;
|
|
1
|
+
{"version":3,"file":"js/MixpanelClient.js","sources":["../../src/js/MixpanelClient.ts"],"sourcesContent":["import type { CreateMixpanelTrackingFunctionOptions, MixpanelTrackingFunction } from \"@workleap-telemetry/core\";\nimport type { Logger } from \"@workleap/logging\";\nimport { getBaseProperties } from \"./properties.ts\";\n\n/**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport interface MixpanelClient {\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n createTrackingFunction: (options?: CreateMixpanelTrackingFunctionOptions) => MixpanelTrackingFunction;\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n setGlobalEventProperty: (key: string, value: unknown) => void;\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n setGlobalEventProperties: (values: Record<string, unknown>) => void;\n}\n\n/**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport interface MixpanelClientOptions {\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n productId?: string;\n /**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n privateQueryParameterNames?: string[];\n}\n\nconst DefaultPrivateQueryParameterNames = [\"token\", \"email\", \"password\"];\n\nexport class MixpanelClientImpl implements MixpanelClient {\n readonly #globalEventProperties = new Map<string, unknown>();\n readonly #endpoint: string;\n readonly #logger: Logger;\n readonly #productId?: string;\n readonly #privateQueryParameterNames: string[];\n\n constructor(endpoint: string, logger: Logger, options: MixpanelClientOptions = {}) {\n const { productId, privateQueryParameterNames = [] } = options;\n\n this.#endpoint = endpoint;\n this.#logger = logger;\n this.#productId = productId;\n this.#privateQueryParameterNames = DefaultPrivateQueryParameterNames.concat(privateQueryParameterNames);\n }\n\n // IMPORTANT: If you update this method, make sure to update the MixpanelPartialClient\n // interface as well in @workleap-telemetry/core.\n createTrackingFunction(options: CreateMixpanelTrackingFunctionOptions = {}) {\n const { productId, targetProductId, additionalProperties = {} } = options;\n\n const trackFunction: MixpanelTrackingFunction = async (eventName, properties, _options = {}) => {\n try {\n const { keepAlive = false } = _options;\n\n const baseProperties = getBaseProperties(this.#privateQueryParameterNames);\n\n const allProperties = {\n ...baseProperties,\n ...Object.fromEntries(this.#globalEventProperties),\n ...additionalProperties,\n ...properties\n };\n\n await fetch(this.#endpoint, {\n method: \"POST\",\n credentials: \"include\",\n keepalive: keepAlive,\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n eventName,\n productIdentifier: productId ?? this.#productId,\n // Not sure why, but it seems important to send \"null\" if not target product identifier\n // are provided.\n targetProductIdentifier: targetProductId ?? null,\n properties: allProperties\n })\n });\n } catch (error: unknown) {\n this.#logger\n .withText(`[mixpanel] An error occurred while sending a tracking event to \"${this.#endpoint}\":`)\n .withError(error as Error)\n .error();\n }\n };\n\n return trackFunction;\n }\n\n setGlobalEventProperty(key: string, value: unknown) {\n this.#globalEventProperties.set(key, value);\n }\n\n setGlobalEventProperties(values: Record<string, unknown>) {\n Object.keys(values).forEach(x => {\n this.#globalEventProperties.set(x, values[x]);\n });\n }\n}\n"],"names":["getBaseProperties","DefaultPrivateQueryParameterNames","MixpanelClientImpl","Map","endpoint","logger","options","productId","privateQueryParameterNames","targetProductId","additionalProperties","trackFunction","eventName","properties","_options","keepAlive","baseProperties","allProperties","Object","fetch","JSON","error","key","value","values","x"],"mappings":";;;AAEoD;AAkCpD,MAAMC,oCAAoC;IAAC;IAAS;IAAS;CAAW;AAEjE,MAAMC;IACA,sBAAsB,GAAG,IAAIC,MAAuB;IACpD,SAAS,CAAS;IAClB,OAAO,CAAS;IAChB,UAAU,CAAU;IACpB,2BAA2B,CAAW;IAE/C,YAAYC,QAAgB,EAAEC,MAAc,EAAEC,UAAiC,CAAC,CAAC,CAAE;QAC/E,MAAM,EAAEC,SAAS,EAAEC,6BAA6B,EAAE,EAAE,GAAGF;QAEvD,IAAI,CAAC,SAAS,GAAGF;QACjB,IAAI,CAAC,OAAO,GAAGC;QACf,IAAI,CAAC,UAAU,GAAGE;QAClB,IAAI,CAAC,2BAA2B,GAAGN,kCAAkC,MAAM,CAACO;IAChF;IAEA,sFAAsF;IACtF,iDAAiD;IACjD,uBAAuBF,UAAiD,CAAC,CAAC,EAAE;QACxE,MAAM,EAAEC,SAAS,EAAEE,eAAe,EAAEC,uBAAuB,CAAC,CAAC,EAAE,GAAGJ;QAElE,MAAMK,gBAA0C,OAAOC,WAAWC,YAAYC,WAAW,CAAC,CAAC;YACvF,IAAI;gBACA,MAAM,EAAEC,YAAY,KAAK,EAAE,GAAGD;gBAE9B,MAAME,iBAAiBhB,iBAAiBA,CAAC,IAAI,CAAC,2BAA2B;gBAEzE,MAAMiB,gBAAgB;oBAClB,GAAGD,cAAc;oBACjB,GAAGE,OAAO,WAAW,CAAC,IAAI,CAAC,sBAAsB,CAAC;oBAClD,GAAGR,oBAAoB;oBACvB,GAAGG,UAAU;gBACjB;gBAEA,MAAMM,MAAM,IAAI,CAAC,SAAS,EAAE;oBACxB,QAAQ;oBACR,aAAa;oBACb,WAAWJ;oBACX,SAAS;wBAAE,gBAAgB;oBAAmB;oBAC9C,MAAMK,KAAK,SAAS,CAAC;wBACjBR;wBACA,mBAAmBL,aAAa,IAAI,CAAC,UAAU;wBAC/C,uFAAuF;wBACvF,gBAAgB;wBAChB,yBAAyBE,mBAAmB;wBAC5C,YAAYQ;oBAChB;gBACJ;YACJ,EAAE,OAAOI,OAAgB;gBACrB,IAAI,CAAC,OAAO,CACP,QAAQ,CAAC,CAAC,gEAAgE,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAC9F,SAAS,CAACA,OACV,KAAK;YACd;QACJ;QAEA,OAAOV;IACX;IAEA,uBAAuBW,GAAW,EAAEC,KAAc,EAAE;QAChD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAACD,KAAKC;IACzC;IAEA,yBAAyBC,MAA+B,EAAE;QACtDN,OAAO,IAAI,CAACM,QAAQ,OAAO,CAACC,CAAAA;YACxB,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAACA,GAAGD,MAAM,CAACC,EAAE;QAChD;IACJ;AACJ"}
|
|
@@ -18,6 +18,11 @@ export interface InitializeMixpanelOptions {
|
|
|
18
18
|
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
19
19
|
*/
|
|
20
20
|
trackingEndpoint?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Names of additional query parameters to redact from the URLs sent with every event.
|
|
23
|
+
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
24
|
+
*/
|
|
25
|
+
privateQueryParameterNames?: string[];
|
|
21
26
|
/**
|
|
22
27
|
* Context including telemetry correlation ids.
|
|
23
28
|
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
@@ -31,12 +31,13 @@ function __resetRegistrationGuard() {
|
|
|
31
31
|
}
|
|
32
32
|
class MixpanelInitializer {
|
|
33
33
|
initialize(envOrTrackingApiBaseUrl, options = {}) {
|
|
34
|
-
const { productId, trackingEndpoint, telemetryContext, logRocketInstrumentationClient, verbose = false, loggers = [] } = options;
|
|
34
|
+
const { productId, trackingEndpoint, privateQueryParameterNames, telemetryContext, logRocketInstrumentationClient, verbose = false, loggers = [] } = options;
|
|
35
35
|
const logger = createCompositeLogger(verbose, loggers);
|
|
36
36
|
const endpoint = getTrackingEndpoint(envOrTrackingApiBaseUrl, trackingEndpoint);
|
|
37
37
|
logger.information("[mixpanel] Mixpanel is initialized.");
|
|
38
38
|
const client = new MixpanelClientImpl(endpoint, logger, {
|
|
39
|
-
productId
|
|
39
|
+
productId,
|
|
40
|
+
privateQueryParameterNames
|
|
40
41
|
});
|
|
41
42
|
// Set the telemetry correlation ids on every Mixpanel event.
|
|
42
43
|
if (telemetryContext) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"js/initializeMixpanel.js","sources":["../../src/js/initializeMixpanel.ts"],"sourcesContent":["import { HasExecutedGuard, type LogRocketInstrumentationPartialClient, type TelemetryContext } from \"@workleap-telemetry/core\";\nimport { createCompositeLogger, type RootLogger } from \"@workleap/logging\";\nimport { getTrackingEndpoint, type MixpanelEnvironment } from \"./env.ts\";\nimport { MixpanelClientImpl, type MixpanelClient } from \"./MixpanelClient.ts\";\nimport { getTelemetryProperties, OtherProperties } from \"./properties.ts\";\n\nlet registrationGuardInstance: HasExecutedGuard | undefined;\n\n// It's important to use a lazy singleton instead of a singleton to avoid\n// any future probleme related to HMR, tree-shaking, SSR, etc..\nfunction getRegistrationGuard() {\n if (!registrationGuardInstance) {\n registrationGuardInstance = new HasExecutedGuard();\n }\n\n return registrationGuardInstance;\n}\n\n// This function should only be used by tests.\nexport function __resetRegistrationGuard() {\n if (registrationGuardInstance) {\n registrationGuardInstance.reset();\n }\n}\n\n///////////////////////////\n\n/**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport interface InitializeMixpanelOptions {\n /**\n * An optional product id that will be attached to every event.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n productId?: string;\n /**\n * An optional tracking endpoint.\n * @default \"tracking/track\"\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n trackingEndpoint?: string;\n /**\n * Context including telemetry correlation ids.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n telemetryContext?: TelemetryContext;\n /**\n * An optional LogRocket client to automatically add the LogRocket\n * session URL to Mixpanel events once it's available.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n logRocketInstrumentationClient?: LogRocketInstrumentationPartialClient;\n /**\n * Whether or not debug information should be logged to the console.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n verbose?: boolean;\n /**\n * An array of RootLogger instances.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n loggers?: RootLogger[];\n}\n\nexport class MixpanelInitializer {\n initialize(envOrTrackingApiBaseUrl: MixpanelEnvironment | (string & {}), options: Omit<InitializeMixpanelOptions, \"globalEventProperties\"> = {}) {\n const { productId, trackingEndpoint, telemetryContext, logRocketInstrumentationClient, verbose = false, loggers = [] } = options;\n\n const logger = createCompositeLogger(verbose, loggers);\n const endpoint = getTrackingEndpoint(envOrTrackingApiBaseUrl, trackingEndpoint);\n\n logger.information(\"[mixpanel] Mixpanel is initialized.\");\n\n const client = new MixpanelClientImpl(endpoint, logger, {\n productId\n });\n\n // Set the telemetry correlation ids on every Mixpanel event.\n if (telemetryContext) {\n for (const [key, value] of Object.entries(getTelemetryProperties(telemetryContext))) {\n client.setGlobalEventProperty(key, value);\n }\n }\n\n // If a LogRocket client is already available, register a listener to get the LogRocket session url\n // once it's available to set it on every Mixpanel event.\n if (logRocketInstrumentationClient) {\n logRocketInstrumentationClient.registerGetSessionUrlListener((sessionUrl: string) => {\n logger.withText(\"[mixpanel] Received LogRocket session replay URL:\").withText(sessionUrl).debug();\n\n client.setGlobalEventProperty(OtherProperties.LogRocketSessionUrl, sessionUrl);\n });\n }\n\n // The cast is to normalize the inferred return type to the \"LogRocketInstrumentationClient\" type.\n return client as MixpanelClient;\n }\n}\n\n/**\n * Initialize Mixpanel telemetry.\n * @param envOrTrackingApiBaseUrl The environment to get the navigation url from or a base URL.\n * @param options Mixpanel instrumentation options.\n * @returns {MixpanelClient} A Mixpanel client instance.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport function initializeMixpanel(envOrTrackingApiBaseUrl: MixpanelEnvironment | (string & {}), options?: InitializeMixpanelOptions) {\n getRegistrationGuard().throw('[mixpanel] Mixpanel has already been initialized. Did you call the \"initializeMixpanel\" function twice?');\n\n return new MixpanelInitializer().initialize(envOrTrackingApiBaseUrl, options);\n}\n"],"names":["HasExecutedGuard","createCompositeLogger","getTrackingEndpoint","MixpanelClientImpl","getTelemetryProperties","OtherProperties","registrationGuardInstance","getRegistrationGuard","__resetRegistrationGuard","MixpanelInitializer","envOrTrackingApiBaseUrl","options","productId","trackingEndpoint","telemetryContext","logRocketInstrumentationClient","verbose","loggers","logger","endpoint","client","key","value","Object","sessionUrl","initializeMixpanel"],"mappings":";;;;;;;;;;;AAA+H;AACpD;AACF;AACK;AACJ;AAE1E,IAAIM;AAEJ,yEAAyE;AACzE,+DAA+D;AAC/D,SAASC;IACL,IAAI,CAACD,2BAA2B;QAC5BA,4BAA4B,IAAIN,gBAAgBA;IACpD;IAEA,OAAOM;AACX;AAEA,8CAA8C;AACvC,SAASE;IACZ,IAAIF,2BAA2B;QAC3BA,0BAA0B,KAAK;IACnC;AACJ;
|
|
1
|
+
{"version":3,"file":"js/initializeMixpanel.js","sources":["../../src/js/initializeMixpanel.ts"],"sourcesContent":["import { HasExecutedGuard, type LogRocketInstrumentationPartialClient, type TelemetryContext } from \"@workleap-telemetry/core\";\nimport { createCompositeLogger, type RootLogger } from \"@workleap/logging\";\nimport { getTrackingEndpoint, type MixpanelEnvironment } from \"./env.ts\";\nimport { MixpanelClientImpl, type MixpanelClient } from \"./MixpanelClient.ts\";\nimport { getTelemetryProperties, OtherProperties } from \"./properties.ts\";\n\nlet registrationGuardInstance: HasExecutedGuard | undefined;\n\n// It's important to use a lazy singleton instead of a singleton to avoid\n// any future probleme related to HMR, tree-shaking, SSR, etc..\nfunction getRegistrationGuard() {\n if (!registrationGuardInstance) {\n registrationGuardInstance = new HasExecutedGuard();\n }\n\n return registrationGuardInstance;\n}\n\n// This function should only be used by tests.\nexport function __resetRegistrationGuard() {\n if (registrationGuardInstance) {\n registrationGuardInstance.reset();\n }\n}\n\n///////////////////////////\n\n/**\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport interface InitializeMixpanelOptions {\n /**\n * An optional product id that will be attached to every event.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n productId?: string;\n /**\n * An optional tracking endpoint.\n * @default \"tracking/track\"\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n trackingEndpoint?: string;\n /**\n * Names of additional query parameters to redact from the URLs sent with every event.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n privateQueryParameterNames?: string[];\n /**\n * Context including telemetry correlation ids.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n telemetryContext?: TelemetryContext;\n /**\n * An optional LogRocket client to automatically add the LogRocket\n * session URL to Mixpanel events once it's available.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n logRocketInstrumentationClient?: LogRocketInstrumentationPartialClient;\n /**\n * Whether or not debug information should be logged to the console.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n verbose?: boolean;\n /**\n * An array of RootLogger instances.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\n loggers?: RootLogger[];\n}\n\nexport class MixpanelInitializer {\n initialize(envOrTrackingApiBaseUrl: MixpanelEnvironment | (string & {}), options: Omit<InitializeMixpanelOptions, \"globalEventProperties\"> = {}) {\n const { productId, trackingEndpoint, privateQueryParameterNames, telemetryContext, logRocketInstrumentationClient, verbose = false, loggers = [] } = options;\n\n const logger = createCompositeLogger(verbose, loggers);\n const endpoint = getTrackingEndpoint(envOrTrackingApiBaseUrl, trackingEndpoint);\n\n logger.information(\"[mixpanel] Mixpanel is initialized.\");\n\n const client = new MixpanelClientImpl(endpoint, logger, {\n productId,\n privateQueryParameterNames\n });\n\n // Set the telemetry correlation ids on every Mixpanel event.\n if (telemetryContext) {\n for (const [key, value] of Object.entries(getTelemetryProperties(telemetryContext))) {\n client.setGlobalEventProperty(key, value);\n }\n }\n\n // If a LogRocket client is already available, register a listener to get the LogRocket session url\n // once it's available to set it on every Mixpanel event.\n if (logRocketInstrumentationClient) {\n logRocketInstrumentationClient.registerGetSessionUrlListener((sessionUrl: string) => {\n logger.withText(\"[mixpanel] Received LogRocket session replay URL:\").withText(sessionUrl).debug();\n\n client.setGlobalEventProperty(OtherProperties.LogRocketSessionUrl, sessionUrl);\n });\n }\n\n // The cast is to normalize the inferred return type to the \"LogRocketInstrumentationClient\" type.\n return client as MixpanelClient;\n }\n}\n\n/**\n * Initialize Mixpanel telemetry.\n * @param envOrTrackingApiBaseUrl The environment to get the navigation url from or a base URL.\n * @param options Mixpanel instrumentation options.\n * @returns {MixpanelClient} A Mixpanel client instance.\n * @see {@link https://workleap.github.io/wl-telemetry}\n */\nexport function initializeMixpanel(envOrTrackingApiBaseUrl: MixpanelEnvironment | (string & {}), options?: InitializeMixpanelOptions) {\n getRegistrationGuard().throw('[mixpanel] Mixpanel has already been initialized. Did you call the \"initializeMixpanel\" function twice?');\n\n return new MixpanelInitializer().initialize(envOrTrackingApiBaseUrl, options);\n}\n"],"names":["HasExecutedGuard","createCompositeLogger","getTrackingEndpoint","MixpanelClientImpl","getTelemetryProperties","OtherProperties","registrationGuardInstance","getRegistrationGuard","__resetRegistrationGuard","MixpanelInitializer","envOrTrackingApiBaseUrl","options","productId","trackingEndpoint","privateQueryParameterNames","telemetryContext","logRocketInstrumentationClient","verbose","loggers","logger","endpoint","client","key","value","Object","sessionUrl","initializeMixpanel"],"mappings":";;;;;;;;;;;AAA+H;AACpD;AACF;AACK;AACJ;AAE1E,IAAIM;AAEJ,yEAAyE;AACzE,+DAA+D;AAC/D,SAASC;IACL,IAAI,CAACD,2BAA2B;QAC5BA,4BAA4B,IAAIN,gBAAgBA;IACpD;IAEA,OAAOM;AACX;AAEA,8CAA8C;AACvC,SAASE;IACZ,IAAIF,2BAA2B;QAC3BA,0BAA0B,KAAK;IACnC;AACJ;AA+CO,MAAMG;IACT,WAAWC,uBAA4D,EAAEC,UAAoE,CAAC,CAAC,EAAE;QAC7I,MAAM,EAAEC,SAAS,EAAEC,gBAAgB,EAAEC,0BAA0B,EAAEC,gBAAgB,EAAEC,8BAA8B,EAAEC,UAAU,KAAK,EAAEC,UAAU,EAAE,EAAE,GAAGP;QAErJ,MAAMQ,SAASlB,qBAAqBA,CAACgB,SAASC;QAC9C,MAAME,WAAWlB,mBAAmBA,CAACQ,yBAAyBG;QAE9DM,OAAO,WAAW,CAAC;QAEnB,MAAME,SAAS,IAAIlB,kBAAkBA,CAACiB,UAAUD,QAAQ;YACpDP;YACAE;QACJ;QAEA,6DAA6D;QAC7D,IAAIC,kBAAkB;YAClB,KAAK,MAAM,CAACO,KAAKC,MAAM,IAAIC,OAAO,OAAO,CAACpB,sBAAsBA,CAACW,mBAAoB;gBACjFM,OAAO,sBAAsB,CAACC,KAAKC;YACvC;QACJ;QAEA,mGAAmG;QACnG,yDAAyD;QACzD,IAAIP,gCAAgC;YAChCA,+BAA+B,6BAA6B,CAAC,CAACS;gBAC1DN,OAAO,QAAQ,CAAC,qDAAqD,QAAQ,CAACM,YAAY,KAAK;gBAE/FJ,OAAO,sBAAsB,CAAChB,mCAAmC,EAAEoB;YACvE;QACJ;QAEA,kGAAkG;QAClG,OAAOJ;IACX;AACJ;AAEA;;;;;;CAMC,GACM,SAASK,mBAAmBhB,uBAA4D,EAAEC,OAAmC;IAChIJ,uBAAuB,KAAK,CAAC;IAE7B,OAAO,IAAIE,sBAAsB,UAAU,CAACC,yBAAyBC;AACzE"}
|
package/dist/js/properties.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const BaseProperties: {
|
|
|
11
11
|
readonly ScreenHeight: "$screen_height";
|
|
12
12
|
readonly ScreenWidth: "$screen_width";
|
|
13
13
|
};
|
|
14
|
-
export declare function getBaseProperties(): {
|
|
14
|
+
export declare function getBaseProperties(privateQueryParameterNames: string[]): {
|
|
15
15
|
$os: string;
|
|
16
16
|
$browser: string;
|
|
17
17
|
$referrer: string;
|
package/dist/js/properties.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { browserName, browserVersion, deviceType, osName, referringDomain } from "./utils.js";
|
|
1
|
+
import { browserName, browserVersion, deviceType, osName, redactQueryParameters, referringDomain } from "./utils.js";
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
@@ -14,16 +14,17 @@ const BaseProperties = {
|
|
|
14
14
|
ScreenHeight: "$screen_height",
|
|
15
15
|
ScreenWidth: "$screen_width"
|
|
16
16
|
};
|
|
17
|
-
function getBaseProperties() {
|
|
17
|
+
function getBaseProperties(privateQueryParameterNames) {
|
|
18
18
|
const userAgent = navigator.userAgent;
|
|
19
19
|
const device = deviceType(userAgent);
|
|
20
20
|
return {
|
|
21
21
|
[BaseProperties.Os]: osName(userAgent),
|
|
22
22
|
[BaseProperties.Browser]: browserName(userAgent, navigator.vendor),
|
|
23
|
-
[BaseProperties.Referrer]: document.referrer,
|
|
23
|
+
[BaseProperties.Referrer]: redactQueryParameters(document.referrer, privateQueryParameterNames),
|
|
24
24
|
[BaseProperties.ReferringDomain]: referringDomain(document.referrer),
|
|
25
25
|
[BaseProperties.Device]: device,
|
|
26
|
-
|
|
26
|
+
// The fragment is removed because it can include sensitive values, such as an access token.
|
|
27
|
+
[BaseProperties.CurrentUrl]: redactQueryParameters(window.location.href.split("#")[0], privateQueryParameterNames),
|
|
27
28
|
[BaseProperties.BrowserVersion]: browserVersion(userAgent, navigator.vendor),
|
|
28
29
|
[BaseProperties.ScreenHeight]: window.screen.height,
|
|
29
30
|
[BaseProperties.ScreenWidth]: window.screen.width,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"js/properties.js","sources":["../../src/js/properties.ts"],"sourcesContent":["import type { MixpanelEventProperties, TelemetryContext } from \"@workleap-telemetry/core\";\nimport { browserName, browserVersion, deviceType, osName, referringDomain } from \"./utils.ts\";\n\nexport const BaseProperties = {\n Browser: \"$browser\",\n BrowserVersion: \"$browser_version\",\n CurrentUrl: \"$current_url\",\n Device: \"$device\",\n IsMobile: \"Is Mobile\",\n Os: \"$os\",\n Referrer: \"$referrer\",\n ReferringDomain: \"$referring_domain\",\n ScreenHeight: \"$screen_height\",\n ScreenWidth: \"$screen_width\"\n} as const;\n\nexport function getBaseProperties() {\n const userAgent = navigator.userAgent;\n const device = deviceType(userAgent);\n\n return {\n [BaseProperties.Os]: osName(userAgent),\n [BaseProperties.Browser]: browserName(userAgent, navigator.vendor),\n [BaseProperties.Referrer]: document.referrer,\n [BaseProperties.ReferringDomain]: referringDomain(document.referrer),\n [BaseProperties.Device]: device,\n [BaseProperties.CurrentUrl]: window.location.href,\n [BaseProperties.BrowserVersion]: browserVersion(userAgent, navigator.vendor),\n [BaseProperties.ScreenHeight]: window.screen.height,\n [BaseProperties.ScreenWidth]: window.screen.width,\n [BaseProperties.IsMobile]: device.length > 0\n } satisfies MixpanelEventProperties;\n}\n\nexport const TelemetryProperties = {\n DeviceId: \"Device Id\",\n TelemetryId: \"Telemetry Id\"\n} as const;\n\nexport function getTelemetryProperties(context: TelemetryContext) {\n return {\n [TelemetryProperties.TelemetryId]: context.telemetryId,\n [TelemetryProperties.DeviceId]: context.deviceId\n } satisfies MixpanelEventProperties;\n}\n\nexport const OtherProperties = {\n LogRocketSessionUrl: \"LogRocket Session Url\"\n};\n"],"names":["browserName","browserVersion","deviceType","osName","referringDomain","BaseProperties","getBaseProperties","userAgent","navigator","device","document","window","TelemetryProperties","getTelemetryProperties","context","OtherProperties"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"js/properties.js","sources":["../../src/js/properties.ts"],"sourcesContent":["import type { MixpanelEventProperties, TelemetryContext } from \"@workleap-telemetry/core\";\nimport { browserName, browserVersion, deviceType, osName, redactQueryParameters, referringDomain } from \"./utils.ts\";\n\nexport const BaseProperties = {\n Browser: \"$browser\",\n BrowserVersion: \"$browser_version\",\n CurrentUrl: \"$current_url\",\n Device: \"$device\",\n IsMobile: \"Is Mobile\",\n Os: \"$os\",\n Referrer: \"$referrer\",\n ReferringDomain: \"$referring_domain\",\n ScreenHeight: \"$screen_height\",\n ScreenWidth: \"$screen_width\"\n} as const;\n\nexport function getBaseProperties(privateQueryParameterNames: string[]) {\n const userAgent = navigator.userAgent;\n const device = deviceType(userAgent);\n\n return {\n [BaseProperties.Os]: osName(userAgent),\n [BaseProperties.Browser]: browserName(userAgent, navigator.vendor),\n [BaseProperties.Referrer]: redactQueryParameters(document.referrer, privateQueryParameterNames),\n [BaseProperties.ReferringDomain]: referringDomain(document.referrer),\n [BaseProperties.Device]: device,\n // The fragment is removed because it can include sensitive values, such as an access token.\n [BaseProperties.CurrentUrl]: redactQueryParameters(window.location.href.split(\"#\")[0], privateQueryParameterNames),\n [BaseProperties.BrowserVersion]: browserVersion(userAgent, navigator.vendor),\n [BaseProperties.ScreenHeight]: window.screen.height,\n [BaseProperties.ScreenWidth]: window.screen.width,\n [BaseProperties.IsMobile]: device.length > 0\n } satisfies MixpanelEventProperties;\n}\n\nexport const TelemetryProperties = {\n DeviceId: \"Device Id\",\n TelemetryId: \"Telemetry Id\"\n} as const;\n\nexport function getTelemetryProperties(context: TelemetryContext) {\n return {\n [TelemetryProperties.TelemetryId]: context.telemetryId,\n [TelemetryProperties.DeviceId]: context.deviceId\n } satisfies MixpanelEventProperties;\n}\n\nexport const OtherProperties = {\n LogRocketSessionUrl: \"LogRocket Session Url\"\n};\n"],"names":["browserName","browserVersion","deviceType","osName","redactQueryParameters","referringDomain","BaseProperties","getBaseProperties","privateQueryParameterNames","userAgent","navigator","device","document","window","TelemetryProperties","getTelemetryProperties","context","OtherProperties"],"mappings":";;;AACqH;AAE9G,MAAMM,iBAAiB;IAC1B,SAAS;IACT,gBAAgB;IAChB,YAAY;IACZ,QAAQ;IACR,UAAU;IACV,IAAI;IACJ,UAAU;IACV,iBAAiB;IACjB,cAAc;IACd,aAAa;AACjB,EAAW;AAEJ,SAASC,kBAAkBC,0BAAoC;IAClE,MAAMC,YAAYC,UAAU,SAAS;IACrC,MAAMC,SAAST,UAAUA,CAACO;IAE1B,OAAO;QACH,CAACH,eAAe,EAAE,CAAC,EAAEH,MAAMA,CAACM;QAC5B,CAACH,eAAe,OAAO,CAAC,EAAEN,WAAWA,CAACS,WAAWC,UAAU,MAAM;QACjE,CAACJ,eAAe,QAAQ,CAAC,EAAEF,qBAAqBA,CAACQ,SAAS,QAAQ,EAAEJ;QACpE,CAACF,eAAe,eAAe,CAAC,EAAED,eAAeA,CAACO,SAAS,QAAQ;QACnE,CAACN,eAAe,MAAM,CAAC,EAAEK;QACzB,4FAA4F;QAC5F,CAACL,eAAe,UAAU,CAAC,EAAEF,qBAAqBA,CAACS,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAEL;QACvF,CAACF,eAAe,cAAc,CAAC,EAAEL,cAAcA,CAACQ,WAAWC,UAAU,MAAM;QAC3E,CAACJ,eAAe,YAAY,CAAC,EAAEO,OAAO,MAAM,CAAC,MAAM;QACnD,CAACP,eAAe,WAAW,CAAC,EAAEO,OAAO,MAAM,CAAC,KAAK;QACjD,CAACP,eAAe,QAAQ,CAAC,EAAEK,OAAO,MAAM,GAAG;IAC/C;AACJ;AAEO,MAAMG,sBAAsB;IAC/B,UAAU;IACV,aAAa;AACjB,EAAW;AAEJ,SAASC,uBAAuBC,OAAyB;IAC5D,OAAO;QACH,CAACF,oBAAoB,WAAW,CAAC,EAAEE,QAAQ,WAAW;QACtD,CAACF,oBAAoB,QAAQ,CAAC,EAAEE,QAAQ,QAAQ;IACpD;AACJ;AAEO,MAAMC,kBAAkB;IAC3B,qBAAqB;AACzB,EAAE"}
|
package/dist/js/utils.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export declare function browserVersion(userAgent: string, vendor?: string, opera
|
|
|
8
8
|
export declare function osName(userAgent: string): "" | "Android" | "BlackBerry" | "Chrome OS" | "Linux" | "Mac OS X" | "Windows" | "Windows Phone" | "iOS";
|
|
9
9
|
export declare function deviceType(userAgent: string): "" | "Android" | "BlackBerry" | "Windows Phone" | "iPad" | "iPhone" | "iPod Touch";
|
|
10
10
|
export declare function referringDomain(referrer: string): string;
|
|
11
|
+
export declare function redactQueryParameters(url: string, queryParameterNames: string[]): string;
|
package/dist/js/utils.js
CHANGED
|
@@ -119,7 +119,19 @@ function referringDomain(referrer) {
|
|
|
119
119
|
}
|
|
120
120
|
return "";
|
|
121
121
|
}
|
|
122
|
+
function redactQueryParameters(url, queryParameterNames) {
|
|
123
|
+
let redactedUrl = url;
|
|
124
|
+
for (const x of queryParameterNames){
|
|
125
|
+
// A name can include regular expression special characters, and can be percent-encoded in the URL, such as "user[email]" and "user%5Bemail%5D".
|
|
126
|
+
const escapedNames = [
|
|
127
|
+
x,
|
|
128
|
+
encodeURIComponent(x)
|
|
129
|
+
].map((y)=>y.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
130
|
+
redactedUrl = redactedUrl.replace(new RegExp(`(?:${escapedNames.join("|")})=([^&]*)`, "gi"), ()=>`${x}=**redacted**`);
|
|
131
|
+
}
|
|
132
|
+
return redactedUrl;
|
|
133
|
+
}
|
|
122
134
|
|
|
123
|
-
export { browserName, browserVersion, deviceType, osName, referringDomain };
|
|
135
|
+
export { browserName, browserVersion, deviceType, osName, redactQueryParameters, referringDomain };
|
|
124
136
|
|
|
125
137
|
//# sourceMappingURL=utils.js.map
|
package/dist/js/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"js/utils.js","sources":["../../src/js/utils.ts"],"sourcesContent":["// This code is adapted from https://github.com/mixpanel/mixpanel-js/blob/master/src/utils.js\nexport function browserName(userAgent: string, vendor?: string, opera?: object) {\n // vendor is undefined for at least IE9\n vendor = vendor || \"\"; // oxlint-disable-line eslint/no-param-reassign\n\n if (opera || userAgent.includes(\" OPR/\")) {\n if (userAgent.includes(\"Mini\")) {\n return \"Opera Mini\";\n }\n\n return \"Opera\";\n } else if (/(BlackBerry|PlayBook|BB10)/i.test(userAgent)) {\n return \"BlackBerry\";\n } else if (userAgent.includes(\"IEMobile\") || userAgent.includes(\"WPDesktop\")) {\n return \"Internet Explorer Mobile\";\n } else if (userAgent.includes(\"Edge\")) {\n return \"Microsoft Edge\";\n } else if (userAgent.includes(\"FBIOS\")) {\n return \"Facebook Mobile\";\n } else if (userAgent.includes(\"Chrome\")) {\n return \"Chrome\";\n } else if (userAgent.includes(\"CriOS\")) {\n return \"Chrome iOS\";\n } else if (userAgent.includes(\"UCWEB\") || userAgent.includes(\"UCBrowser\")) {\n return \"UC Browser\";\n } else if (userAgent.includes(\"FxiOS\")) {\n return \"Firefox iOS\";\n } else if (vendor.includes(\"Apple\")) {\n if (userAgent.includes(\"Mobile\")) {\n return \"Mobile Safari\";\n }\n\n return \"Safari\";\n } else if (userAgent.includes(\"Android\")) {\n return \"Android Mobile\";\n } else if (userAgent.includes(\"Konqueror\")) {\n return \"Konqueror\";\n } else if (userAgent.includes(\"Firefox\")) {\n return \"Firefox\";\n } else if (userAgent.includes(\"MSIE\") || userAgent.includes(\"Trident/\")) {\n return \"Internet Explorer\";\n } else if (userAgent.includes(\"Gecko\")) {\n return \"Mozilla\";\n }\n\n return \"\";\n}\n\n/**\n * This function detects which browser version is running this script,\n * parsing major and minor version (e.g., 42.1). User agent strings from:\n * http://www.useragentstring.com/pages/useragentstring.php\n */\nexport function browserVersion(userAgent: string, vendor?: string, opera?: object) {\n const browser = browserName(userAgent, vendor, opera);\n const versionRegexs: { [key: string]: RegExp } = {\n \"Internet Explorer Mobile\": /rv:(\\d+(\\.\\d+)?)/,\n \"Microsoft Edge\": /Edge\\/(\\d+(\\.\\d+)?)/,\n Chrome: /Chrome\\/(\\d+(\\.\\d+)?)/,\n \"Chrome iOS\": /CriOS\\/(\\d+(\\.\\d+)?)/,\n \"UC Browser\": /(UCBrowser|UCWEB)\\/(\\d+(\\.\\d+)?)/,\n Safari: /Version\\/(\\d+(\\.\\d+)?)/,\n \"Mobile Safari\": /Version\\/(\\d+(\\.\\d+)?)/,\n Opera: /(Opera|OPR)\\/(\\d+(\\.\\d+)?)/,\n Firefox: /Firefox\\/(\\d+(\\.\\d+)?)/,\n \"Firefox iOS\": /FxiOS\\/(\\d+(\\.\\d+)?)/,\n Konqueror: /Konqueror:(\\d+(\\.\\d+)?)/,\n BlackBerry: /BlackBerry (\\d+(\\.\\d+)?)/,\n \"Android Mobile\": /android\\s(\\d+(\\.\\d+)?)/,\n \"Internet Explorer\": /(rv:|MSIE )(\\d+(\\.\\d+)?)/,\n Mozilla: /rv:(\\d+(\\.\\d+)?)/\n };\n const regex = versionRegexs[browser];\n if (regex === undefined) {\n return null;\n }\n const matches = userAgent.match(regex);\n if (!matches) {\n return null;\n }\n\n return parseFloat(matches[matches.length - 2]);\n}\n\nexport function osName(userAgent: string) {\n const a = userAgent;\n if (/Windows/i.test(a)) {\n if (/Phone/.test(a) || /WPDesktop/.test(a)) {\n return \"Windows Phone\";\n }\n\n return \"Windows\";\n } else if (/(iPhone|iPad|iPod)/.test(a)) {\n return \"iOS\";\n } else if (/Android/.test(a)) {\n return \"Android\";\n } else if (/(BlackBerry|PlayBook|BB10)/i.test(a)) {\n return \"BlackBerry\";\n } else if (/Mac/i.test(a)) {\n return \"Mac OS X\";\n } else if (/Linux/.test(a)) {\n return \"Linux\";\n } else if (/CrOS/.test(a)) {\n return \"Chrome OS\";\n }\n\n return \"\";\n}\n\nexport function deviceType(userAgent: string) {\n if (/Windows Phone/i.test(userAgent) || /WPDesktop/.test(userAgent)) {\n return \"Windows Phone\";\n } else if (/iPad/.test(userAgent)) {\n return \"iPad\";\n } else if (/iPod/.test(userAgent)) {\n return \"iPod Touch\";\n } else if (/iPhone/.test(userAgent)) {\n return \"iPhone\";\n } else if (/(BlackBerry|PlayBook|BB10)/i.test(userAgent)) {\n return \"BlackBerry\";\n } else if (/Android/.test(userAgent)) {\n return \"Android\";\n }\n\n return \"\";\n}\n\nexport function referringDomain(referrer: string) {\n const split = referrer.split(\"/\");\n if (split.length >= 3) {\n return split[2];\n }\n\n return \"\";\n}\n"],"names":["browserName","userAgent","vendor","opera","browserVersion","browser","versionRegexs","regex","undefined","matches","parseFloat","osName","a","deviceType","referringDomain","referrer","split"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"js/utils.js","sources":["../../src/js/utils.ts"],"sourcesContent":["// This code is adapted from https://github.com/mixpanel/mixpanel-js/blob/master/src/utils.js\nexport function browserName(userAgent: string, vendor?: string, opera?: object) {\n // vendor is undefined for at least IE9\n vendor = vendor || \"\"; // oxlint-disable-line eslint/no-param-reassign\n\n if (opera || userAgent.includes(\" OPR/\")) {\n if (userAgent.includes(\"Mini\")) {\n return \"Opera Mini\";\n }\n\n return \"Opera\";\n } else if (/(BlackBerry|PlayBook|BB10)/i.test(userAgent)) {\n return \"BlackBerry\";\n } else if (userAgent.includes(\"IEMobile\") || userAgent.includes(\"WPDesktop\")) {\n return \"Internet Explorer Mobile\";\n } else if (userAgent.includes(\"Edge\")) {\n return \"Microsoft Edge\";\n } else if (userAgent.includes(\"FBIOS\")) {\n return \"Facebook Mobile\";\n } else if (userAgent.includes(\"Chrome\")) {\n return \"Chrome\";\n } else if (userAgent.includes(\"CriOS\")) {\n return \"Chrome iOS\";\n } else if (userAgent.includes(\"UCWEB\") || userAgent.includes(\"UCBrowser\")) {\n return \"UC Browser\";\n } else if (userAgent.includes(\"FxiOS\")) {\n return \"Firefox iOS\";\n } else if (vendor.includes(\"Apple\")) {\n if (userAgent.includes(\"Mobile\")) {\n return \"Mobile Safari\";\n }\n\n return \"Safari\";\n } else if (userAgent.includes(\"Android\")) {\n return \"Android Mobile\";\n } else if (userAgent.includes(\"Konqueror\")) {\n return \"Konqueror\";\n } else if (userAgent.includes(\"Firefox\")) {\n return \"Firefox\";\n } else if (userAgent.includes(\"MSIE\") || userAgent.includes(\"Trident/\")) {\n return \"Internet Explorer\";\n } else if (userAgent.includes(\"Gecko\")) {\n return \"Mozilla\";\n }\n\n return \"\";\n}\n\n/**\n * This function detects which browser version is running this script,\n * parsing major and minor version (e.g., 42.1). User agent strings from:\n * http://www.useragentstring.com/pages/useragentstring.php\n */\nexport function browserVersion(userAgent: string, vendor?: string, opera?: object) {\n const browser = browserName(userAgent, vendor, opera);\n const versionRegexs: { [key: string]: RegExp } = {\n \"Internet Explorer Mobile\": /rv:(\\d+(\\.\\d+)?)/,\n \"Microsoft Edge\": /Edge\\/(\\d+(\\.\\d+)?)/,\n Chrome: /Chrome\\/(\\d+(\\.\\d+)?)/,\n \"Chrome iOS\": /CriOS\\/(\\d+(\\.\\d+)?)/,\n \"UC Browser\": /(UCBrowser|UCWEB)\\/(\\d+(\\.\\d+)?)/,\n Safari: /Version\\/(\\d+(\\.\\d+)?)/,\n \"Mobile Safari\": /Version\\/(\\d+(\\.\\d+)?)/,\n Opera: /(Opera|OPR)\\/(\\d+(\\.\\d+)?)/,\n Firefox: /Firefox\\/(\\d+(\\.\\d+)?)/,\n \"Firefox iOS\": /FxiOS\\/(\\d+(\\.\\d+)?)/,\n Konqueror: /Konqueror:(\\d+(\\.\\d+)?)/,\n BlackBerry: /BlackBerry (\\d+(\\.\\d+)?)/,\n \"Android Mobile\": /android\\s(\\d+(\\.\\d+)?)/,\n \"Internet Explorer\": /(rv:|MSIE )(\\d+(\\.\\d+)?)/,\n Mozilla: /rv:(\\d+(\\.\\d+)?)/\n };\n const regex = versionRegexs[browser];\n if (regex === undefined) {\n return null;\n }\n const matches = userAgent.match(regex);\n if (!matches) {\n return null;\n }\n\n return parseFloat(matches[matches.length - 2]);\n}\n\nexport function osName(userAgent: string) {\n const a = userAgent;\n if (/Windows/i.test(a)) {\n if (/Phone/.test(a) || /WPDesktop/.test(a)) {\n return \"Windows Phone\";\n }\n\n return \"Windows\";\n } else if (/(iPhone|iPad|iPod)/.test(a)) {\n return \"iOS\";\n } else if (/Android/.test(a)) {\n return \"Android\";\n } else if (/(BlackBerry|PlayBook|BB10)/i.test(a)) {\n return \"BlackBerry\";\n } else if (/Mac/i.test(a)) {\n return \"Mac OS X\";\n } else if (/Linux/.test(a)) {\n return \"Linux\";\n } else if (/CrOS/.test(a)) {\n return \"Chrome OS\";\n }\n\n return \"\";\n}\n\nexport function deviceType(userAgent: string) {\n if (/Windows Phone/i.test(userAgent) || /WPDesktop/.test(userAgent)) {\n return \"Windows Phone\";\n } else if (/iPad/.test(userAgent)) {\n return \"iPad\";\n } else if (/iPod/.test(userAgent)) {\n return \"iPod Touch\";\n } else if (/iPhone/.test(userAgent)) {\n return \"iPhone\";\n } else if (/(BlackBerry|PlayBook|BB10)/i.test(userAgent)) {\n return \"BlackBerry\";\n } else if (/Android/.test(userAgent)) {\n return \"Android\";\n }\n\n return \"\";\n}\n\nexport function referringDomain(referrer: string) {\n const split = referrer.split(\"/\");\n if (split.length >= 3) {\n return split[2];\n }\n\n return \"\";\n}\n\nexport function redactQueryParameters(url: string, queryParameterNames: string[]) {\n let redactedUrl = url;\n\n for (const x of queryParameterNames) {\n // A name can include regular expression special characters, and can be percent-encoded in the URL, such as \"user[email]\" and \"user%5Bemail%5D\".\n const escapedNames = [x, encodeURIComponent(x)].map(y => y.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n\n redactedUrl = redactedUrl.replace(new RegExp(`(?:${escapedNames.join(\"|\")})=([^&]*)`, \"gi\"), () => `${x}=**redacted**`);\n }\n\n return redactedUrl;\n}\n"],"names":["browserName","userAgent","vendor","opera","browserVersion","browser","versionRegexs","regex","undefined","matches","parseFloat","osName","a","deviceType","referringDomain","referrer","split","redactQueryParameters","url","queryParameterNames","redactedUrl","x","escapedNames","encodeURIComponent","y","RegExp"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workleap/mixpanel",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "A package containing utilities for Mixpanel telemetry.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Workleap",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public",
|
|
33
|
-
"provenance": true
|
|
34
|
-
"registry": "https://registry.npmjs.org/"
|
|
33
|
+
"provenance": true
|
|
35
34
|
},
|
|
36
35
|
"dependencies": {
|
|
37
36
|
"@workleap-telemetry/core": "^2.0.7",
|
|
@@ -50,7 +49,7 @@
|
|
|
50
49
|
},
|
|
51
50
|
"peerDependencies": {
|
|
52
51
|
"@types/react": "^18.0.0 || ^19.0.0",
|
|
53
|
-
"@workleap/logrocket": "^4.0.
|
|
52
|
+
"@workleap/logrocket": "^4.0.7",
|
|
54
53
|
"react": "^18.0.0 || ^19.0.0",
|
|
55
54
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
56
55
|
},
|
package/src/js/MixpanelClient.ts
CHANGED
|
@@ -28,20 +28,28 @@ export interface MixpanelClientOptions {
|
|
|
28
28
|
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
29
29
|
*/
|
|
30
30
|
productId?: string;
|
|
31
|
+
/**
|
|
32
|
+
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
33
|
+
*/
|
|
34
|
+
privateQueryParameterNames?: string[];
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
const DefaultPrivateQueryParameterNames = ["token", "email", "password"];
|
|
38
|
+
|
|
33
39
|
export class MixpanelClientImpl implements MixpanelClient {
|
|
34
40
|
readonly #globalEventProperties = new Map<string, unknown>();
|
|
35
41
|
readonly #endpoint: string;
|
|
36
42
|
readonly #logger: Logger;
|
|
37
43
|
readonly #productId?: string;
|
|
44
|
+
readonly #privateQueryParameterNames: string[];
|
|
38
45
|
|
|
39
46
|
constructor(endpoint: string, logger: Logger, options: MixpanelClientOptions = {}) {
|
|
40
|
-
const { productId } = options;
|
|
47
|
+
const { productId, privateQueryParameterNames = [] } = options;
|
|
41
48
|
|
|
42
49
|
this.#endpoint = endpoint;
|
|
43
50
|
this.#logger = logger;
|
|
44
51
|
this.#productId = productId;
|
|
52
|
+
this.#privateQueryParameterNames = DefaultPrivateQueryParameterNames.concat(privateQueryParameterNames);
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
// IMPORTANT: If you update this method, make sure to update the MixpanelPartialClient
|
|
@@ -53,7 +61,7 @@ export class MixpanelClientImpl implements MixpanelClient {
|
|
|
53
61
|
try {
|
|
54
62
|
const { keepAlive = false } = _options;
|
|
55
63
|
|
|
56
|
-
const baseProperties = getBaseProperties();
|
|
64
|
+
const baseProperties = getBaseProperties(this.#privateQueryParameterNames);
|
|
57
65
|
|
|
58
66
|
const allProperties = {
|
|
59
67
|
...baseProperties,
|
|
@@ -40,6 +40,11 @@ export interface InitializeMixpanelOptions {
|
|
|
40
40
|
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
41
41
|
*/
|
|
42
42
|
trackingEndpoint?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Names of additional query parameters to redact from the URLs sent with every event.
|
|
45
|
+
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
46
|
+
*/
|
|
47
|
+
privateQueryParameterNames?: string[];
|
|
43
48
|
/**
|
|
44
49
|
* Context including telemetry correlation ids.
|
|
45
50
|
* @see {@link https://workleap.github.io/wl-telemetry}
|
|
@@ -65,7 +70,7 @@ export interface InitializeMixpanelOptions {
|
|
|
65
70
|
|
|
66
71
|
export class MixpanelInitializer {
|
|
67
72
|
initialize(envOrTrackingApiBaseUrl: MixpanelEnvironment | (string & {}), options: Omit<InitializeMixpanelOptions, "globalEventProperties"> = {}) {
|
|
68
|
-
const { productId, trackingEndpoint, telemetryContext, logRocketInstrumentationClient, verbose = false, loggers = [] } = options;
|
|
73
|
+
const { productId, trackingEndpoint, privateQueryParameterNames, telemetryContext, logRocketInstrumentationClient, verbose = false, loggers = [] } = options;
|
|
69
74
|
|
|
70
75
|
const logger = createCompositeLogger(verbose, loggers);
|
|
71
76
|
const endpoint = getTrackingEndpoint(envOrTrackingApiBaseUrl, trackingEndpoint);
|
|
@@ -73,7 +78,8 @@ export class MixpanelInitializer {
|
|
|
73
78
|
logger.information("[mixpanel] Mixpanel is initialized.");
|
|
74
79
|
|
|
75
80
|
const client = new MixpanelClientImpl(endpoint, logger, {
|
|
76
|
-
productId
|
|
81
|
+
productId,
|
|
82
|
+
privateQueryParameterNames
|
|
77
83
|
});
|
|
78
84
|
|
|
79
85
|
// Set the telemetry correlation ids on every Mixpanel event.
|
package/src/js/properties.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MixpanelEventProperties, TelemetryContext } from "@workleap-telemetry/core";
|
|
2
|
-
import { browserName, browserVersion, deviceType, osName, referringDomain } from "./utils.ts";
|
|
2
|
+
import { browserName, browserVersion, deviceType, osName, redactQueryParameters, referringDomain } from "./utils.ts";
|
|
3
3
|
|
|
4
4
|
export const BaseProperties = {
|
|
5
5
|
Browser: "$browser",
|
|
@@ -14,17 +14,18 @@ export const BaseProperties = {
|
|
|
14
14
|
ScreenWidth: "$screen_width"
|
|
15
15
|
} as const;
|
|
16
16
|
|
|
17
|
-
export function getBaseProperties() {
|
|
17
|
+
export function getBaseProperties(privateQueryParameterNames: string[]) {
|
|
18
18
|
const userAgent = navigator.userAgent;
|
|
19
19
|
const device = deviceType(userAgent);
|
|
20
20
|
|
|
21
21
|
return {
|
|
22
22
|
[BaseProperties.Os]: osName(userAgent),
|
|
23
23
|
[BaseProperties.Browser]: browserName(userAgent, navigator.vendor),
|
|
24
|
-
[BaseProperties.Referrer]: document.referrer,
|
|
24
|
+
[BaseProperties.Referrer]: redactQueryParameters(document.referrer, privateQueryParameterNames),
|
|
25
25
|
[BaseProperties.ReferringDomain]: referringDomain(document.referrer),
|
|
26
26
|
[BaseProperties.Device]: device,
|
|
27
|
-
|
|
27
|
+
// The fragment is removed because it can include sensitive values, such as an access token.
|
|
28
|
+
[BaseProperties.CurrentUrl]: redactQueryParameters(window.location.href.split("#")[0], privateQueryParameterNames),
|
|
28
29
|
[BaseProperties.BrowserVersion]: browserVersion(userAgent, navigator.vendor),
|
|
29
30
|
[BaseProperties.ScreenHeight]: window.screen.height,
|
|
30
31
|
[BaseProperties.ScreenWidth]: window.screen.width,
|
package/src/js/utils.ts
CHANGED
|
@@ -133,3 +133,16 @@ export function referringDomain(referrer: string) {
|
|
|
133
133
|
|
|
134
134
|
return "";
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
export function redactQueryParameters(url: string, queryParameterNames: string[]) {
|
|
138
|
+
let redactedUrl = url;
|
|
139
|
+
|
|
140
|
+
for (const x of queryParameterNames) {
|
|
141
|
+
// A name can include regular expression special characters, and can be percent-encoded in the URL, such as "user[email]" and "user%5Bemail%5D".
|
|
142
|
+
const escapedNames = [x, encodeURIComponent(x)].map(y => y.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
143
|
+
|
|
144
|
+
redactedUrl = redactedUrl.replace(new RegExp(`(?:${escapedNames.join("|")})=([^&]*)`, "gi"), () => `${x}=**redacted**`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return redactedUrl;
|
|
148
|
+
}
|