Skip to content

Commit

Permalink
Convert to ES modules
Browse files Browse the repository at this point in the history
  • Loading branch information
charmander committed Dec 14, 2024
1 parent 1fff6d5 commit 7fc549b
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 347 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ jobs:
run: |
npm install --no-save --ignore-scripts
tmp=$(mktemp --directory)
bash -O dotglob -O extglob -c 'mv !(node_modules|test.js) "$1"/' bash "$tmp"
bash -O dotglob -O extglob -c 'mv !(node_modules|test.mjs) "$1"/' bash "$tmp"
pack=$(npm pack "$tmp")
node -p '"sha512-" + Buffer.from(process.argv[1], "hex").toString("base64")' $(sha512sum -- "$pack")
tar --strip-components=1 -x -v -f "$pack"
npm install --no-save
node test
node test.mjs
51 changes: 15 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,33 @@ no longer than 72 bytes; an error is produced if these conditions are not met.
## Example

```javascript
const bcrypt = require('bcrypt-small');
import * as bcrypt from 'bcrypt-small';

bcrypt.hash('password', 12, (error, hash) => {
if (error) {
console.error(error);
return;
}
const hash = await bcrypt.hash('password', 12);

bcrypt.compare('password', hash, (error, result) => {
console.log(result); // true
});
await bcrypt.compare('password', hash)
// true

bcrypt.compare('not password', hash, (error, result) => {
console.log(result); // false
});
await bcrypt.compare('not password', hash)
// false

console.log(bcrypt.getRounds(hash)); // 12
});
```

Functions returning built-in promises are provided by `bcrypt-small/promises`:

```javascript
const bcrypt = require('bcrypt-small/promises');

(async () => {
const hash = await bcrypt.hash('password', 12);

console.log(await bcrypt.compare('password', hash)); // true
console.log(await bcrypt.compare('not password', hash)); // false
console.log(bcrypt.getRounds(hash)); // 12
})();
bcrypt.getRounds(hash)
// 12
```


## API

### bcrypt.hash(password, logRounds, callback)
### bcrypt.hash(password, logRounds)

Hashes a password using 2\*\*`logRounds` rounds. The callback receives two
arguments: `(error, hash)`, where `hash` is a 60-character string. `logRounds`
should be at least 4 and at most 31. Aim for 0.1 seconds per hash or more.
Hashes a password using 2\*\*`logRounds` rounds, returning a promise. The hash
is a 60-character string. `logRounds` should be at least 4 and at most 31. Aim
for 0.1 seconds per hash or more.

### bcrypt.compare(password, expectedHash, callback)
### bcrypt.compare(password, expectedHash)

Compares a password to a hash. The callback receives two arguments:
`(error, result)`, where `result` is `true` if the password matches the hash and
`false` if it does not.
Compares a password to a hash, returning a promise that resolves to `true` if
the password matches the hash and `false` if it does not.

### bcrypt.getRounds(hash)

Expand Down
66 changes: 30 additions & 36 deletions index.js → index.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use strict';
import {Buffer} from 'node:buffer';
import * as crypto from 'node:crypto';
import {createRequire} from 'node:module';

const binding = require('./build/Release/bcrypt');
const crypto = require('crypto');
const require = createRequire(import.meta.url);
const binding = require('./build/Release/bcrypt.node');

const BCRYPT_PREFIX = '$2b$';
const BCRYPT_BASE64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
Expand Down Expand Up @@ -43,7 +45,7 @@ const bcryptBase64 = bytes => {
const padLogRounds = logRounds =>
logRounds < 10 ? '0' + logRounds : '' + logRounds;

const hash = (password, logRounds, callback) => {
export const hash = (password, logRounds) => {
if (typeof password !== 'string') {
throw new TypeError('Password must be a string');
}
Expand All @@ -56,27 +58,29 @@ const hash = (password, logRounds, callback) => {
throw new RangeError('logRounds must be at least 4 and at most 31');
}

if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function');
}

if (Buffer.byteLength(password, 'utf8') > 72) {
process.nextTick(callback, new RangeError('Password cannot be longer than 72 UTF-8 bytes'), undefined);
return;
return Promise.reject(new RangeError(`Password cannot be longer than 72 UTF-8 bytes`));
}

if (password.includes('\0')) {
process.nextTick(callback, new Error('Password cannot contain null characters'), undefined);
return;
return Promise.reject(new Error('Password cannot contain null characters'));
}

const saltBytes = crypto.randomBytes(16);
const salt = BCRYPT_PREFIX + padLogRounds(logRounds) + '$' + bcryptBase64(saltBytes);

binding(password, salt, callback);
return new Promise((resolve, reject) => {
binding(password, salt, (error, hash) => {
if (error) {
reject(error);
} else {
resolve(hash);
}
});
});
};

const compare = (password, expectedHash, callback) => {
export const compare = (password, expectedHash) => {
if (typeof password !== 'string') {
throw new TypeError('Password must be a string');
}
Expand All @@ -85,30 +89,26 @@ const compare = (password, expectedHash, callback) => {
throw new TypeError('Hash must be a string');
}

if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function');
}

if (Buffer.byteLength(password, 'utf8') > 72) {
process.nextTick(callback, new RangeError('Password cannot be longer than 72 UTF-8 bytes'), undefined);
return;
return Promise.reject(new RangeError(`Password cannot be longer than 72 UTF-8 bytes`));
}

if (password.indexOf('\0') !== -1) {
process.nextTick(callback, new Error('Password cannot contain null characters'), undefined);
return;
if (password.includes('\0')) {
return Promise.reject(new Error('Password cannot contain null characters'));
}

binding(password, expectedHash, (error, hash) => {
if (error) {
callback(error, undefined);
} else {
callback(null, expectedHash === hash);
}
return new Promise((resolve, reject) => {
binding(password, expectedHash, (error, hash) => {
if (error) {
reject(error);
} else {
resolve(expectedHash === hash);
}
});
});
};

const getRounds = hash => {
export const getRounds = hash => {
if (typeof hash !== 'string') {
throw new TypeError('Hash must be a string');
}
Expand All @@ -121,9 +121,3 @@ const getRounds = hash => {
throw new Error('Invalid hash');
}
};

module.exports = {
hash,
compare,
getRounds,
};
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"url": "https://github.com/charmander/bcrypt-small/issues"
},
"license": "ISC",
"exports": {
".": "./index.mjs"
},
"files": [
"index.js",
"promises.js",
"index.mjs",
"binding.gyp",
"src/bcrypt-addon.c",
"src/bcrypt.c",
Expand All @@ -28,7 +30,7 @@
"url": "https://github.com/charmander/bcrypt-small"
},
"scripts": {
"test": "node test"
"test": "node test.mjs"
},
"engines": {
"node": ">=18.0.0 || ^16.17.0"
Expand Down
49 changes: 0 additions & 49 deletions promises.js

This file was deleted.

Loading

0 comments on commit 7fc549b

Please sign in to comment.