Skip to content

Releases: cloudflare/miniflare

v2.11.0

27 Oct 16:26
Compare
Choose a tag to compare

Features

  • Add support for dead-letter queues. Thanks @jbw1991 for the PR.
  • Add getMiniflareDurableObjectIds() global function to Miniflare's Jest/Vitest environments for listing active Durable Objects. Calling getMiniflareDurableObjectIds("TEST_OBJECT") will return a Promise that resolves to an array of active DurableObjectIds for the TEST_OBJECT namespace. Closes issue #384, thanks @DaniFoldi for the PR.

Fixes

v2.10.0

11 Oct 09:20
Compare
Choose a tag to compare

Features

  • Add support for TextEncoderStream/TextDecoderStream. Closes issue #389, thanks @vlovich.

Fixes

v3.0.0-next.2

28 Sep 16:14
Compare
Choose a tag to compare
v3.0.0-next.2 Pre-release
Pre-release

Fixes

  • If the specified compatibility date is after the latest supported workerd compatibility date, but not in the future, log a warning, and fallback to the latest supported date instead.
  • Resolve Miniflare#ready's Promise with the URL of workerd's HTTP server.

v3.0.0-next.1

28 Sep 11:35
Compare
Choose a tag to compare
v3.0.0-next.1 Pre-release
Pre-release

Miniflare now uses Cloudflare's open-source Workers runtime, workerd, to run your code! 🎉 This is a massive change, and should mean your code runs locally almost-exactly as in production. Most Workers features are supported, including Web Standards, KV, R2, but there are a few things that aren't just yet:

  • Durable Objects are in-memory only and cannot be persisted between reloads
  • The Cache API is not yet implemented
  • Scheduled Events are not yet implemented
  • console.logging an object will not show all its properties

Breaking Changes

  • Miniflare's CLI has been removed. We're still discussing whether it makes sense to keep this, given wrangler dev has additional features such as automatic bundling and TypeScript support. For now, use wrangler dev --experimental-local.

Miniflare API Changes

  • kvNamespaces now accepts an object mapping binding names to namespace IDs, instead of an array of binding names. This means multiple workers can bind the same namespace under different names.

     const mf = new Miniflare({
    -  kvNamespaces: ["NAMESPACE_1", "NAMESPACE_2"],
    +  kvNamespaces: {
    +    NAMESPACE_1: "NAMESPACE_1", // Miniflare <= 2 behaviour
    +    NAMESPACE_2: "ns2",         // Custom namespace ID
    +  },
     });
  • Similarly, r2Buckets now accepts an object mapping binding names to bucket IDs, instead of an array of binding names.

     const mf = new Miniflare({
    -  r2Buckets: ["BUCKET_1", "BUCKET_2"],
    +  r2Buckets: {
    +    BUCKET_1: "BUCKET_1", // Miniflare <= 2 behaviour
    +    BUCKET_2: "bucket2",  // Custom namespace ID
    +  },
     });
  • workerd requires all modules to be known ahead of time, so Miniflare will parse your JavaScript to search for module dependencies when setting modules to true. This has some limitations:

    1. Dynamic import() with non-literal arguments is unsupported.
    2. Any call to a function named require in a CommonJS module will be searched, even if it's not actually the global require function, and just a user-defined function with the same name.

    Because of these limitations, Miniflare allows you to define all your modules manually.

    import { Miniflare } from "@miniflare/tre";
    
    const mf = new Miniflare({
      modules: [
        // The first module must be your entrypoint. `type` must be one of
        // "ESModule", "CommonJS", "Text", "Data" or "CompiledWasm". If `path`
        // isn't relative, it will be converted to a relative path before being
        // passed to `workerd`.
        { type: "ESModule", path: "src/index.mjs" },
        // Optionally, a `contents` `string` or `Uint8Array` can be defined.
        // If omitted, `contents` is loaded from `path`.
        { type: "Text", path: "src/message.txt", contents: "Hello!" },
      ],
    });
  • mounts has been removed and replaced with the workers option.

    import { Miniflare } from "@miniflare/tre";
    
    const message = "The count is ";
    const mf = new Miniflare({
      // Options shared between workers such as HTTP and persistence configuration
      // should always be defined at the top level.
      host: "0.0.0.0",
      port: 8787,
      kvPersist: true,
    
      workers: [
        {
          name: "worker",
          kvNamespaces: { COUNTS: "counts" },
          serviceBindings: {
            INCREMENTER: "incrementer",
            // Service bindings can also be defined as custom functions, with access
            // to anything defined outside Miniflare.
            async CUSTOM(request) {
              // `request` is the incoming `Request` object.
              return new Response(message);
            },
          },
          modules: true,
          script: `export default {
            async fetch(request, env, ctx) {
              // Get the message defined outside
              const response = await env.CUSTOM.fetch("http://host/");
              const message = await response.text();
              
              // Increment the count 3 times
              await env.INCREMENTER.fetch("http://host/");
              await env.INCREMENTER.fetch("http://host/");
              await env.INCREMENTER.fetch("http://host/");
              const count = await env.COUNTS.get("count");
              
              return new Response(message + count);
            }
          }`,
        },
        {
          name: "incrementer",
          // Note we're using the same `COUNTS` namespace as before, but binding it
          // to `NUMBERS` instead.
          kvNamespaces: { NUMBERS: "counts" },
          // Worker formats can be mixed-and-matched
          script: `addEventListener("fetch", (event) => {
            event.respondWith(handleRequest());
          })
          async function handleRequest() {
            const count = parseInt((await NUMBERS.get("count")) ?? "0") + 1;
            await NUMBERS.put("count", count.toString());
            return new Response(count.toString());
          }`,
        },
      ],
    });
    const res = await mf.dispatchFetch("http://localhost");
    console.log(await res.text()); // "The count is 3"

