How to use openpgp.js on node.js (Cloudflare worker) - Error Uncaught ReferenceError: navigator is not defined?

573 views Asked by At

I have a Cloudflare Worker (created from the Typescript template and with the build config barely changed) that needs to use openpgp.js, which as this link shows, is supposed to be supported when running on Node.js.

When I build it with npm run build it seems to work fine, but then when I try to build with wrangler dev, it throws this error:

webpack 5.38.1 compiled successfully in 1147 ms
Error: Something went wrong with the request to Cloudflare...
Uncaught ReferenceError: navigator is not defined
  at line 237 in ./node_modules/openpgp/dist/lightweight/openpgp.min.mjs
  at line 827 in __webpack_require__
  at line 625 in ./src/crypto.ts
  at line 827 in __webpack_require__
  at line 749 in ./src/handler.ts
  at line 827 in __webpack_require__
  at line 1064
  at line 1069
  at line 1071
 [API code: 10021]

This question looks similar, but the solutions there don't seem to apply in my case.

One solution was to use browser-env to expose the global variable navigator to the node.js environment. When I tried that, I got an enormous error that ends like this:

ERROR in ./node_modules/window/node_modules/tough-cookie/lib/cookie.js 34:11-26
Module not found: Error: Can't resolve 'util' in '/Users/renato/programming/projects/renato-worker/node_modules/window/node_modules/tough-cookie/lib'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
...
ERROR in ./node_modules/window/node_modules/tough-cookie/lib/memstore.js 35:11-26
Module not found: Error: Can't resolve 'util' in '/Users/renato/programming/projects/renato-worker/node_modules/window/node_modules/tough-cookie/lib'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default
...

ERROR in /Users/renato/programming/projects/renato-worker/src/index.ts
./src/index.ts 4:23-36
[tsl] ERROR in /Users/renato/programming/projects/renato-worker/src/index.ts(4,24)
      TS7016: Could not find a declaration file for module 'browser-env'. '/Users/renato/programming/projects/renato-worker/node_modules/browser-env/src/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/browser-env` if it exists or add a new declaration (.d.ts) file containing `declare module 'browser-env';`

Trying to install types/browser-env:

▶ npm i --save-dev @types/browser-env
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@types%2fbrowser-env - Not found

Hm, I think this is not what I want anyway, in the docs for browser-env there's a stern warning against doing this:

https://github.com/jsdom/jsdom/wiki/Don't-stuff-jsdom-globals-onto-the-Node-global

A common antipattern we see when people use jsdom is copying globals from a jsdom window onto the Node.js global, and then trying to run the code---intended for a browser---inside Node.js. This is very bad and you should not do it.

The other suggested solution, to use detect-node doesn't apply in my case, I think, because it's not my code that needs to run something in case it's a browser or not... it's a compilation problem it seems, when running the build.

So I am completely stuck.

Is anyone more familiar with the myriad tools involved (webpack, node.js, Typescript, browser APIs, wrangler, Cloudflare API) able to tell me at least which one is causing this and what I can try to fix it?

1

There are 1 answers

0
Renato On

After a long struggle, I found that the problem originates from the way Cloudflare validates the Javascript code before deploying it on its environment (which it does even for running preview mode).

It does not allow certain APIs to be used, including the navigator object which is not usually available in backend Javascript.

This is a problem when using openpgp.js because it uses navigator to identify the userAgent and the number of cores available, unfortunately.

I made a pull request to move access to the navigator to a single place where it could be easily replaced with tools like webpack, but that PR was denied because it was not even necessary, you can just replace navigator with {} for example, and everything should work.

Check the pull request for more details, but in summary, add something like this to your webpack.config.json file:

  plugins: [
    new webpack.ProvidePlugin({
      'navigator': ['{}'],
    }),
  ]

The author of openpgp.js seems keen to remove the dependency on navigator in future versions, so in the near future, this hack may no longer be needed!