@mearl/plugin-tracking 0.0.0-stage → 3.0.0-beta.6
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/LICENSE +201 -0
- package/README.md +4 -2
- package/dist/capability/browser.cjs +406 -0
- package/dist/capability/bundle.json +38 -0
- package/dist/capability/node.cjs +410 -0
- package/dist/capability/preset.json +15 -0
- package/dist/eventParsers.d.ts +31 -0
- package/dist/eventParsers.js +409 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/runtime.d.ts +27 -0
- package/dist/runtime.js +17 -0
- package/package.json +41 -3
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parsers for tracking requests. Callers provide the request URL/body and
|
|
3
|
+
* may provide a stable sequence and timestamp when deterministic IDs are needed.
|
|
4
|
+
*/
|
|
5
|
+
// ─── URL patterns ─────────────────────────────────────────────────────────────
|
|
6
|
+
export const APLUS_URL_PATTERN = /\.mmstat\.com\//;
|
|
7
|
+
export const RUM_URL_PATTERN = /\.rum\.aliyuncs\.com\//;
|
|
8
|
+
export const ARMS_URL_PATTERN = /^arms-retcode\.aliyuncs\.com\//;
|
|
9
|
+
function trackingHostname(url) {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = new URL(url);
|
|
12
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.hostname : '';
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function matchesTrackingHost(url, pattern) {
|
|
19
|
+
return pattern.test(`${trackingHostname(url)}/`);
|
|
20
|
+
}
|
|
21
|
+
export function isTrackingUrl(url) {
|
|
22
|
+
const hostname = trackingHostname(url);
|
|
23
|
+
return (matchesTrackingHost(url, APLUS_URL_PATTERN) ||
|
|
24
|
+
matchesTrackingHost(url, RUM_URL_PATTERN) ||
|
|
25
|
+
matchesTrackingHost(url, ARMS_URL_PATTERN) ||
|
|
26
|
+
/^(?:retcode\.alicdn\.com|beacon\.taobao\.com)$|(?:^|\.)tbsec\./i.test(hostname));
|
|
27
|
+
}
|
|
28
|
+
let aplusCounter = 0;
|
|
29
|
+
let armsCounter = 0;
|
|
30
|
+
let rumCounter = 0;
|
|
31
|
+
function formatTimestamp(ms) {
|
|
32
|
+
const d = new Date(ms);
|
|
33
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
34
|
+
return `${d.getFullYear()}/${pad(d.getMonth() + 1)}/${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
35
|
+
}
|
|
36
|
+
function parseParams(value) {
|
|
37
|
+
const params = {};
|
|
38
|
+
value.split('&').forEach(pair => {
|
|
39
|
+
const eqIndex = pair.indexOf('=');
|
|
40
|
+
if (eqIndex === -1)
|
|
41
|
+
return;
|
|
42
|
+
const rawKey = pair.slice(0, eqIndex);
|
|
43
|
+
const rawValue = pair.slice(eqIndex + 1);
|
|
44
|
+
try {
|
|
45
|
+
params[decodeURIComponent(rawKey)] = rawValue;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
params[rawKey] = rawValue;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return params;
|
|
52
|
+
}
|
|
53
|
+
function decodeParamValue(value) {
|
|
54
|
+
try {
|
|
55
|
+
return decodeURIComponent(value);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function parseJsonParamValue(value) {
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(value);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
const decoded = decodeParamValue(value);
|
|
67
|
+
if (decoded === value)
|
|
68
|
+
return undefined;
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(decoded);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function parseUrlParams(search) {
|
|
78
|
+
const encodedParams = parseParams(search.startsWith('?') ? search.slice(1) : search);
|
|
79
|
+
return Object.fromEntries(Object.entries(encodedParams).map(([key, value]) => [key, decodeParamValue(value)]));
|
|
80
|
+
}
|
|
81
|
+
/** If spm-url is a full URL, return its spm query parameter. */
|
|
82
|
+
export function extractSpmFromUrl(spmOrUrl) {
|
|
83
|
+
if (!/^https?:\/\//i.test(spmOrUrl))
|
|
84
|
+
return spmOrUrl;
|
|
85
|
+
try {
|
|
86
|
+
return new URL(spmOrUrl).searchParams.get('spm') ?? spmOrUrl;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return spmOrUrl;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the point SPM according to event semantics.
|
|
94
|
+
* CLK uses gokey.spm, EXP uses expdata[0].spm, and PV uses spm-url.
|
|
95
|
+
*/
|
|
96
|
+
export function resolveAplusSpm(eventType, raw) {
|
|
97
|
+
const expdata = Array.isArray(raw.expdata) ? raw.expdata : [];
|
|
98
|
+
const expSpm = expdata[0]?.spm || '';
|
|
99
|
+
const gokeyParams = parseParams(raw.gokey || '');
|
|
100
|
+
const gokeySpm = decodeParamValue(gokeyParams.spm || '');
|
|
101
|
+
const rawSpmUrl = raw['spm-url'] || decodeParamValue(gokeyParams['spm-url'] || '');
|
|
102
|
+
const spmUrl = extractSpmFromUrl(rawSpmUrl);
|
|
103
|
+
let pointSpm;
|
|
104
|
+
switch (eventType) {
|
|
105
|
+
case 'CLK':
|
|
106
|
+
pointSpm = gokeySpm || expSpm;
|
|
107
|
+
break;
|
|
108
|
+
case 'EXP':
|
|
109
|
+
pointSpm = expSpm;
|
|
110
|
+
break;
|
|
111
|
+
case 'PV':
|
|
112
|
+
pointSpm = spmUrl;
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
pointSpm = gokeySpm || expSpm;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
mainSpm: pointSpm,
|
|
119
|
+
highlightSpm: pointSpm,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ─── Aplus parser ─────────────────────────────────────────────────────────────
|
|
123
|
+
export function parseAplusRequest(url, options = {}) {
|
|
124
|
+
if (!matchesTrackingHost(url, APLUS_URL_PATTERN))
|
|
125
|
+
return null;
|
|
126
|
+
let urlObj;
|
|
127
|
+
try {
|
|
128
|
+
urlObj = new URL(url);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
// Tracking identifiers can contain an unescaped "+". Decode raw query pairs so
|
|
134
|
+
// it remains a plus sign instead of application/x-www-form-urlencoded whitespace.
|
|
135
|
+
const params = parseUrlParams(urlObj.search);
|
|
136
|
+
const gmkey = params.gmkey ?? 'UNKNOWN';
|
|
137
|
+
const urlPathName = urlObj.pathname.slice(1);
|
|
138
|
+
const pathCategory = urlPathName.split('.')[1];
|
|
139
|
+
const eventType = pathCategory === 'selfdefine' ? 'SDF' : gmkey;
|
|
140
|
+
const gokeyRaw = params.gokey ?? '';
|
|
141
|
+
const gokeyParams = parseParams(gokeyRaw);
|
|
142
|
+
let expdata = [];
|
|
143
|
+
if (gokeyParams.expdata) {
|
|
144
|
+
const parsedExpdata = parseJsonParamValue(gokeyParams.expdata);
|
|
145
|
+
if (Array.isArray(parsedExpdata)) {
|
|
146
|
+
expdata = parsedExpdata.map((item) => {
|
|
147
|
+
if (!item?.exargs || typeof item.exargs !== 'object')
|
|
148
|
+
return item;
|
|
149
|
+
const decoded = {};
|
|
150
|
+
for (const [key, value] of Object.entries(item.exargs)) {
|
|
151
|
+
if (typeof value !== 'string') {
|
|
152
|
+
decoded[key] = value;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const parsedValue = parseJsonParamValue(value);
|
|
156
|
+
decoded[key] = parsedValue === undefined ? value : parsedValue;
|
|
157
|
+
}
|
|
158
|
+
return { ...item, exargs: decoded };
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const rawBizArgs = gokeyParams.bizArgs;
|
|
163
|
+
const parsedBizArgs = rawBizArgs === undefined ? undefined : parseJsonParamValue(rawBizArgs);
|
|
164
|
+
const bizArgs = parsedBizArgs === undefined ? rawBizArgs : parsedBizArgs;
|
|
165
|
+
const pageUrl = params['_p_url'] ?? (gokeyParams['_p_url'] ? decodeParamValue(gokeyParams['_p_url']) : '');
|
|
166
|
+
const { gokey: rawGokey, ...topLevelParams } = params;
|
|
167
|
+
const rawData = {
|
|
168
|
+
...topLevelParams,
|
|
169
|
+
_path: urlPathName,
|
|
170
|
+
_url: url,
|
|
171
|
+
gokey: rawGokey,
|
|
172
|
+
...(expdata.length > 0 ? { expdata } : {}),
|
|
173
|
+
...(bizArgs !== undefined ? { bizArgs } : {}),
|
|
174
|
+
};
|
|
175
|
+
const spm = resolveAplusSpm(eventType, rawData).mainSpm;
|
|
176
|
+
const pathName = urlPathName.split('.').slice(1).join('.');
|
|
177
|
+
const eventName = eventType === 'SDF' ? pathName : spm || pathName || pageUrl;
|
|
178
|
+
const now = options.now ?? Date.now();
|
|
179
|
+
const sequence = options.sequence ?? ++aplusCounter;
|
|
180
|
+
return {
|
|
181
|
+
id: `aplus-${sequence}-${now}`,
|
|
182
|
+
source: 'aplus',
|
|
183
|
+
batchId: 0,
|
|
184
|
+
batchTimestampMs: now,
|
|
185
|
+
batchTimestamp: formatTimestamp(now),
|
|
186
|
+
event_type: eventType,
|
|
187
|
+
name: eventName,
|
|
188
|
+
url,
|
|
189
|
+
timestamp: now,
|
|
190
|
+
_raw: rawData,
|
|
191
|
+
_context: {
|
|
192
|
+
requestUrl: url,
|
|
193
|
+
app: {},
|
|
194
|
+
user: {
|
|
195
|
+
...(params.uidaplus ? { uidaplus: params.uidaplus } : {}),
|
|
196
|
+
...(params['_gr_uid_'] ? { _gr_uid_: params['_gr_uid_'] } : {}),
|
|
197
|
+
},
|
|
198
|
+
session: {},
|
|
199
|
+
view: {
|
|
200
|
+
...(params['spm-url'] ? { 'spm-url': params['spm-url'] } : {}),
|
|
201
|
+
...(params['spm-pre'] ? { 'spm-pre': params['spm-pre'] } : {}),
|
|
202
|
+
...(params['spm-cnt'] ? { 'spm-cnt': params['spm-cnt'] } : {}),
|
|
203
|
+
...(pageUrl ? { page_url: pageUrl } : {}),
|
|
204
|
+
},
|
|
205
|
+
properties: {},
|
|
206
|
+
os: {},
|
|
207
|
+
net: {},
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
// ─── RUM parser ───────────────────────────────────────────────────────────────
|
|
212
|
+
const RUM_TYPE_ALIAS = {
|
|
213
|
+
cst: 'custom',
|
|
214
|
+
};
|
|
215
|
+
const I18N_TRACKER_TYPE_MAP = {
|
|
216
|
+
'i18n-tracker-exp': 'EXP',
|
|
217
|
+
'i18n-tracker-clk': 'CLK',
|
|
218
|
+
};
|
|
219
|
+
function parseRumBody(postData) {
|
|
220
|
+
if (!postData)
|
|
221
|
+
return null;
|
|
222
|
+
if (typeof postData === 'object')
|
|
223
|
+
return postData;
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(postData);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function tryConvertI18nTracker(event, base) {
|
|
232
|
+
const normalizedType = RUM_TYPE_ALIAS[event.type] ?? event.type;
|
|
233
|
+
if (event.event_type !== 'action' || normalizedType !== 'custom')
|
|
234
|
+
return null;
|
|
235
|
+
let snapshots;
|
|
236
|
+
try {
|
|
237
|
+
snapshots = typeof event.snapshots === 'string' ? JSON.parse(event.snapshots) : event.snapshots;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
if (!snapshots || typeof snapshots !== 'object')
|
|
243
|
+
return null;
|
|
244
|
+
const eventType = I18N_TRACKER_TYPE_MAP[snapshots.message];
|
|
245
|
+
if (!eventType)
|
|
246
|
+
return null;
|
|
247
|
+
const spm = [snapshots.c1 ?? '', snapshots.c2 ?? ''].filter(Boolean).join('.');
|
|
248
|
+
const exargs = {};
|
|
249
|
+
if (typeof snapshots.c3 === 'string') {
|
|
250
|
+
snapshots.c3.split('&').forEach((pair) => {
|
|
251
|
+
const eqIndex = pair.indexOf('=');
|
|
252
|
+
if (eqIndex === -1)
|
|
253
|
+
return;
|
|
254
|
+
const key = pair.slice(0, eqIndex);
|
|
255
|
+
const rawValue = pair.slice(eqIndex + 1);
|
|
256
|
+
try {
|
|
257
|
+
exargs[key] = JSON.parse(decodeURIComponent(rawValue));
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
exargs[key] = rawValue;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
const dataAttrs = snapshots.trackerInfo?.dataAttrs;
|
|
265
|
+
if (dataAttrs && typeof dataAttrs === 'object' && dataAttrs['data-biz-args'] && !exargs.bizArgs) {
|
|
266
|
+
try {
|
|
267
|
+
exargs.bizArgs = JSON.parse(dataAttrs['data-biz-args']);
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
exargs.bizArgs = dataAttrs['data-biz-args'];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
...base,
|
|
275
|
+
event_type: eventType,
|
|
276
|
+
name: spm,
|
|
277
|
+
_raw: {
|
|
278
|
+
...event,
|
|
279
|
+
expdata: [
|
|
280
|
+
{
|
|
281
|
+
spm,
|
|
282
|
+
...(Object.keys(exargs).length > 0 ? { exargs } : {}),
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
'spm-url': spm,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
export function parseRumRequest(url, postData, options = {}) {
|
|
290
|
+
if (!matchesTrackingHost(url, RUM_URL_PATTERN))
|
|
291
|
+
return [];
|
|
292
|
+
const parsed = parseRumBody(postData);
|
|
293
|
+
const rawEvents = Array.isArray(parsed?.events) ? parsed.events : [];
|
|
294
|
+
if (rawEvents.length === 0)
|
|
295
|
+
return [];
|
|
296
|
+
const now = options.now ?? Date.now();
|
|
297
|
+
const batchId = options.sequence ?? ++rumCounter;
|
|
298
|
+
const context = {
|
|
299
|
+
requestUrl: url,
|
|
300
|
+
app: parsed?.app ?? {},
|
|
301
|
+
user: parsed?.user ?? {},
|
|
302
|
+
session: parsed?.session ?? {},
|
|
303
|
+
view: parsed?.view ?? {},
|
|
304
|
+
properties: parsed?.properties ?? {},
|
|
305
|
+
os: parsed?.os ?? {},
|
|
306
|
+
net: parsed?.net ?? {},
|
|
307
|
+
};
|
|
308
|
+
return rawEvents.flatMap((rawEvent, index) => {
|
|
309
|
+
if (!rawEvent || typeof rawEvent !== 'object' || Array.isArray(rawEvent))
|
|
310
|
+
return [];
|
|
311
|
+
const event = rawEvent;
|
|
312
|
+
const normalizedType = RUM_TYPE_ALIAS[event.type] ?? event.type;
|
|
313
|
+
const base = {
|
|
314
|
+
id: `${batchId}-${event.event_id ?? index}`,
|
|
315
|
+
batchId,
|
|
316
|
+
batchTimestampMs: now,
|
|
317
|
+
batchTimestamp: formatTimestamp(now),
|
|
318
|
+
type: normalizedType,
|
|
319
|
+
url: event.url,
|
|
320
|
+
timestamp: event.timestamp ?? now,
|
|
321
|
+
success: event.success,
|
|
322
|
+
status_code: event.status_code,
|
|
323
|
+
method: event.method,
|
|
324
|
+
duration: event.duration,
|
|
325
|
+
source: 'rum',
|
|
326
|
+
_context: context,
|
|
327
|
+
};
|
|
328
|
+
const converted = tryConvertI18nTracker(event, base);
|
|
329
|
+
if (converted)
|
|
330
|
+
return [converted];
|
|
331
|
+
return [
|
|
332
|
+
{
|
|
333
|
+
...base,
|
|
334
|
+
event_type: event.event_type ?? 'unknown',
|
|
335
|
+
name: event.name,
|
|
336
|
+
_raw: event,
|
|
337
|
+
},
|
|
338
|
+
];
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
// ─── ARMS parser ──────────────────────────────────────────────────────────────
|
|
342
|
+
export function parseArmsRequest(url, options = {}) {
|
|
343
|
+
if (!matchesTrackingHost(url, ARMS_URL_PATTERN))
|
|
344
|
+
return null;
|
|
345
|
+
let urlObj;
|
|
346
|
+
try {
|
|
347
|
+
urlObj = new URL(url);
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
const params = {};
|
|
353
|
+
urlObj.searchParams.forEach((value, key) => {
|
|
354
|
+
params[key] = value;
|
|
355
|
+
});
|
|
356
|
+
const eventType = (params.t ?? 'unknown').toLowerCase();
|
|
357
|
+
const page = params.page ?? '';
|
|
358
|
+
const now = options.now ?? Date.now();
|
|
359
|
+
const sequence = options.sequence ?? ++armsCounter;
|
|
360
|
+
const parsedTimestamp = params.begin ? Number.parseInt(params.begin, 10) : now;
|
|
361
|
+
const timestamp = Number.isFinite(parsedTimestamp) ? parsedTimestamp : now;
|
|
362
|
+
return {
|
|
363
|
+
id: `arms-${sequence}-${now}`,
|
|
364
|
+
source: 'arms',
|
|
365
|
+
batchId: 0,
|
|
366
|
+
batchTimestampMs: now,
|
|
367
|
+
batchTimestamp: formatTimestamp(now),
|
|
368
|
+
event_type: eventType,
|
|
369
|
+
name: page || url,
|
|
370
|
+
url,
|
|
371
|
+
timestamp,
|
|
372
|
+
_raw: { ...params, _url: url },
|
|
373
|
+
_context: {
|
|
374
|
+
requestUrl: url,
|
|
375
|
+
app: {
|
|
376
|
+
...(params.pid ? { pid: params.pid } : {}),
|
|
377
|
+
...(params.release ? { release: params.release } : {}),
|
|
378
|
+
...(params.environment ? { environment: params.environment } : {}),
|
|
379
|
+
...(params._v ? { sdk_version: params._v } : {}),
|
|
380
|
+
},
|
|
381
|
+
user: {
|
|
382
|
+
...(params.uid ? { uid: params.uid } : {}),
|
|
383
|
+
},
|
|
384
|
+
session: {
|
|
385
|
+
...(params.sid ? { sid: params.sid } : {}),
|
|
386
|
+
},
|
|
387
|
+
view: {
|
|
388
|
+
...(page ? { page } : {}),
|
|
389
|
+
...(params.tag ? { tag: params.tag } : {}),
|
|
390
|
+
},
|
|
391
|
+
properties: {},
|
|
392
|
+
os: {},
|
|
393
|
+
net: {
|
|
394
|
+
...(params.ct ? { ct: params.ct } : {}),
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
/** Parse any supported tracking request into zero or more normalized events. */
|
|
400
|
+
export function parseTrackingRequest(url, postData, options = {}) {
|
|
401
|
+
const rumEvents = parseRumRequest(url, postData, options);
|
|
402
|
+
if (rumEvents.length > 0)
|
|
403
|
+
return rumEvents;
|
|
404
|
+
const aplusEvent = parseAplusRequest(url, options);
|
|
405
|
+
if (aplusEvent)
|
|
406
|
+
return [aplusEvent];
|
|
407
|
+
const armsEvent = parseArmsRequest(url, options);
|
|
408
|
+
return armsEvent ? [armsEvent] : [];
|
|
409
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { isTrackingUrl, parseTrackingRequest, type TrackingParseOptions } from './eventParsers.js';
|
|
2
|
+
export interface TrackingInput {
|
|
3
|
+
url: string;
|
|
4
|
+
body?: string | Record<string, unknown> | null;
|
|
5
|
+
options?: TrackingParseOptions;
|
|
6
|
+
}
|
|
7
|
+
/** The built-in default and independently installed bundles use the same handlers. */
|
|
8
|
+
export default function createTrackingPlugin(): {
|
|
9
|
+
tracking: () => {
|
|
10
|
+
matches: typeof isTrackingUrl;
|
|
11
|
+
parse: typeof parseTrackingRequest;
|
|
12
|
+
};
|
|
13
|
+
decode: (inputs: TrackingInput[]) => ({
|
|
14
|
+
tracking: boolean;
|
|
15
|
+
events: import("@mearl/browser-core").RumEvent[];
|
|
16
|
+
error?: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
tracking: boolean;
|
|
19
|
+
events: never[];
|
|
20
|
+
error: string;
|
|
21
|
+
})[];
|
|
22
|
+
spm: ({ eventType, raw }: {
|
|
23
|
+
eventType?: string;
|
|
24
|
+
raw: Record<string, unknown>;
|
|
25
|
+
}) => import("./eventParsers.js").AplusSpmResolution;
|
|
26
|
+
};
|
|
27
|
+
export { createTrackingPlugin };
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isTrackingUrl, parseTrackingRequest, resolveAplusSpm } from './eventParsers.js';
|
|
2
|
+
/** The built-in default and independently installed bundles use the same handlers. */
|
|
3
|
+
export default function createTrackingPlugin() {
|
|
4
|
+
return {
|
|
5
|
+
tracking: () => ({ matches: isTrackingUrl, parse: parseTrackingRequest }),
|
|
6
|
+
decode: (inputs) => inputs.map(({ url, body, options }) => {
|
|
7
|
+
try {
|
|
8
|
+
return { tracking: isTrackingUrl(url), events: parseTrackingRequest(url, body, options) };
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
return { tracking: isTrackingUrl(url), events: [], error: error instanceof Error ? error.message : String(error) };
|
|
12
|
+
}
|
|
13
|
+
}),
|
|
14
|
+
spm: ({ eventType, raw }) => resolveAplusSpm(eventType, raw),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export { createTrackingPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/plugin-tracking",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "3.0.0-beta.6",
|
|
4
|
+
"description": "Optional Aplus, RUM and ARMS tracking capability for Mearl",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./runtime": {
|
|
15
|
+
"types": "./dist/runtime.d.ts",
|
|
16
|
+
"import": "./dist/runtime.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"!dist/**/__tests__/**",
|
|
22
|
+
"!dist/**/*.test.d.ts"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20.18.0"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@mearl/browser-core": "3.0.0-beta.6"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"esbuild": "^0.28.2",
|
|
32
|
+
"typescript": "^5.8.3"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/mearlhq/mearl.git",
|
|
37
|
+
"directory": "packages/plugin-tracking"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "rm -rf dist/ && tsc && node build.mjs",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run"
|
|
43
|
+
}
|
|
6
44
|
}
|