v2.9.0

16 Sep 14:55
Compare
Choose a tag to compare

Features

  • 💾 Add support for D1. Closes issue #277, thanks @geelen for the PR. Docs coming soon™... 👀

  • 🚪 Add getMiniflareDurableObjectState() and runWithMiniflareDurableObjectGates() functions to the Jest/Vitest environments. This allows you to construct and call instance methods of Durable Objects directly, without having to fetch through a stub. Closes issue #157, thanks @jorroll.

    // Durable Object class, would probably come from an import
    class Counter {
      constructor(state) {
        this.storage = state.storage;
      }
      async fetch() {
        const count = ((await this.storage.get("count")) ?? 0) + 1;
        void this.storage.put("count", count);
        return new Response(String(count));
      }
    }
    
    const env = getMiniflareBindings();
    // Use standard Durable Object bindings to generate IDs
    const id = env.COUNTER.newUniqueId();
    // Get DurableObjectState, and seed data
    const state = await getMiniflareDurableObjectState(id);
    await state.storage.put("count", 3);
    // Construct object directly
    const object = new Counter(state, env);
    // Call instance method directly, closing input gate,
    // and waiting for output gate to open
    const res = await runWithMiniflareDurableObjectGates(state, () =>
      object.fetch(new Request("http://localhost/"))
    );
    expect(await res.text()).toBe("4");
  • 🥷 Don't construct corresponding Durable Object instance when calling Miniflare#getDurableObjectStorage(). This allows you to seed data before your Durable Object's constructor is invoked. Closes issue #300, thanks @spigaz.

  • ☑️ Add support for WebSocket#readyState and WebSocket.READY_STATE_{CONNECTING,OPEN,CLOSING,CLOSED} constants. Note these constant names intentionally deviate from the spec to match the Workers runtime.

  • 📜 Add persistent history to the REPL. This respects the MINIFLARE_REPL_HISTORY, MINIFLARE_REPL_HISTORY_SIZE, and MINIFLARE_REPL_MODE environment variables based on Node's.

  • 💵 Add support for Range, If-Modified-Since and If-None-Match headers on Requests to Cache#match. Closes issue #246.

