UNPKG

liquido

Version:

Conjunto de ferramentas para desenvolvimento de aplicações para a internet 100% em português

244 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Resposta = void 0; const lexador_1 = require("@designliquido/delegua/lexador"); const construtos_1 = require("@designliquido/delegua/construtos"); const declaracoes_1 = require("@designliquido/delegua/declaracoes"); const estruturas_1 = require("@designliquido/delegua/interpretador/estruturas"); const referencia_montao_1 = require("@designliquido/delegua/interpretador/estruturas/referencia-montao"); const gerador_expressoes_1 = require("./utilidades/gerador-expressoes"); /** * Resolve profundamente um valor do montão durante a execução do interpretador, * enquanto os escopos ainda estão ativos e as entradas do montão são válidas. * * Contexto: vetores e dicionários em Delégua são armazenados no montão como * `ReferenciaMontao`. Quando o escopo de execução de rota termina, `executarBloco` * chama `excluirReferencias` e remove essas entradas. Qualquer leitura posterior * via `resolverValor` falha com "Referência para montão não existe". * * Esta função deve ser chamada DURANTE a execução (ex: dentro de `chamar`), * antes do escopo ser desmontado. */ function resolverProfundo(visitante, objeto) { if (objeto === null || objeto === undefined || typeof objeto !== 'object') return objeto; if (objeto instanceof referencia_montao_1.ReferenciaMontao) { return resolverProfundo(visitante, visitante.resolverValor(objeto)); } if (Array.isArray(objeto)) { return objeto.map(el => resolverProfundo(visitante, el)); } if ('propriedades' in objeto && objeto.propriedades) { const resultado = {}; for (const [k, v] of Object.entries(objeto.propriedades)) { resultado[k] = resolverProfundo(visitante, v); } return resultado; } if ('valor' in objeto && objeto.valor !== undefined) { return resolverProfundo(visitante, objeto.valor); } return objeto; } /** * Implementação nativa do método `json()` da classe `Resposta`. * * Não pode ser declarado como método Delégua comum (via `GeradorExpressoes`) * porque um método declarativo apenas copia a `ReferenciaMontao` do argumento * para `respostaJson`. Quando o escopo de execução de rota termina, o montão é limpo e * a referência se torna inválida — Liquido não consegue mais ler o valor. * * Ao sobrescrever `chamar`, temos acesso ao `visitante` (interpretador) enquanto * o escopo ainda está ativo. Chamamos `resolverProfundo` aqui para materializar * todo o grafo de `ReferenciaMontao` em valores JS simples antes da limpeza. */ class MetodoJson extends estruturas_1.DeleguaFuncaoNativa { constructor() { super('json', 1, (_instancia, _args) => null); this.declaracao = new construtos_1.FuncaoConstruto(-1, -1, [ { abrangencia: 'padrao', tipoDado: 'dicionário', nome: new lexador_1.Simbolo('IDENTIFICADOR', 'json', null, -1, -1) } ], []); } async chamar(visitante, argumentos) { const argBruto = visitante.resolverValor(argumentos[0]); const valorResolvido = resolverProfundo(visitante, argBruto); const instancia = this.instancia; if (instancia) { instancia.propriedades['respostaJson'] = valorResolvido; } return instancia; } funcaoPorMetodoDeClasse(instancia) { const copia = new MetodoJson(); copia.instancia = instancia; return copia; } } /** * Implementação nativa do método `lmht()` da classe `Resposta`. * * Assim como `json()` (ver `MetodoJson` acima), não pode ser um método declarativo * gerado por `GeradorExpressoes`: uma atribuição declarativa apenas copia a * `ReferenciaMontao` do(s) argumento(s) para `isto.valores`. A renderização da visão * LMHT (`logicaComumRespostaVisaoLmht`) acontece de forma assíncrona, depois que a * função de rota já retornou e `executarBloco` já chamou `excluirReferencias` — * nesse ponto a referência do montão já foi limpa, e a leitura tardia falha ou * devolve um valor inválido (ver `resolverValores` em `FormatadorLmht`). * * Sobrescrever `chamar` dá acesso ao `visitante` enquanto o escopo da rota ainda * está ativo, permitindo materializar `visao`/`valores` com `resolverProfundo` * antes da limpeza do montão. */ class MetodoLmht extends estruturas_1.DeleguaFuncaoNativa { constructor() { super('lmht', 0, (_instancia, _args) => null); this.declaracao = new construtos_1.FuncaoConstruto(-1, -1, [ { abrangencia: 'multiplo', tipoDado: 'vetor', nome: new lexador_1.Simbolo('IDENTIFICADOR', 'visaoEValores', null, -1, -1) } ], []); } async chamar(visitante, argumentos) { const instancia = this.instancia; if (instancia) { instancia.propriedades['lmht'] = true; if (argumentos.length > 1) { const visaoBruta = visitante.resolverValor(argumentos[0]); instancia.propriedades['visao'] = resolverProfundo(visitante, visaoBruta); const valoresBrutos = visitante.resolverValor(argumentos[1]); instancia.propriedades['valores'] = resolverProfundo(visitante, valoresBrutos); } else if (argumentos.length === 1) { const valoresBrutos = visitante.resolverValor(argumentos[0]); instancia.propriedades['valores'] = resolverProfundo(visitante, valoresBrutos); } // 0 argumentos: nenhuma atribuição; visão resolvida pela rota em logicaComumRespostaVisaoLmht } return instancia; } funcaoPorMetodoDeClasse(instancia) { const copia = new MetodoLmht(); copia.instancia = instancia; return copia; } } /** * Classe de resposta exposta às funções de rota em Delégua/Pituguês. * * Cada método define propriedades na instância e retorna `isto` para encadeamento. * O framework lê essas propriedades em `processarPropriedadesResposta` para montar * a resposta HTTP final. * * Funções de rota **não precisam** usar `retorna` explicitamente. Chamar qualquer * método desta classe como efeito colateral é suficiente — o _framework_ recupera o * objeto do escopo do interpretador quando o valor de retorno da função é nulo. * * Exemplo válido em Delégua: * ``` * liquido.rotaGet(funcao (requisicao, resposta) { * resposta.json([{ "id": 1 }]) * }) * ``` * * Exemplo válido em Pituguês: * ``` * funcao rotaGet(requisicao, resposta): * resposta.json([{ "id": 1 }]) * * liquido.rotaGet(rotaGet) * ``` * * Propriedades lidas pelo framework (em ordem de precedência): * - `destino` → redirecionamento HTTP * - `lmht` → renderização de visão LMHT * - `respostaJson` → resposta JSON * - `mensagem` → resposta em texto puro * - `statusHttp` → código de status (padrão: 200) */ class Resposta extends estruturas_1.DescritorTipoClasse { constructor() { const metodos = {}; const propriedades = [ new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'destino', null, -1, -1), 'texto'), new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'respostaJson', null, -1, -1), 'dicionário'), new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'mensagem', null, -1, -1), 'texto'), new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1), 'numero'), new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'valores', null, -1, -1), 'dicionário'), new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'visao', null, -1, -1), 'texto') ]; // Há duas formas de construção de métodos apenas usando // estruturas de alto nível: declarando cada uma manualmente, // ou usando o gerador de expressões. // O exemplo abaixo utiliza o gerador de expressões. // Seria o equivalente à seguinte implementação em Delégua: // classe Resposta { // // Outros método e propriedades aqui // enviar(mensagem: texto) { // isto.mensagem = mensagem // retorna isto // } // } const geradorExpressoes = new gerador_expressoes_1.GeradorExpressoes(); metodos['enviar'] = geradorExpressoes.gerarMetodo('enviar', geradorExpressoes.gerarConstrutoFuncao([geradorExpressoes.gerarParametro('mensagem', 'texto')], [ geradorExpressoes.gerarAtribuicaoValorEmPropriedadeClasse('mensagem', geradorExpressoes.gerarReferenciaVariavel('mensagem')), geradorExpressoes.gerarRetornoDeFuncao('isto') ])); // O exemplo abaixo gera um método `status` declarando manualmente // todas as estruturas de alto nível de Delégua. // Seria o equivalente à seguinte implementação: // classe Resposta { // // Outros método e propriedades aqui // status(statusHttp: numero) { // isto.statusHttp = statusHttp // retorna isto // } // } metodos['status'] = new estruturas_1.DeleguaFuncao('status', new construtos_1.FuncaoConstruto(-1, -1, [ { abrangencia: 'padrao', tipoDado: 'número', nome: new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1) } ], [ new declaracoes_1.Expressao(new construtos_1.DefinirValor(-1, -1, new construtos_1.Isto(-1, -1, new lexador_1.Simbolo('ISTO', 'isto', null, -1, -1)), new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1), new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1)))), new declaracoes_1.Retorna(new lexador_1.Simbolo('IDENTIFICADOR', 'qualquerCoisa', null, -1, -1), new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', 'isto', null, -1, -1))) ]), null, false); // Assim como `json`, `lmht` precisa sobrescrever `chamar` para acessar o interpretador // e materializar os valores antes da limpeza do montão. // Ler motivos mais detalhados na documentação de `MetodoLmht` deste fonte. // Seria o equivalente à seguinte implementação declarativa (que tem o bug descrito): // classe Resposta { // visao: texto // valores: dicionário // // lmht(...visaoEValores) { // se visaoEValores.tamanho() > 1 { // isto.visao = visaoEValores[0] // isto.valores = visaoEValores[1] // } senão se visaoEValores.tamanho() == 1 { // isto.valores = visaoEValores[0] // } // // 0 argumentos: visao resolvida automaticamente pela rota // // retorna isto // } // } metodos['lmht'] = new MetodoLmht(); metodos['redirecionar'] = geradorExpressoes.gerarMetodo('redirecionar', geradorExpressoes.gerarConstrutoFuncao([geradorExpressoes.gerarParametro('destino', 'texto')], [ geradorExpressoes.gerarAtribuicaoValorEmPropriedadeClasse('destino', geradorExpressoes.gerarReferenciaVariavel('destino')), geradorExpressoes.gerarRetornoDeFuncao('isto') ])); // Há também esta forma de declaração, necessária quando o método precisa sobrescrever `chamar` para acessar o interpretador. // Ler motivos mais detalhados na documentação de `MetodoJson` deste fonte. metodos['json'] = new MetodoJson(); super(new lexador_1.Simbolo('IDENTIFICADOR', 'Resposta', null, -1, -1), undefined, metodos, propriedades); } } exports.Resposta = Resposta; //# sourceMappingURL=resposta.js.map