122 lines
3.9 KiB
TypeScript
122 lines
3.9 KiB
TypeScript
import z from "zod";
|
||
|
||
/**
|
||
* Supported image formats as string literals.
|
||
*/
|
||
const IMAGE_FORMATS = ["png", "jpeg", "gif", "webp", "bmp"] as const;
|
||
|
||
/**
|
||
* Map of supported image format keys to their MIME types.
|
||
*/
|
||
const MIME_MAP = {
|
||
png: "image/png",
|
||
jpeg: "image/jpeg",
|
||
gif: "image/gif",
|
||
webp: "image/webp",
|
||
bmp: "image/bmp",
|
||
} as const;
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------------
|
||
| Raw Image
|
||
|--------------------------------------------------------------------------------
|
||
|
|
||
| Defines the raw structure of an image as stored in the database.
|
||
|
|
||
| This schema captures all the immutable attributes of an image (id, folder
|
||
| reference, URL, file metadata, etc.) but does not include any derived values
|
||
| such as the MIME type.
|
||
|
|
||
*/
|
||
const RawImageSchema = z.strictObject({
|
||
_id: z.uuid().describe("Primary identifier of the image"),
|
||
|
||
folderId: z.uuid().optional().describe("Primary identifier of the folder the image belongs to"),
|
||
|
||
name: z.string().min(1).describe("Original file name (e.g., cat.png)"),
|
||
format: z.enum(IMAGE_FORMATS).describe("Image format"),
|
||
size: z.number().positive().max(500).describe("File size in bytes"),
|
||
hash: z.string().min(1).describe("Image hash to ensure we don't store the same image multiple times"),
|
||
|
||
width: z.number().positive().describe("Image width in pixels"),
|
||
height: z.number().positive().describe("Image height in pixels"),
|
||
|
||
url: z.url().describe("Public URL to access the image"),
|
||
|
||
description: z.string().max(500).optional().describe("Optional description or caption (max 500 chars)"),
|
||
tags: z.array(z.string().min(1)).default([]).describe("Array of tags for search / categorization"),
|
||
|
||
createdAt: z.coerce.date().describe("Timestamp when the image was stored"),
|
||
});
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------------
|
||
| Image
|
||
|--------------------------------------------------------------------------------
|
||
|
|
||
| A strict representation of an image that includes a derived `mime` field. The
|
||
| `ImageSchema` takes `RawImageSchema` and attaches the MIME type based on the
|
||
| format. No extra keys are permitted.
|
||
|
|
||
*/
|
||
export const ImageSchema = RawImageSchema.transform((data) => ({
|
||
...data,
|
||
mime: mimeForFormat(data.format),
|
||
}));
|
||
|
||
export type Image = z.output<typeof ImageSchema>;
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------------
|
||
| Database
|
||
|--------------------------------------------------------------------------------
|
||
|
|
||
| Schemas used for interacting with the persistence layer.
|
||
|
|
||
| `ImageInsertSchema` is used when inserting a new image – it omits
|
||
| automatically generated fields such as `_id` and `createdAt`. It also
|
||
| normalises the `tags` array to contain unique values.
|
||
|
|
||
*/
|
||
|
||
/**
|
||
* Schema used when inserting a new image.
|
||
* It omits _id, and createdAt – those are generated by the DB.
|
||
*/
|
||
export const ImageInsertSchema = z
|
||
.strictObject({
|
||
...RawImageSchema.omit({ _id: true, createdAt: true }).shape,
|
||
})
|
||
.transform((data) => ({
|
||
...data,
|
||
tags: Array.from(new Set(data.tags)),
|
||
}));
|
||
|
||
export type ImageInsert = z.input<typeof ImageInsertSchema>;
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------------
|
||
| Utilities
|
||
|--------------------------------------------------------------------------------
|
||
*/
|
||
|
||
/**
|
||
* Returns the MIME type corresponding to the given image format.
|
||
* If the format is unknown, defaults to "application/octet-stream".
|
||
*
|
||
* @param format - The image format key (e.g., "png").
|
||
*
|
||
* @returns The MIME type string.
|
||
*/
|
||
function mimeForFormat<TKey extends keyof MIMEMap>(format: TKey): MIMEMap[TKey] | "application/octet-stream" {
|
||
return MIME_MAP[format] ?? "application/octet-stream";
|
||
}
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------------
|
||
| Types
|
||
|--------------------------------------------------------------------------------
|
||
*/
|
||
|
||
type MIMEMap = typeof MIME_MAP;
|