github cloudflare/miniflare v2.0.0-next.1

latest releases: v2.14.2, v3.20231025.0, v3.20231023.0...
pre-release2 years ago

Miniflare 2 has been completely redesigned from version 1 with 3 primary design goals:

  1. 📚 Modular: Miniflare 2 splits Workers components (KV, Durable Objects, etc.) into separate packages (@miniflare/kv, @miniflare/durable-objects, etc.) that you can import on their own for testing.
  2. Lightweight: Miniflare 1 included 122 third-party packages with a total install size of 88.3MB. Miniflare 2 reduces this to 24 packages and 11.5MB 🤯. This can probably be reduced further too.
  3. Correct: Miniflare 2 more accurately replicates the quirks and thrown errors of the real Workers runtime, so you'll know before you deploy if things are going to break.

The docs will be updated over the next few weeks.

Notable Changes

  • ✳️ Node.js 16.7.0 is now the minimum required version
  • 🎲 Added support for crypto.randomUUID()
  • 🔐 Added support for the NODE-ED25519 algorithm
  • 📅 Added support for compatibility dates and flags: durable_object_fetch_requires_full_url, fetch_refuses_unknown_protocols, formdata_parser_supports_files
  • 📚 Added a proper CommonJS module loader
  • 🚪 Added Durable Object input and output gates, and write coalescing
  • 🛑 Added the DurableObjectState#blockConcurrencyWhile(callback) method
  • ⚡️ Added a live reload feature (--live-reload) that automatically refreshes your browser when your worker reloads
  • 🗺 Automatically fetch the incoming Request#cf object from a trusted Cloudflare endpoint
  • ✉️ Added support for sending/receiving binary WebSocket messages

Breaking Changes

  • Node.js 16.7.0 is now the minimum required version. You should use the latest Node.js version if possible, as Cloudflare Workers use a very up-to-date version of V8. Consider using a Node.js version manager such as https://volta.sh/ or https://github.com/nvm-sh/nvm.

  • Changed the storage format for Durable Objects and cached responses. If you're using file-system or Redis storage, you'll need to delete these directories/namespaces.

  • Changed the Durable Object ID format to include a hash of the object name. Durable Object IDs generated in Miniflare 1 cannot be used with Miniflare 2.

  • Removed support for the Durable Object script_name option. This was implemented incorrectly in Miniflare 1. It will be re-implemented correctly soon.

  • Removed the non-standard DurableObjectStub#storage() method. To access Durable Object storage outside a worker, use the new Miniflare#getDurableObjectStorage(id) method, passing a DurableObjectId obtained from a stub.

  • Renamed the --disable-cache/disableCache: true option to --no-cache/cache: false

  • Renamed the --disable-updater option to --no-update-check

  • When using the API, wrangler.toml, package.json and .env are now no longer automatically loaded from their default locations. To re-enable this behaviour, set these options to true:

    const mf = new Miniflare({
      wranglerConfigPath: true,
      packagePath: true,
      envPath: true,
    });
  • Replaced the ConsoleLog class with the Log class from @miniflare/shared. You can construct this with a member from the LogLevel enum to control how much information is logged to the console:

    import { Miniflare } from "miniflare";
    import { Log, LogLevel } from "@miniflare/shared";
    
    const mf = new Miniflare({
      log: new Log(LogLevel.DEBUG),
    });
  • Load WASM bindings from the standard wasm_modules wrangler.toml key instead of miniflare.wasm_bindings.

    # wrangler.toml
    [miniflare]
    wasm_bindings = [
      { name = "MODULE1", path="module1.wasm" },
      { name = "MODULE2", path="module2.wasm" }
    ]

    ...should now be...

    # wrangler.toml
    [wasm_modules]
    MODULE1 = "module1.wasm"
    MODULE2 = "module2.wasm"
  • Replaced the Miniflare#reloadOptions() method with the Miniflare#reload() and Miniflare#setOptions({ ... }) methods. reload() will reload options from wrangler.toml (useful if not watching), and setOptions() accepts the same options object as the new Miniflare constructor, applies those options, then reloads the worker.

  • Miniflare#createServer() now always returns a Promise which you must await to get a http.Server/https.Server instance. You may want to check out the new Miniflare#startServer() method which automatically starts a server using the configured host and port.

  • Miniflare no longer includes CommonJS exports. You must use ES modules. If you need to import miniflare in a CommonJS file, use dynamic import: const { Miniflare } = await import("miniflare").

  • Redis support is no longer included by default. If you're persisting KV, Durable Objects or cached responses in Redis, you must install the @miniflare/storage-redis optional peer dependency.

  • Replaced how Miniflare sanitises file paths for file-system storage so namespace separators (/, \, : and |) now create new directories.

Features and Fixes

