diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9ae2650 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,49 @@ +name: ci +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npx aegir lint + - uses: gozala/typescript-error-reporter-action@v1.0.8 + - run: npx aegir build --no-bundle + - run: npx aegir dep-check + test-node: + needs: check + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + node: [12, 14] + fail-fast: true + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - run: npm install + - run: npx nyc --reporter=lcov aegir test -t node -- --bail + - uses: codecov/codecov-action@v1 + test-electron-main: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npx xvfb-maybe aegir test -t electron-main --bail + test-electron-renderer: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npx xvfb-maybe aegir test -t electron-renderer --bail \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index c630e7c..0000000 --- a/.npmignore +++ /dev/null @@ -1,41 +0,0 @@ -yarn.lock -package-lock.json -**/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 -dist -docs -test -.github -.travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8c47422..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: node_js -cache: npm -stages: - - check - - test - - cov - -node_js: - - 'lts/*' - - 'node' - -os: - - linux - - osx - - windows - -script: npx nyc -s npm run test -- --bail -after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov - -jobs: - include: - - stage: check - script: - - npx aegir commitlint --travis - - npx aegir dep-check - - npm run lint - -notifications: - email: false diff --git a/package.json b/package.json index 970b883..99458be 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,13 @@ "description": "Datastore implementation with file system backend", "leadMaintainer": "Alex Potsides ", "main": "src/index.js", + "types": "dist/src/index.d.ts", + "files": [ + "src", + "dist" + ], "scripts": { + "prepare": "aegir build --no-bundle", "test": "aegir test -t node", "build": "aegir build", "lint": "aegir lint", @@ -32,21 +38,24 @@ }, "homepage": "https://github.com/ipfs/js-datastore-fs#readme", "dependencies": { - "datastore-core": "^2.0.0", + "datastore-core": "^3.0.0", "fast-write-atomic": "^0.2.0", - "interface-datastore": "^2.0.0", + "interface-datastore": "^3.0.3", "it-glob": "0.0.10", "mkdirp": "^1.0.4" }, "devDependencies": { - "aegir": "^28.1.0", + "aegir": "^30.3.0", "async-iterator-all": "^1.0.0", - "cids": "^1.0.0", + "cids": "^1.1.5", "detect-node": "^2.0.4", - "ipfs-utils": "^4.0.1", + "ipfs-utils": "^6.0.0", "memdown": "^5.1.0", "rimraf": "^3.0.2" }, + "eslintConfig": { + "extends": "ipfs" + }, "contributors": [ "achingbrain ", "David Dias ", diff --git a/src/index.js b/src/index.js index ddea56c..1fdc676 100644 --- a/src/index.js +++ b/src/index.js @@ -2,8 +2,10 @@ const fs = require('fs') const glob = require('it-glob') +// @ts-ignore const mkdirp = require('mkdirp') const promisify = require('util').promisify +// @ts-ignore const writeAtomic = promisify(require('fast-write-atomic')) const path = require('path') const { @@ -17,6 +19,18 @@ const fsAccess = promisify(fs.access || noop) const fsReadFile = promisify(fs.readFile || noop) const fsUnlink = promisify(fs.unlink || noop) +/** + * @typedef {import('interface-datastore').Datastore} Datastore + * @typedef {import('interface-datastore').Pair} Pair + * @typedef {import('interface-datastore').Query} Query + */ + +/** + * Write a file atomically + * + * @param {string} path + * @param {Uint8Array} contents + */ async function writeFile (path, contents) { try { await writeAtomic(path, contents) @@ -42,8 +56,14 @@ async function writeFile (path, contents) { * * Keys need to be sanitized before use, as they are written * to the file system as is. + * + * @implements {Datastore} */ class FsDatastore extends Adapter { + /** + * @param {string} location + * @param {{ createIfMissing?: boolean; errorIfExists?: boolean; extension?: string; } | undefined} [opts] + */ constructor (location, opts) { super() @@ -64,22 +84,27 @@ class FsDatastore extends Adapter { if (this.opts.errorIfExists) { throw Errors.dbOpenFailedError(new Error(`Datastore directory: ${this.path} already exists`)) } + return Promise.resolve() } catch (err) { if (err.code === 'ERR_NOT_FOUND' && this.opts.createIfMissing) { mkdirp.sync(this.path, { fs: fs }) - return + return Promise.resolve() } throw err } } + close () { + return Promise.resolve() + } + /** * Calculate the directory and file name for a given key. * * @private * @param {Key} key - * @returns {{string, string}} + * @returns {{dir:string, file:string}} */ _encode (key) { const parent = key.parent().toString() @@ -194,7 +219,7 @@ class FsDatastore extends Adapter { * Check for the existence of the given key. * * @param {Key} key - * @returns {Promise} + * @returns {Promise} */ async has (key) { const parts = this._encode(key) @@ -225,7 +250,12 @@ class FsDatastore extends Adapter { } } - async * _all (q) { // eslint-disable-line require-await + /** + * + * @param {Query} q + * @returns {AsyncIterable} + */ + async * _all (q) { let prefix = q.prefix || '**' // strip leading slashes @@ -256,7 +286,7 @@ class FsDatastore extends Adapter { } } } else { - yield * map(files, f => ({ key: this._decode(f) })) + yield * map(files, f => /** @type {Pair} */({ key: this._decode(f) })) } } } diff --git a/test/index.spec.js b/test/index.spec.js index 8670415..5a5d7dc 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -4,18 +4,22 @@ const { expect } = require('aegir/utils/chai') const path = require('path') const promisify = require('util').promisify -const noop = () => {} +// @ts-ignore const mkdirp = require('mkdirp') +// @ts-ignore const rimraf = promisify(require('rimraf')) const fs = require('fs') +const noop = () => {} const fsReadFile = promisify(require('fs').readFile || noop) const Key = require('interface-datastore').Key const utils = require('interface-datastore').utils const ShardingStore = require('datastore-core').ShardingDatastore const sh = require('datastore-core').shard -const isNode = require('detect-node') const TextEncoder = require('ipfs-utils/src/text-encoder') -const utf8Encoder = new TextEncoder('utf8') +const { isNode } = require('ipfs-utils/src/env') +const utf8Encoder = new TextEncoder() +// @ts-ignore +const tests = require('interface-datastore/src/tests') const FsStore = require('../src') @@ -69,6 +73,7 @@ describe('FsDatastore', () => { const fs = new FsStore(dir) expect( + // @ts-ignore fs._encode(new Key('hello/world')) ).to.eql({ dir: path.join(dir, 'hello'), @@ -76,6 +81,7 @@ describe('FsDatastore', () => { }) expect( + // @ts-ignore fs._decode(fs._encode(new Key('hello/world/test:other')).file) ).to.eql( new Key('hello/world/test:other') @@ -153,7 +159,7 @@ describe('FsDatastore', () => { describe('interface-datastore', () => { const dir = utils.tmpdir() - require('interface-datastore/src/tests')({ + tests({ setup: () => { return new FsStore(dir) }, @@ -166,7 +172,7 @@ describe('FsDatastore', () => { describe('interface-datastore (sharding(fs))', () => { const dir = utils.tmpdir() - require('interface-datastore/src/tests')({ + tests({ setup: () => { const shard = new sh.NextToLast(2) return ShardingStore.createOrOpen(new FsStore(dir), shard) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e605b61 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "test", // remove this line if you don't want to type-check tests + "src" + ] +}