Skip to content

Releases

esrun@0.47.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 11:20
2bed609

Added

  • fetch(url, { decompress: false }) (DECISIONS D210), Bun's init member:
    a compressed response body arrives as the server sent it, with
    Content-Encoding and Content-Length intact, and the request asks for
    identity unless it sets its own Accept-Encoding. For a stored object whose
    coding is part of it (an S3 object saved gzipped), a byte-exact proxy, or a
    checksum over the wire bytes.

  • runtime:websocket's serve() serves wss: with secureTransport: "on",
    cert and key, like runtime:http's serve.

  • --proxy=<url> and --no-proxy=<list> (DECISIONS D205): fetch, the
    WebSocket client and the --otel exporter go through an http:// or
    https:// forward proxy — http: targets forwarded, https:/ws:/wss:
    tunnelled with CONNECT, credentials from the URL sent as
    Proxy-Authorization and never printed. --allow-net still judges the
    target. Exceptions take domains, IPs, CIDR blocks and host:port;
    loopback always goes direct. No environment variable is read: esrun warns
    when HTTP(S)_PROXY is set. A refused tunnel is the new code
    ERR_PROXY_TUNNEL.

  • serve({ onError(error, request) }) (DECISIONS D206): answers for a
    handler that threw or returned no Response. Without it the 500's report
    now names the request: runtime:http: GET /path failed (trace …), the query
    left out.

  • --otel reports the outcome of a request: its span carries
    http.response.status_code and error.type and is ERROR for a 5xx or a
    throw, and http.server.request.duration is exported to /v1/metrics —
    counted before sampling. A request span used to close OK whatever happened.

  • The WebSocket client has the 30s connect deadline fetch has, and its
    connect errors carry codes (ERR_DNS, ERR_CONNECTION_REFUSED, ERR_TLS,
    ERR_TIMED_OUT).

  • runtime:pdf sanitize profiles and categories (otf-pdf 0.2):
    sanitize({ profile: "basic" | "privacy" | "strict" | "all" }), and
    comments, hiddenAnnotations, thumbnails, navigation, webCapture,
    structure, hiddenLayers and invisibleText, each counted by
    sanitizeReport. Hidden layers and invisible text are cut from page
    content without moving the visible text. The defaults are unchanged.

Changed

  • The minimum supported Rust version is 1.97, up from 1.96: oxc 0.157, which
    esdev builds with, requires it.

  • runtime:serialization moves to quick-xml 0.42 and toml 1.2. XML input
    nested more than 65,535 elements deep is refused instead of overflowing the
    parser's depth counter; output is unchanged (the writer is our own). esdev's
    HTML splicing moves to html5gum 0.9.

  • brotli 9 (with async-compression 0.4.52) and base64 0.23: one copy of
    each in the binary, matching what bcrypt and the bundler already use. No
    change in behaviour.

  • runtime:websocket moves to tungstenite 0.30: a server refuses a
    handshake whose Sec-WebSocket-Key is not a valid 16-byte key, as RFC 6455
    requires, and large messages read faster. The last copy of the older
    digest crates leaves the binary with it.

  • Password hashing moves to argon2 0.6 and scrypt 0.12, on the same
    digest generation as the rest of the crypto stack, so the older sha2,
    hmac, pbkdf2 and blake2 copies leave the binary. Hashes are written in
    the same PHC format and verify in both directions across the upgrade.

Fixed

  • On Windows, the warning that a proxy variable is ignored names it once:
    HTTP_PROXY and http_proxy are one variable there, and the warning listed
    both.

  • runtime:http keeps a handler's Content-Length on the answer to a HEAD,
    so new Response(null, { headers: { "content-length": "1234" } }) tells the
    client the size a GET would return (RFC 9110 §9.3.2), as Deno does. It was
    dropped, leaving no length at all. Only a plain decimal is kept, and only for
    HEAD; every other response is still framed from its body.

  • fetch with a Range header sends Accept-Encoding: identity, as the Fetch
    standard says (D210), instead of leaving the header out — which accepted any
    coding, so a server could compress the range into a body that cannot be
    decoded.

  • runtime:pdf: a page whose content stream exceeds maxDecodedSize fails
    with ERR_PDF_LIMIT instead of reading as an empty page, and pageText
    reuses the fonts an earlier call loaded on the same document (otf-pdf 0.2).

  • A response the server cut short fails fetch with ERR_CONNECTION_RESET
    and "the connection closed before the response completed", instead of the
    HTTP client's chain of wrappers.

