diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8266f90..b94c65e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,8 @@ jobs: - 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 diff --git a/README.md b/README.md index 098fa05..511e143 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,27 @@ -js-multibase +js-multibase ============ [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) -[![Dependency Status](https://david-dm.org/multiformats/js-multibase.svg?style=flat-square)](https://david-dm.org/multiformats/js-multibase) [![codecov](https://img.shields.io/codecov/c/github/multiformats/js-multibase.svg?style=flat-square)](https://codecov.io/gh/multiformats/js-multibase) -[![Travis CI](https://flat.badgen.net/travis/multiformats/js-multibase)](https://travis-ci.com/multiformats/js-multibase) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/multiformats/js-multibase/ci?label=ci&style=flat-square)](https://github.com/multiformats/js-multibase/actions?query=branch%3Amaster+workflow%3Aci+) > JavaScript implementation of the [multibase](https://github.com/multiformats/multibase) specification -## Lead Maintainer +## Lead Maintainer [Hugo Dias](https://github.com/hugomrdias) -## Table of Contents +## Table of Contents - [Install](#install) - [NPM](#npm) - - [In the Browser through ` @@ -72,55 +57,6 @@ console.log(decodedBytes.toString()) ## API https://multiformats.github.io/js-multibase/ -#### `multibase` - Prefixes an encoded Uint8Array with its multibase code - -``` -const multibased = multibase(, encodedBytes) -``` - -#### `multibase.encode` - Encodes Uint8Array into one of the supported encodings, prefixing it with the multibase code - -```JavaScript -const encodedBuf = multibase.encode(, ) -``` - -#### `multibase.decode` - Decodes Uint8Array or string - -```JavaScript -const decodedBuf = multibase.decode(bufOrString) -``` - -#### `multibase.isEncoded` - Checks if Uint8Array or string is encoded - -```JavaScript -const value = multibase.isEncoded(bytesOrString) -// value is the name of the encoding if it is encoded, false otherwise -``` - -#### `multibase.encoding` - Get the encoding by name or code - -```JavaScript -const value = multibase.encoding(nameOrCode) -// value is an instance of the corresponding `Base` -``` - -#### `multibase.encodingFromData` - Get the encoding from data either a `string` or `Uint8Array` - -```JavaScript -const value = multibase.encodingFromData(data) -// value is an instance of the corresponding `Base` -``` - -#### `multibase.names` - -A frozen `Object` of supported base encoding names mapped to the corresponding `Base` instance. - -#### `multibase.codes` - -A frozen `Object` of supported base encoding codes mapped to the corresponding `Base` instance. - -### Supported Encodings, see [`src/constants.js`](/src/constants.js) - ## Contribute Contributions welcome. Please check out [the issues](https://github.com/multiformats/js-multibase/issues). diff --git a/package.json b/package.json index a5485df..bb8e0ed 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ }, "dependencies": { "@multiformats/base-x": "^4.0.1", - "web-encoding": "^1.0.2" + "web-encoding": "^1.0.4" }, "devDependencies": { - "aegir": "^26.0.0", + "aegir": "^29.0.1", "benchmark": "^2.1.4" }, "engines": { @@ -46,7 +46,16 @@ "npm": ">=6.0.0" }, "eslintConfig": { - "extends": "./node_modules/aegir/src/config/eslintrc.js" + "extends": "ipfs" + }, + "types": "dist/src/index.d.ts", + "typesVersions": { + "*": { + "src/*": [ + "dist/src/*", + "dist/src/*/index" + ] + } }, "contributors": [ "David Dias ", diff --git a/src/base.js b/src/base.js index 8b63b94..635d4ca 100644 --- a/src/base.js +++ b/src/base.js @@ -1,29 +1,28 @@ -// @ts-check 'use strict' const { encodeText } = require('./util') +/** @typedef {import('./types').CodecFactory} CodecFactory */ +/** @typedef {import("./types").BaseName} BaseName */ +/** @typedef {import("./types").BaseCode} BaseCode */ + /** - * @typedef {Object} Codec - * @property {function(Uint8Array):string} encode - * @property {function(string):Uint8Array} decode + * Class to encode/decode in the supported Bases * - * @typedef {function(string):Codec} CodecFactory */ - class Base { /** - * @param {string} name - * @param {string} code - * @param {CodecFactory} implementation + * @param {BaseName} name + * @param {BaseCode} code + * @param {CodecFactory} factory * @param {string} alphabet */ - constructor (name, code, implementation, alphabet) { + constructor (name, code, factory, alphabet) { this.name = name this.code = code this.codeBuf = encodeText(this.code) this.alphabet = alphabet - this.engine = implementation(alphabet) + this.codec = factory(alphabet) } /** @@ -31,7 +30,7 @@ class Base { * @returns {string} */ encode (buf) { - return this.engine.encode(buf) + return this.codec.encode(buf) } /** @@ -44,7 +43,7 @@ class Base { throw new Error(`invalid character '${char}' in '${string}'`) } } - return this.engine.decode(string) + return this.codec.decode(string) } } diff --git a/src/constants.js b/src/constants.js index 7ea3058..979a58d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,11 +1,16 @@ -// @ts-check 'use strict' const baseX = require('@multiformats/base-x') const Base = require('./base.js') -const rfc4648 = require('./rfc4648') +const { rfc4648 } = require('./rfc4648') const { decodeText, encodeText } = require('./util') +/** @typedef {import('./types').CodecFactory} CodecFactory */ +/** @typedef {import('./types').Codec} Codec */ +/** @typedef {import('./types').BaseName} BaseName */ +/** @typedef {import('./types').BaseCode} BaseCode */ + +/** @type {CodecFactory} */ const identity = () => { return { encode: decodeText, @@ -14,10 +19,10 @@ const identity = () => { } /** - * @typedef {import('./base').CodecFactory} CodecFactory * * name, code, implementation, alphabet - * @type {Array<[string, string, CodecFactory, string]>} + * + * @type {Array<[BaseName, BaseCode, CodecFactory, string]>} */ const constants = [ ['identity', '\x00', identity, ''], @@ -45,15 +50,17 @@ const constants = [ ['base64urlpad', 'U', rfc4648(6), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='] ] +/** @type {Record} */ const names = constants.reduce((prev, tupple) => { prev[tupple[0]] = new Base(tupple[0], tupple[1], tupple[2], tupple[3]) return prev -}, {}) +}, /** @type {Record} */({})) +/** @type {Record} */ const codes = constants.reduce((prev, tupple) => { prev[tupple[1]] = names[tupple[0]] return prev -}, {}) +}, /** @type {Record} */({})) module.exports = { names, diff --git a/src/index.js b/src/index.js index 4a06f37..310e597 100644 --- a/src/index.js +++ b/src/index.js @@ -1,20 +1,20 @@ -// @ts-check /** * Implementation of the [multibase](https://github.com/multiformats/multibase) specification. * - * @module Multibase */ 'use strict' const constants = require('./constants') const { encodeText, decodeText, concat } = require('./util') -/** @typedef {import("./base")} Base */ +/** @typedef {import('./base')} Base */ +/** @typedef {import("./types").BaseNameOrCode} BaseNameOrCode */ +/** @typedef {import("./types").BaseCode} BaseCode */ /** * Create a new Uint8Array with the multibase varint+code. * - * @param {string|number} nameOrCode - The multibase name or code number. + * @param {BaseNameOrCode} nameOrCode - The multibase name or code number. * @param {Uint8Array} buf - The data to be prefixed with multibase. * @returns {Uint8Array} * @throws {Error} Will throw if the encoding is not supported @@ -32,7 +32,7 @@ function multibase (nameOrCode, buf) { /** * Encode data with the specified base and add the multibase prefix. * - * @param {string|number} nameOrCode - The multibase name or code number. + * @param {BaseNameOrCode} nameOrCode - The multibase name or code number. * @param {Uint8Array} buf - The data to be encoded. * @returns {Uint8Array} * @throws {Error} Will throw if the encoding is not supported @@ -64,7 +64,7 @@ function decode (data) { if (['f', 'F', 'v', 'V', 't', 'T', 'b', 'B', 'c', 'C', 'h', 'k', 'K'].includes(prefix)) { data = data.toLowerCase() } - const enc = encoding(data[0]) + const enc = encoding(/** @type {BaseCode} */(data[0])) return enc.decode(data.substring(1)) } @@ -72,7 +72,7 @@ function decode (data) { * Is the given data multibase encoded? * * @param {Uint8Array|string} data - * @returns {false|string} + * @returns {false | string} */ function isEncoded (data) { if (data instanceof Uint8Array) { @@ -85,7 +85,7 @@ function isEncoded (data) { } try { - const enc = encoding(data[0]) + const enc = encoding(/** @type {BaseCode} */(data[0])) return enc.name } catch (err) { return false @@ -95,7 +95,7 @@ function isEncoded (data) { /** * Validate encoded data * - * @param {string} name + * @param {BaseNameOrCode} name * @param {Uint8Array} buf * @returns {void} * @throws {Error} Will throw if the encoding is not supported @@ -108,7 +108,7 @@ function validEncode (name, buf) { /** * Get the encoding by name or code * - * @param {string|number} nameOrCode + * @param {BaseNameOrCode} nameOrCode * @returns {Base} * @throws {Error} Will throw if the encoding is not supported */ @@ -134,7 +134,7 @@ function encodingFromData (data) { data = decodeText(data) } - return encoding(data[0]) + return encoding(/** @type {BaseCode} */(data[0])) } exports = module.exports = multibase diff --git a/src/rfc4648.js b/src/rfc4648.js index e1e4ba3..89a1aa2 100644 --- a/src/rfc4648.js +++ b/src/rfc4648.js @@ -1,7 +1,6 @@ -// @ts-check 'use strict' -/** @typedef {import('./base').CodecFactory} CodecFactory */ +/** @typedef {import('./types').CodecFactory} CodecFactory */ /** * @param {string} string @@ -96,10 +95,12 @@ const encode = (data, alphabet, bitsPerChar) => { } /** + * RFC4648 Factory + * * @param {number} bitsPerChar * @returns {CodecFactory} */ -module.exports = (bitsPerChar) => (alphabet) => { +const rfc4648 = (bitsPerChar) => (alphabet) => { return { /** * @param {Uint8Array} input @@ -117,3 +118,5 @@ module.exports = (bitsPerChar) => (alphabet) => { } } } + +module.exports = { rfc4648 } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..84438f8 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,62 @@ +/** + * - Codes of the supported encodings + */ +export type BaseCode = + | '\x00' + | '0' + | '7' + | '9' + | 'f' + | 'F' + | 'v' + | 'V' + | 't' + | 'T' + | 'b' + | 'B' + | 'c' + | 'C' + | 'h' + | 'k' + | 'K' + | 'z' + | 'Z' + | 'm' + | 'M' + | 'u' + | 'U'; + +/** + * - Names of the supported encodings + */ +export type BaseName = + | 'identity' + | 'base2' + | 'base8' + | 'base10' + | 'base16' + | 'base16upper' + | 'base32hex' + | 'base32hexupper' + | 'base32hexpad' + | 'base32hexpadupper' + | 'base32' + | 'base32upper' + | 'base32pad' + | 'base32padupper' + | 'base32z' + | 'base36' + | 'base36upper' + | 'base58btc' + | 'base58flickr' + | 'base64' + | 'base64pad' + | 'base64url' + | 'base64urlpad'; + +export type BaseNameOrCode = BaseCode | BaseName; +export type Codec = { + encode: (buffer: Uint8Array) => string; + decode: (hash: string) => Uint8Array; +}; +export type CodecFactory = (input: string) => Codec; diff --git a/src/util.js b/src/util.js index f63a6fe..653fae1 100644 --- a/src/util.js +++ b/src/util.js @@ -1,6 +1,6 @@ -// @ts-check 'use strict' +// @ts-ignore const { TextEncoder, TextDecoder } = require('web-encoding') const textDecoder = new TextDecoder() @@ -20,8 +20,8 @@ const encodeText = (text) => textEncoder.encode(text) /** * Returns a new Uint8Array created by concatenating the passed Arrays * - * @param {Array>} arrs - * @param {Number} length + * @param {Array>} arrs + * @param {number} length * @returns {Uint8Array} */ function concat (arrs, length) { diff --git a/test/multibase.spec.js b/test/multibase.spec.js index 8fb4bd5..9d80cd9 100644 --- a/test/multibase.spec.js +++ b/test/multibase.spec.js @@ -8,6 +8,11 @@ const constants = require('../src/constants.js') const unsupportedBases = [] +/** + * @typedef {import('../src/types').BaseName} BaseName + */ + +/** @type {Array<[BaseName, string, string]>} */ const supportedBases = [ ['base16', decodeText(Uint8Array.from([0x01])), 'f01'], @@ -95,18 +100,21 @@ describe('multibase', () => { it('fails on no buf', () => { expect(() => { + // @ts-expect-error multibase('base16') }).to.throw(Error) }) they('fails on non supported name', (encode) => { expect(() => { + // @ts-expect-error multibase('base1001', encode('meh')) }).to.throw(Error) }) they('fails on non supported code', (encode) => { expect(() => { + // @ts-expect-error multibase('6', encode('meh')) }).to.throw(Error) }) @@ -253,7 +261,9 @@ describe('multibase.isEncoded', () => { ] invalidInputs.forEach(input => { + // @ts-ignore expect(() => multibase.isEncoded(input)).to.not.throw() + // @ts-ignore expect(multibase.isEncoded(input)).to.be.false() }) }) diff --git a/test/spec-test1.spec.js b/test/spec-test1.spec.js index ab3725f..156849f 100644 --- a/test/spec-test1.spec.js +++ b/test/spec-test1.spec.js @@ -6,6 +6,11 @@ const { expect } = require('aegir/utils/chai') const multibase = require('../src') const constants = require('../src/constants.js') const input = 'Decentralize everything!!' + +/** + * @typedef {import('../src/types').BaseName} BaseName + */ +/** @type {Array<[BaseName, string]>} */ const encoded = [ ['identity', '\x00Decentralize everything!!'], ['base2', '001000100011001010110001101100101011011100111010001110010011000010110110001101001011110100110010100100000011001010111011001100101011100100111100101110100011010000110100101101110011001110010000100100001'], diff --git a/test/spec-test2.spec.js b/test/spec-test2.spec.js index f3f3c6e..88b60dd 100644 --- a/test/spec-test2.spec.js +++ b/test/spec-test2.spec.js @@ -6,6 +6,11 @@ const { expect } = require('aegir/utils/chai') const multibase = require('../src') const constants = require('../src/constants.js') const input = 'yes mani !' + +/** + * @typedef {import('../src/types').BaseName} BaseName + */ +/** @type {Array<[BaseName, string]>} */ const encoded = [ ['identity', '\x00yes mani !'], ['base2', '001111001011001010111001100100000011011010110000101101110011010010010000000100001'], diff --git a/test/spec-test3.spec.js b/test/spec-test3.spec.js index df23891..35f6513 100644 --- a/test/spec-test3.spec.js +++ b/test/spec-test3.spec.js @@ -6,6 +6,11 @@ const { expect } = require('aegir/utils/chai') const multibase = require('../src') const constants = require('../src/constants.js') const input = 'hello world' + +/** + * @typedef {import('../src/types').BaseName} BaseName + */ +/** @type {Array<[BaseName, string]>} */ const encoded = [ ['identity', '\x00hello world'], ['base2', '00110100001100101011011000110110001101111001000000111011101101111011100100110110001100100'], diff --git a/test/spec-test4.spec.js b/test/spec-test4.spec.js index 6d2d6db..3dbfa4b 100644 --- a/test/spec-test4.spec.js +++ b/test/spec-test4.spec.js @@ -6,6 +6,10 @@ const { expect } = require('aegir/utils/chai') const multibase = require('../src') const constants = require('../src/constants.js') const input = '\x00yes mani !' +/** + * @typedef {import('../src/types').BaseName} BaseName + */ +/** @type {Array<[BaseName, string]>} */ const encoded = [ ['identity', '\x00\x00yes mani !'], ['base2', '00000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], diff --git a/test/spec-test5.spec.js b/test/spec-test5.spec.js index 7259af2..97f654f 100644 --- a/test/spec-test5.spec.js +++ b/test/spec-test5.spec.js @@ -6,6 +6,10 @@ const { expect } = require('aegir/utils/chai') const multibase = require('../src') const constants = require('../src/constants.js') const input = '\x00\x00yes mani !' +/** + * @typedef {import('../src/types').BaseName} BaseName + */ +/** @type {Array<[BaseName, string]>} */ const encoded = [ ['identity', '\x00\x00\x00yes mani !'], ['base2', '0000000000000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5225c42 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./node_modules/aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", "test" + ] +}