Fixes

  • Don't wait for waitUntil Promises to resolve before opening WebSocket connections
  • Allow WebSockets to be close()d on receiving a close event. Closes issue #331, thanks @awthwathje.
  • Ensure calling WebSocket#close() before returning a WebSocket response sends the correct close code and reason.
  • Fix delivery of incoming WebSocket error events
  • Ensure only scheduled Durable Object alarms are flushed. Previously, flushing all alarms would attempt to execute the alarm handler of every constructed Durable Object instance, even if that instance hadn't scheduled an alarm, or didn't have an alarm handler.
  • Delay scheduled missed alarms. Previously, if Durable Object persistence was enabled, and an alarm should've executed when Miniflare wasn't running, Miniflare may have crashed on startup. Closes issue #359, thanks @AlCalzone.
  • Allow empty-chunk writes to IdentityTransformStream. Closes issue #374, thanks @cdloh.
  • Don't hang when fetching from Durable Objects with fake-timers installed. Closes issue #190, thanks @vlovich.
  • Match unimplemented Request/Response properties with the Workers runtime. Specifically, throw unimplemented errors when attempting to access Request#{context,mode,credentials,integrity,cache} and Response#{type,useFinalUrl}.
  • Discard Content-Length: NaN headers as a temporary workaround until cloudflare/kv-asset-handler#295 is released. Closes honojs/hono#520, thanks @Cherry.
  • Return byte streams when tee()ing byte streams to match the behaviour of the Workers runtime. Closes issues #317 and #375.
  • Throw TypeError when calling Fetcher#fetch with an illegal this to match the behaviour of the Workers runtime.

v2.8.2

10 Sep 16:50
Compare
Choose a tag to compare

Fixes

v2.8.1

08 Sep 09:50
Compare
Choose a tag to compare

Fixes

  • Add missing @miniflare/queues dependencies. Closes issue #360, thanks @AlCalzone for the PR.
  • Fix support for queues in Jest/Vitest testing environments

v2.8.0

07 Sep 21:09
Compare
Choose a tag to compare

Features

  • ⚡️ Add custom Vitest testing environment. This behaves almost identically to the Jest environment. However, isolated storage must be installed manually in each test file. Call the setupMiniflareIsolatedStorage() global function and use the returned describe function instead of the regular describe/suite functions imported from vitest. See ⚡️ Vitest Environment for more details.
  • 🌐 Populate Workers Sites __STATIC_CONTENT_MANIFEST with site files instead of an empty object. Miniflare will still disable caching of Workers Sites files to ensure the most up-to-date files are always returned. Closes issues #233, #326 and cloudflare/wrangler2#1632. Thanks @ItalyPaleAle, @Skye-31, @CraigglesO, @Hexstream and @PolariTOON.
  • Add global getMiniflareWaitUntil() method and ExecutionContext class to the Jest and Vitest testing environments. This can be used to await the results of waitUntiled Promises in tests. See 🤹️ Jest Environment and ⚡️ Vitest Environment for more details. Closes issue #202, thanks @jamesarosen and @CraigglesO for the PR.
  • ⏳ Match Web Streams implementations with Workers runtime, closes issue #168, thanks @leviwolfe:
    • Add support for the non-standard IdentityTransformStream class.
    • Add support for the streams_enable_constructors compatibility flag. ReadableStream and WritableStream constructors will throw unless this flag is enabled. ReadableByteStreamController, ReadableStreamBYOBRequest, ReadableStreamDefaultController and WritableStreamDefaultController will only be included in the sandbox if this flag is enabled.
    • Add support for the transformstream_enable_standard_constructor compatibility flag. TransformStream will behave like IdentityTransformStream if this isn't enabled, ignoring custom transformers. If transformstream_enable_standard_constructor is set, but streams_enable_constructors isn't, the TransformStream constructor will throw. TransformStreamDefaultController will only be included in the sandbox if both flags are enabled.
    • Add support for BYOB reads to the non-standard FixedLengthStream class.
  • 🇬🇧 Add support for Queues. Docs coming soon™... 👀
  • 🙉 Allow calls to addEventListener, removeEventListener and dispatchEvent in modules mode. Please note, calling addEventListener with a special event type (e.g. fetch, scheduled) will log a warning prompting you to use the export default syntax. Closes issue #207, thanks @Electroid.
  • 🍟 Add experimental and highly-inaccurate request CPU time measurements. These are not representative of deployed worker performance, should only be used for relative comparisons, and may be removed in the future. Enable measurements with the --inaccurate-cpu/[miniflare] inaccurate_cpu/inaccurateCpu option. Closes issue #161. Thanks @alexandernst and @y21.
  • 🦄 Automatically enable watch mode when live_reload = true is set in wrangler.toml.

