Skip to content

Esquema de metadatos OIM ​

Referencia completa para la estructura de metadatos validada con Zod en los archivos .oim.

Visión general del esquema ​

typescript
import { z } from "zod";

export const OimMetadataSchema = z.object({
  oim: z.string().optional(),              // Format identifier
  version: z.string().optional(),          // Schema version
  image: z.string().optional(),            // Base64 image (legacy)
  oim_id: z.string().max(128).optional(),  // Unique tracking ID
  media_type: z.enum(["image", "video"]).optional(),
  creator_user_id: z.string().max(128).optional(),
  image_hash: z.string().max(64).optional(), // SHA-256 of original image
  immutable_lock: z.boolean().optional(),  // Seal file from edits
  allow_external_urls: z.boolean().optional(),
  redirect_membership_verified: z.boolean().optional(),
  terms_accepted: z.boolean().optional(),
  canvas: z.object({
    natural_width: z.number().positive(),
    natural_height: z.number().positive()
  }).optional(),
  lang: z.array(z.string().max(5)).optional(),
  zones: z.array(zoneSchema).optional(),
  slots: z.array(slotSchema),              // Required
  elements: z.array(slotSchema).optional(),
  layers: z.array(slotSchema).optional(),
  signature: z.string().optional(),        // ECDSA signature
  key_version: z.string().optional(),
  timestamp: z.string().datetime().optional(),
}).passthrough();

Referencia de campos ​

Campos de nivel superior ​

CampoTipoRequeridoDescripción
oimstringIdentificador de formato (p. ej., "1.0")
versionstringVersión del esquema
imagestringImagen codificada en Base64 (obsoleto, evitar)
oim_idstring (≤128)ID único para reedición del documento
media_type"image" | "video"Tipo de contenido multimedia
creator_user_idstring (≤128)Identificador de seguimiento del creador
image_hashstring (≤64)Hash SHA-256 de la imagen horneada (Capa −1)
immutable_lockbooleanSi es true, el archivo no puede editarse
allow_external_urlsbooleanPermitir URLs de redirección externas
redirect_membership_verifiedbooleanBandera de verificación de membresía de pago
terms_acceptedbooleanBandera de aceptación de términos y condiciones
canvasobjectDimensiones del canvas { natural_width, natural_height }
langstring[] (≤5 caracteres cada uno)Códigos de idioma disponibles
zonesZone[]Zonas de cuadro delimitador con nombre
slotsSlot[]Definiciones de superposición de texto/imagen
elementsSlot[]Array alternativo de slots
layersSlot[]Array de slots basado en capas
signaturestringFirma criptográfica ECDSA
key_versionstringVersión de la clave utilizada para la firma
timestampISO datetimeMarca de tiempo de creación del paquete

Esquema de Slot ​

typescript
const slotSchema = z.object({
  id: z.string().min(1).max(128),
  type: z.enum(["text", "image", "lottie", "zone", "geo", "shape", "draw", "filter", "container", "group", "component", "mask", "svg", "pattern", "artistic-text"]).optional(),
  content: z.record(z.string(), z.string().max(10000000)).optional(),
  style: styleSchema.optional(),
  position: z.tuple([z.number().min(0).max(100), z.number().min(0).max(100)]).optional(),
  tracking_data: z.array(z.array(z.number()).min(2)).optional(),
  zone_id: z.string().max(128).optional(),
  link: z.string().max(2048).optional(),
  target: z.enum(["_blank", "_self"]).optional(),
  interaction: interactionSchema.optional(),
  
  // Abbreviated keys for size optimization
  p: positionSchema.optional(),                     // alias for position
  s: z.tuple([z.number(), z.number()]).optional(),  // alias for size/dimensions
  st: styleSchema.optional(),                       // alias for style
  c: contentSchema.optional(),                      // alias for content
  t: z.array(trackingKeyframeSchema).optional(),    // alias for tracking_data
});
CampoTipoRequeridoDescripción
idstring (1-128)Identificador único del slot
type"text" | "image" | "lottie" | "zone"Tipo de contenido del slot
contentRecord<lang, text>Diccionario de idioma a contenido (hasta 10MB para imágenes en base64)
styleStyleEstilo visual (ver a continuación)
position[x, y] (0-100)Coordenadas de posición normalizadas
tracking_datanumber[][]Datos de animación de keyframes
zone_idstring (≤128)Zona de contención de destino
linkstring (≤2048)URL de redirección para hotspot/e-commerce
target"_blank" | "_self"Comportamiento de apertura del enlace
p[x, y]Retirada en 1.12.0: el esquema aún la acepta, pero ningún escritor la emite; usa position
s[w, h]Retirada en 1.12.0
stStyleRetirada en 1.12.0: el esquema aún la acepta, pero ningún escritor la emite; usa style
cRecord<lang, text>Retirada en 1.12.0: el esquema aún la acepta, pero ningún escritor la emite; usa content
tnumber[][]Retirada en 1.12.0: el esquema aún la acepta, pero ningún escritor la emite; usa tracking_data

Esquema de Style ​