Removed

  • esrun upgrade (DECISIONS D204). A deployment pins its esrun in its
    image or install step; on a development machine, esdev upgrade now
    updates esrun and esdev together. esrun upgrade exits with an error that
    names both routes. To upgrade an esrun-only machine, re-run the installer
    with ESRUN_VERSION=<version>.

Security

  • rustix 1.1.6, for RUSTSEC-2026-0486 (GHSA-jh2m-fhpj-3vr7): its xattr
    functions marked unwritten bytes as initialized. Neither binary calls them.
  • copy can no longer be pointed outside the root jail. It checked both
    paths when called and opened them by name when it ran, so a copy issued
    with a rename of its directory and a symlink out of the jail in the same
    tick read a file outside the root, or wrote one there. Both ends are now
    pinned by descriptor at the call, like every other mutation. stat,
    exists, readDir, glob scans and makeTempFile/makeTempDir are pinned
    the same way, which closes the same window for threads racing each other.
  • No op hands a worker's entry source back to JavaScript. Starting a worker
    was two ops, and the first, reachable from guest code through __ops,
    returned the entry's text: under --allow-imports alone it read any text
    file in the project, .env included, whatever --allow-read said. The
    spawn now reads the entry itself. A worker refused for want of workers
    says that starting it is what needs the grant.
  • An import loads only a module file (DECISIONS D209). Any other file —
    .env, config.yaml, notes.txt, an extensionless name — is refused
    with the new code ERR_UNKNOWN_FILE_EXTENSION before it is read. The
    loader used to run whatever a specifier named as JavaScript: a YAML mapping
    is a valid script, and a trap on the global object collected every value in
    it, so --allow-imports read project files that --allow-read did not
    grant. A module file is .js, .mjs, .cjs, .json (with
    type: "json") or .wasm; the file you run, and import.meta.resolve(),
    are unaffected.
  • --allow-run=<program> no longer admits every name of a multi-call
    binary.
    Entries were matched on the real file alone, and on Alpine ls
    and sh are both links to busybox — so --allow-run=ls ran sh -c …. The
    name a program is invoked by must now match as well as the file.
  • crypto.subtle RSA is constant-time (DECISIONS D203). It moves to
    RustCrypto rsa 0.10.0-rc.20, which fixes RUSTSEC-2023-0071 ("Marvin"):
    constant-time private-key arithmetic, blinded signing and decryption, and
    implicit rejection. All RSA randomness still comes from the injected
    entropy provider. RSA-OAEP labels may now be any bytes; generateKey
    refuses a modulus outside 1024–8192 bits, and importKey refuses an RSA
    key under 1024 bits with a DataError. Checked against OpenSSL-generated
    keys, signatures and ciphertexts in both directions.

esdev@0.27.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 11:17
2bed609

