Nocter v0.53.0 Release Notes
Nocter v0.53.0 adds the standard-library foundations for structured network services. Programs can own a runtime-sized set of concurrent computations, accept bounded HTTP/1.1 requests, and transfer unique response authority without a framework or hidden task registry.
This release also separates incremental codec transformation from destination I/O. In-memory JSON generation therefore no longer acquires a blocking effect from the writer adapter that reuses the same spelling and traversal rules.
Effect-Neutral JSON Generation
Compact JSON generation now has one nonrecursive pull encoder. The encoder owns semantic traversal and exact JSON spelling, then yields borrowed byte chunks to destination-specific drivers. json.stringify collects those chunks into an owned String without becoming blocking; json.write drives the same encoder through a BlockingWriter and retains its destination effect.
The encoder keeps traversal frames instead of constructing a second semantic tree. Deep values, escaping, number spelling, object member order, allocation failure, and writer failure therefore cross one transformation authority.
Dynamic Structured Task Groups
TaskGroup<T> owns a runtime-sized Vec<future T>:
var tasks: TaskGroup<bool> = TaskGroup.empty()
tasks.add(check_first())
tasks.add(check_second())
while !tasks.is_empty() {
let completed = await tasks.next() otherwise { return false }
if !completed { return false }
}
Adding transfers ownership of a lazy computation. Awaiting next drives retained children fairly and removes one completion-order result. Cancelling a pending next ends only its borrow; destroying the group cancels every child still retained by the group. Fixed-arity task.join and task.race remain convenient structured operations over the same lifecycle model.
Asynchronous HTTP Server
std/http now provides Server, ServerConnection, IncomingRequest, Responder, and OutgoingResponse. Their ownership states permit one request and one response per accepted connection:
let connection = await server.accept_with_timeout(timeout)?
let exchange = await connection.read_request_with_timeout(timeout)?
let request = move exchange.0
let responder = move exchange.1
var response = OutgoingResponse.ok()
response.set_text_body(request.target())
await responder.respond_with_timeout(move response, timeout)?
The request decoder accepts strict HTTP/1.1 origin-form requests, requires one non-empty Host, and supports bounded fixed, chunked, and bodyless framing. It rejects conflicting framing and buffered pipelining. Response encoding owns protocol fields, final-status rules, HEAD suppression, and connection closure. Public Limits bound retained start lines, heads, fields, framing lines, and bodies.
Each timeout-bearing server operation applies one fixed deadline to the complete operation. An expired accept preserves its borrowed listener; an expired request read or response write destroys the consuming connection state. Applications own concurrent admission by retaining handler futures in a TaskGroup, so capacity cannot disagree with hidden server state.
Complete Service and Tooling
The new http-service example runs three concurrent loopback clients through a two-slot handler group. It checks two successful responses and one explicit no-response cleanup path, with finite deadlines on every network operation.
Hover, completion, declaration navigation, and implementation navigation expose the new public server typestate from the checked std/http contract. Native and installed-image tests exercise task-group completion, deadline cancellation, listener reuse, request decoding, response transfer, and complete service execution.
Compatibility and Non-goals
Existing JSON generation and HTTP client source remains valid. The new server deliberately does not add keep-alive, pipelining, protocol upgrade, CONNECT tunnels, streaming bodies, HTTP/2, HTTP/3, or an application framework. Those features require future public typestates rather than hidden state inside the bounded one-request API.
Release Qualification
Release-content commit fb75c5144b90e5ef845401394f21252e296aecdc passed the complete disposable compiler gate. Two independent optimized builds produced byte-identical archives and recursively identical installed homes. The retained archive is 9,201,893 bytes with SHA-256 3592964368201cabc821a72a96b76fcd374caeb009a49a11db28f4bb7c910424 and contains exactly 314 standard-library files. A fresh extraction passed version and installation diagnosis, locked/offline package workflows, native build and execution, every public example, the bounded HTTP service success and explicit no-response paths, framed LSP analysis, immutability checks, and compiler and standard-library tamper rejection. The published release reused this exact retained archive without rebuilding it.