ulidx: Importing node:crypto breaks compat with edge runtimes (Vercel etc)

UnhandledSchemeError: Reading from "node:crypto" is not handled by plugins (Unhandled scheme).

Looks to have been an oversight(?) in https://github.com/perry-mitchell/ulidx/commit/5b90d022e54e122676fab8ac0c2470c3a05192bc

Reverting this commit fixes things.

Vercel edge runtime provides the web crypto API, which is not the same as / not one of the node-compatible modules that can be loaded with the node: prefix: https://nextjs.org/docs/api-reference/edge-runtime

Big thanks for the work on this module!

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 2
  • Comments: 28 (21 by maintainers)

Most upvoted comments

2.0.0 released.

@jiw-mh Agreed, I’m going to add one now.

The sensible thing may be to provide a browser and node entry point →. https://github.com/uuidjs/uuid/blob/4de23a6030e65ac72b3b015680f08e7e292681ed/package.json#L24-L46

Thanks @perry-mitchell - v2.0.0-1 solves the different entrypoints for node/browser and ESM/CJS in my testing, but the browser builds no longer resolve the global crypto functions under the Vercel Edge runtime (and I suspect also Cloudflare Workers in general? Although I haven’t confirmed).

The fix is straightforward - to update detectRoot() to check for globalThis - see #24

(This wasn’t required when I tested the earlier version of your web_build branch as a result of the Webpack transformations, if I understand correctly.)

Cheers!

@perry-mitchell It works with react-native again. Thank you! 🎉

@perry-mitchell Installed it in a project that is setup using vite and esm modules and it works fine.

So I’ve gone back on what I said and created a separate build for each environment anyway:

  • Node ESM
  • Node CJS
  • Browser ESM
  • Browser CJS

So they should all handle as you’d expect. If the tests work I’ll probably bundle and release this as 2.0.0 shortly.

Regarding your exports - my only observation is you have a “browser” entry within the top-level “exports” field, but no top-level “browser” field for any bundlers/older versions that may not support package “exports” (unlike e.g. uuid as referenced previously - see this line).

You’re right, I’ll add that before merging - thanks!

It would be good to have esm for browser too. esbuild and similar do auto transform.

I’m happy for someone to provide such an MR, but I don’t currently have the time to add it.

Big thanks @perry-mitchell - I checked out and built your web-build branch and patched that into my app and it does the job just fine. (NextJS, with both Webpack + Turbopack bundlers).

Point taken re: tree shaking - I think what you’ve added here gives a great combination of “quick to get it working, zero config” and “possible to optimise the bundling with a bit of work to tweak bundler config”. So thanks! 🙌

Regarding your exports - my only observation is you have a “browser” entry within the top-level “exports” field, but no top-level “browser” field for any bundlers/older versions that may not support package "exports" (unlike e.g. uuid as referenced previously - see this line). It might be worth adding that also - although I don’t have a reference for which bundlers may still not support "exports".

it was intended to be included in a project that does such bundling, like Webpack etc… such a process would clean up and remove the crypto module as mentioned at the end of the readme

Got it! Thanks for explaining.

I figured a new solution that feels cleaner and causes the ESM import of the node crypto module to be stripped correctly - details below. My patch to achieve this is a bit scrappy but if this approach sounds useful I can try put together a tidier PR - let me know if it sounds promising!

~~

I’m using the default Webpack bundling config provided by Next.js (via next dev / next build commands). With the current code this was failing with the error in my original post rather than stripping the import of the node crypto module as hoped.

By patching the module in a different way I managed to solve the issue building for the Next.js Edge runtime (thus also I think for browser environments (?) although I haven’t verified). Specifically the Webpack bundler will strip the import of the node crypto module if it determines via tree-shaking that the import will never be referenced at runtime.

Webpack fails to tree-shake the current code seemingly due to the layers of indirection in the logic that first assigns to globalCrypto. To enable successful tree-shaking, I simply patched to add a simple if test that returns early with the result of the browser crypto API:

 function detectPRNG(root) {
+  if (typeof EdgeRuntime === 'string') {
+    return () => {
+      const buffer = new Uint8Array(1);
+      crypto.getRandomValues(buffer);
+      return buffer[0] / 0xff;
+    };
+  }

   ...

This worked for my initial test - here I used the test typeof EdgeRuntime === 'string' as recommended specifically for the Vercel Edge runtime; I haven’t tested yet but I would next try some simpler if tests to try achieve the tree-shaking based on availability of the web crypto API in the global scope rather than the (currently) vendor-specific test for the EdgeRuntime global.

To be able to access the browser crypto API via the global crypto. I also renamed the import of the node crypto module (with ESM it may feel cleaner to instead import the required functions directly as named imports):

-import crypto from 'node:crypto';
+import { default as nodeCrypto } from 'crypto';

  ... subsequent changes as necessary to reference the renamed import

To anyone else having these issues: please consider sticking with v0 for now… there’s no need to upgrade just yet as the feature set is the same.