Added

  • A plugins entry may be several plugins (DECISIONS D214). An export, or
    what its factory returns, may be a list of plugins, and the list takes the
    entry's place in order, so a package made of several plugins needs one
    esdev.json entry. Nested lists flatten and null/false are skipped.
    runtime:build's build({ plugins }) reads its list the same way and also
    awaits promises, as Rollup does. A 0 or "" is refused rather than
    skipped. This used to fail with plugin-0: unknown hook "0".

  • The fullstack template's server is the generated AFW handler
    (@afw/server): API routes, loaders, middleware, server functions and
    server-rendered pages all come through one createHandler, so a new route
    needs no edit to server.js. It ships a server-function sample
    (app/todos/todos.server.js) that the todos page calls and its loader calls
    directly. In TypeScript the sample is typed; that needs
    @opentf/esdev-plugin-afw with its .tsx type fix (after 0.7.0).

  • dev.servers (DECISIONS D208): esdev start runs several servers —
    each with its own target, port, env, args, started after the ones
    it names — restarts only those whose output changed, and shows each one's
    errors on the page under its name. dev.server.proxy may name a server,
    making esdev the front door: one address for the page, the update channel
    and every server, with the page's Host kept. dev.run is unchanged.

  • Server errors in the overlay (DECISIONS D207). When the server
    esdev start runs fails a request or crashes, open pages show it under
    Server error, with the request's method, path and trace id.

  • esdev start and its server are linked: a restart drains in-flight
    requests on every platform, Windows included (it used to kill the server
    there), the loop learns the server is listening when serve() binds, and a
    server whose esdev start is killed exits instead of holding its port.

  • An outbound proxy (DECISIONS D205). fetch, the WebSocket client
    and the --otel exporter — in the programs esdev runs, esdev start's
    server, esdev create's registry lookups and esdev upgrade — follow
    HTTPS_PROXY/HTTP_PROXY/NO_PROXY (lowercase first), as npm and git do.
    --proxy=<url> and --no-proxy=<list> override them; --no-proxy=* turns
    the proxy off.

