Skip to main content
Esta página é a referência do formato de um tipo de objeto: como cada tipo de campo vai no fio, os membros que um campo declara, as conferências que um tipo precisa passar e a niadra-expr, a linguagem das chaves, dos valores, dos temporizadores, das leituras e dos campos derivados. Os vetores de conformidade que fixam o comportamento da linguagem vêm nos repositórios dos dois SDKs, em spec/vectors/niadra-expr.v0.json: o servidor e os dois SDKs rodam o mesmo arquivo.

Tipos de campo e como os valores viajam

Os fields de um evento e um push de objeto levam cada valor em JSON, na forma que o tipo declarado espera: Um valor de outro tipo fica guardado como veio, mas uma expressão sobre o campo não consegue lê-lo como o tipo dele: um valor em dinheiro enviado como a string "38000.00" não é número para uma leitura, um campo derivado ou um temporizador. Um campo que o tipo não declara fica como campo simples e abre uma problema de dados de drift, nunca uma recusa. O que um evento diz de si mesmo (operation, version, last_event_type, source_version, record_ref, input_refs) é escrituração: nunca é drift e só é servido quando o tipo o declara como campo. Cada chave dos fields de um evento vale para todos os objetos dos object_refs dele, então mande um objeto por evento.

O que um campo declara

Um campo plain não declara observers nem unobserved_blocks. Mudar a sensitivity, o access ou o pii de um campo, a licença de uma fonte ou as finalidades do tipo é uma decisão de privacidade: pede também o papel que responde pela segurança.

O que o servidor recusa num tipo

Além da forma do documento, um tipo é recusado, com o caminho de cada entrada que quebra uma regra (por exemplo timers.launch_at.on_fire: transition(scheduled->actve) is not a transition of the lifecycle), quando:
  1. Um campo, um valor, um eixo de tempo ou um campo derivado usa um nome que outro já tem, ou um nome que a linguagem reserva (abaixo).
  2. Um estado que o ciclo de vida, um temporizador ou um vocabulário nomeia não está declarado; um timers_on nomeia um eixo não declarado; um campo de conteúdo, completeness.total_field, um defeito conhecido, uma chave de union ou uma ação de refresh nomeia um campo ou valor não declarado; prefer_source, use_source, quote() ou um vocabulário nomeia uma fonte não declarada; um campo derivado nomeia uma relação não declarada, ou uma cujo tipo o registro não declara, ou um min ou max sobre um campo que esse tipo não tem.
  3. Um eixo derivado não nomeia a regra dele, ou uma regra está num eixo que não é derivado; um tipo derivado por introspecção perdeu a impressão digital.
  4. Um membro quebra a própria regra: um campo plain com observadores, um claim_max_age acima de max_age, e as regras de fontes, ciclo de vida, temporizadores, releitura e finalidades que Tipos de objeto e estado descreve.
  5. Uma expressão não se lê ou não se resolve contra o tipo: as condições (when, active_when, where) dão verdadeiro ou falso, e o due de um temporizador dá uma data ou um instante.

niadra-expr

A linguagem é pequena, determinística e total: sem laços, sem recursão, sem E/S e sem relógio além do que recebe, com texto e aninhamento limitados, e um resultado ou um de três erros para toda entrada. O mesmo texto dá o mesmo resultado no servidor, em cada SDK e no seu worker de resolução.

Tokens

  • Espaços, tabulações e quebras de linha separam tokens.
  • Um nome é [a-z_][a-z0-9_]*, no máximo 64 caracteres, ASCII minúsculo.
  • Um número são dígitos com um . opcional e pelo menos um dígito depois dele; não há sinal, um número negativo vem de uma subtração.
  • Uma duração é um inteiro de no máximo seis dígitos seguido de ms, s, min, h ou d (m não é unidade, e 1.5h se escreve 90min).
  • Uma string vai entre aspas simples, numa linha só, com \' e \\ como únicos escapes, no máximo 256 caracteres.
  • Os operadores são ==, !=, <, <=, >, >=, +, -, (, ), [, ], , e .; as palavras-chave são and, or, not, in, true, false, none, yes, no, unobserved e known_defect.

Gramática

or liga mais frouxo, depois and, not, uma comparação, + e - (da esquerda para a direita) e .. Comparações não se encadeiam: a < b < c é erro. Um nome com pontos como lead.city é um nome só, com partes; depois de um grupo ou de uma chamada, . lê um campo de uma cotação. As palavras lógicas só aparecem de um lado de == ou !=.

Valores e os quatro valores lógicos

unobserved e known_defect são desconhecidos. Um valor presente é um booleano, um número, uma string, uma duração (milissegundos inteiros), uma data, um instante (ao milissegundo) ou uma lista. Não observado nunca é falso: uma comparação com um valor desconhecido é desconhecida, e a negação dela também.

Nomes

Um nome de uma parte é, nesta ordem: um nome de contexto (state, o estado do ciclo de vida; derived_status; watch_count, 0 por padrão; purpose, a finalidade da leitura, display por padrão); um campo, um valor calculado ou um eixo de tempo do objeto; um nome de ausência declarado; senão, um campo que ninguém observou, unobserved. Um nome com pontos é uma entrada declarada desse caminho (lead.city), config.<chave> (o valor de configuração da empresa, ausente quando não definido) ou <campo>.completeness. As palavras-chave e state, derived_status, watch_count, purpose e config são reservados. Um estado é uma string: state == 'open', nunca state == open.

Operadores

  • x == yes é verdadeiro exatamente quando x leva yes, e o mesmo para as outras palavras lógicas; o resultado é sempre verdadeiro ou falso: available == unobserved pergunta se um valor foi observado.
  • Fora isso, quando um dos lados de uma comparação, de + ou de - é desconhecido, o resultado é desconhecido (known_defect quando um deles o leva).
  • Igualdade: duas ausências são iguais quando uma não tem nome ou as duas têm o mesmo (none é qualquer ausência); uma ausência nunca é igual a um valor presente; números pelo valor, strings pelos caracteres, durações pelo tamanho, datas e instantes como instantes (uma data é o início do dia), listas item a item; qualquer outro par é expr_type.
  • Ordem (<, <=, >, >=) é falsa quando um dos lados é ausente; números, strings (por ponto de código), durações, datas e instantes se comparam; qualquer outro tipo é expr_type.
  • in recebe uma lista à direita: verdadeiro quando a esquerda é igual a um item, desconhecido quando um item desconhecido poderia decidir, falso nos outros casos.
  • and, or e not recebem verdadeiro, falso ou desconhecido, com lógica de três valores, da esquerda para a direita, e o primeiro operando que decide para a avaliação: false and x nunca avalia x.
  • + e -: número com número; duração com duração; uma data ou um instante mais ou menos uma duração dá um instante; uma data mais ou menos dias úteis dá uma data; duas datas ou instantes subtraídos dão uma duração. Um operando ausente dá uma ausência.

Funções

Os prazos de um tribunal, de um contrato ou de um produto são regras da sua empresa, como valores declarados: a linguagem só conta dias úteis simples.

Erros e limites

Uma expressão tem no máximo 1.024 caracteres, 256 tokens e 16 grupos aninhados; uma lista literal no máximo 64 itens, uma string no máximo 256 caracteres, um nome no máximo 64 e uma duração no máximo seis dígitos. Uma lista lida de um encaixe tem no máximo 1.000 itens, e uma contagem de dias úteis no máximo 1.000.

Próximos passos

Tipos de objeto e estado

Para que serve um tipo, frescor, a finalidade da leitura e objetos compartilhados.

Leituras tipadas

POST /v1/state/read: campos, valores, leituras e temporizadores.