@momentumcms/plugins-analytics 0.5.0 → 0.5.2
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
CHANGED
|
@@ -1153,6 +1153,31 @@ function json(name, options = {}) {
|
|
|
1153
1153
|
}
|
|
1154
1154
|
|
|
1155
1155
|
// libs/core/src/lib/collections/media.collection.ts
|
|
1156
|
+
var validateFocalPoint = (value) => {
|
|
1157
|
+
if (value === null || value === void 0)
|
|
1158
|
+
return true;
|
|
1159
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
1160
|
+
return "Focal point must be an object with x and y coordinates";
|
|
1161
|
+
}
|
|
1162
|
+
const fp = Object.fromEntries(Object.entries(value));
|
|
1163
|
+
if (!("x" in fp) || !("y" in fp)) {
|
|
1164
|
+
return "Focal point must have both x and y properties";
|
|
1165
|
+
}
|
|
1166
|
+
const { x, y } = fp;
|
|
1167
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
1168
|
+
return "Focal point x must be a finite number";
|
|
1169
|
+
}
|
|
1170
|
+
if (typeof y !== "number" || !Number.isFinite(y)) {
|
|
1171
|
+
return "Focal point y must be a finite number";
|
|
1172
|
+
}
|
|
1173
|
+
if (x < 0 || x > 1) {
|
|
1174
|
+
return `Focal point x must be between 0 and 1 (received ${x})`;
|
|
1175
|
+
}
|
|
1176
|
+
if (y < 0 || y > 1) {
|
|
1177
|
+
return `Focal point y must be between 0 and 1 (received ${y})`;
|
|
1178
|
+
}
|
|
1179
|
+
return true;
|
|
1180
|
+
};
|
|
1156
1181
|
var MediaCollection = defineCollection({
|
|
1157
1182
|
slug: "media",
|
|
1158
1183
|
labels: {
|
|
@@ -1207,6 +1232,14 @@ var MediaCollection = defineCollection({
|
|
|
1207
1232
|
json("focalPoint", {
|
|
1208
1233
|
label: "Focal Point",
|
|
1209
1234
|
description: "Focal point coordinates for image cropping",
|
|
1235
|
+
validate: validateFocalPoint,
|
|
1236
|
+
admin: {
|
|
1237
|
+
hidden: true
|
|
1238
|
+
}
|
|
1239
|
+
}),
|
|
1240
|
+
json("sizes", {
|
|
1241
|
+
label: "Image Sizes",
|
|
1242
|
+
description: "Generated image size variants",
|
|
1210
1243
|
admin: {
|
|
1211
1244
|
hidden: true
|
|
1212
1245
|
}
|
|
@@ -1338,8 +1371,8 @@ function isValidClientEvent(event) {
|
|
|
1338
1371
|
return true;
|
|
1339
1372
|
}
|
|
1340
1373
|
function createIngestRouter(options) {
|
|
1341
|
-
const { eventStore, rateLimit = 100 } = options;
|
|
1342
|
-
const limiter = new RateLimiter(rateLimit);
|
|
1374
|
+
const { eventStore, rateLimit = 100, rateLimitWindow } = options;
|
|
1375
|
+
const limiter = new RateLimiter(rateLimit, rateLimitWindow);
|
|
1343
1376
|
const logger = createLogger("Analytics:Ingest");
|
|
1344
1377
|
const router = (0, import_express.Router)();
|
|
1345
1378
|
router.post("/", (req, res) => {
|
|
@@ -2353,7 +2386,8 @@ function analyticsPlugin(config) {
|
|
|
2353
2386
|
}
|
|
2354
2387
|
const ingestRouter = createIngestRouter({
|
|
2355
2388
|
eventStore,
|
|
2356
|
-
rateLimit: config.ingestRateLimit
|
|
2389
|
+
rateLimit: config.ingestRateLimit,
|
|
2390
|
+
rateLimitWindow: config.ingestRateLimitWindow
|
|
2357
2391
|
});
|
|
2358
2392
|
registerMiddleware({
|
|
2359
2393
|
path: config.ingestPath ?? "/analytics/collect",
|
|
@@ -2456,7 +2490,8 @@ function createAnalyticsMiddleware(plugin) {
|
|
|
2456
2490
|
const { eventStore, analyticsConfig } = plugin;
|
|
2457
2491
|
const ingestRouter = createIngestRouter({
|
|
2458
2492
|
eventStore,
|
|
2459
|
-
rateLimit: analyticsConfig.ingestRateLimit
|
|
2493
|
+
rateLimit: analyticsConfig.ingestRateLimit,
|
|
2494
|
+
rateLimitWindow: analyticsConfig.ingestRateLimitWindow
|
|
2460
2495
|
});
|
|
2461
2496
|
const apiCollector = createApiCollectorMiddleware((event) => eventStore.add(event));
|
|
2462
2497
|
return { ingestRouter, apiCollector };
|
package/index.js
CHANGED
|
@@ -1104,6 +1104,31 @@ function json(name, options = {}) {
|
|
|
1104
1104
|
}
|
|
1105
1105
|
|
|
1106
1106
|
// libs/core/src/lib/collections/media.collection.ts
|
|
1107
|
+
var validateFocalPoint = (value) => {
|
|
1108
|
+
if (value === null || value === void 0)
|
|
1109
|
+
return true;
|
|
1110
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
1111
|
+
return "Focal point must be an object with x and y coordinates";
|
|
1112
|
+
}
|
|
1113
|
+
const fp = Object.fromEntries(Object.entries(value));
|
|
1114
|
+
if (!("x" in fp) || !("y" in fp)) {
|
|
1115
|
+
return "Focal point must have both x and y properties";
|
|
1116
|
+
}
|
|
1117
|
+
const { x, y } = fp;
|
|
1118
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
1119
|
+
return "Focal point x must be a finite number";
|
|
1120
|
+
}
|
|
1121
|
+
if (typeof y !== "number" || !Number.isFinite(y)) {
|
|
1122
|
+
return "Focal point y must be a finite number";
|
|
1123
|
+
}
|
|
1124
|
+
if (x < 0 || x > 1) {
|
|
1125
|
+
return `Focal point x must be between 0 and 1 (received ${x})`;
|
|
1126
|
+
}
|
|
1127
|
+
if (y < 0 || y > 1) {
|
|
1128
|
+
return `Focal point y must be between 0 and 1 (received ${y})`;
|
|
1129
|
+
}
|
|
1130
|
+
return true;
|
|
1131
|
+
};
|
|
1107
1132
|
var MediaCollection = defineCollection({
|
|
1108
1133
|
slug: "media",
|
|
1109
1134
|
labels: {
|
|
@@ -1158,6 +1183,14 @@ var MediaCollection = defineCollection({
|
|
|
1158
1183
|
json("focalPoint", {
|
|
1159
1184
|
label: "Focal Point",
|
|
1160
1185
|
description: "Focal point coordinates for image cropping",
|
|
1186
|
+
validate: validateFocalPoint,
|
|
1187
|
+
admin: {
|
|
1188
|
+
hidden: true
|
|
1189
|
+
}
|
|
1190
|
+
}),
|
|
1191
|
+
json("sizes", {
|
|
1192
|
+
label: "Image Sizes",
|
|
1193
|
+
description: "Generated image size variants",
|
|
1161
1194
|
admin: {
|
|
1162
1195
|
hidden: true
|
|
1163
1196
|
}
|
|
@@ -1302,8 +1335,8 @@ function isValidClientEvent(event) {
|
|
|
1302
1335
|
return true;
|
|
1303
1336
|
}
|
|
1304
1337
|
function createIngestRouter(options) {
|
|
1305
|
-
const { eventStore, rateLimit = 100 } = options;
|
|
1306
|
-
const limiter = new RateLimiter(rateLimit);
|
|
1338
|
+
const { eventStore, rateLimit = 100, rateLimitWindow } = options;
|
|
1339
|
+
const limiter = new RateLimiter(rateLimit, rateLimitWindow);
|
|
1307
1340
|
const logger = createLogger("Analytics:Ingest");
|
|
1308
1341
|
const router = createRouter();
|
|
1309
1342
|
router.post("/", (req, res) => {
|
|
@@ -2317,7 +2350,8 @@ function analyticsPlugin(config) {
|
|
|
2317
2350
|
}
|
|
2318
2351
|
const ingestRouter = createIngestRouter({
|
|
2319
2352
|
eventStore,
|
|
2320
|
-
rateLimit: config.ingestRateLimit
|
|
2353
|
+
rateLimit: config.ingestRateLimit,
|
|
2354
|
+
rateLimitWindow: config.ingestRateLimitWindow
|
|
2321
2355
|
});
|
|
2322
2356
|
registerMiddleware({
|
|
2323
2357
|
path: config.ingestPath ?? "/analytics/collect",
|
|
@@ -2420,7 +2454,8 @@ function createAnalyticsMiddleware(plugin) {
|
|
|
2420
2454
|
const { eventStore, analyticsConfig } = plugin;
|
|
2421
2455
|
const ingestRouter = createIngestRouter({
|
|
2422
2456
|
eventStore,
|
|
2423
|
-
rateLimit: analyticsConfig.ingestRateLimit
|
|
2457
|
+
rateLimit: analyticsConfig.ingestRateLimit,
|
|
2458
|
+
rateLimitWindow: analyticsConfig.ingestRateLimitWindow
|
|
2424
2459
|
});
|
|
2425
2460
|
const apiCollector = createApiCollectorMiddleware((event) => eventStore.add(event));
|
|
2426
2461
|
return { ingestRouter, apiCollector };
|
|
@@ -84,6 +84,31 @@ function json(name, options = {}) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// libs/core/src/lib/collections/media.collection.ts
|
|
87
|
+
var validateFocalPoint = (value) => {
|
|
88
|
+
if (value === null || value === void 0)
|
|
89
|
+
return true;
|
|
90
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
91
|
+
return "Focal point must be an object with x and y coordinates";
|
|
92
|
+
}
|
|
93
|
+
const fp = Object.fromEntries(Object.entries(value));
|
|
94
|
+
if (!("x" in fp) || !("y" in fp)) {
|
|
95
|
+
return "Focal point must have both x and y properties";
|
|
96
|
+
}
|
|
97
|
+
const { x, y } = fp;
|
|
98
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
99
|
+
return "Focal point x must be a finite number";
|
|
100
|
+
}
|
|
101
|
+
if (typeof y !== "number" || !Number.isFinite(y)) {
|
|
102
|
+
return "Focal point y must be a finite number";
|
|
103
|
+
}
|
|
104
|
+
if (x < 0 || x > 1) {
|
|
105
|
+
return `Focal point x must be between 0 and 1 (received ${x})`;
|
|
106
|
+
}
|
|
107
|
+
if (y < 0 || y > 1) {
|
|
108
|
+
return `Focal point y must be between 0 and 1 (received ${y})`;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
};
|
|
87
112
|
var MediaCollection = defineCollection({
|
|
88
113
|
slug: "media",
|
|
89
114
|
labels: {
|
|
@@ -138,6 +163,14 @@ var MediaCollection = defineCollection({
|
|
|
138
163
|
json("focalPoint", {
|
|
139
164
|
label: "Focal Point",
|
|
140
165
|
description: "Focal point coordinates for image cropping",
|
|
166
|
+
validate: validateFocalPoint,
|
|
167
|
+
admin: {
|
|
168
|
+
hidden: true
|
|
169
|
+
}
|
|
170
|
+
}),
|
|
171
|
+
json("sizes", {
|
|
172
|
+
label: "Image Sizes",
|
|
173
|
+
description: "Generated image size variants",
|
|
141
174
|
admin: {
|
|
142
175
|
hidden: true
|
|
143
176
|
}
|
|
@@ -58,6 +58,31 @@ function json(name, options = {}) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// libs/core/src/lib/collections/media.collection.ts
|
|
61
|
+
var validateFocalPoint = (value) => {
|
|
62
|
+
if (value === null || value === void 0)
|
|
63
|
+
return true;
|
|
64
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
65
|
+
return "Focal point must be an object with x and y coordinates";
|
|
66
|
+
}
|
|
67
|
+
const fp = Object.fromEntries(Object.entries(value));
|
|
68
|
+
if (!("x" in fp) || !("y" in fp)) {
|
|
69
|
+
return "Focal point must have both x and y properties";
|
|
70
|
+
}
|
|
71
|
+
const { x, y } = fp;
|
|
72
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
73
|
+
return "Focal point x must be a finite number";
|
|
74
|
+
}
|
|
75
|
+
if (typeof y !== "number" || !Number.isFinite(y)) {
|
|
76
|
+
return "Focal point y must be a finite number";
|
|
77
|
+
}
|
|
78
|
+
if (x < 0 || x > 1) {
|
|
79
|
+
return `Focal point x must be between 0 and 1 (received ${x})`;
|
|
80
|
+
}
|
|
81
|
+
if (y < 0 || y > 1) {
|
|
82
|
+
return `Focal point y must be between 0 and 1 (received ${y})`;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
61
86
|
var MediaCollection = defineCollection({
|
|
62
87
|
slug: "media",
|
|
63
88
|
labels: {
|
|
@@ -112,6 +137,14 @@ var MediaCollection = defineCollection({
|
|
|
112
137
|
json("focalPoint", {
|
|
113
138
|
label: "Focal Point",
|
|
114
139
|
description: "Focal point coordinates for image cropping",
|
|
140
|
+
validate: validateFocalPoint,
|
|
141
|
+
admin: {
|
|
142
|
+
hidden: true
|
|
143
|
+
}
|
|
144
|
+
}),
|
|
145
|
+
json("sizes", {
|
|
146
|
+
label: "Image Sizes",
|
|
147
|
+
description: "Generated image size variants",
|
|
115
148
|
admin: {
|
|
116
149
|
hidden: true
|
|
117
150
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momentumcms/plugins-analytics",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Analytics and content tracking plugin for Momentum CMS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Momentum CMS Contributors",
|
|
@@ -48,11 +48,15 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@momentumcms/
|
|
55
|
-
"
|
|
51
|
+
"@angular/common": "^21.0.0",
|
|
52
|
+
"@angular/core": "^21.0.0",
|
|
53
|
+
"@angular/router": "^21.0.0",
|
|
54
|
+
"@momentumcms/core": "0.5.2",
|
|
55
|
+
"@momentumcms/logger": "0.5.2",
|
|
56
|
+
"@momentumcms/plugins-core": "0.5.2",
|
|
57
|
+
"@momentumcms/server-core": "0.5.2",
|
|
58
|
+
"express": "^4.21.0",
|
|
59
|
+
"rxjs": "^7.0.0"
|
|
56
60
|
},
|
|
57
61
|
"peerDependencies": {
|
|
58
62
|
"pg": "^8.0.0"
|
|
@@ -63,6 +63,8 @@ export interface AnalyticsConfig {
|
|
|
63
63
|
ingestPath?: string;
|
|
64
64
|
/** Rate limit for ingest endpoint (requests per minute per IP). @default 100 */
|
|
65
65
|
ingestRateLimit?: number;
|
|
66
|
+
/** Rate limit window in milliseconds. @default 60000 */
|
|
67
|
+
ingestRateLimitWindow?: number;
|
|
66
68
|
/** Batch flush interval in ms. @default 5000 */
|
|
67
69
|
flushInterval?: number;
|
|
68
70
|
/** Batch size before forced flush. @default 100 */
|
|
@@ -12,8 +12,10 @@ import type { EventStore } from './event-store';
|
|
|
12
12
|
export interface IngestHandlerOptions {
|
|
13
13
|
/** Event store to buffer events */
|
|
14
14
|
eventStore: EventStore;
|
|
15
|
-
/** Rate limit per IP per
|
|
15
|
+
/** Rate limit per IP per window. @default 100 */
|
|
16
16
|
rateLimit?: number;
|
|
17
|
+
/** Rate limit window in milliseconds. @default 60000 */
|
|
18
|
+
rateLimitWindow?: number;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Creates an Express router for the analytics ingest endpoint.
|