@momentumcms/plugins-analytics 0.3.0 → 0.4.1
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/index.cjs +295 -45
- package/index.js +291 -45
- package/lib/analytics-admin-routes.cjs +2108 -0
- package/lib/analytics-admin-routes.js +2195 -0
- package/lib/client/tracker.cjs +541 -0
- package/lib/client/tracker.js +523 -0
- package/lib/collectors/block-field-injector.cjs +206 -0
- package/lib/collectors/block-field-injector.js +179 -0
- package/lib/page-view-tracker.cjs +208 -0
- package/lib/page-view-tracker.js +189 -0
- package/package.json +64 -55
- package/src/index.d.ts +3 -1
- package/src/lib/analytics-admin-routes.d.ts +3 -0
- package/src/lib/analytics-config.types.d.ts +29 -0
- package/src/lib/analytics-plugin.d.ts +1 -1
- package/src/lib/collectors/page-view-collector.d.ts +29 -0
- package/src/lib/page-view-tracker.d.ts +21 -0
- package/src/lib/page-view-tracker.utils.d.ts +28 -0
- package/src/lib/utils/content-route-matcher.d.ts +42 -0
- package/CHANGELOG.md +0 -99
- package/LICENSE +0 -21
package/index.cjs
CHANGED
|
@@ -36,6 +36,10 @@ __export(block_tracker_exports, {
|
|
|
36
36
|
attachBlockTracking: () => attachBlockTracking
|
|
37
37
|
});
|
|
38
38
|
function attachBlockTracking(tracker, container) {
|
|
39
|
+
if (typeof document === "undefined" || !document.body) {
|
|
40
|
+
return () => {
|
|
41
|
+
};
|
|
42
|
+
}
|
|
39
43
|
const root = container ?? document.body;
|
|
40
44
|
const impressionsSeen = /* @__PURE__ */ new Set();
|
|
41
45
|
const hoverCooldowns = /* @__PURE__ */ new Map();
|
|
@@ -405,16 +409,20 @@ __export(src_exports, {
|
|
|
405
409
|
TrackingRules: () => TrackingRules,
|
|
406
410
|
analyticsPlugin: () => analyticsPlugin,
|
|
407
411
|
attachBlockTracking: () => attachBlockTracking,
|
|
412
|
+
compileContentRoutes: () => compileContentRoutes,
|
|
408
413
|
createAnalyticsMiddleware: () => createAnalyticsMiddleware,
|
|
409
414
|
createAnalyticsQueryRouter: () => createAnalyticsQueryRouter,
|
|
410
415
|
createApiCollectorMiddleware: () => createApiCollectorMiddleware,
|
|
411
416
|
createContentPerformanceRouter: () => createContentPerformanceRouter,
|
|
412
417
|
createIngestRouter: () => createIngestRouter,
|
|
418
|
+
createPageViewCollectorMiddleware: () => createPageViewCollectorMiddleware,
|
|
413
419
|
createRuleEngine: () => createRuleEngine,
|
|
414
420
|
createTracker: () => createTracker,
|
|
415
421
|
createTrackingRulesRouter: () => createTrackingRulesRouter,
|
|
416
422
|
injectBlockAnalyticsFields: () => injectBlockAnalyticsFields,
|
|
417
423
|
injectCollectionCollector: () => injectCollectionCollector,
|
|
424
|
+
isBot: () => isBot,
|
|
425
|
+
matchContentRoute: () => matchContentRoute,
|
|
418
426
|
parseUserAgent: () => parseUserAgent,
|
|
419
427
|
postgresAnalyticsAdapter: () => postgresAnalyticsAdapter
|
|
420
428
|
});
|
|
@@ -872,8 +880,210 @@ function createApiCollectorMiddleware(emitter) {
|
|
|
872
880
|
};
|
|
873
881
|
}
|
|
874
882
|
|
|
875
|
-
// libs/plugins/analytics/src/lib/
|
|
883
|
+
// libs/plugins/analytics/src/lib/collectors/page-view-collector.ts
|
|
876
884
|
var import_node_crypto3 = require("node:crypto");
|
|
885
|
+
|
|
886
|
+
// libs/plugins/analytics/src/lib/utils/content-route-matcher.ts
|
|
887
|
+
function escapeRegex(str) {
|
|
888
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
889
|
+
}
|
|
890
|
+
function compileContentRoute(collection, pattern) {
|
|
891
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
892
|
+
const paramNames = [];
|
|
893
|
+
let staticCount = 0;
|
|
894
|
+
const regexParts = segments.map((seg) => {
|
|
895
|
+
if (seg.startsWith(":")) {
|
|
896
|
+
paramNames.push(seg.slice(1));
|
|
897
|
+
return "([^/]+)";
|
|
898
|
+
}
|
|
899
|
+
staticCount++;
|
|
900
|
+
return escapeRegex(seg);
|
|
901
|
+
});
|
|
902
|
+
const regexStr = "^/" + regexParts.join("/") + "/?$";
|
|
903
|
+
return {
|
|
904
|
+
collection,
|
|
905
|
+
pattern,
|
|
906
|
+
regex: new RegExp(regexStr),
|
|
907
|
+
paramNames,
|
|
908
|
+
staticSegments: staticCount
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function compileContentRoutes(routes) {
|
|
912
|
+
const compiled = Object.entries(routes).map(
|
|
913
|
+
([collection, pattern]) => compileContentRoute(collection, pattern)
|
|
914
|
+
);
|
|
915
|
+
compiled.sort((a, b) => b.staticSegments - a.staticSegments);
|
|
916
|
+
return compiled;
|
|
917
|
+
}
|
|
918
|
+
function matchContentRoute(path, routes) {
|
|
919
|
+
for (const route of routes) {
|
|
920
|
+
const match = route.regex.exec(path);
|
|
921
|
+
if (match) {
|
|
922
|
+
const params = {};
|
|
923
|
+
for (let i = 0; i < route.paramNames.length; i++) {
|
|
924
|
+
params[route.paramNames[i]] = match[i + 1];
|
|
925
|
+
}
|
|
926
|
+
return { collection: route.collection, params };
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return void 0;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// libs/plugins/analytics/src/lib/collectors/page-view-collector.ts
|
|
933
|
+
var DEFAULT_EXCLUDE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
934
|
+
".js",
|
|
935
|
+
".css",
|
|
936
|
+
".ico",
|
|
937
|
+
".png",
|
|
938
|
+
".jpg",
|
|
939
|
+
".jpeg",
|
|
940
|
+
".gif",
|
|
941
|
+
".svg",
|
|
942
|
+
".webp",
|
|
943
|
+
".avif",
|
|
944
|
+
".woff",
|
|
945
|
+
".woff2",
|
|
946
|
+
".ttf",
|
|
947
|
+
".eot",
|
|
948
|
+
".map",
|
|
949
|
+
".json",
|
|
950
|
+
".xml",
|
|
951
|
+
".txt",
|
|
952
|
+
".mp4",
|
|
953
|
+
".webm",
|
|
954
|
+
".ogg",
|
|
955
|
+
".mp3"
|
|
956
|
+
]);
|
|
957
|
+
var ALWAYS_EXCLUDED_PREFIXES = [
|
|
958
|
+
"/api/",
|
|
959
|
+
"/admin",
|
|
960
|
+
"/__vite",
|
|
961
|
+
"/@fs/",
|
|
962
|
+
"/@id/",
|
|
963
|
+
"/.analog/",
|
|
964
|
+
"/node_modules/"
|
|
965
|
+
];
|
|
966
|
+
var ALWAYS_EXCLUDED_EXACT = /* @__PURE__ */ new Set([
|
|
967
|
+
"/api",
|
|
968
|
+
"/favicon.ico",
|
|
969
|
+
"/robots.txt",
|
|
970
|
+
"/sitemap.xml",
|
|
971
|
+
"/sitemap-index.xml",
|
|
972
|
+
"/health",
|
|
973
|
+
"/healthz",
|
|
974
|
+
"/ready",
|
|
975
|
+
"/.well-known/security.txt"
|
|
976
|
+
]);
|
|
977
|
+
var BOT_PATTERN = /bot|crawl|spider|slurp|bingpreview|mediapartners|facebookexternalhit|linkedinbot|twitterbot|whatsapp|telegrambot|discordbot|applebot|duckduckbot|yandex|baidu|sogou|ia_archiver|semrush|ahref|mj12bot|dotbot|petalbot|bytespider/i;
|
|
978
|
+
function isBot(ua) {
|
|
979
|
+
if (!ua)
|
|
980
|
+
return false;
|
|
981
|
+
return BOT_PATTERN.test(ua);
|
|
982
|
+
}
|
|
983
|
+
function shouldExcludePath(path, excludeExtensions, excludePaths) {
|
|
984
|
+
if (ALWAYS_EXCLUDED_EXACT.has(path))
|
|
985
|
+
return true;
|
|
986
|
+
for (const prefix of ALWAYS_EXCLUDED_PREFIXES) {
|
|
987
|
+
if (path.startsWith(prefix))
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
for (const pattern of excludePaths) {
|
|
991
|
+
if (path.startsWith(pattern))
|
|
992
|
+
return true;
|
|
993
|
+
}
|
|
994
|
+
const dotIndex = path.lastIndexOf(".");
|
|
995
|
+
if (dotIndex !== -1) {
|
|
996
|
+
const ext = path.slice(dotIndex).toLowerCase();
|
|
997
|
+
if (excludeExtensions.has(ext))
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
function extractUserId(req) {
|
|
1003
|
+
if (!("user" in req))
|
|
1004
|
+
return void 0;
|
|
1005
|
+
const user = req["user"];
|
|
1006
|
+
if (user == null || typeof user !== "object")
|
|
1007
|
+
return void 0;
|
|
1008
|
+
if (!("id" in user))
|
|
1009
|
+
return void 0;
|
|
1010
|
+
const id = user.id;
|
|
1011
|
+
return typeof id === "string" ? id : void 0;
|
|
1012
|
+
}
|
|
1013
|
+
function createPageViewCollectorMiddleware(emitter, options = {}) {
|
|
1014
|
+
const excludeExtensions = options.excludeExtensions ? new Set(options.excludeExtensions) : DEFAULT_EXCLUDE_EXTENSIONS;
|
|
1015
|
+
const excludePaths = options.excludePaths ?? [];
|
|
1016
|
+
const onlySuccessful = options.onlySuccessful !== false;
|
|
1017
|
+
const trackBots = options.trackBots === true;
|
|
1018
|
+
const compiledRoutes = options.contentRoutes ? compileContentRoutes(options.contentRoutes) : void 0;
|
|
1019
|
+
return (req, res, next) => {
|
|
1020
|
+
if (req.method !== "GET") {
|
|
1021
|
+
next();
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const path = req.path;
|
|
1025
|
+
if (shouldExcludePath(path, excludeExtensions, excludePaths)) {
|
|
1026
|
+
next();
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const ua = req.headers["user-agent"];
|
|
1030
|
+
if (!trackBots && isBot(ua)) {
|
|
1031
|
+
next();
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const start = Date.now();
|
|
1035
|
+
res.once("finish", () => {
|
|
1036
|
+
if (onlySuccessful && (res.statusCode < 200 || res.statusCode >= 300)) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const duration = Date.now() - start;
|
|
1040
|
+
const parsed = parseUserAgent(ua);
|
|
1041
|
+
const refHeader = req.headers["referer"] ?? req.headers["referrer"];
|
|
1042
|
+
const referrer = Array.isArray(refHeader) ? refHeader[0] : refHeader;
|
|
1043
|
+
let contentCollection;
|
|
1044
|
+
let contentSlug;
|
|
1045
|
+
if (compiledRoutes) {
|
|
1046
|
+
const routeMatch = matchContentRoute(path, compiledRoutes);
|
|
1047
|
+
if (routeMatch) {
|
|
1048
|
+
contentCollection = routeMatch.collection;
|
|
1049
|
+
contentSlug = routeMatch.params["slug"];
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
const event = {
|
|
1053
|
+
id: (0, import_node_crypto3.randomUUID)(),
|
|
1054
|
+
category: "page",
|
|
1055
|
+
name: "page_view",
|
|
1056
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1057
|
+
userId: extractUserId(req),
|
|
1058
|
+
properties: {
|
|
1059
|
+
method: req.method,
|
|
1060
|
+
path,
|
|
1061
|
+
statusCode: res.statusCode,
|
|
1062
|
+
...contentCollection != null ? { collection: contentCollection } : {},
|
|
1063
|
+
...contentSlug != null ? { slug: contentSlug } : {}
|
|
1064
|
+
},
|
|
1065
|
+
context: {
|
|
1066
|
+
source: "server",
|
|
1067
|
+
url: req.originalUrl,
|
|
1068
|
+
referrer,
|
|
1069
|
+
userAgent: ua,
|
|
1070
|
+
ip: req.ip ?? req.socket.remoteAddress,
|
|
1071
|
+
device: parsed.device,
|
|
1072
|
+
browser: parsed.browser,
|
|
1073
|
+
os: parsed.os,
|
|
1074
|
+
duration,
|
|
1075
|
+
statusCode: res.statusCode,
|
|
1076
|
+
...contentCollection != null ? { collection: contentCollection } : {}
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
emitter(event);
|
|
1080
|
+
});
|
|
1081
|
+
next();
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// libs/plugins/analytics/src/lib/ingest-handler.ts
|
|
1086
|
+
var import_node_crypto4 = require("node:crypto");
|
|
877
1087
|
var import_express = require("express");
|
|
878
1088
|
|
|
879
1089
|
// libs/core/src/lib/collections/define-collection.ts
|
|
@@ -1154,7 +1364,7 @@ function createIngestRouter(options) {
|
|
|
1154
1364
|
}
|
|
1155
1365
|
const partial = raw;
|
|
1156
1366
|
const event = {
|
|
1157
|
-
id: (0,
|
|
1367
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
1158
1368
|
category: partial.category ?? "custom",
|
|
1159
1369
|
name: partial.name ?? "unknown",
|
|
1160
1370
|
// Server-side timestamp (prevents client clock skew)
|
|
@@ -1760,6 +1970,42 @@ function matchesDocumentUrl(eventUrl, documentPath) {
|
|
|
1760
1970
|
}
|
|
1761
1971
|
return pathname === documentPath || pathname === `${documentPath}/`;
|
|
1762
1972
|
}
|
|
1973
|
+
async function queryEventsByDocument(queryFn, eventName, collection, documentId, documentPath, options) {
|
|
1974
|
+
const queryBase = {
|
|
1975
|
+
...options.category ? { category: options.category } : {},
|
|
1976
|
+
name: eventName,
|
|
1977
|
+
from: options.from,
|
|
1978
|
+
to: options.to,
|
|
1979
|
+
limit: 1e3
|
|
1980
|
+
};
|
|
1981
|
+
const [serverResult, allResult] = await Promise.all([
|
|
1982
|
+
queryFn({ ...queryBase, collection }),
|
|
1983
|
+
queryFn(queryBase)
|
|
1984
|
+
]);
|
|
1985
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1986
|
+
const results = [];
|
|
1987
|
+
for (const e of [...serverResult.events, ...allResult.events]) {
|
|
1988
|
+
if (seen.has(e.id))
|
|
1989
|
+
continue;
|
|
1990
|
+
seen.add(e.id);
|
|
1991
|
+
const matchesCollection = e.context.collection === collection || e.properties["collection"] === collection;
|
|
1992
|
+
const matchesSlug = typeof e.properties["slug"] === "string" && e.properties["slug"] === documentId;
|
|
1993
|
+
if (matchesCollection && matchesSlug)
|
|
1994
|
+
results.push(e);
|
|
1995
|
+
}
|
|
1996
|
+
if (results.length > 0)
|
|
1997
|
+
return results;
|
|
1998
|
+
const urlResult = await queryFn({ ...queryBase, search: documentPath });
|
|
1999
|
+
return urlResult.events.filter((e) => matchesDocumentUrl(e.context.url, documentPath));
|
|
2000
|
+
}
|
|
2001
|
+
function countByBlockType(events) {
|
|
2002
|
+
const map = /* @__PURE__ */ new Map();
|
|
2003
|
+
for (const event of events) {
|
|
2004
|
+
const bt = String(event.properties["blockType"] ?? "unknown");
|
|
2005
|
+
map.set(bt, (map.get(bt) ?? 0) + 1);
|
|
2006
|
+
}
|
|
2007
|
+
return map;
|
|
2008
|
+
}
|
|
1763
2009
|
function createContentPerformanceRouter(adapter) {
|
|
1764
2010
|
const router = (0, import_express3.Router)();
|
|
1765
2011
|
router.get("/content-performance", requireAdmin, async (req, res) => {
|
|
@@ -1778,16 +2024,15 @@ function createContentPerformanceRouter(adapter) {
|
|
|
1778
2024
|
return;
|
|
1779
2025
|
}
|
|
1780
2026
|
const documentPath = `/${collection}/${documentId}`;
|
|
1781
|
-
const
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
(e) => matchesDocumentUrl(e.context.url, documentPath)
|
|
2027
|
+
const queryFn = adapter.query.bind(adapter);
|
|
2028
|
+
const dateRange = { from, to };
|
|
2029
|
+
const pageViewEvents = await queryEventsByDocument(
|
|
2030
|
+
queryFn,
|
|
2031
|
+
"page_view",
|
|
2032
|
+
collection,
|
|
2033
|
+
documentId,
|
|
2034
|
+
documentPath,
|
|
2035
|
+
{ category: "page", ...dateRange }
|
|
1791
2036
|
);
|
|
1792
2037
|
const visitorSet = /* @__PURE__ */ new Set();
|
|
1793
2038
|
const referrerMap = /* @__PURE__ */ new Map();
|
|
@@ -1802,38 +2047,26 @@ function createContentPerformanceRouter(adapter) {
|
|
|
1802
2047
|
}
|
|
1803
2048
|
let blockEngagement;
|
|
1804
2049
|
try {
|
|
1805
|
-
const [
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
2050
|
+
const [impressionEvents, hoverEvents] = await Promise.all([
|
|
2051
|
+
queryEventsByDocument(
|
|
2052
|
+
queryFn,
|
|
2053
|
+
"block_impression",
|
|
2054
|
+
collection,
|
|
2055
|
+
documentId,
|
|
2056
|
+
documentPath,
|
|
2057
|
+
dateRange
|
|
2058
|
+
),
|
|
2059
|
+
queryEventsByDocument(
|
|
2060
|
+
queryFn,
|
|
2061
|
+
"block_hover",
|
|
2062
|
+
collection,
|
|
2063
|
+
documentId,
|
|
2064
|
+
documentPath,
|
|
2065
|
+
dateRange
|
|
2066
|
+
)
|
|
1820
2067
|
]);
|
|
1821
|
-
const
|
|
1822
|
-
|
|
1823
|
-
);
|
|
1824
|
-
const hoverEvents = hoverResult.events.filter(
|
|
1825
|
-
(e) => matchesDocumentUrl(e.context.url, documentPath)
|
|
1826
|
-
);
|
|
1827
|
-
const impressionMap = /* @__PURE__ */ new Map();
|
|
1828
|
-
for (const event of impressionEvents) {
|
|
1829
|
-
const bt = String(event.properties["blockType"] ?? "unknown");
|
|
1830
|
-
impressionMap.set(bt, (impressionMap.get(bt) ?? 0) + 1);
|
|
1831
|
-
}
|
|
1832
|
-
const hoverMap = /* @__PURE__ */ new Map();
|
|
1833
|
-
for (const event of hoverEvents) {
|
|
1834
|
-
const bt = String(event.properties["blockType"] ?? "unknown");
|
|
1835
|
-
hoverMap.set(bt, (hoverMap.get(bt) ?? 0) + 1);
|
|
1836
|
-
}
|
|
2068
|
+
const impressionMap = countByBlockType(impressionEvents);
|
|
2069
|
+
const hoverMap = countByBlockType(hoverEvents);
|
|
1837
2070
|
const allTypes = /* @__PURE__ */ new Set([...impressionMap.keys(), ...hoverMap.keys()]);
|
|
1838
2071
|
if (allTypes.size > 0) {
|
|
1839
2072
|
blockEngagement = [];
|
|
@@ -2086,11 +2319,11 @@ function analyticsPlugin(config) {
|
|
|
2086
2319
|
// Browser-safe import paths for the admin config generator
|
|
2087
2320
|
browserImports: {
|
|
2088
2321
|
adminRoutes: {
|
|
2089
|
-
path: "@momentumcms/plugins
|
|
2322
|
+
path: "@momentumcms/plugins-analytics/admin-routes",
|
|
2090
2323
|
exportName: "analyticsAdminRoutes"
|
|
2091
2324
|
},
|
|
2092
2325
|
modifyCollections: {
|
|
2093
|
-
path: "@momentumcms/plugins
|
|
2326
|
+
path: "@momentumcms/plugins-analytics/block-fields",
|
|
2094
2327
|
exportName: "injectBlockAnalyticsFields"
|
|
2095
2328
|
}
|
|
2096
2329
|
},
|
|
@@ -2141,6 +2374,19 @@ function analyticsPlugin(config) {
|
|
|
2141
2374
|
position: "before-api"
|
|
2142
2375
|
});
|
|
2143
2376
|
}
|
|
2377
|
+
if (config.trackPageViews !== false) {
|
|
2378
|
+
const pageViewOptions = typeof config.trackPageViews === "object" ? config.trackPageViews : {};
|
|
2379
|
+
const pageViewCollector = createPageViewCollectorMiddleware(
|
|
2380
|
+
(event) => eventStore.add(event),
|
|
2381
|
+
pageViewOptions
|
|
2382
|
+
);
|
|
2383
|
+
registerMiddleware({
|
|
2384
|
+
path: "/",
|
|
2385
|
+
handler: pageViewCollector,
|
|
2386
|
+
position: "root"
|
|
2387
|
+
});
|
|
2388
|
+
logger.info("Page view tracking enabled");
|
|
2389
|
+
}
|
|
2144
2390
|
if (config.contentPerformance !== false) {
|
|
2145
2391
|
const contentPerfRouter = createContentPerformanceRouter(config.adapter);
|
|
2146
2392
|
registerMiddleware({
|
|
@@ -2226,16 +2472,20 @@ init_rule_engine();
|
|
|
2226
2472
|
TrackingRules,
|
|
2227
2473
|
analyticsPlugin,
|
|
2228
2474
|
attachBlockTracking,
|
|
2475
|
+
compileContentRoutes,
|
|
2229
2476
|
createAnalyticsMiddleware,
|
|
2230
2477
|
createAnalyticsQueryRouter,
|
|
2231
2478
|
createApiCollectorMiddleware,
|
|
2232
2479
|
createContentPerformanceRouter,
|
|
2233
2480
|
createIngestRouter,
|
|
2481
|
+
createPageViewCollectorMiddleware,
|
|
2234
2482
|
createRuleEngine,
|
|
2235
2483
|
createTracker,
|
|
2236
2484
|
createTrackingRulesRouter,
|
|
2237
2485
|
injectBlockAnalyticsFields,
|
|
2238
2486
|
injectCollectionCollector,
|
|
2487
|
+
isBot,
|
|
2488
|
+
matchContentRoute,
|
|
2239
2489
|
parseUserAgent,
|
|
2240
2490
|
postgresAnalyticsAdapter
|
|
2241
2491
|
});
|