Changed

  • esdev create and esdev init ask in Clack's layout and colours, as
    create-afw does (@clack/prompts 1.8): a gray gutter joining the questions
    under a cyan title badge, the question being asked in cyan with a
    ↑/↓ to navigate • Enter: confirm • Esc: back footer, ●/○ options with
    the hint beside the focused one, an inline ● Yes / ○ No for yes/no
    questions, and a text input with its default as the placeholder. Outcomes
    are marked with ticks, crosses and warning signs: a green ✔ on each answer
    and on created, a red ✖ and struck-through answer on a cancel, a gray –
    on a skipped question and a yellow ⚠ on a registry retry. The keys stay as
    they were (Esc steps back, a digit jumps). Esc on the install question now
    reads Skipped; it still installs nothing.

  • esdev create --template=docs leaves the blog out unless asked, as
    create-afw does: the question is now "Add a blog?" and starts on No, and an
    unattended run without --blog writes docs only. --blog adds app/blog/
    with a sample post, a Blog link, and RSS and Atom feeds; --no-blog is
    still accepted.

  • esdev upgrade also updates the esrun installed beside it
    (DECISIONS D204), each to its own newest release, checked against its
    checksums.txt. --only=esdev or --only=esrun (install.sh's spelling)
    limits it to one, and --dry-run reports on both. An esrun elsewhere on
    PATH is left alone. This replaces esrun upgrade, which is removed.

  • The bundler moves to rolldown 1.3.1 and oxc 0.157. Among rolldown's
    fixes since 1.2.3: a use-after-free in async transforms that read a
    tsconfig.json, side effects dropped by tree-shaking (through import
    cycles, after exports.x writes, and when an exported constant was inlined),
    several renaming collisions, chunk execution order, and module namespace
    objects that are now null-prototype and non-extensible. oxc fixes TypeScript
    enums exported across namespace blocks and the lowering of private class
    members and using. The React Compiler pass stays on the oxc it targets.

  • A library build names a non-JavaScript module's output after the whole
    file
    : button.module.css becomes button.module.css.js (was
    button.module.js), and data.json becomes data.json.js, as Rollup and
    Vite library builds name them. A module can no longer collide with a
    button.module.js beside it. The imports, requires and declarations the
    build writes name the new files; a package exports map that names one of
    the old paths needs updating.

Fixed

  • esdev upgrade tries a download again when the network fails it (a
    dropped connection, a truncated or stalled body, a 5xx or 429), up to
    three attempts in all, and shows a progress bar for the archive when
    stderr is a terminal.

Security

  • An import loads only what esdev turns into a module (DECISIONS D209),
    as under esrun: module files, the TypeScript and JSX esdev compiles,
    .css, and whatever a plugin's transform filter admits. Anything else is
    refused with ERR_UNKNOWN_FILE_EXTENSION before it is read — it used to
    run as JavaScript. A plugin with no id filter on its transform still
    claims every file.
  • A web page can no longer attach to --inspect. The debugger answered
    any Host and any Origin, so a page using DNS rebinding could read
    /json/list, attach and run code in the process — Node's CVE-2018-7160.
    It now answers only to localhost or an IP address, refuses a WebSocket
    upgrade carrying any Origin but Chrome DevTools' (devtools://devtools),
    and its session id comes from the OS random source rather than the clock.

@opentf/esrun-types@0.22.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 11:04
2bed609

Added

  • secureTransport, cert and key on runtime:websocket's serve().
  • decompress on RequestInit (D210): false returns a compressed response
    body as the server sent it.

@opentf/esrun-s3@0.1.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 10:50
2bed609

Added

  • The @opentf/esrun-s3 package (DECISIONS D211): an S3 client in JavaScript
    over fetch, signed with SigV4, for AWS S3, Cloudflare R2, Google Cloud
    Storage and S3-compatible stores. Bun's S3Client/S3File surface —
    file(), text(), json(), bytes(), stream(), slice() ranges,
    write(), writer(), exists(), stat(), delete(), presign(),
    list() — plus listAll(), copy() and deleteMany(). Multipart uploads
    for large blobs and streams with bounded memory, aborted on failure;
    conditional reads and writes; CRC32, CRC32C, SHA-1 and SHA-256 checksums;
    SSE-S3, SSE-KMS and SSE-C (HTTPS only); retries with backoff and clock-skew
    correction; refreshing credential providers; S3Client.fromEnv() reading
    Bun's S3_* names and the AWS ones. Objects are read byte for byte,
    Content-Encoding included (decompress: false, D210), so it needs esrun
    0.47.0 or later.
  • Credential providers (DECISIONS D212): instanceMetadata() (IMDSv2 only),
    containerCredentials() (ECS task roles, EKS Pod Identity), webIdentity()
    (STS AssumeRoleWithWebIdentity: EKS IRSA, GitHub Actions OIDC) and
    assumeRole(), each refreshed before its credentials expire.
  • Reads resume where a dropped connection left them: the rest is requested
    with Range and If-Match on the first answer's ETag, up to retry times,
    so a replaced object fails the read instead of splicing two versions.
  • copy() copies sources of any size inside S3: when one CopyObject is
    refused for a source over 5 GiB, it copies the source in ranges
    (UploadPartCopy), pinned to its version or checked against its ETag
    (DECISIONS D213).

esrun@0.46.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 10:03
187e3e5

Security

  • Mutual TLS (DECISIONS D201). serve and listen take requestCert,
    ca and crl to require client certificates, always verified — there is
    no rejectUnauthorized. The handler's info.peerCertificate and an
    accepted socket's opened.peerCertificate carry the verified certificate
    (subject, subjectaltname, fingerprint256, raw, validFrom,
    validTo). Clients present one with cert/key on runtime:net
    connect, sslCert/sslKey on the PostgreSQL and MySQL drivers,
    cert/key or tlsCertificateKeyFile on MongoDB (with MONGODB-X509
    authentication), and Bun's tls: { ca, cert, key } on fetch, which gets a
    connection pool of its own.

  • Certificate rotation (D200): server.setSecureContext({ cert, key }) on
    a TLS serve server or listen listener replaces its credentials for new
    connections without dropping open ones, and refuses a key that does not
    belong to the certificate.

  • Revocation (D202): --tls-crl=<file> checks every TLS client's servers
    against operator-supplied CRLs, and crl does the same for client
    certificates. A CRL past its nextUpdate refuses what it covers; nothing is
    fetched over the network.

  • TLS prefers the hybrid post-quantum key exchange X25519MLKEM768 in every
    client and server (DECISIONS D199). The build had turned rustls's
    preference off, so handshakes used plain X25519 first.

  • esrun upgrade verifies what it installs (DECISIONS D198). It replaced
    the running binary with the downloaded archive without checking it, though
    the CLI reference said it did. It now refuses a release that publishes no
    checksums.txt, or whose archive's SHA-256 does not match it, and leaves the
    installed binary untouched. The new binary is written beside the old one and
    renamed into place, so an interrupted upgrade never leaves half a binary.
    Drafts and pre-releases are no longer offered, and there is no progress bar.

  • fetch runs on the runtime's own HTTP client (D198): hyper's framing
    and hyper-util's connection pool, with redirects, content codings and
    deadlines written to their specifications. Following a redirect now drops
    the body's Content-* headers when the method becomes GET, rejects rather
    than resending a streamed body, and drops Cookie2 and WWW-Authenticate
    along with Authorization, Cookie and Proxy-Authorization on a hop to
    another origin. A decoded body arrives at most 64 KiB at a time.
    TCP_NODELAY is on. reqwest, self_update and rustls-platform-verifier
    are gone, taking security-framework, core-foundation and the system
    proxy readers with them: 25 to 26 fewer crates on each platform.

  • Every TLS client verifies against one trust store (DECISIONS D197).
    fetch and the --otel exporter checked the operating system's store
    while runtime:net, the database drivers and WebSocket checked the
    bundled Mozilla roots, so a CA one API trusted another refused, and
    SSL_CERT_FILE changed whom fetch alone trusted. All of them now use the
    bundled Mozilla roots, or exactly what the new --tls-ca lists. A private
    CA installed only in the operating system is no longer trusted by fetch:
    name it with --tls-ca=bundled --tls-ca=<file>.

  • --tls-ca=<file|bundled> (repeatable) sets the process trust store. A file
    that cannot be read, holds no certificate, holds a private key or any other
    non-certificate block, or has a certificate outside its validity period stops
    startup, naming the file but never quoting it. No environment variable
    changes trust; esrun warns when NODE_EXTRA_CA_CERTS, NODE_USE_SYSTEM_CA,
    DENO_CERT or DENO_TLS_CA_STORE is set.

  • fetch and the --otel exporter no longer read HTTP_PROXY,
    HTTPS_PROXY, ALL_PROXY or the operating system's proxy settings. The
    HTTP client enabled them by default, so an inherited environment variable
    could route every outbound request through a host the deployment never
    named. Requests now go to the host in the URL.

  • The bundled TLS root certificates (webpki-roots 1.0.11) no longer trust
    "emSign Root CA - C1" or "emSign ECC Root CA - C3". Mozilla removed them
    after the October 2026 compromise of eight eMudhra intermediate CA keys;
    "emSign ECC Root CA - G3", the root that signed those intermediates, was
    already dropped in 1.0.10. Applies to every TLS client.

Fixed

  • install.sh supports the fish shell: asked to add ES Runtime to PATH, it
    writes ~/.config/fish/conf.d/es-runtime.fish (honouring
    XDG_CONFIG_HOME) with fish_add_path, instead of appending a POSIX
    export line to ~/.bashrc that fish never reads. The manual instruction
    is printed in fish syntax too.

Changed

  • The site footer centers a visibly external Foundation policies link without
    changing the AFW badge's position.

  • The footer states that the site uses no trackers or analytics.

  • Footer text colors meet WCAG AA contrast against the dark footer background.

  • The AFW footer badge icon uses black strokes.

  • Update the website AFW packages; adapt benchmark components to the current
    compiler's single final view return requirement.

  • The site's benchmark data is re-measured with esrun 0.45.0 (esdev 0.24.0,
    node 26.13.0, bun 1.4.4, deno 2.9.7, llrt 0.9.0-beta, PostgreSQL 18.6) — the
    previous full run published 0.43.0 against node 24 and PostgreSQL 16 — and
    now includes the jsonc_large and json5_large rows.
    website/src/versions.js is regenerated to match. Eleven esrun rows are more
    than 10% faster; crypto_asym (ECDSA P-256 sign + verify) is 55% slower,
    the cost of the move to the 0.14 NIST curves for RUSTSEC-2026-0387.

  • Benchmark charts expose row and column relationships to assistive technology;
    animated benchmarks can be paused from the keyboard, and selector controls
    announce their pressed state.

  • The durable-worker diagram keeps commit and restart markers visible without
    motion and improves the storage-link contrast.

