Buscar/Desenvolver um framework que oferece uma única API em várias linguagens de programação

Desenvolver um framework que oferece uma única API em várias linguagens de programação

Para criar um framework que gera pacotes para várias linguagens a partir de um só código. Define regras para o protocolo JSON entre processos, os tipos exportáveis, a geração de código e os testes que conferem se todas as linguagens se comportam igual.

Para usar estas regras de IA
## Manter o mesmo comportamento e a compatibilidade retroativa em todas as linguagens Um framework entre linguagens deve priorizar um comportamento que permaneça compreensível e equivalente em todas as linguagens compatíveis, e não todos os recursos idiomáticos disponíveis na linguagem de implementação. ### Invariantes fundamentais - Preserve a compatibilidade retroativa do formato do assembly, do protocolo de runtime e das APIs públicas. - Exija comportamento observável equivalente em todas as linguagens-alvo compatíveis. - Gere código que siga as convenções normais de nomenclatura, empacotamento e tipos de cada linguagem-alvo. - Aceite apenas recursos da linguagem de origem que possam ser representados fielmente em todos os alvos. - Prefira correção e interoperabilidade a atalhos de desempenho específicos de um alvo. ### Limites dos pacotes - Separe o núcleo do protocolo, os runtimes hospedeiros, as definições de esquema, os geradores de código, as ferramentas de reflexão, as verificações de compatibilidade e os fixtures de teste. - Mantenha os utilitários de build e os relatórios de conformidade fora dos pacotes de runtime. - Trate cada runtime de linguagem e cada integração opcional como um pacote com responsabilidade independente. - Coloque os fixtures compartilhados onde todos os testes de compiladores, geradores e runtimes possam consumir os mesmos casos de comportamento. - Mantenha componentes que ficam em repositórios separados por trás de dependências explícitas e versionadas. ## Executar o código em um processo núcleo que conversa por JSON com cada linguagem Use um modelo de runtime com dois lados quando as linguagens hospedeiras não puderem executar diretamente a linguagem de implementação. O processo hospedeiro se comunica com um processo de núcleo por mensagens JSON na entrada e na saída padrão, enquanto toda execução na linguagem de implementação permanece no núcleo. ### Limite entre processos - Dê a cada solicitação um discriminador explícito de operação. - Cubra carregamento de assemblies, ciclo de vida de objetos, chamadas de métodos, acesso a propriedades, trabalho assíncrono, estatísticas e callbacks com operações distintas do protocolo. - Use um processo intermediário adicional somente quando um runtime hospedeiro não puder preservar os descritores de arquivo exigidos pelo processo principal. - Mantenha a saída de diagnóstico no fluxo de erro para que ela não corrompa o fluxo do protocolo. ### Sequência de callback Quando o código da linguagem de implementação alcançar um método ou uma propriedade sobrescrita pela linguagem hospedeira: 1. O núcleo envia uma solicitação de callback em vez da resposta original. 2. O hospedeiro executa a sobrescrita e devolve uma mensagem de conclusão com o resultado ou a falha. 3. O núcleo retoma a operação original e devolve sua resposta efetiva. ### Evolução do protocolo - Trate os nomes das solicitações, o significado dos campos, a ordem das respostas e a sequência dos callbacks como contratos de compatibilidade. - Atualize todos os runtimes hospedeiros em conjunto quando o protocolo mudar. - Adicione testes de compatibilidade antes de alterar o formato de uma mensagem ou uma regra de ciclo de vida. ## Decidir se os valores passam entre linguagens por cópia ou por referência Defina uma representação de transmissão para cada categoria de valor portável e torne explícita a semântica de propriedade. Um valor transferido por cópia não deve ser confundido com um objeto que continua sob propriedade do núcleo. ### Representação de transmissão - Envie booleanos, números e strings por valor, usando suas representações em JSON. - Envie listas e mapas por valor, com um envelope explícito e etiquetado quando for necessário distingui-los de outros objetos. - Codifique enumerações com um identificador de membro totalmente qualificado. - Codifique datas com uma representação de timestamp etiquetada e inequívoca. - Codifique estruturas somente de dados com o tipo totalmente qualificado e os dados dos campos. - Codifique instâncias de classe por referência, com um identificador de objeto estável. ### Regras de fronteira - Preserve strings UTF-8, o comportamento de ponto flutuante, a nulabilidade e a semântica de cópia de coleções entre as linguagens. - Mantenha estáveis as chaves etiquetadas da representação de transmissão depois que consumidores passarem a depender delas. - Libere explicitamente as referências a objetos quando o protocolo for responsável pelo gerenciamento remoto do ciclo de vida. - Teste os percursos de ida e volta por todos os runtimes hospedeiros, não apenas pelo lado da linguagem de implementação. ## Exportar só tipos que todas as linguagens conseguem representar Aplique restrições de portabilidade às declarações exportadas pelo ponto de entrada principal do módulo, permitindo que o código interno de implementação use recursos mais amplos da linguagem. ### Superfície da API portável - Ofereça suporte a tipos primitivos, listas, mapas, enumerações, datas, classes, interfaces, estruturas de dados, promessas e valores opacos somente quando todos os alvos tiverem uma representação sólida. - Diferencie interfaces comportamentais de estruturas somente de dados por uma regra explícita de nomenclatura ou esquema. - Exija que estruturas somente de dados contenham propriedades somente leitura e nenhum método. - Permita promessas apenas em posições que todos os alvos possam expor de maneira consistente, como tipos de retorno de métodos. - Trate valores opacos como implicitamente anuláveis quando os alvos com tipagem estática não puderem recuperar um tipo mais preciso. - Passe classes por referência e estruturas semelhantes a valores por cópia. ### Restrições - Rejeite construções exportadas sem suporte, como tuplas, tipos não habitados, inteiros de precisão arbitrária, símbolos e sobrecargas de métodos. - Exija que métodos sobrescritos mantenham assinaturas exatamente compatíveis; não dependa de retornos covariantes nem de parâmetros contravariantes. - Evite uniões quando elas se reduzirem a um objeto sem tipagem nos alvos com tipagem estática. - Não permita que interfaces comportamentais e estruturas somente de dados herdem umas das outras. ## Gerar o pacote de cada linguagem a partir do mesmo assembly compilado Gere cada alvo a partir do mesmo assembly compilado, para que os pacotes de destino compartilhem uma única fonte de verdade da API. ### Pipeline de geração 1. Leia o assembly do pacote compilado na linguagem de implementação. 2. Gere código-fonte idiomático para a linguagem-alvo selecionada. 3. Compile o código-fonte gerado com a cadeia de ferramentas dessa linguagem. 4. Empacote o resultado com o gerenciador de pacotes padrão do ecossistema de destino. ### Projeto dos alvos - Dê a cada linguagem-alvo seu próprio adaptador, construído sobre uma abstração compartilhada de travessia. - Use um alvo maduro como referência ao adicionar um novo alvo, mas preserve os padrões idiomáticos da nova linguagem. - Inclua o artefato original da implementação somente quando a arquitetura do runtime exigir. - Inclua os metadados do assembly necessários para ferramentas posteriores de reflexão ou geração. - Compartilhe entre os alvos os utilitários de formatação, indentação, criação de arquivos e blocos de código. - Atualize os snapshots do código gerado sempre que um alvo mudar. ## Compilar e testar no monorepo com as versões fixas das ferramentas Execute instalação, builds, testes, linting, atualizações de snapshots e geração de relatórios de conformidade por meio da cadeia de ferramentas fixada no workspace do repositório. ### Ambiente e escopo - Comece na raiz do repositório para o bootstrap completo e a validação de todo o repositório. - Execute scripts limitados a um pacote a partir do diretório desse pacote durante iterações focadas. - Respeite as versões declaradas do gerenciador de pacotes e das ferramentas de orquestração. - Limite a concorrência do build quando as dependências entre pacotes tornarem insegura a execução paralela. ### Fluxo de alteração - Identifique o pacote responsável pela alteração antes de editar ou executar comandos. - Primeiro, faça o build e teste o pacote em foco; depois, execute uma validação mais ampla dos contratos compartilhados. - Edite o código-fonte, e não os bundles gerados, e refaça o build pelo pipeline designado. - Mantenha locais as substituições temporárias de runtime e as configurações do depurador. - Atualize snapshots somente depois de confirmar que a alteração gerada é intencional. ## Testar com snapshots e um conjunto comum de testes que todas as linguagens devem passar Combine testes rápidos no nível do pacote com verificações de regressão da saída gerada e uma suíte de conformidade entre linguagens. ### Camadas de validação - Use testes unitários para a lógica dos pacotes e para casos extremos do protocolo. - Use snapshots para detectar alterações não intencionais no código-fonte gerado. - Use fixtures compartilhados que exercitem classes, interfaces, estruturas de dados, enumerações, coleções, chamadas assíncronas e sobrescritas. - Use testes de conformidade para verificar comportamento equivalente em cada binding de linguagem. ### Fixtures e relatórios - Compile os fixtures compartilhados com o mesmo compilador usado por consumidores reais. - Atualize todos os snapshots afetados de geradores, reflexão e runtime quando os fixtures mudarem. - Defina cada caso de conformidade uma única vez e exija que cada binding de linguagem o implemente ou o classifique explicitamente. - Gere novamente e faça commit do relatório de conformidade sempre que os casos ou resultados mudarem. - Faça a validação falhar quando um binding adicionar um caso ausente da definição da suíte ou quando o relatório estiver desatualizado. ## Lançar alterações com snapshots e relatórios em dia, sem force-push Trate o código gerado, os formatos de protocolo, as bibliotecas de runtime e os relatórios como superfícies de lançamento coordenadas. ### Restrições inegociáveis - Não enfraqueça as restrições de tipos exportados sem validação em todos os alvos. - Não altere operações do protocolo nem formatos de serialização etiquetados sem atualizações coordenadas dos runtimes hospedeiros. - Não altere os padrões de geração sem atualizar e revisar os snapshots. - Não presuma segurança entre threads quando as bibliotecas de runtime forem intencionalmente de thread única. - Não introduza dependências circulares entre pacotes quando o grafo de build rejeitar ciclos. - Não modifique diretamente a saída empacotada. ### Pull request e transferência - Use o formato de título, as convenções de escopo e a confirmação de licença de contribuição exigidos pelo repositório. - Inclua todos os snapshots gerados e relatórios de conformidade exigidos pela alteração no código-fonte. - Mantenha fora da alteração arquivos gerados não relacionados, alterações de lockfile e configurações locais. - Se o branch remoto tiver avançado, execute fetch e rebase ou merge, resolva os conflitos, valide e faça push normalmente; nunca faça push forçado. - Registre a sincronização posterior de repositórios ou documentação no checklist de lançamento, em vez de depender da memória.