Cache:

  • Added support for cf.cacheKey, cf.cacheTtl and cf.cacheTtlByStatus on Request. Closes issue #37, thanks @cdloh.
  • Added the CF-Cache-Status: HIT header to matched Responses
  • Log warning when trying to use cache with workers_dev = true in wrangler.toml. Cache operations are a no-op on workers.dev subdomains.
  • Throw errors when trying to cache Web Socket, non-GET, 206 Partial Content, or Vary: * responses
  • Throw an error when trying to open a cache with a name longer than 1024 characters

CLI:

  • Separated command line options into sections
  • Validate types of all command line options

Builds:

  • When running your worker's build script, Miniflare will set the environment variable MINIFLARE=1. Closes issue #65, thanks @maraisr.
  • Added an alias, -B, for the --build-command option
  • Multiple build watch paths can now be specified. If any of them change, your worker will rebuild and reload.
  • Fixed an issue where workers would not rebuild if the build watch path started with ./. Closes issue #53, thanks @janat08.

Standards:

  • Added support for crypto.randomUUID()
  • Added support for the NODE-ED25519 algorithm to crypto.subtle.sign() and crypto.subtle.verify()
  • Throw an error when attempting to create a new TextDecoder with a non-UTF-8 encoding
  • Throw errors when attempting to use FetchEvent/ScheduledEvent methods with incorrectly bound this
  • Throw errors when attempting to call respondWith() twice, or after the fetch handler has finished executing synchronously. Closes issue #63, thanks @Kikobeats.
  • Added support for the unhandledrejection and rejectionhandled events
  • Throw an error (with a suggested fix) when trying to access an env binding globally in modules mode
  • Throw errors when trying to use addEventListener(), removeEventListener() and dispatchEvent() globals in modules mode
  • Split the FetchError: No fetch handler responded and unable to proxy request to upstream? error into more specific errors with suggested fixes
  • Added the non-standard Headers#getAll() method. This can only be used with the Set-Cookie header.
  • Switch to a more spec-compliant fetch implementation, and get crypto, EventTarget and Web Streams from Node.js. Closes issues #56 and #59, thanks @jasnell, @jonathannorris and @SupremeTechnopriest.
  • Throw an error when attempting to construct a WebSocket response with a status other than 101
  • Throw an error when attempting to clone a WebSocket response
  • Added support for the non-standard ReadableStreamBYOBReader#readAtLeast(size, buffer) method
  • Include File in the Miniflare sandbox. Closes issue #66, thanks @tranzium.

Bindings:

  • Added --global KEY=VALUE/globals: { KEY: "value" } option for binding arbitrary values to the global scope. This behaves exactly like the --binding/bindings: { ... } option, but always binds to the global scope, even in modules mode.
  • Added a new global variable MINIFLARE to the Miniflare sandbox, which will always have the value true when your script is running within Miniflare
  • Miniflare now stringifies all environment variables from wrangler.toml. Closes issue #50, thanks @ozburo.

Core:

  • Added support for compatibility dates and flags, specifically the flags durable_object_fetch_requires_full_url, fetch_refuses_unknown_protocols, formdata_parser_supports_files are now supported. This feature is exposed under the --compat-date and --compat-flag CLI options, in addition to the standard keys in wrangler.toml. Closes issue #48, thanks @PaganMuffin.

  • Added a proper CommonJS module loader. Workers built with Webpack will be more likely to work with Miniflare now. Closes issue #44, thanks @TimTinkers.

  • Incoming request headers are now immutable. Closes issue #36, thanks @grahamlyons.

  • Disabled dynamic WebAssembly compilation in the Miniflare sandbox

  • Fixed instanceof on primitives such as Object, Array, Promise, etc. from outside the Miniflare sandbox. This makes it much easier to run Rust workers in Miniflare, as wasm-bindgen frequently generates this code.

  • Added a new --verbose/verbose: true option that enables verbose logging with more debugging information

  • Throw a more helpful error with suggested fixes when Miniflare can't find your worker's script

  • Only rebuild parts of the sandbox that need to change when options are updated

  • Added a new reload event to Miniflare instances that is dispatched whenever the worker reloads:

    const mf = new Miniflare({ ... });
    mf.addEventListener("reload", (event) => {
      console.log("Worker reloaded!");
    });
  • Added a new Miniflare#getGlobalScope() method for getting the global scope of the Miniflare sandbox. This allows you to access and manipulate the Miniflare environment whilst your worker is running without reloading it. Closes issue #38, thanks @cdloh.

  • Added a new Miniflare#startScheduler() method that starts a CRON scheduler that dispatches scheduled events according to CRON expressions in options