esdev@0.26.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 10:01
187e3e5

Security

  • --tls-crl=<file> (DECISIONS D202) is taken wherever --tls-ca is, and
    esdev start passes it to its server. Programs run by esdev get client
    certificates (D201) and setSecureContext (D200) as under esrun.

  • esdev upgrade refuses a release whose archive is not listed in, or does
    not match, its checksums.txt (DECISIONS D198); it used to install the
    download unchecked. esdev create's npm registry lookups and the program's
    fetch use the runtime's own HTTP client, as under esrun.

  • --tls-ca=<file|bundled> (DECISIONS D197) is taken by every command that
    runs a program, and by esdev start and esdev preview, which use it for
    the server they start and for an https dev.server.proxy target. As under
    esrun, the program's fetch now verifies against the bundled roots or what
    --tls-ca lists, not the operating system's store.

  • A program run by esdev no longer sends fetch requests or --otel spans
    through a proxy named by HTTP_PROXY, HTTPS_PROXY or ALL_PROXY, the
    same as under esrun.

  • esdev's dev proxy no longer trusts the eMudhra roots Mozilla removed after
    the October 2026 intermediate CA key compromise ("emSign Root CA - C1",
    "emSign ECC Root CA - C3"; webpki-roots 1.0.11).

Fixed

  • esdev test --browser resolves runtime:test again. The 0.25 alias plugin
    treated every URL-shaped specifier as a path and skipped it, so
    runtime:test — which parses as scheme runtime — was left as an external
    import the browser could not load. An exact alias match now always applies;
    only a prefix match on a path or URL is skipped.

