Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@adonisjs/hash

Package Overview
Dependencies
Maintainers
3
Versions
63
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@adonisjs/hash - npm Package Compare versions

Comparing version 8.3.1-6 to 8.3.1-7

build/chunk-7RS6HCBK.js

32

build/factories/main.d.ts

@@ -1,1 +0,31 @@

export { HashManagerFactory } from './hash_manager.js';
import { ManagerDriverFactory } from '../src/types.js';
import { HashManager } from '../index.js';
import { Scrypt } from '../src/drivers/scrypt.js';
type Config<KnownHashers extends Record<string, ManagerDriverFactory>> = {
default?: keyof KnownHashers;
list: KnownHashers;
};
/**
* Hash manager factory is used to create an instance of hash manager
* for testing
*/
declare class HashManagerFactory<KnownHashers extends Record<string, ManagerDriverFactory> = {
scrypt: () => Scrypt;
}> {
#private;
constructor(config?: {
default?: keyof KnownHashers;
list: KnownHashers;
});
/**
* Merge factory parameters
*/
merge<Hashers extends Record<string, ManagerDriverFactory>>(config: Config<Hashers>): HashManagerFactory<Hashers>;
/**
* Create hash manager instance
*/
create(): HashManager<KnownHashers>;
}
export { HashManagerFactory };

@@ -1,9 +0,40 @@

/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export { HashManagerFactory } from './hash_manager.js';
import {
HashManager
} from "../chunk-WTHUFHHU.js";
import {
Scrypt
} from "../chunk-ZUKNXYT4.js";
import "../chunk-7RS6HCBK.js";
import "../chunk-JSA56AE7.js";
import "../chunk-AXCKF2IS.js";
// factories/hash_manager.ts
var HashManagerFactory = class _HashManagerFactory {
/**
* Config accepted by hash manager
*/
#config;
constructor(config) {
this.#config = config || {
default: "scrypt",
list: {
scrypt: () => new Scrypt({})
}
};
}
/**
* Merge factory parameters
*/
merge(config) {
return new _HashManagerFactory(config);
}
/**
* Create hash manager instance
*/
create() {
return new HashManager(this.#config);
}
};
export {
HashManagerFactory
};

@@ -1,2 +0,109 @@

export { Hash } from './src/hash.js';
export { HashManager } from './src/hash_manager.js';
import { HashDriverContract, ManagerDriverFactory } from './src/types.js';
/**
* Hash and verify values using a dedicated hash driver. The Hash
* works as an adapter across different drivers.
*
* ```ts
* const hash = new Hash(new Argon())
* const hashedPassword = await hash.make('secret')
*
* const isValid = await hash.verify(hashedPassword, 'secret')
* console.log(isValid)
* ```
*/
declare class Hash implements HashDriverContract {
#private;
constructor(driver: HashDriverContract);
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash
*/
isValidHash(value: string): boolean;
/**
* Hash plain text value
*/
make(value: string): Promise<string>;
/**
* Verify the plain text value against an existing hash
*/
verify(hashedValue: string, plainValue: string): Promise<boolean>;
/**
* Find if the hash value needs a rehash or not.
*/
needsReHash(hashedValue: string): boolean;
/**
* Assert the plain value passes the hash verification
*/
assertEquals(hashedValue: string, plainValue: string): Promise<void>;
/**
* Assert the plain value fails the hash verification
*/
assertNotEquals(hashedValue: string, plainValue: string): Promise<void>;
}
/**
* HashManager implements the manager/builder pattern to create a use multiple
* hashing algorithm without self managing hash instance.
*
* ```ts
* const manager = new HashManager({
* default: 'argon',
* list: {
* argon: () => new ArgonDriver(),
* bcrypt: () => new BcryptDriver(),
* }
* })
* ```
*/
declare class HashManager<KnownHashers extends Record<string, ManagerDriverFactory>> implements HashDriverContract {
#private;
constructor(config: {
default?: keyof KnownHashers;
list: KnownHashers;
});
/**
* Use one of the registered hashers to hash values.
*
* ```ts
* manager.use() // returns default hasher
* manager.use('argon')
* ```
*/
use<Hasher extends keyof KnownHashers>(hasher?: Hasher): Hash;
/**
* Fake hash drivers to disable hashing values
*/
fake(): void;
/**
* Restore fake
*/
restore(): void;
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash
*/
isValidHash(value: string): boolean;
/**
* Hash plain text value
*/
make(value: string): Promise<string>;
/**
* Verify the plain text value against an existing hash
*/
verify(hashedValue: string, plainValue: string): Promise<boolean>;
/**
* Find if the hash value needs a rehash or not.
*/
needsReHash(hashedValue: string): boolean;
/**
* Assert the plain value passes the hash verification
*/
assertEquals(hashedValue: string, plainValue: string): Promise<void>;
/**
* Assert the plain value fails the hash verification
*/
assertNotEquals(hashedValue: string, plainValue: string): Promise<void>;
}
export { Hash, HashManager };

19

build/index.js

@@ -1,10 +0,9 @@

/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export { Hash } from './src/hash.js';
export { HashManager } from './src/hash_manager.js';
import {
Hash,
HashManager
} from "./chunk-WTHUFHHU.js";
import "./chunk-AXCKF2IS.js";
export {
Hash,
HashManager
};

@@ -1,2 +0,3 @@

