From 3074af825df8386489718ca77559b74d5316cf1b Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Thu, 4 Nov 2021 18:42:26 +0100 Subject: [PATCH] Support promises and abstract-level --- README.md | 18 ++++++++--- example.js | 4 +-- index.js | 34 ++++++++++++-------- package.json | 5 ++- test.js | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index fb1d6d5..9d480b7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # level-concat-iterator -> Concatenate items from an iterator into an array. +> Concatenate entries from an iterator into an array. [![level badge][level-badge]](https://github.com/Level/awesome) [![npm](https://img.shields.io/npm/v/level-concat-iterator.svg?label=&logo=npm)](https://www.npmjs.com/package/level-concat-iterator) @@ -23,21 +23,29 @@ const db = level('./db') db.put('foo', 'bar', function (err) { if (err) throw err - concat(db.iterator(), function (err, data) { + concat(db.iterator(), function (err, entries) { if (err) throw err - console.log(data) + // [{ key: 'foo', value: 'bar' }] + console.log(entries) }) }) ``` +With promises: + +```js +await db.put('foo', 'bar') +const entries = await concat(db.iterator()) +``` + **If you are upgrading:** please see [`UPGRADING.md`](UPGRADING.md). ## API -### `concat(iterator, cb)` +### `concat(iterator[, callback])` -Takes an `abstract-leveldown` compatible `iterator` as first parameter and calls back with an array of keys and values. Calls back with an error if `iterator.next(cb)` or `iterator.end(cb)` errors. +Takes an `abstract-leveldown` compatible `iterator` as first parameter and calls the `callback` with an array of entries, where each entry is an object in the form `{ key, value }`. Calls the `callback` with an error if `iterator.next()` or `iterator.end()` errors. If no callback is provided, a promise is returned. ## Contributing diff --git a/example.js b/example.js index 51f38be..ff1a682 100644 --- a/example.js +++ b/example.js @@ -6,9 +6,9 @@ const db = level('./db') db.put('foo', 'bar', function (err) { if (err) throw err - concat(db.iterator(), function (err, data) { + concat(db.iterator(), function (err, entries) { if (err) throw err - console.log(data) + console.log(entries) }) }) diff --git a/index.js b/index.js index 4ed5c03..95f6746 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,25 @@ 'use strict' -module.exports = function (iterator, cb) { - const data = [] - const next = function () { - iterator.next(function (err, key, value) { - if (err || (key === undefined && value === undefined)) { - return iterator.end(function (err2) { - cb(err || err2, data) - }) - } - data.push({ key, value }) - next() - }) +const { fromCallback } = require('catering') +const kPromise = Symbol('promise') + +module.exports = function (iterator, callback) { + callback = fromCallback(callback, kPromise) + + // Use close() method of abstract-level or end() of abstract-leveldown + const close = typeof iterator.close === 'function' ? 'close' : 'end' + const entries = [] + + const onnext = function (err, key, value) { + if (err || (key === undefined && value === undefined)) { + return iterator[close](function (err2) { + callback(err || err2, entries) + }) + } + entries.push({ key, value }) + iterator.next(onnext) } - next() + + iterator.next(onnext) + return callback[kPromise] } diff --git a/package.json b/package.json index 0953331..1a8fe3b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "level-concat-iterator", "version": "3.0.0", - "description": "Concatenate items from an iterator into an array.", + "description": "Concatenate entries from an iterator into an array.", "author": "Lars-Magnus Skog ", "license": "MIT", "main": "index.js", @@ -19,6 +19,9 @@ "LICENSE.md", "UPGRADING.md" ], + "dependencies": { + "catering": "^2.1.0" + }, "devDependencies": { "airtap": "^4.0.3", "airtap-playwright": "^1.0.1", diff --git a/test.js b/test.js index 905c346..f3ed234 100644 --- a/test.js +++ b/test.js @@ -22,6 +22,25 @@ test('calls back with error if iterator.next errors', function (t) { }) }) +test('rejects promise if iterator.next errors', function (t) { + t.plan(3) + + const iterator = { + next (cb) { + t.pass('iterator.next called') + process.nextTick(cb, new Error('iterator.next')) + }, + end (cb) { + t.pass('iterator.end called') + process.nextTick(cb) + } + } + + collect(iterator).catch(function (err) { + t.is(err.message, 'iterator.next', 'correct error') + }) +}) + test('happy path calls back with an array', function (t) { t.plan(6) @@ -53,6 +72,34 @@ test('happy path calls back with an array', function (t) { }) }) +test('happy path resolves promise with an array', async function (t) { + t.plan(5) + + let i = 0 + const entries = [ + { key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' } + ] + + const iterator = { + next (cb) { + t.pass('iterator.next called') + if (i < entries.length) { + process.nextTick(cb, null, entries[i].key, entries[i].value) + ++i + } else { + process.nextTick(cb) + } + }, + end (cb) { + t.pass('iterator.end called') + process.nextTick(cb) + } + } + + t.same(await collect(iterator), entries) +}) + test('calls back with error and data if iterator.end errors', function (t) { t.plan(6) @@ -84,6 +131,25 @@ test('calls back with error and data if iterator.end errors', function (t) { }) }) +test('rejects promise if iterator.end errors', function (t) { + t.plan(3) + + const iterator = { + next (cb) { + t.pass('iterator.next called') + process.nextTick(cb) + }, + end (cb) { + t.pass('iterator.end called') + process.nextTick(cb, new Error('iterator.end')) + } + } + + collect(iterator).catch(function (err) { + t.is(err.message, 'iterator.end', 'correct error') + }) +}) + test('calls back with error and partial data if iterator.end errors', function (t) { t.plan(5) @@ -114,3 +180,24 @@ test('calls back with error and partial data if iterator.end errors', function ( t.same(result, [].concat(data[0])) }) }) + +test('prefers iterator.close() over iterator.end()', async function (t) { + t.plan(2) + + const iterator = { + next (cb) { + t.pass('iterator.next called') + process.nextTick(cb) + }, + close (cb) { + t.pass('iterator.close called') + process.nextTick(cb) + }, + end (cb) { + t.fail('iterator.end called') + process.nextTick(cb) + } + } + + await collect(iterator) +})