@opentf/esrun-types@0.21.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 09:36
187e3e5

Added

  • Mutual TLS, rotation and revocation (D200–D202): runtime:net's
    PeerCertificate, SecureContext and Pem; requestCert, ca and crl
    on listen and serve; cert/key on connect; setSecureContext on
    Listener and Server; peerCertificate on SocketInfo and the serve
    handler's info; tls on RequestInit; sslCert/sslKey and MongoDB's
    cert, key, tlsCertificateKeyFile and MONGODB-X509.

Changed

  • runtime:net connect's ca says it adds to the process trust store, the
    bundled roots or what --tls-ca lists (D197).

esdev@0.25.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 16:08
f4dd116

Fixed

  • One resolution rule for every importer (D196). A module takes the
    tsconfig.json nearest to it — or the reference of it that covers the
    file — whatever its extension: a .mdx page a plugin compiles gets the
    project's paths, where it got none, and a .jsx file in a project without
    allowJs no longer takes a parent folder's paths, jsx and decorator
    settings. A project nested in a workspace could bundle two copies of one
    package. esdev.json's resolve.alias now applies before paths in a bundle,
    as it already did in esdev test and esdev <file>, and an alias that names
    nothing fails the build instead of resolving the specifier as written.
    esdev builds with a vendored oxc_resolver carrying this rule
    (crates/vendor/oxc_resolver/PATCHES.md).

esrun@0.45.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 22:44
e246ebc

Removed

  • esrun --import-policy (D194). The runtime loader judged modules by name and
    could not pin what they contained, and a built program — the way esrun runs
    in production — has its packages inlined, so the loader never saw them.
    Which packages ship is now decided only at build time, by esdev's dependency
    policy; a bundle runs without --allow-imports, so nothing else can load.
    --allow-imports=<list> now says so. import.meta.resolve and the loader are
    otherwise unchanged.

Changed

  • On Windows, environment variable names are case-insensitive, as Windows
    makes them (D195). env.PATH, env.path and "PATH" in env reach the
    variable the OS spells Path, and a write or delete in any case changes
    that one variable; --allow-env=PATH grants it; an --env-file entry or a
    RUNTIME_ENV default replaces or keeps it instead of adding a second
    variable beside it; and runtime:system's env over inheritEnv sets or
    removes it, where { PATH: undefined } had let the inherited Path through
    to the child. On Linux and macOS names stay exact.

  • BSON.decode builds documents and the BSON class instances in V8's fast
    mode, as JSON.parse builds objects, where it used to build dictionaries
    (D187). Documents with the same fields share a shape, so reading them is
    several times faster, a decode holds less heap and costs less to collect,
    and a typical collection decodes faster overall; a document of only plain
    fields decodes somewhat slower. bench/bson/decode.mjs measures it. The
    MongoDB driver's results come through the same decoder.

