Skip to content

Commit

Permalink
check for an implementation of AsyncLocalStorage (#866)
Browse files Browse the repository at this point in the history
Add a runtime check for `wrangler dev` local mode to avoid erroring in environments with no `AsyncLocalStorage` class

Certain runtime APIs are only available to workers during the "request context",
which is any code that returns after receiving a request and before returning
a response.

Miniflare emulates this behavior by using an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) and
[checking at runtime](https://github.com/cloudflare/miniflare/blob/master/packages/shared/src/context.ts#L21-L36)
to see if you're using those APIs during the request context.

In certain environments `AsyncLocalStorage` is unavailable, such as in a
[webcontainer](https://github.com/stackblitz/webcontainer-core).
This function figures out if we're able to run those "request context" checks
and returns [a set of options](https://miniflare.dev/core/standards#global-functionality-limits)
that indicate to miniflare whether to run the checks or not.
  • Loading branch information
Cass Fridkin committed May 2, 2022
1 parent 0a79d75 commit 8b227fc
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .changeset/many-numbers-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"wrangler": patch
---

Add a runtime check for `wrangler dev` local mode to avoid erroring in environments with no `AsyncLocalStorage` class

Certain runtime APIs are only available to workers during the "request context",
which is any code that returns after receiving a request and before returning
a response.

Miniflare emulates this behavior by using an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) and
[checking at runtime](https://github.com/cloudflare/miniflare/blob/master/packages/shared/src/context.ts#L21-L36)
to see if you're using those APIs during the request context.

In certain environments `AsyncLocalStorage` is unavailable, such as in a
[webcontainer](https://github.com/stackblitz/webcontainer-core).
This function figures out if we're able to run those "request context" checks
and returns [a set of options](https://miniflare.dev/core/standards#global-functionality-limits)
that indicate to miniflare whether to run the checks or not.
3 changes: 3 additions & 0 deletions packages/wrangler/src/miniflare-cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Log, LogLevel, Miniflare } from "miniflare";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { enumKeys } from "./enum-keys";
import { getRequestContextCheckOptions } from "./request-context";

async function main() {
const args = await yargs(hideBin(process.argv))
Expand All @@ -12,8 +13,10 @@ async function main() {
}).argv;

const logLevel = LogLevel[args.log ?? "INFO"];
const requestContextCheckOptions = await getRequestContextCheckOptions();
const config = {
...JSON.parse((args._[0] as string) ?? "{}"),
...requestContextCheckOptions,
log: new Log(logLevel),
};

Expand Down
40 changes: 40 additions & 0 deletions packages/wrangler/src/miniflare-cli/request-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { MiniflareOptions } from "miniflare";

/**
* Certain runtime APIs are only available to workers during the "request context",
* which is any code that returns after receiving a request and before returning
* a response.
*
* Miniflare emulates this behavior by using an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) and
* [checking at runtime](https://github.com/cloudflare/miniflare/blob/master/packages/shared/src/context.ts#L21-L36)
* to see if you're using those APIs during the request context.
*
* In certain environments `AsyncLocalStorage` is unavailable, such as in a
* [webcontainer](https://github.com/stackblitz/webcontainer-core).
* This function figures out if we're able to run those "request context" checks
* and returns [a set of options](https://miniflare.dev/core/standards#global-functionality-limits)
* that indicate to miniflare whether to run the checks or not.
*/
export const getRequestContextCheckOptions = async (): Promise<
Pick<MiniflareOptions, "globalAsyncIO" | "globalTimers" | "globalRandom">
> => {
// check that there's an implementation of AsyncLocalStorage
let hasAsyncLocalStorage = true;
try {
// ripped from the example here https://nodejs.org/api/async_context.html#class-asynclocalstorage
const { AsyncLocalStorage } = await import("node:async_hooks");
const storage = new AsyncLocalStorage<string>();

hasAsyncLocalStorage = storage.run("some-value", () => {
return storage.getStore() === "some-value";
});
} catch (e) {
hasAsyncLocalStorage = false;
}

return {
globalAsyncIO: hasAsyncLocalStorage,
globalRandom: hasAsyncLocalStorage,
globalTimers: hasAsyncLocalStorage,
};
};
5 changes: 5 additions & 0 deletions packages/wrangler/src/pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { fetchResult } from "./cfetch";
import { readConfig } from "./config";
import { FatalError } from "./errors";
import { logger } from "./logger";
import { getRequestContextCheckOptions } from "./miniflare-cli/request-context";
import openInBrowser from "./open-in-browser";
import { toUrlPath } from "./paths";
import { requireAuth } from "./user";
Expand Down Expand Up @@ -1287,6 +1288,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
? await generateAssetsFetch(directory)
: invalidAssetsFetch;

const requestContextCheckOptions =
await getRequestContextCheckOptions();

const miniflare = new Miniflare({
port,
watch: true,
Expand Down Expand Up @@ -1351,6 +1355,7 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
cachePersist: true,
liveReload,

...requestContextCheckOptions,
...miniflareArgs,
});

Expand Down

0 comments on commit 8b227fc

Please sign in to comment.