v2.0.0-next.1
Pre-releaseMiniflare 2 has been completely redesigned from version 1 with 3 primary design goals:
- 📚 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. - ✨ Lightweight: Miniflare 1 included 122 third-party packages with a total install size of
88.3MB. Miniflare 2 reduces this to 24 packages and11.5MB🤯. This can probably be reduced further too. - ✅ 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-ED25519algorithm - 📅 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#cfobject 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_nameoption. 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 newMiniflare#getDurableObjectStorage(id)method, passing aDurableObjectIdobtained from a stub. -
Renamed the
--disable-cache/disableCache: trueoption to--no-cache/cache: false -
Renamed the
--disable-updateroption to--no-update-check -
When using the API,
wrangler.toml,package.jsonand.envare now no longer automatically loaded from their default locations. To re-enable this behaviour, set these options totrue:const mf = new Miniflare({ wranglerConfigPath: true, packagePath: true, envPath: true, });
-
Replaced the
ConsoleLogclass with theLogclass from@miniflare/shared. You can construct this with a member from theLogLevelenumto 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_moduleswrangler.tomlkey instead ofminiflare.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 theMiniflare#reload()andMiniflare#setOptions({ ... })methods.reload()will reload options fromwrangler.toml(useful if not watching), andsetOptions()accepts the same options object as thenew Miniflareconstructor, applies those options, then reloads the worker. -
Miniflare#createServer()now always returns aPromisewhich you must await to get ahttp.Server/https.Serverinstance. You may want to check out the newMiniflare#startServer()method which automatically starts a server using the configuredhostandport. -
Miniflare no longer includes CommonJS exports. You must use ES modules. If you need to import
miniflarein 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-redisoptional 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.cacheTtlandcf.cacheTtlByStatusonRequest. Closes issue #37, thanks @cdloh. - Added the
CF-Cache-Status: HITheader to matchedResponses - Log warning when trying to use cache with
workers_dev = trueinwrangler.toml. Cache operations are a no-op onworkers.devsubdomains. - Throw errors when trying to cache Web Socket, non-
GET,206 Partial Content, orVary: *responses - Throw an error when trying to
opena cache with a name longer than1024characters
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-commandoption - 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-ED25519algorithm tocrypto.subtle.sign()andcrypto.subtle.verify() - Throw an error when attempting to create a new
TextDecoderwith a non-UTF-8 encoding - Throw errors when attempting to use
FetchEvent/ScheduledEventmethods with incorrectly boundthis - Throw errors when attempting to call
respondWith()twice, or after thefetchhandler has finished executing synchronously. Closes issue #63, thanks @Kikobeats. - Added support for the
unhandledrejectionandrejectionhandledevents - Throw an error (with a suggested fix) when trying to access an
envbinding globally in modules mode - Throw errors when trying to use
addEventListener(),removeEventListener()anddispatchEvent()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 theSet-Cookieheader. - Switch to a more spec-compliant
fetchimplementation, and getcrypto,EventTargetand 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
Filein 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
MINIFLAREto the Miniflare sandbox, which will always have the valuetruewhen 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_filesare now supported. This feature is exposed under the--compat-dateand--compat-flagCLI options, in addition to the standard keys inwrangler.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
instanceofon primitives such asObject,Array,Promise, etc. from outside the Miniflare sandbox. This makes it much easier to run Rust workers in Miniflare, aswasm-bindgenfrequently generates this code. -
Added a new
--verbose/verbose: trueoption 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
Miniflareinstances 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 dispatchesscheduledevents according to CRON expressions in options
Durable Objects:
- Added input and output gates for ensuring consistency without explicit transactions
- Added write coalescing for
put/deletewithout interleavingawaits for automatic atomic writes - Added the
DurableObjectState#blockConcurrencyWhile(callback)method. This prevents newfetchevents 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?)againsthttps://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
2KiBorundefined - Throw an error when values are greater than
32KiB - Throw an error when attempting to
get,putordeletemore than128keys, or when attempting to modify more than128keys in a transaction - Throw an error when attempting to
putanundefinedvalue - 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-reloadoption, and return an HTML response containing a<body>tag with theContent-Typeset totext/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#cfobject 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
metaProvideroption that allows you fetch metadata for an incomingRequest: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
Requestobject conversion logic into aconvertNodeRequest(req, upstream?, meta?)function. You can import this from@miniflare/http-server. -
Only return a pretty-error page when the request
Acceptheader includestext/html -
Added a new
Miniflare#startServer(options?)method that starts an HTTP server using the configuredportandhost.optionscan be ahttp.ServerOptionsorhttps.ServerOptionsobject. Closes issue #39, thanks @amlwwalker -
Include a default
Content-Typeheader oftext/plaininResponses. Closes issue #57, thanks @Rysertio.
KV:
- Throw an error when keys are empty,
.,..,undefined, or greater than512Bwhen 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
cacheTtloption is less than60s - Throw an error when
expirationTtlis non-numeric, less than or equal 0, or less than60s - Throw an error when
expirationis non-numeric, less than or equal the current time, or less than60sin the future - Throw an error when the
limitpassed toKVNamespace#list()is non-numeric, less than or equal0, or greater than1000
Scheduler:
- Moved the
/.mf/scheduledendpoint 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#readyStateproperty. Closes issue #47, thanks @aboodman. - Throw an error when attempting to use a
WebSocketin aResponsethat has already been used - Throw an error when attempting to use a
WebSocketin aResponseafter callingaccept()on it - Throw an error when attempting to construct a
WebSocketusing theWebSocketconstructor - Throw an error when attempting to call
WebSocket#send()orWebSocket#close()without first callingaccept(). Closes issue #43, thanks @aboodman. - Throw an error when attempting to call
WebSocket#send()after callingclose() - 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