Ubuntu TechHive

How fetch Connects JavaScript Runtimes to Durable Compute

How fetch Connects JavaScript Runtimes to Durable Compute

De runtimes de JavaScript a computação portátil e com estado...

Como o fetch conecta Runtimes JavaScript à Computação Durável

Computação Durável

JavaScript é um alvo de execução comum; as APIs da Web Platform fornecem uma interface compartilhada entre seus runtimes. fetch conecta navegadores e servidores, enquanto o mesmo modelo Request=/=Response também alcança atores nomeados e com estado. A mudança importante é o que o host pode recuperar após uma requisição — ou um processo inteiro — terminar.

Considere um fluxo de trabalho que busca uma entrada, chama um serviço de IA, aguarda horas por uma aprovação humana e, então, publica um resultado. Seu tempo de vida excede qualquer requisição HTTP ou processo de servidor individual. Runtimes JavaScript e Web APIs fornecem um modelo de programação familiar; a durabilidade permite que esse trabalho sobreviva à execução que o iniciou.1

Runtimes e Web Platform APIs

Um motor executa JavaScript; um runtime adiciona I/O. O núcleo compartilhado de Web APIs: fetch, Request, Response, Headers, streams, URL e Web Crypto.

Runtime Ambiente típico Suporte a Web API
Browser Cliente Núcleo + DOM
Node.js Servidor / cloud VM Núcleo; adaptadores de servidor HTTP
Deno Servidor / cloud VM Núcleo; Deno.serve
Bun Servidor / cloud VM Núcleo; Bun.serve
QuickJS Motor embarcado Web APIs fornecidas pelo host
workerd Cloudflare / self-hosted Subconjunto de Worker; handler fetch
celld Frota self-hosted Subconjunto compatível com Workers

O suporte varia conforme o runtime/versão; DOM, CORS, cookies e streaming não são uniformes. Cliente: fetch(request) -> Promise. Servidor: handler(Request) -> Response | Promise; as APIs de inicialização diferem.

Esta fronteira comum é onde as peças se conectam: uma chamada fetch pode ficar dentro de um passo de fluxo de trabalho durável, um Request pode ser roteado para um objeto com estado nomeado, e um host pode suspender um programa enquanto o trabalho externo é concluído. A interface HTTP permanece familiar enquanto o host adiciona persistência, agendamento e recuperação — não porque o Fetch em si seja durável, mas porque a durabilidade pode ser construída em torno dele.

A equipe do Deno anunciou um ano de correções de bugs/segurança em outubro de 2026, encerrando então o desenvolvimento de seu runtime; o Deno permanece open source.

Execução durável

  • Fluxos de trabalho (Workflows): persistem resultados de passos; após interrupção, o replay reutiliza passos concluídos e tenta novamente o trabalho inacabado. Torne efeitos externos idempotentes.
  • dquickts: tira um snapshot do heap JS e da pilha suspensa em pontos de segurança (safepoints); restaura e retoma em outro processo usando a mesma versão do motor. O I/O do host é separado.

dquickts: salve o programa em execução, não apenas o resultado de um passo

A extensão QuickJS demonstra uma forma mais profunda de interrupção e retomada: um host pode pausar o JavaScript mesmo no meio de um loop, serializar o heap do runtime e a pilha de chamadas suspensa, e gravar esse snapshot em disco. Objetos, closures, variáveis locais, jobs de promise pendentes e a posição da instrução salva fazem parte do estado de execução recuperável.2

O processo host original pode então encerrar. Um novo processo carrega o snapshot em uma versão compatível do motor e retoma o programa na instrução salva, em vez de reexecutar o fluxo de trabalho para reconstruir seu progresso. Isso torna a hibernação e longas esperas possíveis sem manter o processo original residente.