Added

  • environment() in runtime:process: the deployment environment, read at
    run time — RUNTIME_ENV, else NODE_ENV, else "production" (D192).
    RUNTIME_ENV is proposed as one name for every runtime and language, for
    the question NODE_ENV has been made to answer alongside the kind of
    build; it needs --allow-env=RUNTIME_ENV,NODE_ENV and nothing more.
    process.env.NODE_ENV in bundles is unchanged.
  • JSONC and JSON5 in runtime:serialization (D191), each mirroring the
    global JSON with parse(text, reviver?), stringify(value, replacer?, space?) and validate(text, opts?). JSONC reads JSON with comments and
    trailing commas, as tsconfig.json and VS Code's settings are written;
    JSON5 reads JSON5 1.0.0. Both give back exactly what JSON.parse builds
    for the JSON they share, built by the engine straight from the text.
    JSONC.stringify is JSON.stringify; JSON5.stringify writes JSON5 as the
    json5 package does, with toJSON5() and a quote option. Errors name the
    dialect and end at line L column C.
  • JSONC.edit(text, path, value) and JSON5.edit(text, path, value) change
    one value in a config file and keep every other byte: comments, blank
    lines, key order and quotes (D191). A missing key is added in the style of
    its siblings, missing objects on the path are created, and undefined
    removes the member with its comma and its line's comment. A json_edit
    fuzz target checks that every edit means exactly the change asked for.
  • es-runtime-json, the reader behind the coming JSONC and JSON5 in
    runtime:serialization (D191). JSONC is JSON with // and /* */
    comments and one trailing comma; JSON5 is JSON5 1.0.0 in full. One lexer
    reads both and reports each value to a sink, so the engine can build values
    with no intermediate tree. Nesting stops at 256 levels, and errors give the
    line and column. Every case of JSONTestSuite and json5-tests runs in
    cargo test, and a json fuzz target checks that whatever JSONC reads,
    JSON5 reads the same way.

Fixed

  • A PostgreSQL cancel() — direct, or from an aborted signal — returns only
    once the server has acted on it, as libpq's PQcancel does. It returned as
    soon as the request was sent, so a cancel that arrived after its query had
    finished, or one made with nothing running, cancelled the next query
    instead: 297 of 300 queries sent straight after an idle cancel() were.

Testing / CI

  • bench/run.sh gains jsonc_large and json5_large: each runtime parses a
    5,000-record document with what it ships — runtime:serialization, Bun's
    Bun.JSONC and Bun.JSON5, Deno's @std/jsonc, and for Node (and Deno's
    JSON5) the jsonc-parser and json5 packages — and must read it the same.
  • scripts/fs-discipline.sh lists the DNS resolver's read of
    /etc/resolv.conf and the MongoDB specification tests' reads, which had
    left the check failing.
  • The sharded hibernating-room test waits for both clients to receive the
    last broadcast before printing, where it waited for one and failed when the
    other's copy was still in flight.
  • The MongoDB job waits for its servers by asking each from inside its own
    container, for up to three minutes, and prints the server's log when one
    never answers. It started a mongosh container per probe and gave up after
    two minutes without saying why.
  • The MongoDB 8 leg of the driver job checks the runner's kernel first.
    MongoDB 8 refuses to start on Linux 6.19 through 7.0.13, no MongoDB release
    fixes it, and GitHub's runners are on such kernels, so there the leg posts a
    warning and stops; MongoDB 7 runs every driver test, and the leg resumes by
    itself on a kernel from 7.0.14.
  • The release leaves es-runtime-json unpublished, as it does the other
    internal crates; it had tried to publish it to crates.io and failed.
  • The benchmark's comparison dependencies move to Hono 4.13 and
    puppeteer-core 25.16, past advisories in Hono's serveStatic and in the zod
    that puppeteer pulls in; pnpm audit is clean. Nothing in them ships.
  • The fuzz build turns on std for branches, which turso_core brings in
    without it. On nightly that crate then calls core::intrinsics::abort,
    which nightly removed on 2026-10-27, so every fuzz run had failed to build.

