0

I am working with a javascript library that uses global.crypto, for example, makes a call to global.crypto.getRandomValues(). I want to use the library from the command line (1) for testing my scripts and knowledge, (2) perhaps for back-end code.

How can I make the global.crypto API available to that module from the command line?


Working example:

library.js:

export function getUUID() {
  return crypto.randomUUID();
}

main.js:

import { getUUID } from "./library.js";
console.log(getUUID());

index.html:

<!DOCTYPE html>
<html>
<body>
  <script type="module" src="main.js"></script>
</body>
</html>

Opening index.html from localhost writes a UUID to the console, but running "node main.js" gives

file:/.../library.js:2
  return crypto.randomUUID();
  ^

ReferenceError: crypto is not defined
    at getUUID (file:/.../library.js:2:3)
    at file:/.../main.js:3:13
    at ModuleJob.run (node:internal/modules/esm/module_job:195:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:337:24)
    at async loadESM (node:internal/process/esm_loader:88:5)
    at async handleMainPromise (node:internal/modules/run_main:61:12)

Edit based on tobiv's answer, here is a solution that works for me, however, may not be best practice? Note that I'm hoping not to edit the library file itself.

main.js (updated)

import * as crypto from 'crypto';
global.crypto = crypto.webcrypto;
import { getUUID } from "./library.js";
console.log(getUUID());

Now running "node main.js" correctly prints a UUID. And if I comment out the first two lines of main.js, I get a version that works in the browser.

Or for CommonJS and the library I'm actually using:

const crypto = require('node:crypto').webcrypto;
global.crypto = crypto;
const library = require("browser-passworder");
console.log(library.generateSalt());

I'm a bit concerned about robustness here if modules load asynchronously, but it's the best solution I've found.

usul
  • 784
  • 5
  • 16
  • Where do you import/load that crypto library? – tobiv Sep 19 '22 at 17:28
  • 1
    @tobiv by "that crypto library", are you referring to global.crypto? This seems to be available in the browser automatically, i.e. my example above works for me as written: if I serve index.html at localhost and access it in Firefox, it succesfully runs and prints a UUID to the console. If you mean the javascript library I'm referring to that uses global.crypto, I load it at the top of my own javascript file with: import * as passworder from 'browser-passworder' – usul Sep 19 '22 at 19:43

1 Answers1

1

Use require('node:crypto').webcrypto to access the module in node:

const crypto = require('node:crypto').webcrypto;

console.log(crypto.randomUUID());

https://nodejs.dev/en/api/v18/webcrypto/

tobiv
  • 821
  • 1
  • 8
  • 20
  • Thank you! This answers a lot of my question. There is still one piece: can I give the library I'm calling access to this webcrypto? (Without changing the code of the library, otherwise I could fork it and put this at the top.) – usul Sep 19 '22 at 23:57
  • 1
    One thing that worked for me, but may not be the best approach: in main.js, first import webcrypto as you describe. Then set `global.crypto = crypto;`. Then import the library. Took this idea from https://stackoverflow.com/a/4481382/658176 – usul Sep 20 '22 at 00:11
  • 1
    I've used a `if (typeof exports === 'object')` guard to be able to switch between a native browser and node implementation of a library. https://github.com/qzind/tray/blob/a07a96b54691a215e9f93aaff4fcee7a0c5ef436/js/qz-tray.js#L641-L651 – tresf Sep 20 '22 at 04:59