@momentumcms/core 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momentumcms/core",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Core collection config, fields, hooks, and access control for Momentum CMS",
5
5
  "license": "MIT",
6
6
  "author": "Momentum CMS Contributors",
package/src/index.cjs CHANGED
@@ -445,6 +445,31 @@ function validateRowCount(name, label, count, minRows, maxRows, errors) {
445
445
  }
446
446
 
447
447
  // libs/core/src/lib/collections/media.collection.ts
448
+ var validateFocalPoint = (value) => {
449
+ if (value === null || value === void 0)
450
+ return true;
451
+ if (typeof value !== "object" || Array.isArray(value)) {
452
+ return "Focal point must be an object with x and y coordinates";
453
+ }
454
+ const fp = Object.fromEntries(Object.entries(value));
455
+ if (!("x" in fp) || !("y" in fp)) {
456
+ return "Focal point must have both x and y properties";
457
+ }
458
+ const { x, y } = fp;
459
+ if (typeof x !== "number" || !Number.isFinite(x)) {
460
+ return "Focal point x must be a finite number";
461
+ }
462
+ if (typeof y !== "number" || !Number.isFinite(y)) {
463
+ return "Focal point y must be a finite number";
464
+ }
465
+ if (x < 0 || x > 1) {
466
+ return `Focal point x must be between 0 and 1 (received ${x})`;
467
+ }
468
+ if (y < 0 || y > 1) {
469
+ return `Focal point y must be between 0 and 1 (received ${y})`;
470
+ }
471
+ return true;
472
+ };
448
473
  var MediaCollection = defineCollection({
449
474
  slug: "media",
450
475
  labels: {
@@ -499,6 +524,14 @@ var MediaCollection = defineCollection({
499
524
  json("focalPoint", {
500
525
  label: "Focal Point",
501
526
  description: "Focal point coordinates for image cropping",
527
+ validate: validateFocalPoint,
528
+ admin: {
529
+ hidden: true
530
+ }
531
+ }),
532
+ json("sizes", {
533
+ label: "Image Sizes",
534
+ description: "Generated image size variants",
502
535
  admin: {
503
536
  hidden: true
504
537
  }
package/src/index.js CHANGED
@@ -364,6 +364,31 @@ function validateRowCount(name, label, count, minRows, maxRows, errors) {
364
364
  }
365
365
 
366
366
  // libs/core/src/lib/collections/media.collection.ts
367
+ var validateFocalPoint = (value) => {
368
+ if (value === null || value === void 0)
369
+ return true;
370
+ if (typeof value !== "object" || Array.isArray(value)) {
371
+ return "Focal point must be an object with x and y coordinates";
372
+ }
373
+ const fp = Object.fromEntries(Object.entries(value));
374
+ if (!("x" in fp) || !("y" in fp)) {
375
+ return "Focal point must have both x and y properties";
376
+ }
377
+ const { x, y } = fp;
378
+ if (typeof x !== "number" || !Number.isFinite(x)) {
379
+ return "Focal point x must be a finite number";
380
+ }
381
+ if (typeof y !== "number" || !Number.isFinite(y)) {
382
+ return "Focal point y must be a finite number";
383
+ }
384
+ if (x < 0 || x > 1) {
385
+ return `Focal point x must be between 0 and 1 (received ${x})`;
386
+ }
387
+ if (y < 0 || y > 1) {
388
+ return `Focal point y must be between 0 and 1 (received ${y})`;
389
+ }
390
+ return true;
391
+ };
367
392
  var MediaCollection = defineCollection({
368
393
  slug: "media",
369
394
  labels: {
@@ -418,6 +443,14 @@ var MediaCollection = defineCollection({
418
443
  json("focalPoint", {
419
444
  label: "Focal Point",
420
445
  description: "Focal point coordinates for image cropping",
446
+ validate: validateFocalPoint,
447
+ admin: {
448
+ hidden: true
449
+ }
450
+ }),
451
+ json("sizes", {
452
+ label: "Image Sizes",
453
+ description: "Generated image size variants",
421
454
  admin: {
422
455
  hidden: true
423
456
  }
@@ -3,6 +3,7 @@
3
3
  * Defines the structure of collections (similar to Payload CMS)
4
4
  */
5
5
  import type { Field } from '../fields/field.types';
6
+ import type { ImageSizeConfig } from '../storage';
6
7
  export interface AccessArgs {
7
8
  req: RequestContext;
8
9
  id?: string | number;
@@ -137,6 +138,10 @@ export interface UploadCollectionConfig {
137
138
  pathField?: string;
138
139
  /** Field name for the public URL. @default 'url' */
139
140
  urlField?: string;
141
+ /** Image sizes to generate on upload (images only) */
142
+ imageSizes?: ImageSizeConfig[];
143
+ /** Default output format for generated sizes. @default 'original' */
144
+ formatPreference?: 'jpeg' | 'webp' | 'avif' | 'original';
140
145
  }
141
146
  export interface TimestampsConfig {
142
147
  /** Add createdAt field */
@@ -2,6 +2,13 @@
2
2
  * Built-in Media Collection for Momentum CMS
3
3
  * Stores metadata for uploaded files
4
4
  */
5
+ import type { ValidateFunction } from '../fields/field.types';
6
+ /**
7
+ * Validates a focalPoint value: null/undefined is allowed (optional field).
8
+ * When present, must be a plain object with exactly `x` and `y` properties,
9
+ * both finite numbers in the [0, 1] range.
10
+ */
11
+ export declare const validateFocalPoint: ValidateFunction;
5
12
  /**
6
13
  * Built-in Media collection for storing file upload metadata.
7
14
  * Users can override this by defining their own 'media' collection.
@@ -24,6 +31,14 @@ export interface MediaDocument {
24
31
  x: number;
25
32
  y: number;
26
33
  };
34
+ sizes?: Record<string, {
35
+ url: string;
36
+ path: string;
37
+ width: number;
38
+ height: number;
39
+ mimeType: string;
40
+ filesize: number;
41
+ }>;
27
42
  createdAt: string;
28
43
  updatedAt: string;
29
44
  }
@@ -1,7 +1,57 @@
1
1
  /**
2
2
  * Storage module for Momentum CMS
3
- * Defines interfaces for file storage adapters
3
+ * Defines interfaces for file storage adapters and image processing
4
4
  */
5
+ /**
6
+ * Describes one output size for image processing.
7
+ */
8
+ export interface ImageSizeConfig {
9
+ /** Named key for this size (e.g. 'thumbnail', 'medium') */
10
+ name: string;
11
+ /** Target width in pixels. Undefined = proportional from height. */
12
+ width?: number;
13
+ /** Target height in pixels. Undefined = proportional from width. */
14
+ height?: number;
15
+ /**
16
+ * Resizing strategy.
17
+ * - 'contain': shrink to fit, no cropping
18
+ * - 'cover': resize + crop to fill exact dimensions (uses focalPoint)
19
+ * - 'fill': stretch to exact dimensions
20
+ * - 'width': resize to width, height proportional
21
+ * - 'height': resize to height, width proportional
22
+ * @default 'cover'
23
+ */
24
+ fit?: 'contain' | 'cover' | 'fill' | 'width' | 'height';
25
+ /** Output format. When undefined, uses source format or global formatPreference. */
26
+ format?: 'jpeg' | 'webp' | 'avif' | 'png';
27
+ /** JPEG/WebP/AVIF quality (1-100). @default 80 */
28
+ quality?: number;
29
+ }
30
+ /**
31
+ * Image dimensions in pixels.
32
+ */
33
+ export interface ImageDimensions {
34
+ width: number;
35
+ height: number;
36
+ }
37
+ /**
38
+ * Pluggable image processor interface.
39
+ * Implement this to provide custom image processing backends.
40
+ */
41
+ export interface ImageProcessor {
42
+ /** Detect image dimensions without full decode when possible. */
43
+ getDimensions(buffer: Uint8Array, mimeType: string): Promise<ImageDimensions>;
44
+ /** Process one size variant. Returns the processed buffer and its dimensions. */
45
+ processVariant(buffer: Uint8Array, mimeType: string, size: ImageSizeConfig, focalPoint?: {
46
+ x: number;
47
+ y: number;
48
+ }): Promise<{
49
+ buffer: Uint8Array;
50
+ width: number;
51
+ height: number;
52
+ mimeType: string;
53
+ }>;
54
+ }
5
55
  /**
6
56
  * Represents an uploaded file before storage.
7
57
  */