luchy 1.1.0 → 1.2.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/README.md CHANGED
@@ -123,6 +123,15 @@ window.luchy.reset();
123
123
  `identify`/`reset` write through to the script tag's `data-identity` /
124
124
  `data-props`, so the tag stays the single source of truth.
125
125
 
126
+ ### UTM attribution
127
+
128
+ Every pageview reads `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`
129
+ and `utm_content` from `location.search` and adds the present ones to its
130
+ payload (trimmed, at most 200 characters). Nothing else from the query string
131
+ is sent, and a key in the pageview's own `payload` wins over the URL. Ingest
132
+ keeps the first `utm_source` / `utm_medium` / `utm_campaign` of a session as
133
+ its attribution.
134
+
126
135
  ## Identity
127
136
 
128
137
  Luchy ties hits to your users only when your server vouches for them. An
@@ -218,6 +227,9 @@ await luchy.pageview({ pathname: '/pricing', payload: { plan: 'pro' } });
218
227
  is the server.
219
228
  - Nothing ever rejects — analytics must not break the request it is
220
229
  describing. Pass `onError` if you want failures in your logs.
230
+ - UTM attribution is not read from any URL here: pass the same keys
231
+ (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`) in
232
+ `payload` and the session is attributed exactly as for the browser script.
221
233
 
222
234
  ## API client (`luchy/api`)
223
235
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,0EAA0E;AAC1E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,CAAC,IAAI,CAAC,EAAE;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,UAAU,CACR,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,UAAU,EACpB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAClD,0DAA0D;IAC1D,KAAK,IAAI,IAAI,CAAC;IACd,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAChD,CAAC;AAgBF;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CA2Z7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,0EAA0E;AAC1E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,CAAC,IAAI,CAAC,EAAE;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,UAAU,CACR,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,UAAU,EACpB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAClD,0DAA0D;IAC1D,KAAK,IAAI,IAAI,CAAC;IACd,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAChD,CAAC;AAgBF;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAka7D"}
package/dist/index.js CHANGED
@@ -21,6 +21,24 @@ function collectBrowserData() {
21
21
  language: navigator.language || navigator.languages?.[0] || "en"
22
22
  };
23
23
  }
24
+ const UTM_KEYS = [
25
+ "utm_source",
26
+ "utm_medium",
27
+ "utm_campaign",
28
+ "utm_term",
29
+ "utm_content"
30
+ ];
31
+ const MAX_LENGTH = 200;
32
+ function utmParams(search) {
33
+ const params = new URLSearchParams(search);
34
+ const utm = {};
35
+ for (const key of UTM_KEYS) {
36
+ const value = params.get(key)?.trim().slice(0, MAX_LENGTH);
37
+ if (!value) continue;
38
+ utm[key] = value;
39
+ }
40
+ return utm;
41
+ }
24
42
  function parseProps(raw) {
25
43
  if (!raw) return void 0;
26
44
  try {
@@ -73,6 +91,11 @@ function luchy(options) {
73
91
  if (!global) return payload;
74
92
  return { ...global, ...payload };
75
93
  }
94
+ function withUtm(payload) {
95
+ const utm = utmParams(location.search || "");
96
+ if (Object.keys(utm).length === 0) return payload;
97
+ return { ...utm, ...payload };
98
+ }
76
99
  const shouldTrack = () => {
77
100
  if (trackLocalhost) return true;
78
101
  return location.hostname !== "localhost" && location.hostname !== "127.0.0.1";
@@ -88,7 +111,7 @@ function luchy(options) {
88
111
  data: {
89
112
  pathname,
90
113
  referrer,
91
- payload: withProps(data?.payload),
114
+ payload: withProps(withUtm(data?.payload)),
92
115
  identity: currentIdentity(),
93
116
  ...browserData
94
117
  }
@@ -62,6 +62,25 @@ var __async = (__this, __arguments, generator) => {
62
62
  language: navigator.language || ((_a = navigator.languages) == null ? void 0 : _a[0]) || "en"
63
63
  };
64
64
  }
65
+ const UTM_KEYS = [
66
+ "utm_source",
67
+ "utm_medium",
68
+ "utm_campaign",
69
+ "utm_term",
70
+ "utm_content"
71
+ ];
72
+ const MAX_LENGTH = 200;
73
+ function utmParams(search) {
74
+ var _a;
75
+ const params = new URLSearchParams(search);
76
+ const utm = {};
77
+ for (const key of UTM_KEYS) {
78
+ const value = (_a = params.get(key)) == null ? void 0 : _a.trim().slice(0, MAX_LENGTH);
79
+ if (!value) continue;
80
+ utm[key] = value;
81
+ }
82
+ return utm;
83
+ }
65
84
  function parseProps(raw) {
66
85
  if (!raw) return void 0;
67
86
  try {
@@ -114,6 +133,11 @@ var __async = (__this, __arguments, generator) => {
114
133
  if (!global) return payload;
115
134
  return __spreadValues(__spreadValues({}, global), payload);
116
135
  }
136
+ function withUtm(payload) {
137
+ const utm = utmParams(location.search || "");
138
+ if (Object.keys(utm).length === 0) return payload;
139
+ return __spreadValues(__spreadValues({}, utm), payload);
140
+ }
117
141
  const shouldTrack = () => {
118
142
  if (trackLocalhost) return true;
119
143
  return location.hostname !== "localhost" && location.hostname !== "127.0.0.1";
@@ -130,7 +154,7 @@ var __async = (__this, __arguments, generator) => {
130
154
  data: __spreadValues({
131
155
  pathname,
132
156
  referrer,
133
- payload: withProps(data == null ? void 0 : data.payload),
157
+ payload: withProps(withUtm(data == null ? void 0 : data.payload)),
134
158
  identity: currentIdentity()
135
159
  }, browserData)
136
160
  });
Binary file
Binary file
@@ -62,6 +62,25 @@ var __async = (__this, __arguments, generator) => {
62
62
  language: navigator.language || ((_a = navigator.languages) == null ? void 0 : _a[0]) || "en"
63
63
  };
64
64
  }
65
+ const UTM_KEYS = [
66
+ "utm_source",
67
+ "utm_medium",
68
+ "utm_campaign",
69
+ "utm_term",
70
+ "utm_content"
71
+ ];
72
+ const MAX_LENGTH = 200;
73
+ function utmParams(search) {
74
+ var _a;
75
+ const params = new URLSearchParams(search);
76
+ const utm = {};
77
+ for (const key of UTM_KEYS) {
78
+ const value = (_a = params.get(key)) == null ? void 0 : _a.trim().slice(0, MAX_LENGTH);
79
+ if (!value) continue;
80
+ utm[key] = value;
81
+ }
82
+ return utm;
83
+ }
65
84
  function parseProps(raw) {
66
85
  if (!raw) return void 0;
67
86
  try {
@@ -114,6 +133,11 @@ var __async = (__this, __arguments, generator) => {
114
133
  if (!global) return payload;
115
134
  return __spreadValues(__spreadValues({}, global), payload);
116
135
  }
136
+ function withUtm(payload) {
137
+ const utm = utmParams(location.search || "");
138
+ if (Object.keys(utm).length === 0) return payload;
139
+ return __spreadValues(__spreadValues({}, utm), payload);
140
+ }
117
141
  const shouldTrack = () => {
118
142
  if (trackLocalhost) return true;
119
143
  return location.hostname !== "localhost" && location.hostname !== "127.0.0.1";
@@ -130,7 +154,7 @@ var __async = (__this, __arguments, generator) => {
130
154
  data: __spreadValues({
131
155
  pathname,
132
156
  referrer,
133
- payload: withProps(data == null ? void 0 : data.payload),
157
+ payload: withProps(withUtm(data == null ? void 0 : data.payload)),
134
158
  identity: currentIdentity()
135
159
  }, browserData)
136
160
  });
Binary file
Binary file
package/dist/utm.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The UTM parameters present in a query string, trimmed and capped at 200
3
+ * characters. Nothing else from the query string is kept.
4
+ */
5
+ export declare function utmParams(search: string): Record<string, string>;
6
+ //# sourceMappingURL=utm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utm.d.ts","sourceRoot":"","sources":["../src/utm.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAShE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luchy",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Luchy tracking - browser script, server tracker and React Router middleware",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",