openpetswithchatandmcp/apps/desktop/src/codex-pets-core.ts
2026-05-13 17:13:41 +02:00

35 lines
1.7 KiB
TypeScript

export const maxCodexPetJsonBytes = 128 * 1024;
export const maxCodexSpritesheetBytes = 100 * 1024 * 1024;
export const maxCodexThumbnailSourceBytes = 24 * 1024 * 1024;
export const maxCodexPets = 100;
export interface CodexPetMetadata {
readonly id: string;
readonly displayName: string;
readonly description: string;
readonly spritesheetPath: "spritesheet.webp";
}
export function validateCodexPetMetadata(value: unknown, folderName: string): CodexPetMetadata {
if (!isSafeCodexPetId(folderName)) throw new Error("Codex pet folder name is invalid.");
if (!isRecord(value)) throw new Error("pet.json must be an object.");
if (value.id !== folderName || typeof value.id !== "string") throw new Error("Codex pet id must match its folder name.");
if (!isSafeCodexPetId(value.id)) throw new Error("Codex pet id is invalid.");
if (typeof value.displayName !== "string" || value.displayName.trim().length === 0 || value.displayName.length > 80) throw new Error("Codex pet displayName is invalid.");
if (typeof value.description !== "string" || value.description.trim().length === 0 || value.description.length > 500) throw new Error("Codex pet description is invalid.");
if (value.spritesheetPath !== "spritesheet.webp") throw new Error("Codex pet spritesheetPath must be spritesheet.webp.");
return {
id: value.id,
displayName: value.displayName.trim(),
description: value.description.trim(),
spritesheetPath: "spritesheet.webp",
};
}
function isSafeCodexPetId(value: string): boolean {
return /^[a-z0-9][a-z0-9_-]{0,63}$/.test(value) && value !== "builtin";
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}