Fixes

  • Return Responses with immutable headers from fetches to Durable Objects and service bindings. Closes issue #346, thanks @Cherry.
  • Fix CryptoKey#algorithm.name property of NODE-ED25519 keys. Closes issue panva/jose#446, thanks @ItalyPaleAle.
  • Disable automatic insertion of Sec-WebSocket-Protocol header. Closes issue #179, thanks @aboodman and @grgbkr.
  • Return Content-Length header is custom Content-Encoding is specified. Closes issue #313, thanks @vlovich.
  • Require "automatic" instead of "auto" for the encodeBody option when constructing Requests. Closes issue #357, thanks @GregBrimble for the PR.
  • Remove request.cf.cacheTtl/request.cf.cacheTtlByStatus support from the Cache API to match the behaviour of the Workers runtime, which only supports request.cf.cacheKey.

v2.7.1

22 Aug 16:27
Compare
Choose a tag to compare

Fixes

  • Ensure initialisation is complete before tear down in Miniflare#dispose(). Closes issue #341, thanks @vlovich.
  • Ensure DurableObjectTransaction operations are executed in program order. Closes issue #344, thanks @vlovich.

v2.7.0

19 Aug 17:07
Compare
Choose a tag to compare

⚠️ Miniflare's minimum supported Node.js version is now 16.13.0. This was the first LTS release of Node.js 16.

We recommend you 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.

Features

  • 🎉 Add support for easily mocking outbound fetch requests. See 🕸 Web Standards for more details. Closes issue #162, thanks @william1616 for the PR.

    test("mocks fetch", async () => {
      // Get correctly set up `MockAgent`
      const fetchMock = getMiniflareFetchMock();
      // Throw when no matching mocked request is found
      fetchMock.disableNetConnect();
      // Mock request to https://example.com/thing
      const origin = fetchMock.get("https://example.com");
      origin.intercept({ method: "GET", path: "/thing" }).reply(200, "Mocked response!");
    
      const res = await fetch("https://example.com/thing");
      const text = await res.text();
      expect(text).toBe("Mocked response!");
    });
  • 🚽 Add support to immediately invoke ("flush") scheduled Durable Object alarms in the 🤹 Jest Environment. Closes issue #322, thanks @robertcepa and @CraigglesO for the PR.

    test("flushes alarms", async () => {
      // Get Durable Object stub
      const env = getMiniflareBindings();
      const id = env.TEST_OBJECT.newUniqueId();
      const stub = env.TEST_OBJECT.get(id);
    
      // Schedule Durable Object alarm
      await stub.fetch("http://localhost/");
    
      // Flush all alarms...
      await flushMiniflareDurableObjectAlarms();
      // ...or specify an array of `DurableObjectId`s to flush
      await flushMiniflareDurableObjectAlarms([id]);
    });
  • 🪣 Add support for R2 bucket bindings to the 🤹 Jest Environment. Closes issue #305, thanks @Cerberus for the PR.

  • 2️⃣ Add support for Wrangler 2's routes property. Closes issue #254, thanks @jrencz for the PR.

  • ⚠️ Upgrade undici to 5.9.1. Thanks @yusukebe and @cameron-robey for the PRs.

Fixes