@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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// libs/plugins/analytics/src/lib/collectors/block-field-injector.ts
|
|
21
|
+
var block_field_injector_exports = {};
|
|
22
|
+
__export(block_field_injector_exports, {
|
|
23
|
+
injectBlockAnalyticsFields: () => injectBlockAnalyticsFields
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(block_field_injector_exports);
|
|
26
|
+
|
|
27
|
+
// libs/core/src/lib/collections/define-collection.ts
|
|
28
|
+
function defineCollection(config) {
|
|
29
|
+
const collection = {
|
|
30
|
+
timestamps: true,
|
|
31
|
+
// Enable timestamps by default
|
|
32
|
+
...config
|
|
33
|
+
};
|
|
34
|
+
if (!collection.slug) {
|
|
35
|
+
throw new Error("Collection must have a slug");
|
|
36
|
+
}
|
|
37
|
+
if (!collection.fields || collection.fields.length === 0) {
|
|
38
|
+
throw new Error(`Collection "${collection.slug}" must have at least one field`);
|
|
39
|
+
}
|
|
40
|
+
if (!/^[a-z][a-z0-9-]*$/.test(collection.slug)) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Collection slug "${collection.slug}" must be kebab-case (lowercase letters, numbers, and hyphens, starting with a letter)`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return collection;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// libs/core/src/lib/fields/field-builders.ts
|
|
49
|
+
function text(name, options = {}) {
|
|
50
|
+
return {
|
|
51
|
+
name,
|
|
52
|
+
type: "text",
|
|
53
|
+
...options
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function number(name, options = {}) {
|
|
57
|
+
return {
|
|
58
|
+
name,
|
|
59
|
+
type: "number",
|
|
60
|
+
...options
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function checkbox(name, options = {}) {
|
|
64
|
+
return {
|
|
65
|
+
name,
|
|
66
|
+
type: "checkbox",
|
|
67
|
+
...options,
|
|
68
|
+
defaultValue: options.defaultValue ?? false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function group(name, options) {
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
type: "group",
|
|
75
|
+
...options
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function json(name, options = {}) {
|
|
79
|
+
return {
|
|
80
|
+
name,
|
|
81
|
+
type: "json",
|
|
82
|
+
...options
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// libs/core/src/lib/collections/media.collection.ts
|
|
87
|
+
var MediaCollection = defineCollection({
|
|
88
|
+
slug: "media",
|
|
89
|
+
labels: {
|
|
90
|
+
singular: "Media",
|
|
91
|
+
plural: "Media"
|
|
92
|
+
},
|
|
93
|
+
upload: {
|
|
94
|
+
mimeTypes: ["image/*", "application/pdf", "video/*", "audio/*"]
|
|
95
|
+
},
|
|
96
|
+
admin: {
|
|
97
|
+
useAsTitle: "filename",
|
|
98
|
+
defaultColumns: ["filename", "mimeType", "filesize", "createdAt"]
|
|
99
|
+
},
|
|
100
|
+
fields: [
|
|
101
|
+
text("filename", {
|
|
102
|
+
required: true,
|
|
103
|
+
label: "Filename",
|
|
104
|
+
description: "Original filename of the uploaded file"
|
|
105
|
+
}),
|
|
106
|
+
text("mimeType", {
|
|
107
|
+
required: true,
|
|
108
|
+
label: "MIME Type",
|
|
109
|
+
description: "File MIME type (e.g., image/jpeg, application/pdf)"
|
|
110
|
+
}),
|
|
111
|
+
number("filesize", {
|
|
112
|
+
label: "File Size",
|
|
113
|
+
description: "File size in bytes"
|
|
114
|
+
}),
|
|
115
|
+
text("path", {
|
|
116
|
+
label: "Storage Path",
|
|
117
|
+
description: "Path/key where the file is stored",
|
|
118
|
+
admin: {
|
|
119
|
+
hidden: true
|
|
120
|
+
}
|
|
121
|
+
}),
|
|
122
|
+
text("url", {
|
|
123
|
+
label: "URL",
|
|
124
|
+
description: "Public URL to access the file"
|
|
125
|
+
}),
|
|
126
|
+
text("alt", {
|
|
127
|
+
label: "Alt Text",
|
|
128
|
+
description: "Alternative text for accessibility"
|
|
129
|
+
}),
|
|
130
|
+
number("width", {
|
|
131
|
+
label: "Width",
|
|
132
|
+
description: "Image width in pixels (for images only)"
|
|
133
|
+
}),
|
|
134
|
+
number("height", {
|
|
135
|
+
label: "Height",
|
|
136
|
+
description: "Image height in pixels (for images only)"
|
|
137
|
+
}),
|
|
138
|
+
json("focalPoint", {
|
|
139
|
+
label: "Focal Point",
|
|
140
|
+
description: "Focal point coordinates for image cropping",
|
|
141
|
+
admin: {
|
|
142
|
+
hidden: true
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
],
|
|
146
|
+
access: {
|
|
147
|
+
// Media is readable by anyone by default
|
|
148
|
+
read: () => true,
|
|
149
|
+
// Only authenticated users can create/update/delete
|
|
150
|
+
create: ({ req }) => !!req?.user,
|
|
151
|
+
update: ({ req }) => !!req?.user,
|
|
152
|
+
delete: ({ req }) => !!req?.user
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// libs/plugins/analytics/src/lib/collectors/block-field-injector.ts
|
|
157
|
+
function createAnalyticsGroupField() {
|
|
158
|
+
return group("_analytics", {
|
|
159
|
+
label: "Analytics",
|
|
160
|
+
admin: { collapsible: true, defaultOpen: false },
|
|
161
|
+
fields: [
|
|
162
|
+
checkbox("trackImpressions", { label: "Track Impressions" }),
|
|
163
|
+
checkbox("trackHover", { label: "Track Hover" })
|
|
164
|
+
]
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function hasAnalyticsField(fields) {
|
|
168
|
+
return fields.some((f) => f.name === "_analytics" && f.type === "group");
|
|
169
|
+
}
|
|
170
|
+
function hasBlocks(field) {
|
|
171
|
+
return field.type === "blocks" && "blocks" in field;
|
|
172
|
+
}
|
|
173
|
+
function hasNestedFields(field) {
|
|
174
|
+
return "fields" in field && Array.isArray(field.fields);
|
|
175
|
+
}
|
|
176
|
+
function hasTabs(field) {
|
|
177
|
+
return field.type === "tabs" && "tabs" in field;
|
|
178
|
+
}
|
|
179
|
+
function injectIntoFields(fields) {
|
|
180
|
+
for (const field of fields) {
|
|
181
|
+
if (hasBlocks(field)) {
|
|
182
|
+
for (const blockConfig of field.blocks) {
|
|
183
|
+
if (!hasAnalyticsField(blockConfig.fields)) {
|
|
184
|
+
blockConfig.fields.push(createAnalyticsGroupField());
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if ((field.type === "group" || field.type === "array" || field.type === "collapsible" || field.type === "row") && hasNestedFields(field)) {
|
|
189
|
+
injectIntoFields(field.fields);
|
|
190
|
+
}
|
|
191
|
+
if (hasTabs(field)) {
|
|
192
|
+
for (const tab of field.tabs) {
|
|
193
|
+
injectIntoFields(tab.fields);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function injectBlockAnalyticsFields(collections) {
|
|
199
|
+
for (const collection of collections) {
|
|
200
|
+
injectIntoFields(collection.fields);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
204
|
+
0 && (module.exports = {
|
|
205
|
+
injectBlockAnalyticsFields
|
|
206
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// libs/core/src/lib/collections/define-collection.ts
|
|
2
|
+
function defineCollection(config) {
|
|
3
|
+
const collection = {
|
|
4
|
+
timestamps: true,
|
|
5
|
+
// Enable timestamps by default
|
|
6
|
+
...config
|
|
7
|
+
};
|
|
8
|
+
if (!collection.slug) {
|
|
9
|
+
throw new Error("Collection must have a slug");
|
|
10
|
+
}
|
|
11
|
+
if (!collection.fields || collection.fields.length === 0) {
|
|
12
|
+
throw new Error(`Collection "${collection.slug}" must have at least one field`);
|
|
13
|
+
}
|
|
14
|
+
if (!/^[a-z][a-z0-9-]*$/.test(collection.slug)) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Collection slug "${collection.slug}" must be kebab-case (lowercase letters, numbers, and hyphens, starting with a letter)`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return collection;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// libs/core/src/lib/fields/field-builders.ts
|
|
23
|
+
function text(name, options = {}) {
|
|
24
|
+
return {
|
|
25
|
+
name,
|
|
26
|
+
type: "text",
|
|
27
|
+
...options
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function number(name, options = {}) {
|
|
31
|
+
return {
|
|
32
|
+
name,
|
|
33
|
+
type: "number",
|
|
34
|
+
...options
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function checkbox(name, options = {}) {
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
type: "checkbox",
|
|
41
|
+
...options,
|
|
42
|
+
defaultValue: options.defaultValue ?? false
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function group(name, options) {
|
|
46
|
+
return {
|
|
47
|
+
name,
|
|
48
|
+
type: "group",
|
|
49
|
+
...options
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function json(name, options = {}) {
|
|
53
|
+
return {
|
|
54
|
+
name,
|
|
55
|
+
type: "json",
|
|
56
|
+
...options
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// libs/core/src/lib/collections/media.collection.ts
|
|
61
|
+
var MediaCollection = defineCollection({
|
|
62
|
+
slug: "media",
|
|
63
|
+
labels: {
|
|
64
|
+
singular: "Media",
|
|
65
|
+
plural: "Media"
|
|
66
|
+
},
|
|
67
|
+
upload: {
|
|
68
|
+
mimeTypes: ["image/*", "application/pdf", "video/*", "audio/*"]
|
|
69
|
+
},
|
|
70
|
+
admin: {
|
|
71
|
+
useAsTitle: "filename",
|
|
72
|
+
defaultColumns: ["filename", "mimeType", "filesize", "createdAt"]
|
|
73
|
+
},
|
|
74
|
+
fields: [
|
|
75
|
+
text("filename", {
|
|
76
|
+
required: true,
|
|
77
|
+
label: "Filename",
|
|
78
|
+
description: "Original filename of the uploaded file"
|
|
79
|
+
}),
|
|
80
|
+
text("mimeType", {
|
|
81
|
+
required: true,
|
|
82
|
+
label: "MIME Type",
|
|
83
|
+
description: "File MIME type (e.g., image/jpeg, application/pdf)"
|
|
84
|
+
}),
|
|
85
|
+
number("filesize", {
|
|
86
|
+
label: "File Size",
|
|
87
|
+
description: "File size in bytes"
|
|
88
|
+
}),
|
|
89
|
+
text("path", {
|
|
90
|
+
label: "Storage Path",
|
|
91
|
+
description: "Path/key where the file is stored",
|
|
92
|
+
admin: {
|
|
93
|
+
hidden: true
|
|
94
|
+
}
|
|
95
|
+
}),
|
|
96
|
+
text("url", {
|
|
97
|
+
label: "URL",
|
|
98
|
+
description: "Public URL to access the file"
|
|
99
|
+
}),
|
|
100
|
+
text("alt", {
|
|
101
|
+
label: "Alt Text",
|
|
102
|
+
description: "Alternative text for accessibility"
|
|
103
|
+
}),
|
|
104
|
+
number("width", {
|
|
105
|
+
label: "Width",
|
|
106
|
+
description: "Image width in pixels (for images only)"
|
|
107
|
+
}),
|
|
108
|
+
number("height", {
|
|
109
|
+
label: "Height",
|
|
110
|
+
description: "Image height in pixels (for images only)"
|
|
111
|
+
}),
|
|
112
|
+
json("focalPoint", {
|
|
113
|
+
label: "Focal Point",
|
|
114
|
+
description: "Focal point coordinates for image cropping",
|
|
115
|
+
admin: {
|
|
116
|
+
hidden: true
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
],
|
|
120
|
+
access: {
|
|
121
|
+
// Media is readable by anyone by default
|
|
122
|
+
read: () => true,
|
|
123
|
+
// Only authenticated users can create/update/delete
|
|
124
|
+
create: ({ req }) => !!req?.user,
|
|
125
|
+
update: ({ req }) => !!req?.user,
|
|
126
|
+
delete: ({ req }) => !!req?.user
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// libs/plugins/analytics/src/lib/collectors/block-field-injector.ts
|
|
131
|
+
function createAnalyticsGroupField() {
|
|
132
|
+
return group("_analytics", {
|
|
133
|
+
label: "Analytics",
|
|
134
|
+
admin: { collapsible: true, defaultOpen: false },
|
|
135
|
+
fields: [
|
|
136
|
+
checkbox("trackImpressions", { label: "Track Impressions" }),
|
|
137
|
+
checkbox("trackHover", { label: "Track Hover" })
|
|
138
|
+
]
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function hasAnalyticsField(fields) {
|
|
142
|
+
return fields.some((f) => f.name === "_analytics" && f.type === "group");
|
|
143
|
+
}
|
|
144
|
+
function hasBlocks(field) {
|
|
145
|
+
return field.type === "blocks" && "blocks" in field;
|
|
146
|
+
}
|
|
147
|
+
function hasNestedFields(field) {
|
|
148
|
+
return "fields" in field && Array.isArray(field.fields);
|
|
149
|
+
}
|
|
150
|
+
function hasTabs(field) {
|
|
151
|
+
return field.type === "tabs" && "tabs" in field;
|
|
152
|
+
}
|
|
153
|
+
function injectIntoFields(fields) {
|
|
154
|
+
for (const field of fields) {
|
|
155
|
+
if (hasBlocks(field)) {
|
|
156
|
+
for (const blockConfig of field.blocks) {
|
|
157
|
+
if (!hasAnalyticsField(blockConfig.fields)) {
|
|
158
|
+
blockConfig.fields.push(createAnalyticsGroupField());
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if ((field.type === "group" || field.type === "array" || field.type === "collapsible" || field.type === "row") && hasNestedFields(field)) {
|
|
163
|
+
injectIntoFields(field.fields);
|
|
164
|
+
}
|
|
165
|
+
if (hasTabs(field)) {
|
|
166
|
+
for (const tab of field.tabs) {
|
|
167
|
+
injectIntoFields(tab.fields);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function injectBlockAnalyticsFields(collections) {
|
|
173
|
+
for (const collection of collections) {
|
|
174
|
+
injectIntoFields(collection.fields);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
injectBlockAnalyticsFields
|
|
179
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result)
|
|
25
|
+
__defProp(target, key, result);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// libs/plugins/analytics/src/lib/page-view-tracker.ts
|
|
30
|
+
var page_view_tracker_exports = {};
|
|
31
|
+
__export(page_view_tracker_exports, {
|
|
32
|
+
DEFAULT_EXCLUDE_PREFIXES: () => DEFAULT_EXCLUDE_PREFIXES,
|
|
33
|
+
PAGE_VIEW_TRACKING_CONFIG: () => PAGE_VIEW_TRACKING_CONFIG,
|
|
34
|
+
PageViewTrackerService: () => PageViewTrackerService,
|
|
35
|
+
buildPageViewEvent: () => buildPageViewEvent,
|
|
36
|
+
providePageViewTracking: () => providePageViewTracking,
|
|
37
|
+
shouldTrackNavigation: () => shouldTrackNavigation
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(page_view_tracker_exports);
|
|
40
|
+
var import_core = require("@angular/core");
|
|
41
|
+
var import_common = require("@angular/common");
|
|
42
|
+
var import_router = require("@angular/router");
|
|
43
|
+
var import_http = require("@angular/common/http");
|
|
44
|
+
var import_rxjs = require("rxjs");
|
|
45
|
+
|
|
46
|
+
// libs/plugins/analytics/src/lib/utils/content-route-matcher.ts
|
|
47
|
+
function escapeRegex(str) {
|
|
48
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
49
|
+
}
|
|
50
|
+
function compileContentRoute(collection, pattern) {
|
|
51
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
52
|
+
const paramNames = [];
|
|
53
|
+
let staticCount = 0;
|
|
54
|
+
const regexParts = segments.map((seg) => {
|
|
55
|
+
if (seg.startsWith(":")) {
|
|
56
|
+
paramNames.push(seg.slice(1));
|
|
57
|
+
return "([^/]+)";
|
|
58
|
+
}
|
|
59
|
+
staticCount++;
|
|
60
|
+
return escapeRegex(seg);
|
|
61
|
+
});
|
|
62
|
+
const regexStr = "^/" + regexParts.join("/") + "/?$";
|
|
63
|
+
return {
|
|
64
|
+
collection,
|
|
65
|
+
pattern,
|
|
66
|
+
regex: new RegExp(regexStr),
|
|
67
|
+
paramNames,
|
|
68
|
+
staticSegments: staticCount
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function compileContentRoutes(routes) {
|
|
72
|
+
const compiled = Object.entries(routes).map(
|
|
73
|
+
([collection, pattern]) => compileContentRoute(collection, pattern)
|
|
74
|
+
);
|
|
75
|
+
compiled.sort((a, b) => b.staticSegments - a.staticSegments);
|
|
76
|
+
return compiled;
|
|
77
|
+
}
|
|
78
|
+
function matchContentRoute(path, routes) {
|
|
79
|
+
for (const route of routes) {
|
|
80
|
+
const match = route.regex.exec(path);
|
|
81
|
+
if (match) {
|
|
82
|
+
const params = {};
|
|
83
|
+
for (let i = 0; i < route.paramNames.length; i++) {
|
|
84
|
+
params[route.paramNames[i]] = match[i + 1];
|
|
85
|
+
}
|
|
86
|
+
return { collection: route.collection, params };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// libs/plugins/analytics/src/lib/page-view-tracker.utils.ts
|
|
93
|
+
var DEFAULT_EXCLUDE_PREFIXES = ["/admin", "/api/"];
|
|
94
|
+
function shouldTrackNavigation(url, isFirstNavigation, excludePrefixes) {
|
|
95
|
+
if (isFirstNavigation)
|
|
96
|
+
return false;
|
|
97
|
+
const path = url.split("?")[0].split("#")[0];
|
|
98
|
+
for (const prefix of excludePrefixes) {
|
|
99
|
+
if (path.startsWith(prefix))
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
function buildPageViewEvent(url, compiledRoutes) {
|
|
105
|
+
const path = url.split("?")[0].split("#")[0];
|
|
106
|
+
const properties = { path };
|
|
107
|
+
if (compiledRoutes) {
|
|
108
|
+
const routeMatch = matchContentRoute(path, compiledRoutes);
|
|
109
|
+
if (routeMatch) {
|
|
110
|
+
properties["collection"] = routeMatch.collection;
|
|
111
|
+
properties["slug"] = routeMatch.params["slug"];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
name: "page_view",
|
|
116
|
+
category: "page",
|
|
117
|
+
properties
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// libs/plugins/analytics/src/lib/page-view-tracker.ts
|
|
122
|
+
var PAGE_VIEW_TRACKING_CONFIG = new import_core.InjectionToken(
|
|
123
|
+
"PAGE_VIEW_TRACKING_CONFIG"
|
|
124
|
+
);
|
|
125
|
+
function getOrCreateStorageId(storage, key) {
|
|
126
|
+
let id = storage.getItem(key);
|
|
127
|
+
if (!id) {
|
|
128
|
+
id = Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
129
|
+
storage.setItem(key, id);
|
|
130
|
+
}
|
|
131
|
+
return id;
|
|
132
|
+
}
|
|
133
|
+
var PageViewTrackerService = class {
|
|
134
|
+
constructor() {
|
|
135
|
+
this.router = (0, import_core.inject)(import_router.Router);
|
|
136
|
+
this.http = (0, import_core.inject)(import_http.HttpClient);
|
|
137
|
+
this.platformId = (0, import_core.inject)(import_core.PLATFORM_ID);
|
|
138
|
+
this.destroyRef = (0, import_core.inject)(import_core.DestroyRef);
|
|
139
|
+
this.config = (0, import_core.inject)(PAGE_VIEW_TRACKING_CONFIG);
|
|
140
|
+
this.doc = (0, import_core.inject)(import_common.DOCUMENT);
|
|
141
|
+
this.isFirstNavigation = true;
|
|
142
|
+
this.endpoint = this.config.endpoint ?? "/api/analytics/collect";
|
|
143
|
+
this.compiledRoutes = this.config.contentRoutes ? compileContentRoutes(this.config.contentRoutes) : void 0;
|
|
144
|
+
this.excludePrefixes = this.config.excludePrefixes ? [...DEFAULT_EXCLUDE_PREFIXES, ...this.config.excludePrefixes] : DEFAULT_EXCLUDE_PREFIXES;
|
|
145
|
+
if (!(0, import_common.isPlatformBrowser)(this.platformId))
|
|
146
|
+
return;
|
|
147
|
+
const sub = this.router.events.pipe((0, import_rxjs.filter)((e) => e instanceof import_router.NavigationEnd)).subscribe((event) => {
|
|
148
|
+
const url = event.urlAfterRedirects;
|
|
149
|
+
if (!shouldTrackNavigation(url, this.isFirstNavigation, this.excludePrefixes)) {
|
|
150
|
+
this.isFirstNavigation = false;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.isFirstNavigation = false;
|
|
154
|
+
this.trackPageView(url);
|
|
155
|
+
});
|
|
156
|
+
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
157
|
+
}
|
|
158
|
+
trackPageView(url) {
|
|
159
|
+
const eventPayload = buildPageViewEvent(url, this.compiledRoutes);
|
|
160
|
+
const win = this.doc.defaultView;
|
|
161
|
+
let visitorId;
|
|
162
|
+
let sessionId;
|
|
163
|
+
try {
|
|
164
|
+
if (win?.localStorage)
|
|
165
|
+
visitorId = getOrCreateStorageId(win.localStorage, "_m_vid");
|
|
166
|
+
if (win?.sessionStorage)
|
|
167
|
+
sessionId = getOrCreateStorageId(win.sessionStorage, "_m_sid");
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
const clientEvent = {
|
|
171
|
+
...eventPayload,
|
|
172
|
+
context: {
|
|
173
|
+
url: win?.location?.href ?? "",
|
|
174
|
+
referrer: this.doc.referrer
|
|
175
|
+
},
|
|
176
|
+
visitorId,
|
|
177
|
+
sessionId
|
|
178
|
+
};
|
|
179
|
+
this.http.post(this.endpoint, { events: [clientEvent] }).subscribe();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
PageViewTrackerService = __decorateClass([
|
|
183
|
+
(0, import_core.Injectable)()
|
|
184
|
+
], PageViewTrackerService);
|
|
185
|
+
function providePageViewTracking(config) {
|
|
186
|
+
return (0, import_core.makeEnvironmentProviders)([
|
|
187
|
+
{ provide: PAGE_VIEW_TRACKING_CONFIG, useValue: config },
|
|
188
|
+
PageViewTrackerService,
|
|
189
|
+
{
|
|
190
|
+
provide: import_core.ENVIRONMENT_INITIALIZER,
|
|
191
|
+
multi: true,
|
|
192
|
+
useFactory: () => {
|
|
193
|
+
(0, import_core.inject)(PageViewTrackerService);
|
|
194
|
+
return () => {
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
]);
|
|
199
|
+
}
|
|
200
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
201
|
+
0 && (module.exports = {
|
|
202
|
+
DEFAULT_EXCLUDE_PREFIXES,
|
|
203
|
+
PAGE_VIEW_TRACKING_CONFIG,
|
|
204
|
+
PageViewTrackerService,
|
|
205
|
+
buildPageViewEvent,
|
|
206
|
+
providePageViewTracking,
|
|
207
|
+
shouldTrackNavigation
|
|
208
|
+
});
|