From 0e11f035c9230e7f6d79c159ace9b80de88cb5eb Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 24 Nov 2020 11:38:29 +0000 Subject: [PATCH] feat: add ts types with aegir this is a best effort approach since multiformats/multiformats will probably superseed this repo soon. --- .github/workflows/main.yml | 67 +++++++++++++++++++ .npmignore | 35 ---------- .travis.yml | 40 ------------ package.json | 10 ++- src/cid-util.js | 2 +- src/index.d.ts | 126 ------------------------------------ src/index.js | 101 +++++++++++++++++------------ test/cid-util.spec.js | 4 +- test/helpers/gen-cid.js | 2 +- test/index.spec.js | 9 +++ test/profiling/cidperf-x.js | 1 + tsconfig.json | 10 +++ 12 files changed, 159 insertions(+), 248 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .npmignore delete mode 100644 .travis.yml delete mode 100644 src/index.d.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b94c65e --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,67 @@ +name: ci +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: yarn + - run: yarn lint + - run: yarn build + - uses: gozala/typescript-error-reporter-action@v1.0.4 + - run: yarn aegir dep-check -- -i aegir + - uses: ipfs/aegir/actions/bundle-size@master + name: size + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + 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: yarn + - run: npx nyc --reporter=lcov npm run test:node -- --bail + - uses: codecov/codecov-action@v1 + test-chrome: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: yarn + - run: yarn aegir test -t browser -t webworker + test-firefox: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: yarn + - run: yarn aegir test -t browser -t webworker -- --browsers FirefoxHeadless + test-electron-main: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: yarn + - run: npx xvfb-maybe yarn aegir test -t electron-main --bail + test-electron-renderer: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: yarn + - run: npx xvfb-maybe yarn aegir test -t electron-renderer --bail \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 2501f36..0000000 --- a/.npmignore +++ /dev/null @@ -1,35 +0,0 @@ -**/node_modules/ -**/*.log -test/repo-tests* - -# Logs -logs -*.log - -coverage -.nyc_output - -# 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 - -test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d0cf281..0000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -language: node_js -cache: npm -stages: - - check - - test - - cov - -node_js: - - '10' - -os: - - linux - - osx - - windows - -script: npx nyc -s npm run test:node -- --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 - - - stage: test - name: chrome - addons: - chrome: stable - script: npx aegir test -t browser -t webworker - - - stage: test - name: firefox - addons: - firefox: latest - script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless - -notifications: - email: false diff --git a/package.json b/package.json index 67547fe..8b4385d 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,10 @@ "coverage": "aegir coverage", "docs": "aegir docs" }, + "files": [ + "src", + "dist" + ], "pre-push": [ "lint", "test" @@ -35,20 +39,22 @@ "url": "https://github.com/multiformats/js-cid/issues" }, "dependencies": { - "class-is": "^1.1.0", "multibase": "^3.0.1", "multicodec": "^2.0.1", "multihashes": "^3.0.1", "uint8arrays": "^1.1.0" }, "devDependencies": { - "aegir": "^26.0.0", + "aegir": "^29.0.1", "multihashing-async": "^2.0.1" }, "engines": { "node": ">=4.0.0", "npm": ">=3.0.0" }, + "eslintConfig": { + "extends": "ipfs" + }, "contributors": [ "David Dias ", "Volker Mische ", diff --git a/src/cid-util.js b/src/cid-util.js index a0ecfa2..c796bd5 100644 --- a/src/cid-util.js +++ b/src/cid-util.js @@ -9,7 +9,7 @@ const CIDUtil = { * Returns undefined if it is a valid CID. * * @param {any} other - * @returns {string} + * @returns {string|undefined} */ checkCIDComponents: function (other) { if (other == null) { diff --git a/src/index.d.ts b/src/index.d.ts deleted file mode 100644 index 0f2f2d2..0000000 --- a/src/index.d.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Class representing a CID `` - * , as defined in [ipld/cid](https://github.com/multiformats/cid). - */ -declare class CID { - /** - * Create a new CID. - * - * The algorithm for argument input is roughly: - * ``` - * if (cid) - * -> create a copy - * else if (str) - * if (1st char is on multibase table) -> CID String - * else -> bs58 encoded multihash - * else if (Uint8Array) - * if (1st byte is 0 or 1) -> CID - * else -> multihash - * else if (Number) - * -> construct CID by parts - * ``` - * - * @example - * new CID(, , , ) - * new CID() - * new CID() - * new CID() - * new CID() - * new CID() - */ - constructor( - version: CIDVersion, - codec: string | number, - multhash: Uint8Array, - multibaseName?: string - ); - constructor(cid: CID|string|Uint8Array); - - /** - * The version of the CID. - */ - readonly version: CIDVersion; - - /** - * The codec of the CID. - * @deprecated - */ - readonly codec: string; - - /** - * The codec of the CID in its number form. - */ - readonly code: number; - - /** - * The multihash of the CID. - */ - readonly multihash: Uint8Array; - - /** - * Multibase name as string. - * @deprecated - */ - readonly multibaseName: string; - - /** - * The CID as a `Uint8Array` - */ - readonly bytes: Uint8Array; - - /** - * The prefix of the CID. - * @deprecated - */ - readonly prefix: Uint8Array; - - /** - * Convert to a CID of version `0`. - */ - toV0(): CID; - - /** - * Convert to a CID of version `1`. - */ - toV1(): CID; - - /** - * Encode the CID into a string. - * - * @param base Base encoding to use. - */ - toBaseEncodedString(base?: string): string; - - /** - * Encode the CID into a string. - */ - toString(base?: string): string; - - /** - * Serialize to a plain object. - */ - toJSON(): { codec: string; version: 0 | 1; hash: Uint8Array }; - - /** - * Compare equality with another CID. - * - * @param other The other CID. - */ - equals(other: any): boolean; - - /** - * Test if the given input is a valid CID object. - * Throws if it is not. - * - * @param other The other CID. - */ - static validateCID(other: any): void; - - static isCID(mixed: any): mixed is CID; - - static codecs: Record; -} - -type CIDVersion = 0|1 - -export = CID diff --git a/src/index.js b/src/index.js index 4ea38ea..eed3c06 100644 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,6 @@ const multibase = require('multibase') const multicodec = require('multicodec') const codecs = require('multicodec/src/base-table.json') const CIDUtil = require('./cid-util') -const withIs = require('class-is') const uint8ArrayConcat = require('uint8arrays/concat') const uint8ArrayToString = require('uint8arrays/to-string') const uint8ArrayEquals = require('uint8arrays/equals') @@ -15,25 +14,23 @@ const codecInts = Object.keys(codecs).reduce((p, name) => { return p }, {}) +const symbol = Symbol.for('@ipld/js-cid/CID') + /** * @typedef {Object} SerializedCID - * @param {string} codec - * @param {number} version - * @param {Uint8Array} multihash + * @property {string} codec + * @property {number} version + * @property {Uint8Array} hash */ - /** - * Test if the given input is a CID. - * @function isCID - * @memberof CID - * @static - * @param {any} other - * @returns {bool} + * @typedef {0|1} CIDVersion + * @typedef {import('multibase').BaseNameOrCode} BaseNameOrCode */ /** * Class representing a CID `` * , as defined in [ipld/cid](https://github.com/multiformats/cid). + * * @class CID */ class CID { @@ -54,7 +51,7 @@ class CID { * -> construct CID by parts * ``` * - * @param {string|Uint8Array|CID} version + * @param {CIDVersion | string | Uint8Array | CID} version * @param {string|number} [codec] * @param {Uint8Array} [multihash] * @param {string} [multibaseName] @@ -68,9 +65,10 @@ class CID { * new CID() */ constructor (version, codec, multihash, multibaseName) { - if (_CID.isCID(version)) { + Object.defineProperty(this, symbol, { value: true }) + if (CID.isCID(version)) { // version is an exising CID instance - const cid = version + const cid = /** @type {CID} */(version) this.version = cid.version this.codec = cid.codec this.multihash = cid.multihash @@ -85,7 +83,7 @@ class CID { if (baseName) { // version is a CID String encoded with multibase, so v1 const cid = multibase.decode(version) - this.version = parseInt(cid.slice(0, 1).toString('hex'), 16) + this.version = parseInt(cid[0].toString(), 16) this.codec = multicodec.getCodec(cid.slice(1)) this.multihash = multicodec.rmPrefix(cid.slice(1)) this.multibaseName = baseName @@ -97,13 +95,12 @@ class CID { this.multibaseName = 'base58btc' } CID.validateCID(this) - Object.defineProperty(this, 'string', { value: version }) + this.string = version return } if (version instanceof Uint8Array) { - const firstByte = version.slice(0, 1) - const v = parseInt(firstByte.toString('hex'), 16) + const v = parseInt(version[0].toString(), 16) if (v === 1) { // version is a CID Uint8Array const cid = version @@ -125,7 +122,9 @@ class CID { // otherwise, assemble the CID from the parameters /** - * @type {number} + * The version of the CID. + * + * @type {CIDVersion} */ this.version = version @@ -134,16 +133,23 @@ class CID { } /** + * The codec of the CID. + * * @type {string} */ this.codec = codec /** + * The multihash of the CID. + * * @type {Uint8Array} */ this.multihash = multihash /** + * Multibase name as string. + * + * @deprecated * @type {string} */ this.multibaseName = multibaseName || (version === 0 ? 'base58btc' : 'base32') @@ -154,10 +160,8 @@ class CID { /** * The CID as a `Uint8Array` * - * @return {Uint8Array} - * @readonly + * @returns {Uint8Array} * - * @memberOf CID */ get bytes () { let bytes = this._bytes @@ -175,17 +179,16 @@ class CID { } // Cache this Uint8Array so it doesn't have to be recreated - Object.defineProperty(this, '_bytes', { value: bytes }) + this._bytes = bytes } return bytes } /** - * Get the prefix of the CID. + * The prefix of the CID. * * @returns {Uint8Array} - * @readonly */ get prefix () { const codec = multicodec.getCodeVarint(this.codec) @@ -197,6 +200,11 @@ class CID { return prefix } + /** + * The codec of the CID in its number form. + * + * @returns {number} + */ get code () { return codecs[this.codec] } @@ -221,7 +229,7 @@ class CID { throw new Error('Cannot convert non 32 byte multihash CID to CIDv0') } - return new _CID(0, this.codec, this.multihash) + return new CID(0, this.codec, this.multihash) } /** @@ -230,20 +238,20 @@ class CID { * @returns {CID} */ toV1 () { - return new _CID(1, this.codec, this.multihash) + return new CID(1, this.codec, this.multihash) } /** * Encode the CID into a string. * - * @param {string} [base=this.multibaseName] - Base encoding to use. + * @param {BaseNameOrCode} [base=this.multibaseName] - Base encoding to use. * @returns {string} */ toBaseEncodedString (base = this.multibaseName) { - if (this.string && base === this.multibaseName) { + if (this.string && this.string.length !== 0 && base === this.multibaseName) { return this.string } - let str = null + let str if (this.version === 0) { if (base !== 'base58btc') { throw new Error('not supported with CIDv0, to support different bases, please migrate the instance do CIDv1, you can do that through cid.toV1()') @@ -256,7 +264,7 @@ class CID { } if (base === this.multibaseName) { // cache the string value - Object.defineProperty(this, 'string', { value: str }) + this.string = str } return str } @@ -264,12 +272,18 @@ class CID { /** * CID(QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n) * - * @returns {String} + * @returns {string} */ [Symbol.for('nodejs.util.inspect.custom')] () { return 'CID(' + this.toString() + ')' } + /** + * Encode the CID into a string. + * + * @param {BaseNameOrCode} [base=this.multibaseName] - Base encoding to use. + * @returns {string} + */ toString (base) { return this.toBaseEncodedString(base) } @@ -291,7 +305,7 @@ class CID { * Compare equality with another CID. * * @param {CID} other - * @returns {bool} + * @returns {boolean} */ equals (other) { return this.codec === other.codec && @@ -303,7 +317,7 @@ class CID { * Test if the given input is a valid CID object. * Throws if it is not. * - * @param {any} other + * @param {any} other - The other CID. * @returns {void} */ static validateCID (other) { @@ -312,13 +326,18 @@ class CID { throw new Error(errorMsg) } } -} -const _CID = withIs(CID, { - className: 'CID', - symbolName: '@ipld/js-cid/CID' -}) + /** + * Check if object is a CID instance + * + * @param {any} value + * @returns {boolean} + */ + static isCID (value) { + return value instanceof CID || Boolean(value && value[symbol]) + } +} -_CID.codecs = codecs +CID.codecs = codecs -module.exports = _CID +module.exports = CID diff --git a/test/cid-util.spec.js b/test/cid-util.spec.js index f5d6b40..df87d69 100644 --- a/test/cid-util.spec.js +++ b/test/cid-util.spec.js @@ -51,14 +51,14 @@ describe('CIDUtil', () => { invalid.forEach((i) => it(`new CID(0, 'dag-pb', ${i instanceof Uint8Array ? 'Uint8Array' : 'String'}<${i.toString()}>)`, () => { expect(() => { - const errMsg = CIDUtil.checkCIDComponents(0, 'dag-pb', i) + const errMsg = CIDUtil.checkCIDComponents(0) expect(errMsg).to.exist() }).to.not.throw() })) invalid.forEach((i) => it(`new CID(1, 'dag-pb', ${i instanceof Uint8Array ? 'Uint8Array' : 'String'}<${i.toString()}>)`, () => { expect(() => { - const errMsg = CIDUtil.checkCIDComponents(1, 'dag-pb', i) + const errMsg = CIDUtil.checkCIDComponents(1) expect(errMsg).to.exist() }).to.not.throw() })) diff --git a/test/helpers/gen-cid.js b/test/helpers/gen-cid.js index f1bd41d..4fa6f58 100644 --- a/test/helpers/gen-cid.js +++ b/test/helpers/gen-cid.js @@ -22,7 +22,7 @@ async function main () { console.log('CID String (multibase included)') console.log(cidStr) console.log('CID in hex (multibase not included)') - console.log(cid.toString('hex')) + console.log(cid.toString()) } main() diff --git a/test/index.spec.js b/test/index.spec.js index 61e3472..912008e 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -107,6 +107,7 @@ describe('CID', () => { const cidStr = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n' const oldCid = new CID(cidStr) + // @ts-ignore delete oldCid.multibaseName // Fake it const newCid = new CID(oldCid) @@ -243,6 +244,7 @@ describe('CID', () => { const cidStr = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u' const oldCid = new CID(cidStr) + // @ts-ignore delete oldCid.multibaseName // Fake it const newCid = new CID(oldCid) @@ -351,6 +353,7 @@ describe('CID', () => { const invalidVersions = [-1, 2] invalidVersions.forEach((i) => it(`new CID(${i}, 'dag-pb', bytes)`, () => { + // @ts-expect-error expect(() => new CID(i, 'dag-pb', hash)).to.throw() })) }) @@ -410,16 +413,19 @@ describe('CID', () => { it('should cache string representation when it matches the multibaseName it was constructed with', () => { // not string to cache yet const cid = new CID(1, 'dag-pb', hash, 'base32') + // @ts-ignore expect(cid.string).to.be.undefined() // we dont cache alternate base encodings yet. expect(cid.toBaseEncodedString('base64')).to.equal('mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt') + // @ts-ignore expect(cid.string).to.be.undefined() const base32String = 'bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' expect(cid.toBaseEncodedString()).to.equal(base32String) // it cached! + // @ts-ignore expect(cid.string).to.equal(base32String) // Make sure custom implementation detail properties don't leak into // the prototype @@ -428,13 +434,16 @@ describe('CID', () => { expect(cid.toBaseEncodedString('base64')).to.equal('mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt') // alternate base not cached! + // @ts-ignore expect(cid.string).to.equal(base32String) }) it('should cache string representation when constructed with one', () => { const base32String = 'bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' const cid = new CID(base32String) + // @ts-ignore expect(cid.string).to.equal(base32String) expect(cid.toBaseEncodedString('base64')).to.equal('mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt') + // @ts-ignore expect(cid.string).to.equal(base32String) expect(cid.toBaseEncodedString()).to.equal(base32String) }) diff --git a/test/profiling/cidperf-x.js b/test/profiling/cidperf-x.js index 9135437..8f17cef 100644 --- a/test/profiling/cidperf-x.js +++ b/test/profiling/cidperf-x.js @@ -26,6 +26,7 @@ class CIDPerfX { // i: Running-counter. // print: If true, it'll print/dump the CID data. run (i, print) { + // @ts-ignore const cid = new CID(this.version, this.codec, this.mh) if (print === true) { console.log('i=' + i, cid) 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" + ] +}