Durable Objects:

  • Added input and output gates for ensuring consistency without explicit transactions
  • Added write coalescing for put/delete without interleaving awaits for automatic atomic writes
  • Added the DurableObjectState#blockConcurrencyWhile(callback) method. This prevents new fetch events being delivered to your object whilst the callback runs. Closes issue #45, thanks @gmencz.
  • Added the DurableObjectId#equals(id) method for comparing if 2 Durable Object IDs have the same hex-ID
  • Automatically resolve relative URLs passed to DurableObjectStub#fetch(input, init?) against https://fast-host. Closes issue #27, thanks @halzy.
  • Throw an error if the string passed to DurableObjectNamespace#idFromString(hexId) is not 64 hex digits
  • Throw an error if the hex-ID passed to DurableObjectNamespace#idFromString(hexId) is for a different Durable Object
  • Throw an error if the ID passed to DurableObjectNamespace#get(id) is for a different Durable Object
  • Throw an error when keys are greater than 2KiB or undefined
  • Throw an error when values are greater than 32KiB
  • Throw an error when attempting to get, put or delete more than 128 keys, or when attempting to modify more than 128 keys in a transaction
  • Throw an error when attempting to put an undefined value
  • Throw an error when attempting to list keys with a negative limit
  • Throw an error when attempting to perform an operation in a rolledback transaction or in a transaction that has already committed
  • Throw an error when attempting to call deleteAll() in a transaction
  • Use the same V8 serialization as Cloudflare Workers to store values
  • Fixed an issue where keys added in a transaction callback were not reported as deleted in the same transaction
  • Fixed an issue where keys added in a transaction callback were not included in the list of keys in the same transaction

HTTP Server:

  • Added a live reload feature (powered by HTMLRewriter 😄), that automatically refreshes your browser when your worker reloads. For this to work, pass the --live-reload option, and return an HTML response containing a <body> tag with the Content-Type set to text/html:

    addEventListener("fetch", (event) => {
      const body = `
        <!DOCTYPE html>
        <html>
        <body>
          <p>Try update me!</p>
        </body>
        </html>
      `;
    
      const res = new Response(body, {
        headers: { "Content-Type": "text/html; charset=utf-8" },
      });
    
      event.respondWith(res);
    });
  • Automatically fetch the incoming Request#cf object from a trusted Cloudflare endpoint, so the values are the same as you'd get for real. Closes issue #61, thanks @aaronsdevera and @Electroid.

  • Added a metaProvider option that allows you fetch metadata for an incoming Request:

    const mf = new Miniflare({
      async metaProvider(req) {
        return {
          forwardedProto: req.headers["X-Forwarded-Proto"],
          realIp: req.headers["X-Forwarded-For"],
          cf: {
            // Could get these from a geo-IP database
            colo: "SFO",
            country: "US",
            // ...
          },
        };
      },
    });
  • Split out the Node request to Request object conversion logic into a convertNodeRequest(req, upstream?, meta?) function. You can import this from @miniflare/http-server.

  • Only return a pretty-error page when the request Accept header includes text/html

  • Added a new Miniflare#startServer(options?) method that starts an HTTP server using the configured port and host. options can be a http.ServerOptions or https.ServerOptions object. Closes issue #39, thanks @amlwwalker

  • Include a default Content-Type header of text/plain in Responses. Closes issue #57, thanks @Rysertio.

KV:

  • Throw an error when keys are empty, ., .., undefined, or greater than 512B when UTF-8 encoded
  • Throw an error when values are greater than 25MiB
  • Throw an error when metadata is greater than 1KiB
  • Throw an error when the cacheTtl option is less than 60s
  • Throw an error when expirationTtl is non-numeric, less than or equal 0, or less than 60s
  • Throw an error when expiration is non-numeric, less than or equal the current time, or less than 60s in the future
  • Throw an error when the limit passed to KVNamespace#list() is non-numeric, less than or equal 0, or greater than 1000

Scheduler:

  • Moved the /.mf/scheduled endpoint for triggering scheduled events to /cdn-cgi/mf/scheduled. Closes issue #42, thanks @ObsidianMinor.

Web Sockets:

  • Added support for sending/receiving binary messages. Closes issue #67, thanks @NostalgiaRunner.
  • Removed the WebSocket#readyState property. Closes issue #47, thanks @aboodman.
  • Throw an error when attempting to use a WebSocket in a Response that has already been used
  • Throw an error when attempting to use a WebSocket in a Response after calling accept() on it
  • Throw an error when attempting to construct a WebSocket using the WebSocket constructor
  • Throw an error when attempting to call WebSocket#send() or WebSocket#close() without first calling accept(). Closes issue #43, thanks @aboodman.
  • Throw an error when attempting to call WebSocket#send() after calling close()
  • Throw an error when attempting to call WebSocket#close() on an already closed WebSocket
  • Throw an error when attempting to call WebSocket#close() with an invalid close code

Don't miss a new miniflare release

NewReleases is sending notifications on new releases.