diff --git a/.gitignore b/.gitignore index e33e621..7ad9e67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,39 +1,9 @@ -package-lock.json -yarn.lock -**/node_modules/ -**/*.log -test/repo-tests* - -# Logs -logs -*.log - -coverage - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -build - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules - -lib +build dist -docs -types +.docs +.coverage +node_modules +package-lock.json +yarn.lock +.vscode diff --git a/package.json b/package.json index 2fa7864..743e4fb 100644 --- a/package.json +++ b/package.json @@ -49,19 +49,19 @@ "exports": { ".": { "types": "./dist/src/index.d.ts", - "import": "./src/index.js" + "import": "./dist/src/index.js" }, "./base": { - "types": "./src/base.d.ts", - "import": "./src/base.js" + "types": "./dist/src/base.d.ts", + "import": "./dist/src/base.js" }, "./errors": { - "types": "./src/errors.d.ts", - "import": "./src/errors.js" + "types": "./dist/src/errors.d.ts", + "import": "./dist/src/errors.js" }, "./memory": { - "types": "./src/memory.d.ts", - "import": "./src/memory.js" + "types": "./dist/src/memory.d.ts", + "import": "./dist/src/memory.js" } }, "eslintConfig": { @@ -172,16 +172,12 @@ }, "dependencies": { "err-code": "^3.0.1", - "interface-blockstore": "^4.0.0", - "interface-store": "^3.0.0", - "it-all": "^2.0.0", - "it-drain": "^2.0.0", - "it-filter": "^2.0.0", - "it-take": "^2.0.0", + "interface-blockstore": "^5.0.0", + "interface-store": "^4.0.0", "multiformats": "^11.0.0" }, "devDependencies": { - "aegir": "^37.9.0", - "interface-blockstore-tests": "^4.0.0" + "aegir": "^38.1.7", + "interface-blockstore-tests": "^5.0.0" } } diff --git a/src/base.js b/src/base.js deleted file mode 100644 index dceea63..0000000 --- a/src/base.js +++ /dev/null @@ -1,241 +0,0 @@ -import drain from 'it-drain' -import filter from 'it-filter' -import take from 'it-take' -import all from 'it-all' - -/** - * Collect all values from the iterable and sort them using - * the passed sorter function - * - * @template T - * @param {AsyncIterable | Iterable} iterable - * @param {(a: T, b: T) => -1 | 0 | 1} sorter - * @returns {AsyncIterable} - */ -const sortAll = (iterable, sorter) => { - return (async function * () { - const values = await all(iterable) - yield * values.sort(sorter) - })() -} - -/** - * @typedef {import('interface-blockstore').Options} Options - * @typedef {import('interface-blockstore').Pair} Pair - * @typedef {import('interface-blockstore').Blockstore} Blockstore - * @typedef {import('interface-blockstore').Query} Query - * @typedef {import('interface-blockstore').KeyQuery} KeyQuery - * @typedef {import('interface-blockstore').Batch} Batch - * - * @typedef {import('multiformats').CID} CID - */ - -/** - * @template O - * @typedef {import('interface-store').AwaitIterable} AwaitIterable - */ - -/** - * @implements {Blockstore} - */ -export class BaseBlockstore { - /** - * @returns {Promise} - */ - open () { - return Promise.reject(new Error('.open is not implemented')) - } - - /** - * @returns {Promise} - */ - close () { - return Promise.reject(new Error('.close is not implemented')) - } - - /** - * @param {CID} key - * @param {Uint8Array} val - * @param {Options} [options] - * @returns {Promise} - */ - put (key, val, options) { - return Promise.reject(new Error('.put is not implemented')) - } - - /** - * @param {CID} key - * @param {Options} [options] - * @returns {Promise} - */ - get (key, options) { - return Promise.reject(new Error('.get is not implemented')) - } - - /** - * @param {CID} key - * @param {Options} [options] - * @returns {Promise} - */ - has (key, options) { - return Promise.reject(new Error('.has is not implemented')) - } - - /** - * @param {CID} key - * @param {Options} [options] - * @returns {Promise} - */ - delete (key, options) { - return Promise.reject(new Error('.delete is not implemented')) - } - - /** - * @param {AwaitIterable} source - * @param {Options} [options] - * @returns {AsyncIterable} - */ - async * putMany (source, options = {}) { - for await (const { key, value } of source) { - await this.put(key, value, options) - yield { key, value } - } - } - - /** - * @param {AwaitIterable} source - * @param {Options} [options] - * @returns {AsyncIterable} - */ - async * getMany (source, options = {}) { - for await (const key of source) { - yield this.get(key, options) - } - } - - /** - * @param {AwaitIterable} source - * @param {Options} [options] - * @returns {AsyncIterable} - */ - async * deleteMany (source, options = {}) { - for await (const key of source) { - await this.delete(key, options) - yield key - } - } - - /** - * @returns {Batch} - */ - batch () { - /** @type {Pair[]} */ - let puts = [] - /** @type {CID[]} */ - let dels = [] - - return { - put (key, value) { - puts.push({ key, value }) - }, - - delete (key) { - dels.push(key) - }, - commit: async (options) => { - await drain(this.putMany(puts, options)) - puts = [] - await drain(this.deleteMany(dels, options)) - dels = [] - } - } - } - - /** - * Extending classes should override `query` or implement this method - * - * @param {Query} q - * @param {Options} [options] - * @returns {AsyncIterable} - */ - // eslint-disable-next-line require-yield - async * _all (q, options) { - throw new Error('._all is not implemented') - } - - /** - * Extending classes should override `queryKeys` or implement this method - * - * @param {KeyQuery} q - * @param {Options} [options] - * @returns {AsyncIterable} - */ - // eslint-disable-next-line require-yield - async * _allKeys (q, options) { - throw new Error('._allKeys is not implemented') - } - - /** - * @param {Query} q - * @param {Options} [options] - */ - query (q, options) { - let it = this._all(q, options) - - if (q.prefix != null) { - it = filter(it, (/** @type {Pair} */ e) => - e.key.toString().startsWith(q.prefix || '') - ) - } - - if (Array.isArray(q.filters)) { - it = q.filters.reduce((it, f) => filter(it, f), it) - } - - if (Array.isArray(q.orders)) { - it = q.orders.reduce((it, f) => sortAll(it, f), it) - } - - if (q.offset != null) { - let i = 0 - it = filter(it, () => i++ >= (q.offset || 0)) - } - - if (q.limit != null) { - it = take(it, q.limit) - } - - return it - } - - /** - * @param {KeyQuery} q - * @param {Options} [options] - */ - queryKeys (q, options) { - let it = this._allKeys(q, options) - - if (q.prefix != null) { - it = filter(it, (/** @type {CID} */ cid) => cid.toString().startsWith(q.prefix || '')) - } - - if (Array.isArray(q.filters)) { - it = q.filters.reduce((it, f) => filter(it, f), it) - } - - if (Array.isArray(q.orders)) { - it = q.orders.reduce((it, f) => sortAll(it, f), it) - } - - if (q.offset != null) { - let i = 0 - it = filter(it, () => i++ >= /** @type {number} */ (q.offset)) - } - - if (q.limit != null) { - it = take(it, q.limit) - } - - return it - } -} diff --git a/src/base.ts b/src/base.ts new file mode 100644 index 0000000..ab26195 --- /dev/null +++ b/src/base.ts @@ -0,0 +1,48 @@ +import type { Blockstore, Options, Pair } from 'interface-blockstore' +import type { CID } from 'multiformats/cid' +import type { AwaitIterable } from 'interface-store' + +export class BaseBlockstore implements Blockstore { + async has (key: CID, options?: Options): Promise { + return await Promise.reject(new Error('.has is not implemented')) + } + + async put (key: CID, val: Uint8Array, options?: Options): Promise { + await Promise.reject(new Error('.put is not implemented')) + } + + async * putMany (source: AwaitIterable, options?: Options): AsyncIterable { + for await (const { cid, block } of source) { + await this.put(cid, block, options) + yield { cid, block } + } + } + + async get (key: CID, options?: Options): Promise { + return await Promise.reject(new Error('.get is not implemented')) + } + + async * getMany (source: AwaitIterable, options?: Options): AsyncIterable { + for await (const key of source) { + yield this.get(key, options) + } + } + + async delete (key: CID, options?: Options): Promise { + await Promise.reject(new Error('.delete is not implemented')) + } + + async * deleteMany (source: AwaitIterable, options?: Options): AsyncIterable { + for await (const key of source) { + await this.delete(key, options) + yield key + } + } + + /** + * Extending classes should override `query` or implement this method + */ + async * getAll (options?: Options): AwaitIterable { // eslint-disable-line require-yield + throw new Error('.getAll is not implemented') + } +} diff --git a/src/errors.js b/src/errors.js deleted file mode 100644 index bec9d21..0000000 --- a/src/errors.js +++ /dev/null @@ -1,17 +0,0 @@ -import errCode from 'err-code' - -/** - * @param {Error} [err] - */ -export function notFoundError (err) { - err = err || new Error('Not Found') - return errCode(err, 'ERR_NOT_FOUND') -} - -/** - * @param {Error} [err] - */ -export function abortedError (err) { - err = err || new Error('Aborted') - return errCode(err, 'ERR_ABORTED') -} diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..56645d3 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,31 @@ +import errCode from 'err-code' + +export function openFailedError (err?: Error): Error { + err = err ?? new Error('Open failed') + return errCode(err, 'ERR_OPEN_FAILED') +} + +export function closeFailedError (err?: Error): Error { + err = err ?? new Error('Close failed') + return errCode(err, 'ERR_CLOSE_FAILED') +} + +export function putFailedError (err?: Error): Error { + err = err ?? new Error('Put failed') + return errCode(err, 'ERR_PUT_FAILED') +} + +export function deleteFailedError (err?: Error): Error { + err = err ?? new Error('Delete failed') + return errCode(err, 'ERR_DELETE_FAILED') +} + +export function notFoundError (err?: Error): Error { + err = err ?? new Error('Not Found') + return errCode(err, 'ERR_NOT_FOUND') +} + +export function abortedError (err?: Error): Error { + err = err ?? new Error('Aborted') + return errCode(err, 'ERR_ABORTED') +} diff --git a/src/index.js b/src/index.ts similarity index 100% rename from src/index.js rename to src/index.ts diff --git a/src/memory.js b/src/memory.js deleted file mode 100644 index bca9239..0000000 --- a/src/memory.js +++ /dev/null @@ -1,74 +0,0 @@ -import { BaseBlockstore } from './base.js' -import { base32 } from 'multiformats/bases/base32' -import * as raw from 'multiformats/codecs/raw' -import { CID } from 'multiformats/cid' -import * as Digest from 'multiformats/hashes/digest' -import * as Errors from './errors.js' - -/** - * @typedef {import('interface-blockstore').Pair} Pair - * @typedef {import('interface-blockstore').Blockstore} Blockstore - * @typedef {import('interface-store').Options} Options - */ - -/** - * @class MemoryBlockstore - * @implements {Blockstore} - */ -export class MemoryBlockstore extends BaseBlockstore { - constructor () { - super() - - /** @type {Record} */ - this.data = {} - } - - open () { - return Promise.resolve() - } - - close () { - return Promise.resolve() - } - - /** - * @param {CID} key - * @param {Uint8Array} val - */ - async put (key, val) { // eslint-disable-line require-await - this.data[base32.encode(key.multihash.bytes)] = val - } - - /** - * @param {CID} key - */ - async get (key) { - const exists = await this.has(key) - if (!exists) throw Errors.notFoundError() - return this.data[base32.encode(key.multihash.bytes)] - } - - /** - * @param {CID} key - */ - async has (key) { // eslint-disable-line require-await - return this.data[base32.encode(key.multihash.bytes)] !== undefined - } - - /** - * @param {CID} key - */ - async delete (key) { // eslint-disable-line require-await - delete this.data[base32.encode(key.multihash.bytes)] - } - - async * _all () { - yield * Object.entries(this.data) - .map(([key, value]) => ({ key: CID.createV1(raw.code, Digest.decode(base32.decode(key))), value })) - } - - async * _allKeys () { - yield * Object.entries(this.data) - .map(([key]) => CID.createV1(raw.code, Digest.decode(base32.decode(key)))) - } -} diff --git a/src/memory.ts b/src/memory.ts new file mode 100644 index 0000000..45c4305 --- /dev/null +++ b/src/memory.ts @@ -0,0 +1,49 @@ +import { BaseBlockstore } from './base.js' +import { base32 } from 'multiformats/bases/base32' +import * as raw from 'multiformats/codecs/raw' +import { CID } from 'multiformats/cid' +import * as Digest from 'multiformats/hashes/digest' +import * as Errors from './errors.js' +import type { AwaitIterable } from 'interface-store' +import type { Pair } from 'interface-blockstore' + +export class MemoryBlockstore extends BaseBlockstore { + private readonly data: Map + + constructor () { + super() + + this.data = new Map() + } + + async put (key: CID, val: Uint8Array): Promise { // eslint-disable-line require-await + this.data.set(base32.encode(key.multihash.bytes), val) + } + + async get (key: CID): Promise { + const buf = this.data.get(base32.encode(key.multihash.bytes)) + + if (buf == null) { + throw Errors.notFoundError() + } + + return buf + } + + async has (key: CID): Promise { + return this.data.has(base32.encode(key.multihash.bytes)) + } + + async delete (key: CID): Promise { + this.data.delete(base32.encode(key.multihash.bytes)) + } + + async * getAll (): AwaitIterable { + for (const [key, value] of this.data.entries()) { + yield { + cid: CID.createV1(raw.code, Digest.decode(base32.decode(key))), + block: value + } + } + } +} diff --git a/test/memory.spec.js b/test/memory.spec.ts similarity index 95% rename from test/memory.spec.js rename to test/memory.spec.ts index f65bec7..d42c548 100644 --- a/test/memory.spec.js +++ b/test/memory.spec.ts @@ -5,7 +5,6 @@ import { MemoryBlockstore } from '../src/memory.js' describe('memory', () => { describe('interface-datastore', () => { - // @ts-ignore interfaceBlockstoreTests({ setup () { return new MemoryBlockstore() diff --git a/tsconfig.json b/tsconfig.json index 8708ca6..13a3599 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist", - "emitDeclarationOnly": true + "outDir": "dist" }, "include": [ "src",