A conexão com o I/O do fluxo de trabalho é igualmente importante: JS_SuspendCall() pode estacionar uma chamada de host, e JS_ResumeWith() pode fornecer seu resultado após restaurar o runtime. O exemplo do projeto usa uma função de host semelhante ao fetch para ilustrar essa fronteira; não é uma implementação nativa da API Fetch padrão. O host ainda detém o I/O externo e o tratamento seguro de efeitos colaterais repetidos.3

%%{init: {"themeVariables": {"fontSize": "18px"}, "flowchart": {"htmlLabels": false, "curve": "linear", "nodeSpacing": 24, "rankSpacing": 32}}}%%
flowchart TB
  subgraph WF["Fluxo de Trabalho"]
    direction TB
    W1["Executar um passo"] --> W2[("Persistir resultado do passo")]
    W2 -->|"processo reinicia"| W3["Replay do fluxo
Reutilizar resultados salvos"]
end subgraph JS["dquickts"] direction TB R1["Suspender dentro de um loop"] --> R2[("Snapshot de Heap + pilha
Gravado em disco")]
R2 -->|"novo processo"| R3["Restaurar + retomar
Instrução salva"]
end classDef running fill:#f8fafc,stroke:#64748b,color:#0f172a classDef saved fill:#eff6ff,stroke:#2563eb,color:#0f172a classDef resumed fill:#ecfdf5,stroke:#059669,color:#0f172a class W1,R1 running class W2,R2 saved class W3,R3 resumed style WF fill:#fff,stroke:#cbd5e1,color:#0f172a style JS fill:#fff,stroke:#cbd5e1,color:#0f172a

Durable Objects

Um ator nomeado e single-threaded possui armazenamento SQLite/KV fortemente consistente. A Cloudflare adiciona roteamento distribuído, armazenamento, alarmes e hibernação de WebSocket sobre o modelo de Web API do workerd. fetch é a costura HTTP, não a durabilidade em si; RPC é outro ponto de entrada. O estado persistido sobrevive à expulsão; as pilhas de chamadas JS não.

Para cargas de trabalho semelhantes a fluxos de trabalho, estes são blocos de construção complementares: um objeto pode possuir o estado de um job ou agente, persistir o progresso e agendar um alarme para continuar mais tarde. A hibernação de WebSocket da Cloudflare pode manter clientes conectados enquanto o objeto está fora da memória. Isso recupera um ator a partir do estado armazenado, em vez de restaurar sua pilha de execução como o dquickts faz.4

%%{init: {"themeVariables": {"fontSize": "18px"}, "flowchart": {"htmlLabels": false, "curve": "linear", "nodeSpacing": 24, "rankSpacing": 32}}}%%
flowchart TB
  C["Navegador ou servidor
fetch(request)"]
W["Worker
Request → Response"]
D["Durable Object / cell
Handler de fetch nomeado"]
S[("SQLite / KV privado
Sobrevive à expulsão")]
E["Eventos de despertar
Alarme / WebSocket"]
C <-- -->|"Request / Response"| W W <-- -->|"ID do objeto + stub.fetch"| D D <-- -->|"leitura / escrita"| S E -.->|despertar| D classDef handler fill:#f8fafc,stroke:#64748b,color:#0f172a classDef actor fill:#ecfdf5,stroke:#059669,color:#0f172a classDef saved fill:#eff6ff,stroke:#2563eb,color:#0f172a class C,W,E handler class D actor class S saved

celld: implantação neutra em relação ao provedor

O celld traz o modelo de Workers/Durable Objects para sua infraestrutura: V8 + SQLite por célula + um bucket de armazenamento de objetos compartilhado; lacunas de compatibilidade se aplicam.

Após a mudança da equipe do Deno para a Cloudflare, o plano anunciado é fundir o código e as ideias de auto-hospedagem distribuída do celld no workerd — não é uma integração concluída.

Linguagens que compilam para JavaScript

Uma escolha de linguagem não precisa ser uma escolha de hospedagem

ClojureScript, ReScript e TypeScript compilam para JavaScript em vez de exigir uma VM de produção separada. Seus bindings/interop expõem fetch, streams e outras APIs de host: escolher uma linguagem não precisa significar abandonar o ecossistema de runtime do JavaScript.

As estruturas de dados do ClojureScript ou os tipos do ReScript não alteram a fronteira Request=/=Response. A saída compatível pode ter como alvo navegadores, servidores ou hosts duráveis. Isso é portabilidade de interface, não uma garantia de que todo bundle roda inalterado: JS emitido, módulos e dependências devem se ajustar ao host. Código apenas para DOM e bibliotecas específicas do Node permanecem restrições de plataforma.

FastTwitch: mapas ClojureScript, Fetch nativo

O FastTwitch expressa aplicações HTTP como mapas de request/response estilo Ring e funções de handler e middleware comuns. Seus adaptadores conectam esses mapas a objetos Request=/=Response nativos; seus clientes usam fetch nativo, streams de corpo e opções de requisição. Node HTTP, Deno.serve e Bun.serve diferem internamente, mas o contrato de handler voltado para a aplicação permanece consistente.

O benefício prático: reutilize roteamento, autenticação e lógica de aplicação ao mudar de runtime em vez de reconstruir a fronteira HTTP. Recursos específicos de runtime ainda exigem adaptadores e testes correspondentes. O ReScript pode se vincular às mesmas Web APIs; o FastTwitch é o exemplo em ClojureScript, não um requisito para outras linguagens.

FastTwitch Celld: de handlers HTTP para células nomeadas e com estado

Considere uma aplicação de chat: dê a cada sala sua própria célula em vez de coordenar todas as salas dentro de um processo de servidor. O FastTwitch Celld adiciona declarações de célula em ClojureScript, pontos de entrada HTTP/RPC, armazenamento, alarmes e bindings de WebSocket. O HTTP ainda usa o modelo Fetch; a plataforma roteia cada sala para sua célula nomeada, e o código da aplicação persiste o histórico de mensagens no armazenamento SQLite/KV daquela célula.

Após expulsão ou reinicialização, uma nova ativação pode reconstruir a sala a partir desse histórico armazenado. Um atom comum do ClojureScript não é automaticamente durável; nem a pilha de chamadas. Esta é uma recuperação de estado estilo Durable Objects, não snapshots de execução estilo dquickts. A extensão ainda não está pronta para lançamento.

%%{init: {"themeVariables": {"fontSize": "18px"}, "flowchart": {"htmlLabels": false, "curve": "linear", "nodeSpacing": 24, "rankSpacing": 32}}}%%
flowchart TB
  H["Aplicação ClojureScript
Handlers + middleware"]
F["FastTwitch
Adaptadores Fetch nativos"]
N["Node · Deno · Bun
Aplicação HTTP"]
X["FastTwitch Celld
Pontos de entrada de célula"]
C["Célula: sala-42
HTTP · RPC · eventos"]
S[("SQLite / KV privado
Histórico de mensagens")]
H --> F F --> N F -->|estender| X X --> C C <-- -->|"persistir / restaurar"| S classDef application fill:#f8fafc,stroke:#64748b,color:#0f172a classDef bridge fill:#eff6ff,stroke:#2563eb,color:#0f172a classDef durable fill:#ecfdf5,stroke:#059669,color:#0f172a class H,N application class F,X bridge class C,S durable

A linha condutora: runtimes compartilhados e Web APIs tornam este um modelo de aplicação comum, não uma stack de rede separada para cada linguagem ou host. Journals de fluxo de trabalho preservam o trabalho concluído, snapshots de runtime preservam o estado de execução, e objetos duráveis preservam os dados e o ciclo de vida de uma unidade endereçável. Estas são maneiras diferentes de permitir que cargas de trabalho modernas sobrevivam além de uma única requisição ou processo.