From b5fd7879934bd72fe0dd60a4b807a4699cad63a9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 29 Mar 2021 17:13:56 +0200 Subject: [PATCH] feat!: remove default export, multiformats@7, full type check & gen BREAKING CHANGE: * remove `default` export, see below * check, generate and publish full TypeScript types * use multiformats@7 and its non-default exports and updated types * export types based on latest multiformats structure * publish with "main" Prior to this change this module was imported as ```js import dagcbor from '@ipld/dag-cbor' ``` Now it is imported as ```js import * as dagcbor from `@ipld/dag-cbor' ``` --- .github/workflows/mikeals-workflow.yml | 2 +- index.js | 55 +++++++++------- package.json | 52 +++++++++------ test/test-basics.js | 4 +- test/ts-use/.gitignore | 3 + test/ts-use/package.json | 11 ++++ test/ts-use/src/main.ts | 45 +++++++++++++ test/ts-use/tsconfig.json | 9 +++ tsconfig.json | 87 ++++++++------------------ 9 files changed, 164 insertions(+), 104 deletions(-) create mode 100644 test/ts-use/.gitignore create mode 100644 test/ts-use/package.json create mode 100644 test/ts-use/src/main.ts create mode 100644 test/ts-use/tsconfig.json diff --git a/.github/workflows/mikeals-workflow.yml b/.github/workflows/mikeals-workflow.yml index aee2678..3933be8 100644 --- a/.github/workflows/mikeals-workflow.yml +++ b/.github/workflows/mikeals-workflow.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [12.x, 14.x] + node-version: [12.x, 14.x, 16.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} diff --git a/index.js b/index.js index f7c12f5..af039a6 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,20 @@ import * as cborg from 'cborg' -import CID from 'multiformats/cid' -import { codec } from 'multiformats/codecs/codec' +import { CID } from 'multiformats/cid' + +/** + * @template {number} Code + * @template T + * @typedef {import('multiformats/codecs/interface').BlockCodec} BlockCodec + */ // https://github.com/ipfs/go-ipfs/issues/3570#issuecomment-273931692 const CID_CBOR_TAG = 42 -const code = 0x71 -const name = 'dag-cbor' -// this will receive all Objects, we need to filter out anything that's not -// a CID and return `null` for that so it's encoded as normal /** + * cidEncoder will receive all Objects during encode, it needs to filter out + * anything that's not a CID and return `null` for that so it's encoded as + * normal. + * * @param {any} obj * @returns {cborg.Token[]|null} */ @@ -32,6 +37,9 @@ function cidEncoder (obj) { } /** + * Intercept all `undefined` values from an object walk and reject the entire + * object if we find one. + * * @returns {null} */ function undefinedEncoder () { @@ -39,6 +47,10 @@ function undefinedEncoder () { } /** + * Intercept all `number` values from an object walk and reject the entire + * object if we find something that doesn't fit the IPLD data model (NaN & + * Infinity). + * * @param {number} num * @returns {null} */ @@ -61,15 +73,6 @@ const encodeOptions = { } } -/** - * @template T - * @param {T} node - * @returns {Uint8Array} - */ -function encode (node) { - return cborg.encode(node, encodeOptions) -} - /** * @param {Uint8Array} bytes * @returns {CID} @@ -97,11 +100,21 @@ decodeOptions.tags[CID_CBOR_TAG] = cidDecoder /** * @template T - * @param {Uint8Array} data - * @returns {T} + * @type {BlockCodec<0x71, T>} */ -function decode (data) { - return cborg.decode(data, decodeOptions) +export const { name, code, decode, encode } = { + name: 'dag-cbor', + code: 0x71, + /** + * @template T + * @param {T} node + * @returns {Uint8Array} + */ + encode: (node) => cborg.encode(node, encodeOptions), + /** + * @template T + * @param {Uint8Array} data + * @returns {T} + */ + decode: (data) => cborg.decode(data, decodeOptions) } - -export default codec({ name, code, encode, decode }) diff --git a/package.json b/package.json index 4313dba..ed332aa 100644 --- a/package.json +++ b/package.json @@ -1,52 +1,66 @@ { "name": "@ipld/dag-cbor", "version": "0.0.0-dev", - "description": "JS implementation of dag-cbor", + "description": "JS implementation of DAG-CBOR", + "main": "index.js", + "types": "./types/index.d.ts", "type": "module", - "types": "index.d.ts", "scripts": { - "lint": "standard", + "lint": "standard *.js test/*.js", "build": "npm run build:js && npm run build:types", - "build:js": "npm_config_yes=true npx ipjs@latest build --tests", - "build:types": "tsc --build", - "check": "tsc --build --noEmit --noErrorTruncation", - "publish": "npm_config_yes=true npx ipjs@latest publish", - "test:cjs": "npm run build && mocha dist/cjs/node-test/test-*.js", + "build:js": "ipjs build --tests --main && npm run build:copy", + "build:copy": "cp -a tsconfig.json index.js dist/ && mkdir -p dist/test && cp test/*.js dist/test/", + "build:types": "npm run build:copy && cd dist && tsc --build", + "publish": "ipjs publish", + "test:cjs": "npm run build:js && mocha dist/cjs/node-test/test-*.js && npm run test:cjs:browser", "test:node": "hundreds mocha test/test-*.js", - "test:browser": "polendina --cleanup dist/cjs/node-test/test-*.js", - "test": "npm run lint && npm run test:node && npm run test:cjs && npm run test:browser", - "coverage": "c8 --reporter=html mocha test/test-*.js && npx st -d coverage -p 8080" + "test:cjs:browser": "polendina --page --worker --serviceworker --cleanup dist/cjs/node-test/test-*.js", + "test:ts": "npm run build:types && npm run test --prefix test/ts-use", + "test": "npm run lint && npm run test:node && npm run test:cjs && npm run test:ts", + "coverage": "c8 --reporter=html mocha test/test-*.js && npm_config_yes=true npx st -d coverage -p 8080" }, "exports": { "import": "./index.js" }, + "license": "(Apache-2.0 AND MIT)", "repository": { "type": "git", "url": "git+https://github.com/ipld/js-dag-cbor.git" }, "keywords": [ - "IPFS" + "IPFS", + "IPLD" ], - "license": "MIT", "bugs": { "url": "https://github.com/ipld/js-dag-cbor/issues" }, "homepage": "https://github.com/ipld/js-dag-cbor", "dependencies": { - "cborg": "^1.1.2", - "multiformats": "^4.6.1" + "cborg": "^1.2.1", + "multiformats": "^7.0.0" }, "devDependencies": { - "chai": "^4.3.3", + "chai": "^4.3.4", "hundreds": "^0.0.9", - "ipld-garbage": "^2.0.0", - "mocha": "^8.3.1", + "ipjs": "^5.0.0", + "ipld-garbage": "^3.0.0", + "mocha": "^8.3.2", "polendina": "^1.1.0", "standard": "^16.0.3", - "typescript": "^4.2.3" + "typescript": "^4.2.4" }, "directories": { "test": "test" }, + "typesVersions": { + "*": { + "*": [ + "types/*" + ], + "types/*": [ + "types/*" + ] + } + }, "author": "Mikeal Rogers (https://www.mikealrogers.com/)" } diff --git a/test/test-basics.js b/test/test-basics.js index 52f57e8..2d9343c 100644 --- a/test/test-basics.js +++ b/test/test-basics.js @@ -1,8 +1,8 @@ /* eslint-env mocha */ 'use strict' -import garbage from 'ipld-garbage' +import { garbage } from 'ipld-garbage' import chai from 'chai' -import dagcbor from '../index.js' +import * as dagcbor from '../index.js' import { bytes, CID } from 'multiformats' const { encode, decode } = dagcbor diff --git a/test/ts-use/.gitignore b/test/ts-use/.gitignore new file mode 100644 index 0000000..6881777 --- /dev/null +++ b/test/ts-use/.gitignore @@ -0,0 +1,3 @@ +node_modules +src/main.js +tsconfig.tsbuildinfo diff --git a/test/ts-use/package.json b/test/ts-use/package.json new file mode 100644 index 0000000..7456b05 --- /dev/null +++ b/test/ts-use/package.json @@ -0,0 +1,11 @@ +{ + "name": "ts-use", + "private": true, + "dependencies": { + "@ipld/dag-cbor": "file:../../dist/", + "multiformats": "file:../../node_modules/multiformats" + }, + "scripts": { + "test": "npm install && npx -p typescript tsc && node src/main.js" + } +} diff --git a/test/ts-use/src/main.ts b/test/ts-use/src/main.ts new file mode 100644 index 0000000..eec6724 --- /dev/null +++ b/test/ts-use/src/main.ts @@ -0,0 +1,45 @@ +import { deepStrictEqual } from 'assert' + +import { BlockEncoder, BlockDecoder, BlockCodec } from 'multiformats/codecs/interface' +import * as dagCbor from '@ipld/dag-cbor' + +const main = () => { + // make sure we have a full CodecFeature + useCodecFeature(dagCbor) +} + +function useCodecFeature (codec: BlockCodec<0x71, any>) { + // use only as a BlockEncoder + useEncoder(codec) + + // use only as a BlockDecoder + useDecoder(codec) + + // use as a full BlockCodec which does both BlockEncoder & BlockDecoder + useBlockCodec(codec) +} + +function useEncoder (encoder: BlockEncoder) { + deepStrictEqual(encoder.code, 0x71) + deepStrictEqual(encoder.name, 'dag-cbor') + deepStrictEqual(Array.from(encoder.encode('blip')), [100, 98, 108, 105, 112]) + console.log('[TS] ✓ { encoder: BlockEncoder }') +} + +function useDecoder (decoder: BlockDecoder) { + deepStrictEqual(decoder.code, 0x71) + deepStrictEqual(decoder.decode(Uint8Array.from([100, 98, 108, 105, 112])), 'blip') + console.log('[TS] ✓ { decoder: BlockDecoder }') +} + +function useBlockCodec (blockCodec: BlockCodec) { + deepStrictEqual(blockCodec.code, 0x71) + deepStrictEqual(blockCodec.name, 'dag-cbor') + deepStrictEqual(Array.from(blockCodec.encode('blip')), [100, 98, 108, 105, 112]) + deepStrictEqual(blockCodec.decode(Uint8Array.from([100, 98, 108, 105, 112])), 'blip') + console.log('[TS] ✓ {}:BlockCodec') +} + +main() + +export default main diff --git a/test/ts-use/tsconfig.json b/test/ts-use/tsconfig.json new file mode 100644 index 0000000..ff14759 --- /dev/null +++ b/test/ts-use/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "strict": true, + "moduleResolution": "node", + "noImplicitAny": true, + "skipLibCheck": true, + "incremental": true + } +} diff --git a/tsconfig.json b/tsconfig.json index 1bad745..58e8c8d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,66 +1,31 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ - "incremental": true, /* Enable incremental compilation */ - "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - "allowJs": true, /* Allow javascript files to be compiled. */ - "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "emitDeclarationOnly": true, - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true, /* Enable strict checking of function types. */ - "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "allowJs": true, + "checkJs": true, + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": false, + "noImplicitAny": true, + "noImplicitThis": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictFunctionTypes": false, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "strictBindCallApply": true, + "strict": true, + "alwaysStrict": true, + "esModuleInterop": true, + "target": "ES2018", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "outDir": "types", + "skipLibCheck": true, + "stripInternal": true, + "resolveJsonModule": true, + "baseUrl": ".", + "emitDeclarationOnly": true }, "include": [ "index.js"