diff --git a/.travis.yml b/.travis.yml index be3ad28..e5aa466 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,24 @@ language: node_js cache: npm +branches: + only: + - master + - /^release\/.*$/ stages: - check - test - cov node_js: - - '12' + - '14' + - '15' os: - linux - osx - windows -script: npx nyc -s npm run test:node -- --bail +script: npx nyc -s npm run test -- -t node --bail after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov jobs: diff --git a/README.md b/README.md index c15303d..68cc58d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ - [`bucket.toJSON()`](#buckettojson) - [`bucket.prettyPrint()`](#bucketprettyprint) - [`bucket.tableSize()`](#buckettablesize) - - [`hamt.isBucket(other)`](#hamtisbucketother) - [Contribute](#contribute) - [License](#license) @@ -53,18 +52,18 @@ ### Example ```javascript -const hamt = require('hamt-sharding') +const { createHAMT } = require('hamt-sharding') const crypto = require('crypto-promise') -// decide how to hash things, can return a Promise -const hashFn = async (value) => { +// decide how to hash buffers made from keys, can return a Promise +const hashFn = async (buf) => { return crypto .createHash('sha256') - .update(value) + .update(buf) .digest() } -const bucket = hamt({ +const bucket = createHAMT({ hashFn: hashFn }) @@ -77,14 +76,14 @@ const output = await bucket.get('key') ## API ```javascript -const hamt = require('hamt-sharding') +const { createHAMT } = require('hamt-sharding') ``` ### `bucket.put(key, value)` ```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) +const { createHAMT } = require('hamt-sharding') +const bucket = createHAMT({...}) await bucket.put('key', 'value') ``` @@ -92,8 +91,8 @@ await bucket.put('key', 'value') ### `bucket.get(key)` ```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) +const { createHAMT } = require('hamt-sharding') +const bucket = createHAMT({...}) await bucket.put('key', 'value') @@ -103,8 +102,8 @@ console.info(await bucket.get('key')) // 'value' ### `bucket.del(key)` ```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) +const { createHAMT } = require('hamt-sharding') +const bucket = createHAMT({...}) await bucket.put('key', 'value') await bucket.del('key', 'value') @@ -115,8 +114,8 @@ console.info(await bucket.get('key')) // undefined ### `bucket.leafCount()` ```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) +const { createHAMT } = require('hamt-sharding') +const bucket = createHAMT({...}) console.info(bucket.leafCount()) // 0 @@ -128,8 +127,8 @@ console.info(bucket.leafCount()) // 1 ### `bucket.childrenCount()` ```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) +const { createHAMT } = require('hamt-sharding') +const bucket = createHAMT({...}) console.info(bucket.childrenCount()) // 0 @@ -142,8 +141,8 @@ console.info(bucket.childrenCount()) // 234 -- dependent on hashing algorithm ### `bucket.eachLeafSeries()` ```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) +const { createHAMT } = require('hamt-sharding') +const bucket = createHAMT({...}) await bucket.put('key', 'value') @@ -157,15 +156,6 @@ for await (const child of bucket.eachLeafSeries()) { ### `bucket.toJSON()` ### `bucket.prettyPrint()` ### `bucket.tableSize()` -### `hamt.isBucket(other)` - -```javascript -const hamt = require('hamt-sharding') -const bucket = hamt({...}) - -console.info(hamt.isBucket(bucket)) // true -console.info(hamt.isBucket(true)) // false -``` ## Contribute diff --git a/package.json b/package.json index 65e825b..0e0189a 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,7 @@ "main": "src/index.js", "scripts": { "test": "aegir test", - "test:node": "aegir test -t node", - "test:browser": "aegir test -t browser", - "test:webworker": "aegir test -t webworker", - "build": "aegir build", + "prepare": "aegir build --no-bundle", "lint": "aegir lint", "release": "aegir release", "release-minor": "aegir release --type minor", @@ -35,14 +32,15 @@ }, "homepage": "https://github.com/ipfs-shipyard/js-hamt-sharding#readme", "devDependencies": { - "aegir": "^20.5.0", - "chai": "^4.2.0", - "dirty-chai": "^2.0.1" + "aegir": "^30.3.0", + "multihashing-async": "^2.1.0" }, "dependencies": { - "sparse-array": "^1.3.1" + "sparse-array": "^1.3.1", + "uint8arrays": "^2.1.2" }, "contributors": [ "achingbrain " - ] + ], + "types": "dist/src/index.d.ts" } diff --git a/src/bucket.js b/src/bucket.js index 5d17bfe..43f6fe7 100644 --- a/src/bucket.js +++ b/src/bucket.js @@ -1,40 +1,87 @@ 'use strict' +// @ts-ignore const SparseArray = require('sparse-array') -const wrapHash = require('./consumable-hash') - -const defaultOptions = { - bits: 8 -} - +const uint8ArrayFromString = require('uint8arrays/from-string') + +/** + * @typedef {import('./consumable-hash').InfiniteHash} InfiniteHash + * @typedef {import('../').UserBucketOptions} UserBucketOptions + */ + +/** + * @template V + * @typedef {object} BucketChild + * @property {string} key + * @property {V} value + * @property {InfiniteHash} hash + */ + +/** + * @template B + * + * @typedef {object} SA + * @property {number} length + * @property {() => B[]} compactArray + * @property {(i: number) => B} get + * @property {(i: number, value: B) => void} set + * @property { (fn: (acc: A, curr: B, index: number) => A, initial: A) => B} reduce + * @property {(fn: (item: B) => boolean) => B | undefined} find + * @property {() => number[]} bitField + * @property {(i: number) => void} unset + */ + +/** + * @template T + * + * @typedef {object} BucketPosition + * @property {Bucket} bucket + * @property {number} pos + * @property {InfiniteHash} hash + * @property {BucketChild} [existingChild] + */ + +/** + * @typedef {object} BucketOptions + * @property {number} bits + * @property {(value: Uint8Array | InfiniteHash) => InfiniteHash} hash + */ + +/** + * @template T + */ class Bucket { - constructor (options, parent, posAtParent) { - this._options = Object.assign({}, defaultOptions, options) + /** + * @param {BucketOptions} options + * @param {Bucket} [parent] + * @param {number} [posAtParent=0] + */ + constructor (options, parent, posAtParent = 0) { + this._options = options this._popCount = 0 this._parent = parent this._posAtParent = posAtParent - if (!this._options.hashFn) { - throw new Error('please define an options.hashFn') - } - - // make sure we only wrap options.hashFn once in the whole tree - if (!this._options.hash) { - this._options.hash = wrapHash(this._options.hashFn) - } + /** @type {SA | BucketChild>} */ this._children = new SparseArray() - } - static isBucket (o) { - return o instanceof Bucket + /** @type {string | null} */ + this.key = null } + /** + * @param {string} key + * @param {T} value + */ async put (key, value) { const place = await this._findNewBucketAndPos(key) await place.bucket._putAt(place, key, value) } + /** + * @param {string} key + */ async get (key) { const child = await this._findChild(key) @@ -43,6 +90,9 @@ class Bucket { } } + /** + * @param {string} key + */ async del (key) { const place = await this._findPlace(key) const child = place.bucket._at(place.pos) @@ -52,8 +102,13 @@ class Bucket { } } + /** + * @returns {number} + */ leafCount () { - return this._children.compactArray().reduce((acc, child) => { + const children = this._children.compactArray() + + return children.reduce((acc, child) => { if (child instanceof Bucket) { return acc + child.leafCount() } @@ -70,21 +125,33 @@ class Bucket { return this._children.get(0) } + /** + * @returns {Iterable>} + */ * eachLeafSeries () { const children = this._children.compactArray() for (const child of children) { if (child instanceof Bucket) { - for (const c2 of child.eachLeafSeries()) { - yield c2 - } + yield * child.eachLeafSeries() } else { yield child } } + + // this is necessary because tsc requires a @return annotation as it + // can't derive a return type due to the recursion, and eslint requires + // a return statement when there is a @return annotation + return [] } + /** + * @param {(value: BucketChild, index: number) => T} map + * @param {(reduced: any) => any} reduce + */ serialize (map, reduce) { + /** @type {T[]} */ + const acc = [] // serialize to a custom non-sparse representation return reduce(this._children.reduce((acc, child, index) => { if (child) { @@ -95,9 +162,13 @@ class Bucket { } } return acc - }, [])) + }, acc)) } + /** + * @param {(value: BucketChild) => Promise} asyncMap + * @param {(reduced: any) => Promise} asyncReduce + */ asyncTransform (asyncMap, asyncReduce) { return asyncTransformBucket(this, asyncMap, asyncReduce) } @@ -114,17 +185,31 @@ class Bucket { return Math.pow(2, this._options.bits) } + /** + * @param {string} key + * @returns {Promise | undefined>} + */ async _findChild (key) { const result = await this._findPlace(key) const child = result.bucket._at(result.pos) + if (child instanceof Bucket) { + // should not be possible, this._findPlace should always + // return a location for a child, not a bucket + return undefined + } + if (child && child.key === key) { return child } } + /** + * @param {string | InfiniteHash} key + * @returns {Promise>} + */ async _findPlace (key) { - const hashValue = this._options.hash(key) + const hashValue = this._options.hash(typeof key === 'string' ? uint8ArrayFromString(key) : key) const index = await hashValue.take(this._options.bits) const child = this._children.get(index) @@ -136,23 +221,26 @@ class Bucket { return { bucket: this, pos: index, - hash: hashValue + hash: hashValue, + existingChild: child } } + /** + * @param {string | InfiniteHash} key + * @returns {Promise>} + */ async _findNewBucketAndPos (key) { const place = await this._findPlace(key) - const child = place.bucket._at(place.pos) - if (child && child.key !== key) { + if (place.existingChild && place.existingChild.key !== key) { // conflict - const bucket = new Bucket(this._options, place.bucket, place.pos) place.bucket._putObjectAt(place.pos, bucket) // put the previous value - const newPlace = await bucket._findPlace(child.hash) - newPlace.bucket._putAt(newPlace, child.key, child.value) + const newPlace = await bucket._findPlace(place.existingChild.hash) + newPlace.bucket._putAt(newPlace, place.existingChild.key, place.existingChild.value) return bucket._findNewBucketAndPos(place.hash) } @@ -161,6 +249,11 @@ class Bucket { return place } + /** + * @param {BucketPosition} place + * @param {string} key + * @param {T} value + */ _putAt (place, key, value) { this._putObjectAt(place.pos, { key: key, @@ -169,6 +262,10 @@ class Bucket { }) } + /** + * @param {number} pos + * @param {Bucket | BucketChild} object + */ _putObjectAt (pos, object) { if (!this._children.get(pos)) { this._popCount++ @@ -176,7 +273,14 @@ class Bucket { this._children.set(pos, object) } + /** + * @param {number} pos + */ _delAt (pos) { + if (pos === -1) { + throw new Error('Invalid position') + } + if (this._children.get(pos)) { this._popCount-- } @@ -190,12 +294,13 @@ class Bucket { // remove myself from parent, replacing me with my only child const onlyChild = this._children.find(exists) - if (!(onlyChild instanceof Bucket)) { + if (onlyChild && !(onlyChild instanceof Bucket)) { const hash = onlyChild.hash hash.untake(this._options.bits) const place = { pos: this._posAtParent, - hash: hash + hash: hash, + bucket: this._parent } this._parent._putAt(place, onlyChild.key, onlyChild.value) } @@ -205,23 +310,45 @@ class Bucket { } } + /** + * @param {number} index + * @returns {BucketChild | Bucket | undefined} + */ _at (index) { return this._children.get(index) } } +/** + * @param {any} o + */ function exists (o) { return Boolean(o) } +/** + * + * @param {*} node + * @param {number} index + */ function mapNode (node, index) { return node.key } +/** + * @param {*} nodes + */ function reduceNodes (nodes) { return nodes } +/** + * @template T + * + * @param {Bucket} bucket + * @param {(value: BucketChild) => Promise} asyncMap + * @param {(reduced: any) => Promise} asyncReduce + */ async function asyncTransformBucket (bucket, asyncMap, asyncReduce) { const output = [] @@ -236,9 +363,9 @@ async function asyncTransformBucket (bucket, asyncMap, asyncReduce) { children: mappedChildren }) } - - return asyncReduce(output) } + + return asyncReduce(output) } module.exports = Bucket diff --git a/src/consumable-buffer.js b/src/consumable-buffer.js index 1b0d6c9..b52566a 100644 --- a/src/consumable-buffer.js +++ b/src/consumable-buffer.js @@ -23,6 +23,9 @@ const STOP_MASKS = [ ] module.exports = class ConsumableBuffer { + /** + * @param {Uint8Array} value + */ constructor (value) { this._value = value this._currentBytePos = value.length - 1 @@ -37,6 +40,9 @@ module.exports = class ConsumableBuffer { return this._value.length * 8 } + /** + * @param {number} bits + */ take (bits) { let pendingBits = bits let result = 0 @@ -59,6 +65,9 @@ module.exports = class ConsumableBuffer { return result } + /** + * @param {number} bits + */ untake (bits) { this._currentBitPos += bits while (this._currentBitPos > 7) { @@ -72,11 +81,20 @@ module.exports = class ConsumableBuffer { } } +/** + * @param {number} byte + * @param {number} start + * @param {number} length + */ function byteBitsToInt (byte, start, length) { const mask = maskFor(start, length) return (byte & mask) >>> start } +/** + * @param {number} start + * @param {number} length + */ function maskFor (start, length) { return START_MASKS[start] & STOP_MASKS[Math.min(length + start - 1, 7)] } diff --git a/src/consumable-hash.js b/src/consumable-hash.js index a71901f..d32ddb5 100644 --- a/src/consumable-hash.js +++ b/src/consumable-hash.js @@ -1,9 +1,16 @@ 'use strict' const ConsumableBuffer = require('./consumable-buffer') +const uint8ArrayConcat = require('uint8arrays/concat') -module.exports = function wrapHash (hashFn) { - return function hashing (value) { +/** + * @param {(value: Uint8Array) => Promise} hashFn + */ +function wrapHash (hashFn) { + /** + * @param {InfiniteHash | Uint8Array} value + */ + function hashing (value) { if (value instanceof InfiniteHash) { // already a hash. return it return value @@ -11,21 +18,34 @@ module.exports = function wrapHash (hashFn) { return new InfiniteHash(value, hashFn) } } + + return hashing } class InfiniteHash { + /** + * + * @param {Uint8Array} value + * @param {(value: Uint8Array) => Promise} hashFn + */ constructor (value, hashFn) { - if ((typeof value) !== 'string' && !Buffer.isBuffer(value)) { - throw new Error('can only hash strings or buffers') + if (!(value instanceof Uint8Array)) { + throw new Error('can only hash Uint8Arrays') } + this._value = value this._hashFn = hashFn this._depth = -1 this._availableBits = 0 this._currentBufferIndex = 0 + + /** @type {ConsumableBuffer[]} */ this._buffers = [] } + /** + * @param {number} bits + */ async take (bits) { let pendingBits = bits @@ -51,6 +71,9 @@ class InfiniteHash { return result } + /** + * @param {number} bits + */ untake (bits) { let pendingBits = bits @@ -71,7 +94,7 @@ class InfiniteHash { async _produceMoreBits () { this._depth++ - const value = this._depth ? this._value + this._depth : this._value + const value = this._depth ? uint8ArrayConcat([this._value, Uint8Array.from([this._depth])]) : this._value const hashValue = await this._hashFn(value) const buffer = new ConsumableBuffer(hashValue) @@ -79,3 +102,6 @@ class InfiniteHash { this._availableBits += buffer.availableBits() } } + +module.exports = wrapHash +module.exports.InfiniteHash = InfiniteHash diff --git a/src/index.js b/src/index.js index bdb5c46..eac625d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,31 @@ 'use strict' const Bucket = require('./bucket') +const wrapHash = require('./consumable-hash') -module.exports = function createHAMT (options) { - return new Bucket(options) +/** + * @typedef {object} UserBucketOptions + * @property {(value: Uint8Array) => Promise} hashFn + * @property {number} [bits=8] + */ + +/** + * @param {UserBucketOptions} options + */ +function createHAMT (options) { + if (!options || !options.hashFn) { + throw new Error('please define an options.hashFn') + } + + const bucketOptions = { + bits: options.bits || 8, + hash: wrapHash(options.hashFn) + } + + return new Bucket(bucketOptions) } -module.exports.isBucket = Bucket.isBucket +module.exports = { + createHAMT, + Bucket +} diff --git a/test/hamt-consumable-buffer.spec.js b/test/hamt-consumable-buffer.spec.js index 3ee255c..fe7b2bc 100644 --- a/test/hamt-consumable-buffer.spec.js +++ b/test/hamt-consumable-buffer.spec.js @@ -1,15 +1,16 @@ /* eslint-env mocha */ 'use strict' -const expect = require('chai').expect +const { expect } = require('aegir/utils/chai') const ConsumableBuffer = require('../src/consumable-buffer') describe('HAMT: consumable buffer', () => { + /** @type {ConsumableBuffer} */ let buf it('can create an empty one', () => { - buf = new ConsumableBuffer([]) + buf = new ConsumableBuffer(Uint8Array.from([])) }) it('from which I can take nothing', () => { @@ -22,7 +23,7 @@ describe('HAMT: consumable buffer', () => { }) it('can create one with one zeroed byte', () => { - buf = new ConsumableBuffer([0]) + buf = new ConsumableBuffer(Uint8Array.from([0])) }) it('from which I can take nothing', () => { @@ -35,7 +36,7 @@ describe('HAMT: consumable buffer', () => { }) it('can create one with one byte with ones', () => { - buf = new ConsumableBuffer([0b11111111]) + buf = new ConsumableBuffer(Uint8Array.from([0b11111111])) }) it('from which I can take nothing', () => { @@ -58,7 +59,7 @@ describe('HAMT: consumable buffer', () => { }) it('can create one with 3 full bytes', () => { - buf = new ConsumableBuffer([0xff, 0xff, 0xff]) + buf = new ConsumableBuffer(Uint8Array.from([0xff, 0xff, 0xff])) }) it('from which I can take nothing', () => { @@ -76,7 +77,7 @@ describe('HAMT: consumable buffer', () => { }) it('can create one with 3 full bytes', () => { - buf = new ConsumableBuffer([0xff, 0xff, 0xff]) + buf = new ConsumableBuffer(Uint8Array.from([0xff, 0xff, 0xff])) }) it('from which I can take 2 bits at a time', () => { @@ -90,7 +91,7 @@ describe('HAMT: consumable buffer', () => { }) it('can create one with 3 full bytes', () => { - buf = new ConsumableBuffer([0xff, 0xff, 0xff]) + buf = new ConsumableBuffer(Uint8Array.from([0xff, 0xff, 0xff])) }) it('from which I can take every bit', () => { diff --git a/test/hamt-consumable-hash.spec.js b/test/hamt-consumable-hash.spec.js index a8d708e..7c839ad 100644 --- a/test/hamt-consumable-hash.spec.js +++ b/test/hamt-consumable-hash.spec.js @@ -1,45 +1,51 @@ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const crypto = require('crypto') +const { expect } = require('aegir/utils/chai') +const multihashing = require('multihashing-async') +const uint8ArrayFromString = require('uint8arrays/from-string') -const ConsumableHash = require('../src/consumable-hash') +const wrapHash = require('../src/consumable-hash') + +/** + * @typedef {import('../src/consumable-hash').InfiniteHash} InfiniteHash + */ describe('HAMT: consumable hash', () => { + const val = uint8ArrayFromString('some value') + /** @type {(value: Uint8Array | InfiniteHash) => InfiniteHash} */ let hash beforeEach(() => { - hash = ConsumableHash(hashFn) + hash = wrapHash(hashFn) }) it('should refuse to hash a non String or buffer', () => { try { + // @ts-expect-error not a string or Uint8Array hash(1) throw new Error('Should have refused to hash value') } catch (err) { - expect(err.message).to.include('can only hash strings or buffers') + expect(err.message).to.include('can only hash Uint8Arrays') } }) it('can take a 0 length value', async () => { - const result = await hash('some value').take(0) + const result = await hash(val).take(0) expect(result).to.be.eql(0) }) it('can take a 10 bit value', async () => { - const result = await hash('some value').take(10) + const result = await hash(val).take(10) expect(result).to.be.eql(110) }) it('can keep on taking a 10 bit value', async () => { let iter = 100 - const h = hash('some value') + const h = hash(val) while (iter > 0) { const result = await h.take(10) @@ -51,7 +57,7 @@ describe('HAMT: consumable hash', () => { }) it('can untake all', async () => { - const h = hash('some value') + const h = hash(val) await h.take(10 * 100) @@ -61,7 +67,7 @@ describe('HAMT: consumable hash', () => { it('keeps taking the same values after untaking all', async () => { let iter = 100 const values = [] - const h = hash('some value') + const h = hash(val) while (iter > 0) { values.push(await h.take(10)) @@ -80,9 +86,12 @@ describe('HAMT: consumable hash', () => { }) }) -function hashFn (value) { - return crypto - .createHash('sha256') - .update(value) - .digest() +/** + * @param {string | Uint8Array} value + */ +async function hashFn (value) { + const multihash = await multihashing(value instanceof Uint8Array ? value : uint8ArrayFromString(value), 'sha2-256') + + // remove the multihash identifier + return multihash.slice(2) } diff --git a/test/hamt.spec.js b/test/hamt.spec.js index db83966..6ec171c 100644 --- a/test/hamt.spec.js +++ b/test/hamt.spec.js @@ -1,18 +1,25 @@ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const crypto = require('crypto') - -const HAMT = require('..') - -const hashFn = function (value, callback) { - return crypto - .createHash('sha256') - .update(value) - .digest() +const { expect } = require('aegir/utils/chai') +const multihashing = require('multihashing-async') +const uint8ArrayFromString = require('uint8arrays/from-string') + +const { createHAMT } = require('..') + +/** + * @template T + * @typedef {import('../src').Bucket} Bucket + */ + +/** + * @param {string | Uint8Array} value + */ +const hashFn = async function (value) { + const multihash = await multihashing(value instanceof Uint8Array ? value : uint8ArrayFromString(value), 'sha2-256') + + // remove the multihash identifier + return multihash.slice(2) } const options = { @@ -21,15 +28,17 @@ const options = { describe('HAMT', () => { describe('basic', () => { + /** @type {Bucket} */ let bucket beforeEach(() => { - bucket = HAMT(options) + bucket = createHAMT(options) }) it('should require a hash function', () => { try { - HAMT() + // @ts-ignore options are not optional + createHAMT() throw new Error('Should have required a hash function') } catch (err) { @@ -39,7 +48,8 @@ describe('HAMT', () => { it('should require a hash function with options', () => { try { - HAMT({}) + // @ts-ignore hashFn is required + createHAMT({}) throw new Error('Should have required a hash function') } catch (err) { @@ -47,10 +57,6 @@ describe('HAMT', () => { } }) - it('should recognise a bucket as a bucket', () => { - expect(HAMT.isBucket(bucket)).to.be.true() - }) - it('get unknown key returns undefined', async () => { const result = await bucket.get('unknown') @@ -145,10 +151,11 @@ describe('HAMT', () => { }) describe('many keys', () => { + /** @type {Bucket} */ let bucket beforeEach(() => { - bucket = HAMT(options) + bucket = createHAMT(options) }) it('accepts putting many keys', async () => { @@ -172,6 +179,10 @@ describe('HAMT', () => { const masterHead = keys.pop() + if (!masterHead) { + throw new Error('masterHead not found') + } + for (const head of keys.reverse()) { expect(await bucket.get(head)).to.eql(head) @@ -191,10 +202,11 @@ describe('HAMT', () => { }) describe('exhausting hash', () => { + /** @type {Bucket} */ let bucket beforeEach(() => { - bucket = HAMT({ + bucket = createHAMT({ hashFn: smallHashFn, bits: 2 }) @@ -204,16 +216,20 @@ describe('HAMT', () => { await insertKeys(400, bucket) }) - function smallHashFn (value) { - return crypto - .createHash('sha256') - .update(value) - .digest() - .slice(0, 2) // just return the 2 first bytes of the hash + /** + * @param {string | Uint8Array} value + */ + async function smallHashFn (value) { + const hash = await hashFn(value) + return hash.slice(0, 2) // just return the 2 first bytes of the hash } }) }) +/** + * @param {number} count + * @param {Bucket} bucket + */ async function insertKeys (count, bucket) { const keys = Array.from({ length: count }, (_, i) => i.toString()) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..108d285 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", + "test" + ] +}