@momentumcms/plugins-analytics 0.5.1 → 0.5.3

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.1",
3
+ "version": "0.5.3",
4
4
  "description": "Analytics and content tracking plugin for Momentum CMS",
5
5
  "license": "MIT",
6
6
  "author": "Momentum CMS Contributors",
@@ -51,10 +51,10 @@
51
51
  "@angular/common": "^21.0.0",
52
52
  "@angular/core": "^21.0.0",
53
53
  "@angular/router": "^21.0.0",
54
- "@momentumcms/core": "0.5.1",
55
- "@momentumcms/logger": "0.5.1",
56
- "@momentumcms/plugins-core": "0.5.1",
57
- "@momentumcms/server-core": "0.5.1",
54
+ "@momentumcms/core": "0.5.3",
55
+ "@momentumcms/logger": "0.5.3",
56
+ "@momentumcms/plugins-core": "0.5.3",
57
+ "@momentumcms/server-core": "0.5.3",
58
58
  "express": "^4.21.0",
59
59
  "rxjs": "^7.0.0"
60
60
  },
@@ -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 minute. @default 100 */
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.