esdev@0.24.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 22:43
e246ebc

Removed

  • esdev <file> --import-policy (D194), with esrun's: a dependency policy is
    applied by esdev build, which names it in esdev.json.

Added

  • esdev build applies the import policy esdev.json names as
    build.importPolicy to what every build ships (D193): each module read into
    a bundle, for every target and the dev loop's builds. A deployed bundle
    inlines its packages, so the runtime loader never saw them and a policy
    governed nothing that shipped. A refusal lists every refused package and
    writes nothing; "unlisted": "warn" reports instead; build tools whose code
    does not ship are never judged.
  • An import policy's allow entries can pin a package:
    { "package": "zod", "version": "3.23.8", "integrity": "h1:…", "reason": "…" }.
    esdev build checks each shipped copy's installed version and, where
    integrity is given, Go's h1: hash of its installed files; a mismatch
    fails the build in every mode (D193).
  • esdev build --inventory=<file> writes what the build shipped as a draft
    import policy — each package's version and h1: hash, an empty reason,
    and the project's own directories — for a security team to review and
    commit. The configured policy is still applied, and the draft is written
    even when it refuses; the reviewed file itself is never overwritten.
  • build.subresourceIntegrity adds integrity="sha384-…" to every
    <script> and stylesheet, preload or modulepreload <link> a release
    HTML build writes, so a browser refuses a file that changed after the build.
    Off by default, as a CDN that rewrites files breaks it.

Changed

  • The programs esdev runs get RUNTIME_ENV when the shell has not set it
    (D192): development for esdev <file> and the server esdev start runs,
    test under esdev test, so environment() in runtime:process says
    where code is running. esdev build still defines only
    process.env.NODE_ENV, which describes the build.
  • esdev --install-types adds its compilerOptions.types entry to a
    tsconfig.json with comments or trailing commas, keeping both and the
    file's layout, where it used to leave such a file alone and print the lines
    to add (D191). A file that is not valid JSONC is still left alone, and says
    so.
  • The spa, fullstack, docs and library starters follow the framework's
    rename from OTF Web to AFW: they depend on
    @opentf/esdev-plugin-afw, @opentf/afw, @opentf/afw-docs,
    @opentf/afw-cli and @opentf/afw-test, the docs config is
    afw.config.js, routes come from @afw/routes, and TypeScript projects get
    afw-env.d.ts. The menu group reads "AFW". Projects scaffolded earlier keep
    working on the @opentf/web* packages they were pinned to.

Removed

  • esdev build and esdev start no longer recognise a project built by the
    retired otfw CLI (an otfw.config.js, or scripts calling otfw) to point
    at its own toolchain. AFW projects are esdev projects with an esdev.json;
    a directory without one gets the ordinary missing-esdev.json error.

Fixed

  • esdev build --minify keeps the space after a closing ] or ) in a
    selector, which is the descendant combinator: [data-theme="dark"] * was
    written as [data-theme="dark"]*, which matches the themed element alone.
    Every Tailwind dark: utility built on a class or attribute variant matched
    only <html>, so a minified site's dark mode left its own components light.
    [open] .x and :not(.a) .b lost their space the same way.

  • The docs starter builds its search index: its config named an unknown
    search provider (otf), so the build skipped the index and the navbar
    search had nothing to query. The fullstack starter's sample middleware
    sends x-powered-by: afw.

  • The docs starter's root layout uses the theme's afwd-shell classes, so
    the navbar, page and footer stack as the theme lays them out.

Testing / CI

  • The hot-replacement tests attach to the browser's page rather than to the
    first target its debugger lists. Newer Chrome lists its own UI and its
    extensions' service workers first, so every test waited on a page that was
    not there and failed on the Ubuntu 26.04 runner. The browser's profile is
    now kept outside the watched project, where each write the browser made to
    it was a rebuild and a reload the test had not asked for.