Valores de Atributo
Los atributos son la forma en que OneEntry describe el contenido: una página, producto, bloque, usuario, pedido o campo de formulario lleva un mapa de valores de atributo indexados por marcador. El SDK normaliza cada atributo de cada respuesta a la misma forma, por lo que el mismo campo se ve igual sin importar qué módulo lo devolvió.
const page = await Pages.getPageByUrl('catalog');
page.attributeValues.title.value; // "Catalog" — string
page.attributeValues.amount.value; // 5 — number
page.attributeValues.cover.value; // { downloadLink } — the file object itself
page.attributeValues.notes.value; // null — no value set
La forma normalizada
Un valor de atributo es un IAttributeValue: { type, value, position?, additionalFields? }. Lo que value contiene depende de type:
| Tipo de atributo | value |
|---|---|
string, text | string |
integer, float, real | number — convertido desde la forma de cadena de la API |
image, file con un archivo | el objeto de archivo en sí |
image, file con varios archivos | un array de objetos de archivo |
groupOfImages | siempre un array — es una colección por definición |
list | un array |
timeInterval | un array de grupos — ver Intervalos de Tiempo |
| sin valor establecido | siempre null |
Los atributos de un solo archivo se desenvuelven
Cuando un atributo image o file contiene exactamente un archivo, su value es el objeto de archivo en sí. Solo los valores con dos o más archivos permanecen como un array.
const block = await Blocks.getBlockByMarker('promo');
// before: block.attributeValues.img.value[0].downloadLink
// now: block.attributeValues.img.value.downloadLink
Esto se aplica en cada módulo. Anteriormente, el desenvuelto solo se realizaba en productos, menús, formularios, campos de datos de formularios, conjuntos de atributos, colecciones de integración y Pages.searchPage, y solo en la clave attributeValues — en cualquier otro lugar (bloques, todos los demás métodos de páginas, Products.getProductsEmptyPage, Products.getProductBlockById, admins, descuentos, plantillas, pedidos, usuarios) el mismo atributo llegaba como un array de un elemento, por lo que los consumidores tenían que ramificarse según la forma. Los attributes de formularios, los campos de datos de formularios y los additionalFields anidados nunca se desenvuelven en absoluto.
⚠️ Migración: el código que lee
value[0]de productos o menús no se ve afectado — esos módulos ya devolvían el objeto. El código que leevalue[0]de bloques, páginas, usuarios o pedidos debe eliminar el índice.
groupOfImages es una colección por definición y siempre permanece como un array. En el lado de la solicitud, IBodyTypeFile.value está tipado como IFileValue | IFileValue[] en consecuencia.
Los números son números
Los valores integer, float y real se convierten a un número. real solía dejarse como una cadena, por lo que el mismo campo numérico llegaba al consumidor como 10 o como "10" dependiendo de cuál de los tres tipos se declarara:
const page = await Pages.getPageByUrl('catalog');
// before: page.attributeValues.amount.value // "5"
// now: page.attributeValues.amount.value // 5
La normalización numérica también se aplica a atributos de formularios y campos de datos de formularios, que se omitieron por completo — un campo de rating de un atributo de formulario integer es un number, no una cadena.
Al enviar datos, envía una cadena: IBodyTypeStringNumberFloat.value es string | number | null, y las respuestas regresan normalizadas.
Un valor vacío es siempre null
La API devuelve un mapa de localización vacío para un valor no establecido. El SDK solía pasarlo para tipos similares a texto, mientras que los tipos numéricos se convertían en null — el mismo estado de "sin valor" tenía tres representaciones. Ahora es siempre null.
if (page.attributeValues.notes.value === null) {
// nothing configured for this attribute
}
⚠️ Migración: un
integer/floatno establecido ya no es0.Number(null)es0, por lo que unnullexplícito de la API solía informarse como un cero real — un valor indistinguible de un0configurado.
Todo está ordenado por posición
attributeValues siempre se ha devuelto en orden de position, y los attributes de formularios ahora también. La API devuelve los campos de formulario desordenados — un campo con position: 10 podría llegar después de position: 14 — por lo que renderizar un formulario en el orden del CMS requería ordenar en el lado del consumidor.
Un formulario sin atributos devuelve attributes: []. La API envía un objeto vacío en ese caso, y el SDK lo normaliza a un array vacío, por lo que attributes es siempre IFormAttribute[] y form.attributes.map(...) es seguro en cada formulario.
Campos anidados: additionalFields
Los valores de atributo anidados llegan bajo additionalFields. Por defecto, el SDK convierte el array que devuelve la API en un objeto indexado por marker; establece rawData: true en la configuración para mantener el array original — ver Formato de Campos Adicionales.
// default (rawData: false)
attribute.additionalFields['my_field'].value;
// rawData: true
attribute.additionalFields.find((f) => f.marker === 'my_field').value;
Los additionalFields anidados pasan por la misma normalización que los atributos de nivel superior — los archivos individuales se desenvuelven y los números también se convierten allí.
Tipando un valor de atributo
IAttributeValue.value está tipado como unknown, porque su forma depende de type en tiempo de ejecución — lo que también significa que el compilador no puede detectar una suposición incorrecta sobre él. La regla de un solo archivo anterior es donde eso afecta: un helper escrito como
const file = Array.isArray(value) ? value[0] : undefined; // single image → undefined
compila, construye y renderiza una página sin imágenes y sin errores en ninguna parte. Accede al valor a través de los guards y helpers en su lugar.
Archivos
getAttributeFiles devuelve cada archivo de un atributo image, file o groupOfImages, colapsando las formas de un solo archivo y array en una lista. Cualquier otra cosa produce un array vacío, por lo que es seguro en un atributo arbitrario:
import { getAttributeFile, getAttributeFiles } from 'oneentry';
const gallery = getAttributeFiles(product.attributeValues.images); // IAttributeFile[]
const cover = getAttributeFile(page.attributeValues.cover); // IAttributeFile | null
const blurDataURL = cover?.previewLink?.default?.[0]; // base64 placeholder
IAttributeFile se exporta: filename, downloadLink, size, contentType, además del opcional previewLink (previews indexados por plantilla, cada uno un par [base64 placeholder, url]) y defaultPreview.
Campos anidados
additionalFields es Record<string, IAttributeValue> | unknown[] — la API devuelve un array vacío cuando un atributo no tiene ninguno, y esa unión hace que cada campo lea un error de tipo. getAdditionalFields lo colapsa a un mapa (y indexa el array rawData: true por marker):
import { getAdditionalFields } from 'oneentry';
const alt = getAdditionalFields(page.attributeValues.cover).alt?.value;
Guards
isFileAttribute, isStringAttribute, isNumberAttribute, isListAttribute y isTimeIntervalAttribute reducen un IAttributeValue para que su value sea accesible sin una conversión:
import { isStringAttribute } from 'oneentry';
import type { IAttributeValues } from 'oneentry';
function readText(values: IAttributeValues, marker: string): string {
const attr = values[marker];
return isStringAttribute(attr) ? (attr.value ?? '') : '';
}
La unión discriminada
ITypedAttributeValue es IAttributeValue como una unión sobre type — IFileAttributeValue, IStringAttributeValue, INumberAttributeValue, IListAttributeValue, ITimeIntervalAttributeValue. Está cerrada a propósito: un miembro suelto (type: string) coincidiría con cada case y colapsaría la reducción de nuevo a unknown. Los tipos que el SDK no modela (entity, date, personalizados) permanecen en IAttributeValue.
Es optativo — IAttributeValues mantiene el suelto IAttributeValue, así que reduce a la unión con los guards, o anota un valor que controlas:
import type { ITypedAttributeValue } from 'oneentry';
function render(attr: ITypedAttributeValue) {
switch (attr.type) {
case 'image':
case 'file':
return attr.value; // IAttributeFile | IAttributeFile[] | null
case 'integer':
return attr.value; // number | null
}
}
Cuando la API cambia
Los tipos describen la API tal como era cuando se construyó el SDK; no pueden notar que cambie después. Para los valores de atributo, el modo de fallo es silencioso — si un campo de archivo se renombrara, getAttributeFiles simplemente devolvería una lista vacía y la página se renderizaría sin imágenes.
Activar la validación de respuestas hace que eso sea ruidoso:
const api = defineOneEntry('your-url', {
token: 'your-app-token',
validation: { enabled: true, strictMode: false, logErrors: true },
});
attributeValues solía estar exento de validación por completo. Ahora se verifica donde el contrato de la API está fijo — la forma de los archivos de atributos image, file y groupOfImages, incluidos los campos anidados — mientras que todo lo definido por tu propio conjunto de atributos permanece abierto. Un archivo que perdió o renombró un campo se informa como un problema de validación nombrando el marcador y el índice; los campos que la API agrega son aceptados.
Con strictMode: false (la configuración de producción recomendada) los datos aún pasan y el problema se registra; con strictMode: true la llamada devuelve un IError que lleva validationErrors.
🔗 Documentación Relacionada
- Intervalos de Tiempo - expandiendo atributos
timeIntervalen slots - Importando Tipos -
IAttributeValue,IAttributeValuesy el resto - Módulo AttributesSets - cómo se configuran los atributos