typescript
const styleSchema = z.object({
  font: z.string().max(64).optional(),
  color: z.string().max(64).optional(),
  size: z.string().max(16).optional(),
  fontSize: z.string().max(16).optional(),
  fontWeight: z.union([z.string(), z.number()]).optional(),
  fontStyle: z.enum(["normal", "italic", "oblique"]).optional(),
  textDecoration: z.string().max(64).optional(),
  textTransform: z.enum(["none", "uppercase", "lowercase", "capitalize"]).optional(),
  fontSizeOverrides: z.record(z.string(), z.string()).optional(),
  fontSizes: z.record(z.string(), z.string()).optional(),
  maxWidths: z.record(z.string(), z.string()).optional(),
  outline: z.string().max(128).optional(),
  objectFit: z.enum(["contain", "cover", "fill", "none", "scale-down"]).optional(),
  verticalAlign: z.enum(["top", "center", "bottom", "mixed", "justify"]).optional(),
  animation: z.object({
    loop: z.enum(["none", "pulse", "float", "bounce", "spin", "glow", "custom"]).optional(),
    hover: z.enum(["none", "grow", "shrink", "lift", "glow", "custom"]).optional(),
    custom_loop_css: z.string().max(500).optional(),
    custom_keyframes: z.string().max(2000).optional(),
    custom_hover_css: z.string().max(500).optional(),
    duration: z.number().min(0.1).max(20).optional(),
    delay: z.number().min(0).max(10).optional(),
  }).optional(),
  shader_fx: z.object({
    blur: z.number().min(0).max(10).optional(),
    grain: z.number().min(0).max(1).optional(),
    crt_curve: z.boolean().optional(),
  }).optional(),
}).passthrough();

Campos de Style ​

CampoTipoDescripción
fontstring (≤64)Nombre de la familia de fuente
colorstring (≤64)Valor de color CSS
size / fontSizestring (≤16)Tamaño de fuente (p. ej., "48px")
fontWeightstring | numberPeso de fuente (p. ej., "700" o 700)
fontStyle"normal" | "italic" | "oblique"Estilo de fuente
textDecorationstring (≤64)Decoración del texto
textTransform"none" | "uppercase" | "lowercase" | "capitalize"Transformación del texto
outlinestring (≤128)CSS de contorno del texto
objectFit"contain" | "cover" | "fill" | "none" | "scale-down"Modo de ajuste de la imagen
verticalAlign"top" | "center" | "bottom" | "mixed" | "justify"Alineación vertical

Animación ​

CampoTipoDescripción
loopenumTipo de animación en bucle
hoverenumTipo de animación al pasar el cursor
custom_loop_cssstring (≤500)CSS personalizado para el bucle
custom_keyframesstring (≤2000)Definición personalizada de @keyframes
custom_hover_cssstring (≤500)CSS personalizado para hover
durationnumber (0.1-20)Duración de la animación en segundos
delaynumber (0-10)Retraso de la animación en segundos

Efectos de shader ​

CampoTipoDescripción
blurnumber (0-10)Intensidad del desenfoque
grainnumber (0-1)Intensidad del grano de película
crt_curvebooleanEfecto de curvatura CRT

Esquema de Zone ​

typescript
const zoneSchema = z.object({
  id: z.string().min(1).max(128),
  position: z.tuple([z.number().min(0).max(100), z.number().min(0).max(100)]),
  size: z.tuple([z.number().min(0).max(100), z.number().min(0).max(100)]),
  style: z.record(z.string(), z.any()).optional(),
});
CampoTipoDescripción
idstring (1-128)Identificador de la zona
position[x, y] (0-100)Origen de la zona (esquina superior izquierda)
size[width, height] (0-100)Dimensiones de la zona
styleRecord<string, any>Estilos específicos de la zona

Ejemplo completo ​

json
{
  "oim": "1.0",
  "version": "1.0",
  "lang": ["en", "es"],
  "canvas": {
    "natural_width": 1920,
    "natural_height": 1080
  },
  "slots": [
    {
      "id": "main_title",
      "type": "text",
      "content": {
        "en": "Welcome to OIM",
        "es": "Bienvenido a OIM"
      },
      "position": [50, 20],
      "style": {
        "font": "Bricolage Grotesque",
        "fontSize": "48px",
        "fontWeight": "800",
        "color": "#f59e0b",
        "textTransform": "uppercase"
      }
    },
    {
      "id": "cta_button",
      "type": "text",
      "content": {
        "en": "Learn More",
        "es": "Saber Más"
      },
      "position": [50, 80],
      "style": {
        "fontSize": "24px",
        "color": "#ffffff",
        "animation": {
          "loop": "pulse",
          "duration": 2.0
        }
      },
      "link": "https://example.com",
      "target": "_blank"
    }
  ],
  "zones": [
    {
      "id": "header_zone",
      "position": [0, 0],
      "size": [100, 30]
    }
  ]
}

Restricciones de seguridad ​

RestricciónPropósito
Longitudes máximas de stringsPrevenir desbordamiento de búfer
Validación regex de colorPrevenir inyección de CSS
Máximo 50 slotsPrevenir agotamiento de recursos
content ≤ 10MBPermitir imágenes base64 pero limitar el tamaño
position 0-100Solo coordenadas normalizadas
.passthrough()Permitir campos compatibles hacia adelante

Tipos de TypeScript ​

typescript
export type OIMMetadata = z.infer<typeof OimMetadataSchema>;
export type Slot = z.infer<typeof slotSchema>;
export type Style = z.infer<typeof styleSchema>;
export type Zone = z.infer<typeof zoneSchema>;

Véase también ​