Valores de Atributo
Atributos são como o OneEntry descreve o conteúdo: uma página, produto, bloco, usuário, pedido ou campo de formulário carrega um mapa de valores de atributo indexados por marcador. O SDK normaliza cada atributo de cada resposta para a mesma forma, então o mesmo campo parece o mesmo, não importa qual módulo o retornou.
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
A forma normalizada
Um valor de atributo é um IAttributeValue: { type, value, position?, additionalFields? }. O que value contém depende de type:
| Tipo de atributo | value |
|---|---|
string, text | string |
integer, float, real | number — convertido da forma de string da API |
image, file com um arquivo | o próprio objeto de arquivo |
image, file com vários arquivos | um array de objetos de arquivo |
groupOfImages | sempre um array — é uma coleção por definição |
list | um array |
timeInterval | um array de grupos — veja Intervalos de Tempo |
| nenhum valor definido | sempre null |
Atributos de arquivo único são descompactados
Quando um atributo image ou file contém exatamente um arquivo, seu value é o próprio objeto de arquivo. Apenas valores com dois ou mais arquivos permanecem como um array.
const block = await Blocks.getBlockByMarker('promo');
// before: block.attributeValues.img.value[0].downloadLink
// now: block.attributeValues.img.value.downloadLink
Isso se aplica em todos os módulos. Anteriormente, a descompactação ocorria apenas em produtos, menus, formulários, campos de dados de formulários, conjuntos de atributos, coleções de integração e Pages.searchPage, e apenas na chave attributeValues — em todos os outros lugares (blocos, todos os outros métodos de páginas, Products.getProductsEmptyPage, Products.getProductBlockById, admins, descontos, templates, pedidos, usuários) o mesmo atributo chegava como um array de um elemento, então os consumidores tinham que ramificar com base na forma. Os attributes de formulário, campos de dados de formulário e additionalFields aninhados nunca foram descompactados.
⚠️ Migração: código que lê
value[0]de produtos ou menus não é afetado — esses módulos já retornavam o objeto. Código que lêvalue[0]de blocos, páginas, usuários ou pedidos deve remover o índice.
groupOfImages é uma coleção por definição e sempre permanece um array. No lado da requisição, IBodyTypeFile.value é tipado como IFileValue | IFileValue[] de acordo.
Números são números
Valores integer, float e real são convertidos para um número. real costumava ser deixado como uma string, então o mesmo campo numérico chegava ao consumidor como 10 ou como "10", dependendo de qual dos três tipos foi declarado:
const page = await Pages.getPageByUrl('catalog');
// before: page.attributeValues.amount.value // "5"
// now: page.attributeValues.amount.value // 5
A normalização numérica também ocorre em atributos de formulário e campos de dados de formulário, que foram totalmente ignorados — um campo de rating de um atributo de formulário integer é um number, não uma string.
Ao enviar dados, envie uma string: IBodyTypeStringNumberFloat.value é string | number | null, e as respostas voltam normalizadas.
Um valor vazio é sempre nulo
A API retorna um mapa de localização vazio para um valor não definido. O SDK costumava passar isso para tipos semelhantes a texto, enquanto tipos numéricos se tornavam null — o mesmo estado de "sem valor" tinha três representações. Agora é sempre null.
if (page.attributeValues.notes.value === null) {
// nothing configured for this attribute
}
⚠️ Migração: um
integer/floatnão definido não é mais0.Number(null)é0, então umnullexplícito da API costumava ser relatado como um zero real — um valor indistinguível de um0configurado.
Tudo é ordenado por posição
attributeValues sempre foi retornado na ordem de position, e os attributes de formulário agora também são. A API retorna campos de formulário desordenados — um campo com position: 10 poderia chegar após position: 14 — então renderizar um formulário na ordem do CMS exigia ordenação do lado do consumidor.
Um formulário sem atributos retorna attributes: []. A API envia um objeto vazio nesse caso, e o SDK o normaliza para um array vazio, então attributes é sempre IFormAttribute[] e form.attributes.map(...) é seguro em todos os formulários.
Campos aninhados: additionalFields
Valores de atributo aninhados chegam sob additionalFields. Por padrão, o SDK converte o array que a API retorna em um objeto indexado por marker; defina rawData: true na configuração para manter o array original — veja Formato de Campos Adicionais.
// default (rawData: false)
attribute.additionalFields['my_field'].value;
// rawData: true
attribute.additionalFields.find((f) => f.marker === 'my_field').value;
additionalFields aninhados passam pela mesma normalização que atributos de nível superior — arquivos únicos são descompactados e números também são convertidos lá.
Tipando um valor de atributo
IAttributeValue.value é tipado como unknown, porque sua forma depende de type em tempo de execução — o que também significa que o compilador não pode detectar uma suposição errada sobre isso. A regra de arquivo único acima é onde isso se torna problemático: um helper escrito como
const file = Array.isArray(value) ? value[0] : undefined; // single image → undefined
compila, constrói e renderiza uma página sem imagens e sem erro algum. Acesse o valor através dos guards e helpers em vez disso.
Arquivos
getAttributeFiles retorna todos os arquivos de um atributo image, file ou groupOfImages, colapsando as formas de arquivo único e array em uma lista. Qualquer outra coisa resulta em um array vazio, então é seguro em um atributo arbitrário:
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 é exportado: filename, downloadLink, size, contentType, além do opcional previewLink (previews indexados por template, cada um um par [base64 placeholder, url]) e defaultPreview.
Campos aninhados
additionalFields é Record<string, IAttributeValue> | unknown[] — a API retorna um array vazio quando um atributo não tem nenhum, e essa união faz com que cada campo gere um erro de tipo. getAdditionalFields colapsa isso em um mapa (e indexa o array rawData: true por marker):
import { getAdditionalFields } from 'oneentry';
const alt = getAdditionalFields(page.attributeValues.cover).alt?.value;
Guards
isFileAttribute, isStringAttribute, isNumberAttribute, isListAttribute e isTimeIntervalAttribute restringem um IAttributeValue para que seu value seja acessível sem uma conversão:
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 ?? '') : '';
}
A união discriminada
ITypedAttributeValue é IAttributeValue como uma união sobre type — IFileAttributeValue, IStringAttributeValue, INumberAttributeValue, IListAttributeValue, ITimeIntervalAttributeValue. É fechado de propósito: um membro solto (type: string) corresponderia a todos os case e colapsaria a restrição de volta para unknown. Tipos que o SDK não modela (entity, date, personalizados) permanecem em IAttributeValue.
É opcional — IAttributeValues mantém o solto IAttributeValue, então restrinja-se à união com os guards, ou anote um valor que você controla:
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
}
}
Quando a API muda
Os tipos descrevem a API como era quando o SDK foi construído; eles não podem perceber mudanças posteriores. Para valores de atributo, o modo de falha é silencioso — se um campo de arquivo fosse renomeado, getAttributeFiles simplesmente retornaria uma lista vazia e a página seria renderizada sem imagens.
Ativar a validação de resposta torna isso barulhento:
const api = defineOneEntry('your-url', {
token: 'your-app-token',
validation: { enabled: true, strictMode: false, logErrors: true },
});
attributeValues costumava ser isento de validação completamente. Agora é verificado onde o contrato da API está fixo — a forma dos arquivos de atributos image, file e groupOfImages, incluindo campos aninhados — enquanto tudo definido pelo seu próprio conjunto de atributos permanece aberto. Um arquivo que perdeu ou renomeou um campo é relatado como um problema de validação nomeando o marcador e o índice; campos que a API adiciona são aceitos.
Com strictMode: false (a configuração recomendada para produção), os dados ainda passam e o problema é registrado; com strictMode: true, a chamada retorna um IError contendo validationErrors.
🔗 Documentação Relacionada
- Intervalos de Tempo - expandindo atributos
timeIntervalem slots - Importando Tipos -
IAttributeValue,IAttributeValuese o resto - Módulo AttributesSets - como os atributos são configurados