import type { ArgonConfig, HashDriverContract } from '../types.js';
import { HashDriverContract, ArgonConfig } from '../types.js';
/**

@@ -16,3 +17,3 @@ * Hash driver built on top of "argon2" hash algorigthm. Under the hood

*/
export declare class Argon implements HashDriverContract {
declare class Argon implements HashDriverContract {
#private;

@@ -69,1 +70,3 @@ constructor(config: ArgonConfig);

}
export { Argon };

@@ -1,295 +0,241 @@

/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { safeEqual } from '@poppinss/utils';
import { PhcFormatter } from '../phc_formatter.js';
import { MAX_UINT24, MAX_UINT32, EnumValidator, RangeValidator, randomBytesAsync, } from '../helpers.js';
/**
* Hash driver built on top of "argon2" hash algorigthm. Under the hood
* we make use of the "argon2" npm package.
*
* The Argon implementation uses the PHC formatting for creating
* and verifying hashes.
*
* ```ts
* const argon = new Argon({})
*
* await argon.make('secret')
* // $argon2id$v=19$t=3,m=4096,p=1$drxJBWzWahR5tMubp+a1Sw$L/Oh2uw6QKW77i/KQ8eGuOt3ui52hEmmKlu1KBVBxiM
* ```
*/
export class Argon {
/**
* Lazily loaded argon2 binding. Since it is a peer dependency
* we cannot import it at top level
*/
#binding;
/**
* Config with defaults merged
*/
#config;
/**
* Formatter to serialize and deserialize phc string
*/
#phcFormatter = new PhcFormatter();
/**
* Supported variants
*/
#variants = {
i: 0,
d: 1,
id: 2,
import {
EnumValidator,
MAX_UINT24,
MAX_UINT32,
RangeValidator,
randomBytesAsync
} from "../../chunk-7RS6HCBK.js";
import {
PhcFormatter
} from "../../chunk-JSA56AE7.js";
import "../../chunk-AXCKF2IS.js";
// src/drivers/argon.ts
import { safeEqual } from "@poppinss/utils";
var Argon = class {
/**
* Lazily loaded argon2 binding. Since it is a peer dependency
* we cannot import it at top level
*/
#binding;
/**
* Config with defaults merged
*/
#config;
/**
* Formatter to serialize and deserialize phc string
*/
#phcFormatter = new PhcFormatter();
/**
* Supported variants
*/
#variants = {
i: 0,
d: 1,
id: 2
};
/**
* A list of supported argon ids
*/
#ids = ["argon2d", "argon2i", "argon2id"];
constructor(config) {
this.#config = {
version: 19,
variant: "id",
iterations: 3,
memory: 65536,
parallelism: 4,
saltSize: 16,
hashLength: 32,
...config
};
/**
* A list of supported argon ids
*/
#ids = ['argon2d', 'argon2i', 'argon2id'];
constructor(config) {
this.#config = {
version: 0x13,
variant: 'id',
iterations: 3,
memory: 65536,
parallelism: 4,
saltSize: 16,
hashLength: 32,
...config,
};
this.#validateConfig();
this.#validateConfig();
}
/**
* Dynamically importing underlying binding
*/
async #importBinding() {
if (this.#binding) {
return this.#binding;
}
/**
* Dynamically importing underlying binding
*/
async #importBinding() {
if (this.#binding) {
return this.#binding;
}
this.#binding = await import('argon2');
return this.#binding;
this.#binding = await import("argon2");
return this.#binding;
}
/**
* Validate configuration options
*/
#validateConfig() {
RangeValidator.validate("iterations", this.#config.iterations, [2, MAX_UINT32]);
RangeValidator.validate("parallelism", this.#config.parallelism, [1, MAX_UINT24]);
RangeValidator.validate("memory", this.#config.memory, [
8 * this.#config.parallelism,
MAX_UINT32
]);
EnumValidator.validate("variant", this.#config.variant, Object.keys(this.#variants));
RangeValidator.validate("saltSize", this.#config.saltSize, [8, 1024]);
RangeValidator.validate("hashLength", this.#config.hashLength, [4, MAX_UINT32]);
EnumValidator.validate("version", this.#config.version, [16, 19]);
Object.freeze(this.#config);
}
/**
* Validate phc hash string
*/
#validatePhcString(phcString) {
const phcNode = this.#phcFormatter.deserialize(phcString);
if (!phcNode.version) {
phcNode.version = 16;
}
/**
* Validate configuration options
*/
#validateConfig() {
RangeValidator.validate('iterations', this.#config.iterations, [2, MAX_UINT32]);
RangeValidator.validate('parallelism', this.#config.parallelism, [1, MAX_UINT24]);
RangeValidator.validate('memory', this.#config.memory, [
8 * this.#config.parallelism,
MAX_UINT32,
]);
EnumValidator.validate('variant', this.#config.variant, Object.keys(this.#variants));
RangeValidator.validate('saltSize', this.#config.saltSize, [8, 1024]);
RangeValidator.validate('hashLength', this.#config.hashLength, [4, MAX_UINT32]);
EnumValidator.validate('version', this.#config.version, [0x10, 0x13]);
Object.freeze(this.#config);
if (!phcNode.params) {
throw new TypeError(`No "params" found in the phc string`);
}
/**
* Validate phc hash string
*/
#validatePhcString(phcString) {
const phcNode = this.#phcFormatter.deserialize(phcString);
/**
* Old argon strings without version
*/
if (!phcNode.version) {
phcNode.version = 0x10;
}
/**
* Validate top level properties to exist
*/
if (!phcNode.params) {
throw new TypeError(`No "params" found in the phc string`);
}
if (!phcNode.salt) {
throw new TypeError(`No "salt" found in the phc string`);
}
if (!phcNode.hash) {
throw new TypeError(`No "hash" found in the phc string`);
}
RangeValidator.validate('salt.byteLength', phcNode.salt.byteLength, [8, 1024]);
RangeValidator.validate('hash.byteLength', phcNode.hash.byteLength, [4, MAX_UINT32]);
/**
* Validate id
*/
EnumValidator.validate('id', phcNode.id, this.#ids);
/**
* Validate variant and extract it
*/
const variant = phcNode.id.split('argon2')[1];
EnumValidator.validate('variant', variant, Object.keys(this.#variants));
/**
* Validate rest of the properties
*/
EnumValidator.validate('version', phcNode.version, [0x10, 0x13]);
RangeValidator.validate('t', phcNode.params.t, [1, MAX_UINT32]);
RangeValidator.validate('p', phcNode.params.p, [1, MAX_UINT24]);
RangeValidator.validate('m', phcNode.params.m, [8 * phcNode.params.p, MAX_UINT32]);
return {
id: phcNode.id,
version: phcNode.version,
hash: phcNode.hash,
salt: phcNode.salt,
params: {
t: phcNode.params.t,
m: phcNode.params.m,
p: phcNode.params.p,
},
variant: variant,
};
if (!phcNode.salt) {
throw new TypeError(`No "salt" found in the phc string`);
}
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash.
*
* ```ts
* argon.isValidHash('hello world') // false
* argon.isValidHash('$argon2id$v=19$t=3,m=4096,p=1$drxJBWzWahR5tMubp+a1Sw$L/Oh2uw6QKW77i/KQ8eGuOt3ui52hEmmKlu1KBVBxiM')
* ```
*/
isValidHash(value) {
try {
this.#validatePhcString(value);
return true;
}
catch {
return false;
}
if (!phcNode.hash) {
throw new TypeError(`No "hash" found in the phc string`);
}
/**
* Hash a plain text value
*
* ```ts
* const hash = await argon.make('password')
* ```
*/
async make(value) {
const driver = await this.#importBinding();
const salt = await randomBytesAsync(this.#config.saltSize);
/**
* Generate hash
*/
const hash = await driver.hash(value, {
salt,
version: this.#config.version,
type: this.#variants[this.#config.variant],
timeCost: this.#config.iterations,
memoryCost: this.#config.memory,
parallelism: this.#config.parallelism,
hashLength: this.#config.hashLength,
raw: true,
});
/**
* Serialize hash
*/
return this.#phcFormatter.serialize(salt, hash, {
id: `argon2${this.#config.variant}`,
version: this.#config.version,
params: {
t: this.#config.iterations,
m: this.#config.memory,
p: this.#config.parallelism,
},
});
RangeValidator.validate("salt.byteLength", phcNode.salt.byteLength, [8, 1024]);
RangeValidator.validate("hash.byteLength", phcNode.hash.byteLength, [4, MAX_UINT32]);
EnumValidator.validate("id", phcNode.id, this.#ids);
const variant = phcNode.id.split("argon2")[1];
EnumValidator.validate("variant", variant, Object.keys(this.#variants));
EnumValidator.validate("version", phcNode.version, [16, 19]);
RangeValidator.validate("t", phcNode.params.t, [1, MAX_UINT32]);
RangeValidator.validate("p", phcNode.params.p, [1, MAX_UINT24]);
RangeValidator.validate("m", phcNode.params.m, [8 * phcNode.params.p, MAX_UINT32]);
return {
id: phcNode.id,
version: phcNode.version,
hash: phcNode.hash,
salt: phcNode.salt,
params: {
t: phcNode.params.t,
m: phcNode.params.m,
p: phcNode.params.p
},
variant
};
}
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash.
*
* ```ts
* argon.isValidHash('hello world') // false
* argon.isValidHash('$argon2id$v=19$t=3,m=4096,p=1$drxJBWzWahR5tMubp+a1Sw$L/Oh2uw6QKW77i/KQ8eGuOt3ui52hEmmKlu1KBVBxiM')
* ```
*/
isValidHash(value) {
try {
this.#validatePhcString(value);
return true;
} catch {
return false;
}
/**
* Verify the plain text value against an existing hash
*
* ```ts
* if (await argon.verify(hash, plainText)) {
*
* }
* ```
*/
async verify(hashedValue, plainValue) {
const driver = await this.#importBinding();
try {
/**
* De-serialize hash and ensure all Phc string properties
* to exist.
*/
const phcNode = this.#validatePhcString(hashedValue);
/**
* Generate a new hash with the same properties
* as the existing hash
*/
const newHash = await driver.hash(plainValue, {
salt: phcNode.salt,
version: phcNode.version,
type: this.#variants[phcNode.variant],
timeCost: phcNode.params.t,
memoryCost: phcNode.params.m,
parallelism: phcNode.params.p,
hashLength: phcNode.hash.byteLength,
raw: true,
});
/**
* Ensure both are equal
*/
return safeEqual(newHash, phcNode.hash);
}
catch {
return false;
}
}
/**
* Hash a plain text value
*
* ```ts
* const hash = await argon.make('password')
* ```
*/
async make(value) {
const driver = await this.#importBinding();
const salt = await randomBytesAsync(this.#config.saltSize);
const hash = await driver.hash(value, {
salt,
version: this.#config.version,
type: this.#variants[this.#config.variant],
timeCost: this.#config.iterations,
memoryCost: this.#config.memory,
parallelism: this.#config.parallelism,
hashLength: this.#config.hashLength,
raw: true
});
return this.#phcFormatter.serialize(salt, hash, {
id: `argon2${this.#config.variant}`,
version: this.#config.version,
params: {
t: this.#config.iterations,
m: this.#config.memory,
p: this.#config.parallelism
}
});
}
/**
* Verify the plain text value against an existing hash
*
* ```ts
* if (await argon.verify(hash, plainText)) {
*
* }
* ```
*/
async verify(hashedValue, plainValue) {
const driver = await this.#importBinding();
try {
const phcNode = this.#validatePhcString(hashedValue);
const newHash = await driver.hash(plainValue, {
salt: phcNode.salt,
version: phcNode.version,
type: this.#variants[phcNode.variant],
timeCost: phcNode.params.t,
memoryCost: phcNode.params.m,
parallelism: phcNode.params.p,
hashLength: phcNode.hash.byteLength,
raw: true
});
return safeEqual(newHash, phcNode.hash);
} catch {
return false;
}
/**
* Find if the hash value needs a rehash or not. The rehash is
* required when.
*
* 1. The argon2 version is changed
* 2. Number of iterations are changed
* 3. The memory value is changed
* 4. The parellelism value is changed
* 5. The argon variant is changed
*
* ```ts
* const isValid = await argon.verify(hash, plainText)
*
* // Plain password is valid and hash needs a rehash
* if (isValid && await argon.needsReHash(hash)) {
* const newHash = await argon.make(plainText)
* }
* ```
*/
needsReHash(value) {
const phcNode = this.#phcFormatter.deserialize(value);
if (!this.#ids.includes(phcNode.id)) {
throw new TypeError('Value is not a valid argon hash');
}
/**
* If config version is separate from hash version, then a
* re-hash is needed
*/
if (phcNode.version !== this.#config.version) {
return true;
}
/**
* If config variant is not same as the hash variant, then a
* re-hash is needed
*/
if (phcNode.id !== `argon2${this.#config.variant}`) {
return true;
}
/**
* Make sure all the encoded params are same as the config.
* Otherwise a re-hash is needed
*/
if (!phcNode.params) {
return true;
}
if (phcNode.params.m !== this.#config.memory) {
return true;
}
if (phcNode.params.t !== this.#config.iterations) {
return true;
}
if (phcNode.params.p !== this.#config.parallelism) {
return true;
}
return false;
}
/**
* Find if the hash value needs a rehash or not. The rehash is
* required when.
*
* 1. The argon2 version is changed
* 2. Number of iterations are changed
* 3. The memory value is changed
* 4. The parellelism value is changed
* 5. The argon variant is changed
*
* ```ts
* const isValid = await argon.verify(hash, plainText)
*
* // Plain password is valid and hash needs a rehash
* if (isValid && await argon.needsReHash(hash)) {
* const newHash = await argon.make(plainText)
* }
* ```
*/
needsReHash(value) {
const phcNode = this.#phcFormatter.deserialize(value);
if (!this.#ids.includes(phcNode.id)) {
throw new TypeError("Value is not a valid argon hash");
}
}
if (phcNode.version !== this.#config.version) {
return true;
}
if (phcNode.id !== `argon2${this.#config.variant}`) {
return true;
}
if (!phcNode.params) {
return true;
}
if (phcNode.params.m !== this.#config.memory) {
return true;
}
if (phcNode.params.t !== this.#config.iterations) {
return true;
}
if (phcNode.params.p !== this.#config.parallelism) {
return true;
}
return false;
}
};
export {
Argon
};

@@ -1,2 +0,3 @@

import type { HashDriverContract, BcryptConfig } from '../types.js';
import { HashDriverContract, BcryptConfig } from '../types.js';
/**

@@ -16,3 +17,3 @@ * Hash driver built on top of "bcrypt" hash algorigthm. Under the hood

*/
export declare class Bcrypt implements HashDriverContract {
declare class Bcrypt implements HashDriverContract {
#private;

@@ -67,1 +68,3 @@ constructor(config: BcryptConfig);

}
export { Bcrypt };

@@ -1,240 +0,411 @@

/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import * as bcryptBase64 from '../legacy/bcrypt_base64.cjs';
import { safeEqual } from '@poppinss/utils';
import { PhcFormatter } from '../phc_formatter.js';
import { EnumValidator, randomBytesAsync, RangeValidator } from '../helpers.js';
/**
* Hash driver built on top of "bcrypt" hash algorigthm. Under the hood
* we make use of the "bcrypt" npm package.
*
* The Bcrypt implementation uses the PHC formatting for creating
* and verifying hashes.
*
* ```ts
* const bcrypt = new Bcrypt({})
*
* await bcrypt.make('secret')
* // $bcrypt$v=98$r=10$Jtxi46WJ26OQ0khsYLLlnw$knXGfuRFsSjXdj88JydPOnUIglvm1S8
* ```
*/
export class Bcrypt {
/**
* Lazily loaded bcrypt binding. Since it is a peer dependency
* we cannot import it at top level
*/
#binding;
/**
* Config with defaults merged
*/
#config;
/**
* Formatter to serialize and deserialize phc string
*/
#phcFormatter = new PhcFormatter();
constructor(config) {
this.#config = {
rounds: 10,
saltSize: 16,
version: 0x62,
...config,
};
this.#validateConfig();
}
/**
* Dynamically importing underlying binding
*/
async #importBinding() {
if (this.#binding) {
return this.#binding;
import {
EnumValidator,
RangeValidator,
randomBytesAsync
} from "../../chunk-7RS6HCBK.js";
import {
PhcFormatter
} from "../../chunk-JSA56AE7.js";
import {
__commonJS,
__toESM
} from "../../chunk-AXCKF2IS.js";
// src/legacy/bcrypt_base64.cjs
var require_bcrypt_base64 = __commonJS({
"src/legacy/bcrypt_base64.cjs"(exports, module) {
var BASE64_CODE = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
var BASE64_INDEX = [
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
0,
1,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
-1,
-1,
-1,
-1,
-1,
-1,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
-1,
-1,
-1,
-1,
-1
];
function encode2(buff) {
const len = buff.byteLength;
let off = 0;
const stra = [];
while (off < len) {
let c1 = buff[off++] & 255;
stra.push(BASE64_CODE[c1 >> 2 & 63]);
c1 = (c1 & 3) << 4;
if (off >= len) {
stra.push(BASE64_CODE[c1 & 63]);
break;
}
this.#binding = await import('bcrypt');
return this.#binding;
let c2 = buff[off++] & 255;
c1 |= c2 >> 4 & 15;
stra.push(BASE64_CODE[c1 & 63]);
c1 = (c2 & 15) << 2;
if (off >= len) {
stra.push(BASE64_CODE[c1 & 63]);
break;
}
c2 = buff[off++] & 255;
c1 |= c2 >> 6 & 3;
stra.push(BASE64_CODE[c1 & 63]);
stra.push(BASE64_CODE[c2 & 63]);
}
return stra.join("");
}
/**
* Generates salt for bcrypt
*/
#generateBcryptSalt(salt, version, rounds) {
const bcryptVersionCharCode = String.fromCharCode(version);
const paddedRounds = rounds > 9 ? `${rounds}` : `0${rounds}`;
return `$2${bcryptVersionCharCode}$${paddedRounds}$${bcryptBase64.encode(salt)}`;
function decode2(str) {
let off = 0;
let olen = 0;
const slen = str.length;
const stra = [];
const len = str.length;
while (off < slen - 1 && olen < len) {
let code = str.charCodeAt(off++);
const c1 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
code = str.charCodeAt(off++);
const c2 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
if (c1 === -1 || c2 === -1)
break;
let o = c1 << 2 >>> 0;
o |= (c2 & 48) >> 4;
stra.push(String.fromCharCode(o));
if (++olen >= len || off >= slen)
break;
code = str.charCodeAt(off++);
const c3 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
if (c3 === -1)
break;
o = (c2 & 15) << 4 >>> 0;
o |= (c3 & 60) >> 2;
stra.push(String.fromCharCode(o));
if (++olen >= len || off >= slen)
break;
code = str.charCodeAt(off++);
const c4 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
o = (c3 & 3) << 6 >>> 0;
o |= c4;
stra.push(String.fromCharCode(o));
++olen;
}
const buffa = [];
for (off = 0; off < olen; off++)
buffa.push(stra[off].charCodeAt(0));
return Buffer.from(buffa);
}
/**
* Validate config
*/
#validateConfig() {
RangeValidator.validate('rounds', this.#config.rounds, [4, 31]);
RangeValidator.validate('saltSize', this.#config.saltSize, [8, 1024]);
EnumValidator.validate('version', this.#config.version, [0x61, 0x62]);
Object.freeze(this.#config);
module.exports = {
encode: encode2,
decode: decode2
};
}
});
// src/drivers/bcrypt.ts
var bcryptBase64 = __toESM(require_bcrypt_base64(), 1);
import { safeEqual } from "@poppinss/utils";
var Bcrypt = class {
/**
* Lazily loaded bcrypt binding. Since it is a peer dependency
* we cannot import it at top level
*/
#binding;
/**
* Config with defaults merged
*/
#config;
/**
* Formatter to serialize and deserialize phc string
*/
#phcFormatter = new PhcFormatter();
constructor(config) {
this.#config = {
rounds: 10,
saltSize: 16,
version: 98,
...config
};
this.#validateConfig();
}
/**
* Dynamically importing underlying binding
*/
async #importBinding() {
if (this.#binding) {
return this.#binding;
}
/**
* Validate phc hash string
*/
#validatePhcString(phcString) {
const phcNode = this.#phcFormatter.deserialize(phcString);
/**
* Old bcrypt strings without version
*/
if (!phcNode.version) {
phcNode.version = 0x61;
}
/**
* Validate top level properties to exist
*/
if (phcNode.id !== 'bcrypt') {
throw new TypeError(`Invalid "id" found in the phc string`);
}
if (!phcNode.params) {
throw new TypeError(`No "params" found in the phc string`);
}
if (!phcNode.salt) {
throw new TypeError(`No "salt" found in the phc string`);
}
if (!phcNode.hash) {
throw new TypeError(`No "hash" found in the phc string`);
}
if (!phcNode.hash.byteLength) {
throw new TypeError(`No "hash" found in the phc string`);
}
RangeValidator.validate('salt.byteLength', phcNode.salt.byteLength, [8, 1024]);
/**
* Validate rest of the properties
*/
EnumValidator.validate('version', phcNode.version, [0x61, 0x62]);
RangeValidator.validate('r', phcNode.params.r, [4, 31]);
return {
id: phcNode.id,
version: phcNode.version,
hash: phcNode.hash,
salt: phcNode.salt,
params: {
r: phcNode.params.r,
},
};
this.#binding = await import("bcrypt");
return this.#binding;
}
/**
* Generates salt for bcrypt
*/
#generateBcryptSalt(salt, version, rounds) {
const bcryptVersionCharCode = String.fromCharCode(version);
const paddedRounds = rounds > 9 ? `${rounds}` : `0${rounds}`;
return `$2${bcryptVersionCharCode}$${paddedRounds}$${bcryptBase64.encode(salt)}`;
}
/**
* Validate config
*/
#validateConfig() {
RangeValidator.validate("rounds", this.#config.rounds, [4, 31]);
RangeValidator.validate("saltSize", this.#config.saltSize, [8, 1024]);
EnumValidator.validate("version", this.#config.version, [97, 98]);
Object.freeze(this.#config);
}
/**
* Validate phc hash string
*/
#validatePhcString(phcString) {
const phcNode = this.#phcFormatter.deserialize(phcString);
if (!phcNode.version) {
phcNode.version = 97;
}
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash.
*
* ```ts
* bcrypt.isValidHash('hello world') // false
* bcrypt.isValidHash('$bcrypt$v=98$r=10$Jtxi46WJ26OQ0khsYLLlnw$knXGfuRFsSjXdj88JydPOnUIglvm1S8')
* ```
*/
isValidHash(value) {
try {
this.#validatePhcString(value);
return true;
}
catch {
return false;
}
if (phcNode.id !== "bcrypt") {
throw new TypeError(`Invalid "id" found in the phc string`);
}
/**
* Hash a plain text value
*
* ```ts
* const hash = await bcrypt.make('password')
* ```
*/
async make(value) {
const driver = await this.#importBinding();
/**
* Generate salt including bcrypt formatted salt
*/
const salt = await randomBytesAsync(this.#config.saltSize);
const bcryptSalt = this.#generateBcryptSalt(salt, this.#config.version, this.#config.rounds);
/**
* Generate hash
*/
const bcryptHash = await driver.hash(value, bcryptSalt);
const hash = bcryptBase64.decode(bcryptHash.split(bcryptSalt)[1]);
return this.#phcFormatter.serialize(salt, hash, {
id: 'bcrypt',
version: this.#config.version,
params: {
r: this.#config.rounds,
},
});
if (!phcNode.params) {
throw new TypeError(`No "params" found in the phc string`);
}
/**
* Verify the plain text value against an existing hash
*
* ```ts
* if (await bcrypt.verify(hash, plainText)) {
*
* }
* ```
*/
async verify(hashedValue, plainValue) {
const driver = await this.#importBinding();
try {
if (hashedValue.startsWith('$2b') || hashedValue.startsWith('$2a')) {
return await driver.compare(plainValue, hashedValue);
}
/**
* De-serialize hash and ensure all Phc string properties
* to exist.
*/
const phcNode = this.#validatePhcString(hashedValue);
const bcryptSalt = this.#generateBcryptSalt(phcNode.salt, phcNode.version, phcNode.params.r);
const bcryptHash = await driver.hash(plainValue, bcryptSalt);
const hash = bcryptBase64.decode(bcryptHash.split(bcryptSalt)[1]);
return safeEqual(hash, phcNode.hash);
}
catch {
return false;
}
if (!phcNode.salt) {
throw new TypeError(`No "salt" found in the phc string`);
}
/**
* Find if the hash value needs a rehash or not. The rehash is
* required when.
*
* 1. The bcrypt version is changed
* 2. Number of rounds are changed
* 3. Bcrypt hash is not using MCF hash format
*
* ```ts
* const isValid = await bcrypt.verify(hash, plainText)
*
* // Plain password is valid and hash needs a rehash
* if (isValid && await bcrypt.needsReHash(hash)) {
* const newHash = await bcrypt.make(plainText)
* }
* ```
*/
needsReHash(value) {
if (value.startsWith('$2b') || value.startsWith('$2a')) {
return true;
}
const phcNode = this.#phcFormatter.deserialize(value);
if (phcNode.id !== 'bcrypt') {
throw new TypeError('Value is not a valid bcrypt hash');
}
/**
* If config version is separate from hash version, then a
* re-hash is needed
*/
if (phcNode.version !== this.#config.version) {
return true;
}
/**
* Make sure all the encoded params are same as the config.
* Otherwise a re-hash is needed
*/
if (!phcNode.params) {
return true;
}
if (phcNode.params.r !== this.#config.rounds) {
return true;
}
return false;
if (!phcNode.hash) {
throw new TypeError(`No "hash" found in the phc string`);
}
}
if (!phcNode.hash.byteLength) {
throw new TypeError(`No "hash" found in the phc string`);
}
RangeValidator.validate("salt.byteLength", phcNode.salt.byteLength, [8, 1024]);
EnumValidator.validate("version", phcNode.version, [97, 98]);
RangeValidator.validate("r", phcNode.params.r, [4, 31]);
return {
id: phcNode.id,
version: phcNode.version,
hash: phcNode.hash,
salt: phcNode.salt,
params: {
r: phcNode.params.r
}
};
}
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash.
*
* ```ts
* bcrypt.isValidHash('hello world') // false
* bcrypt.isValidHash('$bcrypt$v=98$r=10$Jtxi46WJ26OQ0khsYLLlnw$knXGfuRFsSjXdj88JydPOnUIglvm1S8')
* ```
*/
isValidHash(value) {
try {
this.#validatePhcString(value);
return true;
} catch {
return false;
}
}
/**
* Hash a plain text value
*
* ```ts
* const hash = await bcrypt.make('password')
* ```
*/
async make(value) {
const driver = await this.#importBinding();
const salt = await randomBytesAsync(this.#config.saltSize);
const bcryptSalt = this.#generateBcryptSalt(salt, this.#config.version, this.#config.rounds);
const bcryptHash = await driver.hash(value, bcryptSalt);
const hash = bcryptBase64.decode(bcryptHash.split(bcryptSalt)[1]);
return this.#phcFormatter.serialize(salt, hash, {
id: "bcrypt",
version: this.#config.version,
params: {
r: this.#config.rounds
}
});
}
/**
* Verify the plain text value against an existing hash
*
* ```ts
* if (await bcrypt.verify(hash, plainText)) {
*
* }
* ```
*/
async verify(hashedValue, plainValue) {
const driver = await this.#importBinding();
try {
if (hashedValue.startsWith("$2b") || hashedValue.startsWith("$2a")) {
return await driver.compare(plainValue, hashedValue);
}
const phcNode = this.#validatePhcString(hashedValue);
const bcryptSalt = this.#generateBcryptSalt(phcNode.salt, phcNode.version, phcNode.params.r);
const bcryptHash = await driver.hash(plainValue, bcryptSalt);
const hash = bcryptBase64.decode(bcryptHash.split(bcryptSalt)[1]);
return safeEqual(hash, phcNode.hash);
} catch {
return false;
}
}
/**
* Find if the hash value needs a rehash or not. The rehash is
* required when.
*
* 1. The bcrypt version is changed
* 2. Number of rounds are changed
* 3. Bcrypt hash is not using MCF hash format
*
* ```ts
* const isValid = await bcrypt.verify(hash, plainText)
*
* // Plain password is valid and hash needs a rehash
* if (isValid && await bcrypt.needsReHash(hash)) {
* const newHash = await bcrypt.make(plainText)
* }
* ```
*/
needsReHash(value) {
if (value.startsWith("$2b") || value.startsWith("$2a")) {
return true;
}
const phcNode = this.#phcFormatter.deserialize(value);
if (phcNode.id !== "bcrypt") {
throw new TypeError("Value is not a valid bcrypt hash");
}
if (phcNode.version !== this.#config.version) {
return true;
}
if (!phcNode.params) {
return true;
}
if (phcNode.params.r !== this.#config.rounds) {
return true;
}
return false;
}
};
export {
Bcrypt
};

@@ -1,2 +0,3 @@

import type { ScryptConfig, HashDriverContract } from '../types.js';
import { HashDriverContract, ScryptConfig } from '../types.js';
/**

@@ -16,3 +17,3 @@ * Hash driver built on top of "scrypt" hash algorigthm. Under the hood

*/
export declare class Scrypt implements HashDriverContract {
declare class Scrypt implements HashDriverContract {
#private;

@@ -67,1 +68,3 @@ constructor(config: ScryptConfig);

}
export { Scrypt };

@@ -1,236 +0,9 @@

/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { safeEqual } from '@poppinss/utils';
import { PhcFormatter } from '../phc_formatter.js';
import { randomBytesAsync, RangeValidator, scryptAsync, MAX_UINT32 } from '../helpers.js';
/**
* Hash driver built on top of "scrypt" hash algorigthm. Under the hood
* we make use of the Node.js crypto module
*
* The Scrypt implementation uses the PHC formatting for creating
* and verifying hashes.
*
* ```ts
* const scrypt = new Scrypt({})
*
* await scrypt.make('secret')
* // $scrypt$n=16384,r=8,p=1$iILKD1gVSx6bqualYqyLBQ$DNzIISdmTQS6sFdQ1tJ3UCZ7Uun4uGHNjj0x8FHOqB0pf2LYsu9Xaj5MFhHg21qBz8l5q/oxpeV+ZkgTAj+OzQ
* ```
*/
export class Scrypt {
/**
* Config with defaults merged
*/
#config;
/**
* Formatter to serialize and deserialize phc string
*/
#phcFormatter = new PhcFormatter();
constructor(config) {
this.#config = {
cost: 16384,
blockSize: 8,
parallelization: 1,
saltSize: 16,
keyLength: 64,
maxMemory: 32 * 1024 * 1024,
...config,
};
this.#validateConfig();
}
/**
* Validate config
*/
#validateConfig() {
RangeValidator.validate('blockSize', this.#config.blockSize, [1, MAX_UINT32]);
RangeValidator.validate('cost', this.#config.cost, [2, MAX_UINT32]);
RangeValidator.validate('parallelization', this.#config.parallelization, [
1,
Math.floor(((Math.pow(2, 32) - 1) * 32) / (128 * this.#config.blockSize)),
]);
RangeValidator.validate('saltSize', this.#config.saltSize, [8, 1024]);
RangeValidator.validate('keyLength', this.#config.keyLength, [64, 128]);
RangeValidator.validate('maxMemory', this.#config.maxMemory, [
128 * this.#config.cost * this.#config.blockSize + 1,
MAX_UINT32,
]);
Object.freeze(this.#config);
}
/**
* Validate phc hash string
*/
#validatePhcString(phcString) {
const phcNode = this.#phcFormatter.deserialize(phcString);
/**
* Validate top level properties to exist
*/
if (phcNode.id !== 'scrypt') {
throw new TypeError(`Invalid "id" found in the phc string`);
}
if (!phcNode.params) {
throw new TypeError(`No "params" found in the phc string`);
}
if (!phcNode.salt) {
throw new TypeError(`No "salt" found in the phc string`);
}
if (!phcNode.hash) {
throw new TypeError(`No "hash" found in the phc string`);
}
RangeValidator.validate('hash.byteLength', phcNode.hash.byteLength, [64, 128]);
RangeValidator.validate('salt.byteLength', phcNode.salt.byteLength, [8, 1024]);
/**
* blockSize
*/
RangeValidator.validate('r', phcNode.params.r, [1, MAX_UINT32]);
/**
* Cost
*/
RangeValidator.validate('n', phcNode.params.n, [1, MAX_UINT32]);
/**
* Parallelization
*/
RangeValidator.validate('p', phcNode.params.p, [
1,
Math.floor(((Math.pow(2, 32) - 1) * 32) / (128 * phcNode.params.r)),
]);
return {
id: phcNode.id,
hash: phcNode.hash,
salt: phcNode.salt,
params: {
r: phcNode.params.r,
n: phcNode.params.n,
p: phcNode.params.p,
},
};
}
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash.
*
* ```ts
* scrypt.isValidHash('hello world') // false
* scrypt.isValidHash('$scrypt$n=16384,r=8,p=1$iILKD1gVSx6bqualYqyLBQ$DNzIISdmTQS6sFdQ1tJ3UCZ7Uun4uGHNjj0x8FHOqB0pf2LYsu9Xaj5MFhHg21qBz8l5q/oxpeV+ZkgTAj+OzQ')
* ```
*/
isValidHash(value) {
try {
this.#validatePhcString(value);
return true;
}
catch {
return false;
}
}
/**
* Hash a plain text value
*
* ```ts
* const hash = await scrypt.make('password')
* ```
*/
async make(value) {
const salt = await randomBytesAsync(this.#config.saltSize);
/**
* Generate hash
*/
const hash = await scryptAsync(value, salt, this.#config.keyLength, {
cost: this.#config.cost,
blockSize: this.#config.blockSize,
parallelization: this.#config.parallelization,
maxmem: this.#config.maxMemory,
});
/**
* Serialize hash
*/
return this.#phcFormatter.serialize(salt, hash, {
id: 'scrypt',
params: {
n: this.#config.cost,
r: this.#config.blockSize,
p: this.#config.parallelization,
},
});
}
/**
* Verify the plain text value against an existing hash
*
* ```ts
* if (await scrypt.verify(hash, plainText)) {
*
* }
* ```
*/
async verify(hashedValue, plainValue) {
try {
/**
* De-serialize hash and ensure all Phc string properties
* to exist.
*/
const phcNode = this.#validatePhcString(hashedValue);
/**
* Generate a new hash with the same properties
* as the existing hash
*/
const newHash = await scryptAsync(plainValue, phcNode.salt, phcNode.hash.byteLength, {
cost: phcNode.params.n,
blockSize: phcNode.params.r,
parallelization: phcNode.params.p,
maxmem: this.#config.maxMemory,
});
/**
* Ensure both are equal
*/
return safeEqual(newHash, phcNode.hash);
}
catch {
return false;
}
}
/**
* Find if the hash value needs a rehash or not. The rehash is
* required when.
*
* 1. The cost value is changed
* 2. The blockSize value is changed
* 3. The parallelization value is changed
*
* ```ts
* const isValid = await scrypt.verify(hash, plainText)
*
* // Plain password is valid and hash needs a rehash
* if (isValid && await scrypt.needsReHash(hash)) {
* const newHash = await scrypt.make(plainText)
* }
* ```
*/
needsReHash(value) {
const phcNode = this.#phcFormatter.deserialize(value);
if (phcNode.id !== 'scrypt') {
throw new TypeError('Value is not a valid scrypt hash');
}
/**
* Make sure all the encoded params are same as the config.
* Otherwise a re-hash is needed
*/
if (!phcNode.params) {
return true;
}
if (phcNode.params.n !== this.#config.cost) {
return true;
}
if (phcNode.params.r !== this.#config.blockSize) {
return true;
}
if (phcNode.params.p !== this.#config.parallelization) {
return true;
}
return false;
}
}
import {
Scrypt
} from "../../chunk-ZUKNXYT4.js";
import "../../chunk-7RS6HCBK.js";
import "../../chunk-JSA56AE7.js";
import "../../chunk-AXCKF2IS.js";
export {
Scrypt
};

@@ -1,3 +0,3 @@

/// <reference types="@types/node" resolution-mode="require"/>
import { PhcNode } from './types.js';
/**

@@ -7,3 +7,3 @@ * Phc formatter is used to serialize a hash to a phc string and

*/
export declare class PhcFormatter<Params extends Record<string, string | number> = Record<string, string | number>> {
declare class PhcFormatter<Params extends Record<string, string | number> = Record<string, string | number>> {
/**

@@ -22,1 +22,3 @@ * Serialize salt and hash with predefined options.

}
export { PhcFormatter };

@@ -1,34 +0,7 @@

/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// @ts-expect-error
import phc from '@phc/format';
/**
* Phc formatter is used to serialize a hash to a phc string and
* deserialize it back to a PHC object.
*/
export class PhcFormatter {
/**
* Serialize salt and hash with predefined options.
*/
serialize(salt, hash, options) {
return phc.serialize({
id: options.id,
version: options.version,
params: options.params,
salt,
hash,
});
}
/**
* Deserialize a PHC string to an object
*/
deserialize(phcString) {
return phc.deserialize(phcString);
}
}
import {
PhcFormatter
} from "../chunk-JSA56AE7.js";
import "../chunk-AXCKF2IS.js";
export {
PhcFormatter
};

@@ -1,6 +0,5 @@

/// <reference types="@types/node" resolution-mode="require"/>
/**
* The contract Hash drivers should adhere to
*/
export interface HashDriverContract {
interface HashDriverContract {
/**

@@ -27,3 +26,3 @@ * Check if the value is a valid hash. This method just checks

*/
export type PhcNode<Params extends Record<string, string | number> = Record<string, string | number>> = {
type PhcNode<Params extends Record<string, string | number> = Record<string, string | number>> = {
id: string;

@@ -38,7 +37,7 @@ salt: Buffer;

*/
export type ArgonVariants = 'd' | 'i' | 'id';
type ArgonVariants = 'd' | 'i' | 'id';
/**
* Shape of argon2 config
*/
export type ArgonConfig = {
type ArgonConfig = {
/**

@@ -96,3 +95,3 @@ * The argon hash type to use.

*/
export type BcryptConfig = {
type BcryptConfig = {
/**

@@ -121,3 +120,3 @@ * The cost of processing the data

*/
export type ScryptConfig = {
type ScryptConfig = {
/**

@@ -165,2 +164,4 @@ * CPU/memory cost parameter. Must be a power of two greater than one.

*/
export type ManagerDriverFactory = () => HashDriverContract;
type ManagerDriverFactory = () => HashDriverContract;
export { ArgonConfig, ArgonVariants, BcryptConfig, HashDriverContract, ManagerDriverFactory, PhcNode, ScryptConfig };
{
"name": "@adonisjs/hash",
"version": "8.3.1-6",
"version": "8.3.1-7",
"description": "Framework agnostic Password hashing package with support for PHC string format",

@@ -8,6 +8,3 @@ "main": "build/index.js",

"files": [
"build/src",
"build/factories",
"build/index.d.ts",
"build/index.js"
"build"
],

@@ -31,3 +28,3 @@ "exports": {

"typecheck": "tsc --noEmit",
"compile": "npm run lint && npm run clean && tsc",
"compile": "npm run lint && npm run clean && tsup-node",
"build": "npm run compile",

@@ -53,26 +50,27 @@ "release": "np",

"@adonisjs/tsconfig": "^1.1.8",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@japa/assert": "^2.0.0-1",
"@japa/expect-type": "^2.0.0-0",
"@japa/runner": "^3.0.0-3",
"@swc/core": "^1.3.78",
"@commitlint/cli": "^17.8.0",
"@commitlint/config-conventional": "^17.8.0",
"@japa/assert": "^2.0.0",
"@japa/expect-type": "^2.0.0",
"@japa/runner": "^3.0.2",
"@swc/core": "1.3.82",
"@types/bcrypt": "^5.0.0",
"@types/node": "^20.5.3",
"argon2": "^0.31.0",
"@types/node": "^20.8.6",
"argon2": "^0.31.1",
"bcrypt": "^5.1.1",
"c8": "^8.0.1",
"cross-env": "^7.0.3",
"del-cli": "^5.0.0",
"eslint": "^8.47.0",
"del-cli": "^5.1.0",
"eslint": "^8.51.0",
"github-label-sync": "^2.3.1",
"husky": "^8.0.3",
"np": "^8.0.4",
"prettier": "^3.0.2",
"prettier": "^3.0.3",
"ts-node": "^10.9.1",
"typescript": "^5.1.6"
"tsup": "^7.2.0",
"typescript": "^5.2.2"
},
"dependencies": {
"@phc/format": "^1.0.0",
"@poppinss/utils": "^6.5.0-5"
"@poppinss/utils": "^6.5.0"
},

@@ -127,3 +125,19 @@ "peerDependencies": {

},
"prettier": "@adonisjs/prettier-config"
"prettier": "@adonisjs/prettier-config",
"tsup": {
"entry": [
"./index.ts",
"./src/drivers/argon.ts",
"./src/drivers/bcrypt.ts",
"./src/drivers/scrypt.ts",
"./src/phc_formatter.ts",
"./src/types.ts",
"./factories/main.ts"
],
"outDir": "./build",
"clean": true,
"format": "esm",
"dts": true,
"target": "esnext"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc