Repository: ipfs/js-ipfs-http-client Branch: master Commit: 995abb41b83c Files: 238 Total size: 14.6 MB Directory structure: gitextract_col4nkm0/ ├── .aegir.js ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── COPYRIGHT ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples/ │ ├── browser-pubsub/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── index.js │ │ ├── package.json │ │ └── util.js │ ├── bundle-webpack/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ ├── server.js │ │ ├── src/ │ │ │ ├── App.js │ │ │ └── index.js │ │ └── webpack.config.js │ ├── files-api/ │ │ └── files-api.js │ ├── name-api/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── index.js │ │ └── package.json │ ├── sub-module/ │ │ ├── bundles-size-KBs.csv │ │ ├── complete-module.js │ │ ├── modules/ │ │ │ ├── add/ │ │ │ │ └── add.js │ │ │ ├── bitswap/ │ │ │ │ └── bitswap.js │ │ │ ├── block/ │ │ │ │ └── block.js │ │ │ ├── bootstrap/ │ │ │ │ └── bootstrap.js │ │ │ ├── cat/ │ │ │ │ └── cat.js │ │ │ ├── commands/ │ │ │ │ └── commands.js │ │ │ ├── config/ │ │ │ │ └── config.js │ │ │ ├── dht/ │ │ │ │ └── dht.js │ │ │ ├── diag/ │ │ │ │ └── diag.js │ │ │ ├── files/ │ │ │ │ └── files.js │ │ │ ├── get/ │ │ │ │ └── get.js │ │ │ ├── id/ │ │ │ │ └── id.js │ │ │ ├── key/ │ │ │ │ └── key.js │ │ │ ├── log/ │ │ │ │ └── log.js │ │ │ ├── ls/ │ │ │ │ └── ls.js │ │ │ ├── mount/ │ │ │ │ └── mount.js │ │ │ ├── name/ │ │ │ │ └── name.js │ │ │ ├── object/ │ │ │ │ └── object.js │ │ │ ├── pin/ │ │ │ │ └── pin.js │ │ │ ├── ping/ │ │ │ │ └── ping.js │ │ │ ├── pubsub/ │ │ │ │ └── pubsub.js │ │ │ ├── refs/ │ │ │ │ └── refs.js │ │ │ ├── repo/ │ │ │ │ └── repo.js │ │ │ ├── swarm/ │ │ │ │ └── swarm.js │ │ │ ├── update/ │ │ │ │ └── update.js │ │ │ └── version/ │ │ │ └── version.js │ │ ├── package.json │ │ ├── test-modules-size.sh │ │ └── webpack.config.js │ └── upload-file-via-browser/ │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ └── src/ │ ├── App.js │ └── index.js ├── greenkeeper.json ├── maintainer.json ├── package.json ├── src/ │ ├── add/ │ │ ├── form-data.browser.js │ │ ├── form-data.js │ │ └── index.js │ ├── bitswap/ │ │ ├── index.js │ │ ├── stat.js │ │ ├── unwant.js │ │ └── wantlist.js │ ├── block/ │ │ ├── get.js │ │ ├── index.js │ │ ├── put.js │ │ ├── rm.js │ │ └── stat.js │ ├── bootstrap/ │ │ ├── add.js │ │ ├── index.js │ │ ├── list.js │ │ └── rm.js │ ├── cat.js │ ├── commands.js │ ├── config/ │ │ ├── get.js │ │ ├── index.js │ │ ├── profiles/ │ │ │ ├── apply.js │ │ │ ├── index.js │ │ │ └── list.js │ │ ├── replace.js │ │ └── set.js │ ├── dag/ │ │ ├── get.js │ │ ├── index.js │ │ ├── put.js │ │ └── resolve.js │ ├── dht/ │ │ ├── find-peer.js │ │ ├── find-provs.js │ │ ├── get.js │ │ ├── index.js │ │ ├── provide.js │ │ ├── put.js │ │ └── query.js │ ├── diag/ │ │ ├── cmds.js │ │ ├── index.js │ │ ├── net.js │ │ └── sys.js │ ├── dns.js │ ├── files/ │ │ ├── chmod.js │ │ ├── cp.js │ │ ├── flush.js │ │ ├── index.js │ │ ├── ls.js │ │ ├── mkdir.js │ │ ├── mv.js │ │ ├── read.js │ │ ├── rm.js │ │ ├── stat.js │ │ ├── touch.js │ │ ├── utils.js │ │ └── write.js │ ├── get-endpoint-config.js │ ├── get.js │ ├── id.js │ ├── index.js │ ├── key/ │ │ ├── export.js │ │ ├── gen.js │ │ ├── import.js │ │ ├── index.js │ │ ├── list.js │ │ ├── rename.js │ │ └── rm.js │ ├── lib/ │ │ ├── buffer-to-form-data.browser.js │ │ ├── buffer-to-form-data.js │ │ ├── configure.js │ │ ├── encode-buffer-uri-component.js │ │ ├── error-handler.js │ │ ├── mode-to-string.js │ │ ├── mtime-to-object.js │ │ ├── object-to-camel-with-metadata.js │ │ └── object-to-camel.js │ ├── log/ │ │ ├── index.js │ │ ├── level.js │ │ ├── ls.js │ │ └── tail.js │ ├── ls.js │ ├── mount.js │ ├── name/ │ │ ├── index.js │ │ ├── publish.js │ │ ├── pubsub/ │ │ │ ├── cancel.js │ │ │ ├── index.js │ │ │ ├── state.js │ │ │ └── subs.js │ │ └── resolve.js │ ├── object/ │ │ ├── data.js │ │ ├── get.js │ │ ├── index.js │ │ ├── links.js │ │ ├── new.js │ │ ├── patch/ │ │ │ ├── add-link.js │ │ │ ├── append-data.js │ │ │ ├── index.js │ │ │ ├── rm-link.js │ │ │ └── set-data.js │ │ ├── put.js │ │ └── stat.js │ ├── pin/ │ │ ├── add.js │ │ ├── index.js │ │ ├── ls.js │ │ └── rm.js │ ├── ping.js │ ├── pubsub/ │ │ ├── index.js │ │ ├── ls.js │ │ ├── peers.js │ │ ├── publish.js │ │ ├── subscribe.js │ │ ├── subscription-tracker.js │ │ └── unsubscribe.js │ ├── refs/ │ │ ├── index.js │ │ └── local.js │ ├── repo/ │ │ ├── gc.js │ │ ├── index.js │ │ ├── stat.js │ │ └── version.js │ ├── resolve.js │ ├── stats/ │ │ ├── bw.js │ │ └── index.js │ ├── stop.js │ ├── swarm/ │ │ ├── addrs.js │ │ ├── connect.js │ │ ├── disconnect.js │ │ ├── index.js │ │ ├── localAddrs.js │ │ └── peers.js │ ├── update.js │ └── version.js └── test/ ├── commands.spec.js ├── constructor.spec.js ├── custom-headers.spec.js ├── dag.spec.js ├── diag.spec.js ├── endpoint-config.spec.js ├── exports.spec.js ├── files-mfs.spec.js ├── fixtures/ │ ├── .gitattributes │ ├── 15mb.random │ ├── r-config.json │ ├── ssl/ │ │ ├── cert.pem │ │ └── privkey.pem │ ├── test-folder/ │ │ ├── .hiddenTest.txt │ │ ├── add │ │ ├── cat │ │ ├── files/ │ │ │ ├── hello.txt │ │ │ └── ipfs.txt │ │ ├── ipfs-add │ │ ├── ls │ │ └── version │ ├── testconfig.json │ └── testfile.txt ├── get.spec.js ├── interface.spec.js ├── key.spec.js ├── lib.configure.spec.js ├── lib.error-handler.spec.js ├── log.spec.js ├── node/ │ └── swarm.js ├── node.js ├── ping.spec.js ├── repo.spec.js ├── request-api.spec.js ├── stats.spec.js ├── sub-modules.spec.js └── utils/ ├── factory.js └── throws-async.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .aegir.js ================================================ 'use strict' const createServer = require('ipfsd-ctl').createServer const EchoServer = require('interface-ipfs-core/src/utils/echo-http-server') const server = createServer({ host: '127.0.0.1', port: 43134 }, { type: 'go', ipfsHttpModule: require('./'), ipfsBin: require('go-ipfs-dep').path() }) const echoServer = EchoServer.createServer() module.exports = { bundlesize: { maxSize: '90kB' }, webpack: { resolve: { mainFields: ['browser', 'main'] } }, karma: { files: [{ pattern: 'node_modules/interface-ipfs-core/test/fixtures/**/*', watched: false, served: true, included: false }], browserNoActivityTimeout: 210 * 1000, singleRun: true }, hooks: { node: { pre: () => echoServer.start(), post: () => echoServer.stop() }, browser: { pre: () => { return Promise.all([ server.start(), echoServer.start() ]) }, post: () => { return Promise.all([ server.stop(), echoServer.stop() ]) } } } } ================================================ FILE: .gitattributes ================================================ * text=auto test/fixtures/** text eol=lf ================================================ FILE: .gitignore ================================================ package-lock.json yarn.lock docs **/node_modules **/*.log test/setup/tmp-disposable-nodes-addrs.json dist coverage .nyc_output **/*.swp examples/sub-module/**/bundle.js examples/sub-module/**/*-minified.js examples/sub-module/*-bundle.js ================================================ FILE: .travis.yml ================================================ language: node_js cache: npm branches: only: - master - /^release\/.*$/ stages: - check - test - cov node_js: - '12' - '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 build --bundlesize - npx aegir dependency-check - npm run lint - stage: test name: chrome addons: chrome: stable script: npx aegir test -t browser - stage: test name: chrome webworker addons: chrome: stable script: npx aegir test -t webworker - stage: test name: firefox addons: firefox: latest script: npx aegir test -t browser -- --browsers FirefoxHeadless - stage: test name: firefox webworker addons: firefox: latest script: npx aegir test -t webworker -- --browsers FirefoxHeadless - stage: test name: electron-main os: osx script: - npx aegir test -t electron-main --bail - stage: test name: electron-renderer os: osx script: - npx aegir test -t electron-renderer --bail notifications: email: false ================================================ FILE: CHANGELOG.md ================================================ # [42.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v42.0.0-pre.2...v42.0.0) (2020-02-04) There are significant and breaking API changes in this release. Please see the [migration guide](https://gist.github.com/alanshaw/04b2ddc35a6fff25c040c011ac6acf26). ### Bug Fixes * interface tests ([#1233](https://github.com/ipfs/js-ipfs-http-client/issues/1233)) ([d3eee0d](https://github.com/ipfs/js-ipfs-http-client/commit/d3eee0d)) ### Features * `add` results now include `mode` and `mtime` properties if they were set. * `files.chmod` has been added. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#fileschmod) for info. * `files.flush` now returns the root CID for the path that was flushed (`/` by default) * `files.ls` results now include `mode` and `mtime` properties if they were set. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#ls) for more info. * `files.mkdir` now accepts `mode` and `mtime` options to allow setting mode and mtime metadata. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#filesmkdir) for more info. * `files.stat` result now includes `mode` and `mtime` properties if they were set. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#filesstat) for more info. * `files.touch` has been added. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#filestouch) for info. * `files.write` now accepts `mode` and `mtime` options to allow setting mode and mtime metadata. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#fileswrite) for more info. * `object.get` now accepts a `timeout` option. It will cause the method to throw with a `TimeoutError` if no data is received within the timeout window. It can be passed as a `number` or a `string`. If a `number` is passed it is interpreted as milliseconds, if a string is passed it is interpreted as a [human readable duration](https://www.npmjs.com/package/parse-duration). * `pin.add` now accepts a `timeout` option. It will cause the method to throw with a `TimeoutError` if no data is received within the timeout window. It can be passed as a `number` or a `string`. If a `number` is passed it is interpreted as milliseconds, if a string is passed it is interpreted as a [human readable duration](https://www.npmjs.com/package/parse-duration). * `refs` now accepts a `timeout` option. It will cause the method to throw with a `TimeoutError` if no data is received within the timeout window. It can be passed as a `number` or a `string`. If a `number` is passed it is interpreted as milliseconds, if a string is passed it is interpreted as a [human readable duration](https://www.npmjs.com/package/parse-duration). ### BREAKING CHANGES * Callbacks are no longer supported on any API methods. Please use a utility such as [`callbackify`](https://www.npmjs.com/package/callbackify) on API methods that return Promises to emulate previous behaviour. See the [migration guide](https://gist.github.com/alanshaw/04b2ddc35a6fff25c040c011ac6acf26#migrating-from-callbacks) for more info. * `add` now returns an async iterable. * `add` now accepts `mode` and `mtime` options on inputs to allow setting mode and mtime metadata for added files. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#add) for more info. * `add` results now contain a `cid` property (a [CID instance](https://github.com/multiformats/js-cid)) instead of a string `hash` property. * `addReadableStream`, `addPullStream` have been removed. Please see the [migration guide](https://gist.github.com/alanshaw/04b2ddc35a6fff25c040c011ac6acf26#migrating-to-async-iterables) for more info. * `addFromStream` has been removed. Use `add` instead. * `addFromFs` has been removed. Please use the exported `globSource` utility and pass the result to `add`. See the [glob source documentation](https://github.com/ipfs/js-ipfs-http-client#glob-source) for more details and an example. * `addFromURL` has been removed. Please use the exported `urlSource` utility and pass the result to `add`. See the [URL source documentation](https://github.com/ipfs/js-ipfs-http-client#url-source) for more details and an example. * `bitswap.stat` result has changed - `wantlist` and values are now an array of [CID](https://github.com/multiformats/js-cid) instances and `peers` is now a `string[]` of peer IDs. * `bitswap.wantlist` now returns an array of [CID](https://github.com/multiformats/js-cid) instances. * `block.rm` now returns an async iterable. * `block.rm` now yields objects of `{ cid: CID, error: Error }`. * `block.stat` result now contains a `cid` property (whose value is a [CID instance](https://github.com/multiformats/js-cid)) instead of a `key` property. * `dht.findProvs`, `dht.provide`, `dht.put` and `dht.query` now all return an async iterable. * `dht.findPeer`, `dht.findProvs`, `dht.provide`, `dht.put` and `dht.query` now yield/return an object `{ id: string, addrs: Multiaddr[] }` instead of a `PeerInfo` instance(s). * `files.lsPullStream` and `files.lsReadableStream` have been removed. Please see the [migration guide](https://gist.github.com/alanshaw/04b2ddc35a6fff25c040c011ac6acf26#migrating-to-async-iterables) for more info. * `files.ls` now returns an async iterable. * `files.ls` results now contain a `cid` property (whose value is a [CID instance](https://github.com/multiformats/js-cid)) instead of a `hash` property. * `files.ls` no longer takes a `long` option (in core) - you will receive all data by default. * `files.readPullStream` and `files.readReadableStream` have been removed. Please see the [migration guide](https://gist.github.com/alanshaw/04b2ddc35a6fff25c040c011ac6acf26#migrating-to-async-iterables) for more info. * `files.read` now returns an async iterable. * `files.stat` result now contains a `cid` property (whose value is a [CID instance](https://github.com/multiformats/js-cid)) instead of a `hash` property. * `get` now returns an async iterable. The `content` property value for objects yielded from the iterator is now an async iterable that yields [`BufferList`](https://github.com/rvagg/bl) objects. * `id` result has changed, the `addresses` property is now a `Multiaddr[]` * `name.resolve` now returns an async iterable. It yields increasingly more accurate resolved values as they are discovered until the best value is selected from the quorum of 16. The "best" resolved value is the last item yielded from the iterator. If you are interested only in this best value you could use `it-last` to extract it like so: ```js const last = require('it-last') await last(ipfs.name.resolve('/ipns/QmHash')) ``` * `ls` now returns an async iterable. * `ls` results now contain a `cid` property (whose value is a [CID instance](https://github.com/multiformats/js-cid)) instead of a `hash` property. * `ls` results now include `mode` and `mtime` properties if they were set. See the [core interface docs](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#ls) for more info. * `pin.add` results now contain a `cid` property (a [CID instance](https://github.com/multiformats/js-cid)) instead of a string `hash` property. * `pin.ls` now returns an async iterable. * `pin.ls` results now contain a `cid` property (a [CID instance](https://github.com/multiformats/js-cid)) instead of a string `hash` property. * `pin.rm` results now contain a `cid` property (a [CID instance](https://github.com/multiformats/js-cid)) instead of a string `hash` property. * `ping` now returns an async iterable. * `refs` and `refs.local` now return an async iterable. * `repo.gc` now returns an async iterable. * `stats.bw` now returns an async iterable. * `swarm.peers` now returns an array of objects with a `peer` property that is a `string`, instead of a `PeerId` instance. * `swarm.addrs` now returns an array of objects `{ id: string, addrs: Multiaddr[] }` instead of `PeerInfo` instances. * The protocol _name_ for peer IDs in multiaddrs has changed from 'ipfs' to 'p2p'. There's no changes to data on the wire but this change is seen when multiaddrs are converted to strings. # [42.0.0-pre.0](https://github.com/ipfs/js-ipfs-http-client/compare/v41.0.1...v42.0.0-pre.0) (2020-01-23) ## [41.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v41.0.0...v41.0.1) (2020-01-23) # [41.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v40.2.1...v41.0.0) (2020-01-12) ### Bug Fixes * return CIDs from files.flush ([#1216](https://github.com/ipfs/js-ipfs-http-client/issues/1216)) ([13f8d7a](https://github.com/ipfs/js-ipfs-http-client/commit/13f8d7a)) ### Code Refactoring * removes format option ([#1218](https://github.com/ipfs/js-ipfs-http-client/issues/1218)) ([4ef26cd](https://github.com/ipfs/js-ipfs-http-client/commit/4ef26cd)) ### BREAKING CHANGES * `format` option is no longer supported as everything is `dag-pb` all of the time. Follows on from https://github.com/ipfs/js-ipfs-mfs/pull/69 ## [40.2.1](https://github.com/ipfs/js-ipfs-http-client/compare/v40.2.0...v40.2.1) (2020-01-09) # [40.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v40.1.0...v40.2.0) (2020-01-09) ### Features * support UnixFSv1.5 metadata ([#1186](https://github.com/ipfs/js-ipfs-http-client/issues/1186)) ([da9d17a](https://github.com/ipfs/js-ipfs-http-client/commit/da9d17a)) # [40.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v40.0.1...v40.1.0) (2019-12-10) ### Features * expose import concurrency controls ([#1187](https://github.com/ipfs/js-ipfs-http-client/issues/1187)) ([47093d5](https://github.com/ipfs/js-ipfs-http-client/commit/47093d5)) ## [40.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v40.0.0...v40.0.1) (2019-11-27) ### Bug Fixes * pin ls with multiple CIDs ([#1184](https://github.com/ipfs/js-ipfs-http-client/issues/1184)) ([2f3763f](https://github.com/ipfs/js-ipfs-http-client/commit/2f3763f)) # [40.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v39.0.2...v40.0.0) (2019-11-22) ### Code Refactoring * async await roundup ([#1173](https://github.com/ipfs/js-ipfs-http-client/issues/1173)) ([3e5967a](https://github.com/ipfs/js-ipfs-http-client/commit/3e5967a)), closes [#1103](https://github.com/ipfs/js-ipfs-http-client/issues/1103) * convert config API to async await ([#1155](https://github.com/ipfs/js-ipfs-http-client/issues/1155)) ([621973c](https://github.com/ipfs/js-ipfs-http-client/commit/621973c)) * move files to root level ([#1150](https://github.com/ipfs/js-ipfs-http-client/issues/1150)) ([559a97d](https://github.com/ipfs/js-ipfs-http-client/commit/559a97d)) ### Features * support name.resolve of peerid as cid ([#1145](https://github.com/ipfs/js-ipfs-http-client/issues/1145)) ([2d9afc8](https://github.com/ipfs/js-ipfs-http-client/commit/2d9afc8)) ### Reverts * chore: update multiaddr to version 7.2.0 ([#1136](https://github.com/ipfs/js-ipfs-http-client/issues/1136)) ([#1143](https://github.com/ipfs/js-ipfs-http-client/issues/1143)) ([4131d09](https://github.com/ipfs/js-ipfs-http-client/commit/4131d09)) ### BREAKING CHANGES * The `log.tail` method now returns an async iterator that yields log messages. Use it like: ```js for await (const message of ipfs.log.tail()) { console.log(message) } ``` * The response to a call to `log.level` now returns an object that has camel cased keys. i.e. `Message` and `Error` properties have changed to `message` and `error`. * Dropped support for go-ipfs <= 0.4.4 in `swarm.peers` response. * The signature for `ipfs.mount` has changed from `ipfs.mount([ipfsPath], [ipnsPath])` to `ipfs.mount([options])`. Where `options` is an optional object that may contain two boolean properties `ipfsPath` and `ipnsPath`. The response object has also changed to be camel case. See https://docs.ipfs.io/reference/api/http/#api-v0-mount. * Default ping `count` of 1 in client has been removed. The default ping count is now whatever the IPFS node defaults it to (currently 10). If you specifically need 1 ping message then please pass `count: 1` in options for `ipfs.ping()`. * Multi parameter constructor options are no longer supported. To create a new IPFS HTTP client, pass a single parameter to the constructor. The parameter can be one of: * String, formatted as one of: * Multiaddr e.g. /ip4/127.0.0.1/tcp/5001 * URL e.g. http://127.0.0.1:5001 * [Multiaddr](https://www.npmjs.com/package/multiaddr) instance * Object, in format of either: * Address and path e.g. `{ apiAddr: '/ip4/127.0.0.1/tcp/5001': apiPath: '/api/v0' }` (Note: `apiAddr` can also be a string in URL form or a Multiaddr instance) * Node.js style address e.g. `{ host: '127.0.0.1', port: 5001, protocol: 'http' }` * Errors returned from request failures are now all [`HTTPError`](https://github.com/sindresorhus/ky/blob/c0d9d2bb07e4c122a08f019b39e9c55a4c9324f3/index.js#L117-L123)s which carry a `response` property. This is a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) that can be used to inspect _all_ information relating to the HTTP response. This means that the `err.status` or `err.statusCode` property should now be accessed via `err.response.status`. * files in `src/files-regular` have moved to `src`. The `src/files-mfs` directory has been renamed to `src/files`. If you were previously requiring files from these directories e.g. `require('ipfs-http-client/src/files-regular/add')` then please be aware that they have moved. * Kebab case options are no longer supported. Please use camel case option names as defined in the [`interface-ipfs-core`](https://github.com/ipfs/interface-js-ipfs-core/tree/master/SPEC) docs. e.g. the `allow-offline` option to `name.publish` should be passed as `allowOffline`. * Note that you can pass [additional query string parameters](https://github.com/ipfs/js-ipfs-http-client#additional-options) in the `searchParams` option available to all API methods. ## [39.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v39.0.1...v39.0.2) (2019-10-23) ### Bug Fixes * use non-strict equivalence for options.preload ([#1134](https://github.com/ipfs/js-ipfs-http-client/issues/1134)) ([432e1e8](https://github.com/ipfs/js-ipfs-http-client/commit/432e1e8)) ## [39.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v39.0.0...v39.0.1) (2019-10-21) ### Bug Fixes * expose preload argument ([#1129](https://github.com/ipfs/js-ipfs-http-client/issues/1129)) ([c82b031](https://github.com/ipfs/js-ipfs-http-client/commit/c82b031)) * increase default timeout and respect value passed to `ky.extend` ([#1130](https://github.com/ipfs/js-ipfs-http-client/issues/1130)) ([25b6043](https://github.com/ipfs/js-ipfs-http-client/commit/25b6043)) # [39.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v38.2.0...v39.0.0) (2019-10-15) # [38.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v38.1.0...v38.2.0) (2019-10-06) ### Features * adds ipfs.block.rm method ([#1123](https://github.com/ipfs/js-ipfs-http-client/issues/1123)) ([2f0eff7](https://github.com/ipfs/js-ipfs-http-client/commit/2f0eff7)) # [38.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v38.0.1...v38.1.0) (2019-10-04) ### Bug Fixes * get correct remote node config ([5b53e22](https://github.com/ipfs/js-ipfs-http-client/commit/5b53e22)) * pull in preconfigured chai from interface tests ([93765c1](https://github.com/ipfs/js-ipfs-http-client/commit/93765c1)) ### Features * add methods for listing config profiles ([1c3d92a](https://github.com/ipfs/js-ipfs-http-client/commit/1c3d92a)) ### BREAKING CHANGES * Configuration profiles API has changed: ```javascript Promise<{oldCfg, newCfg}> ipfs.config.profile(name, opts) // is now Promise<{old, new}> ipfs.config.profiles.apply(name, opts) ``` * Possibly contentious; Adds `callbackify` as a dependency, see https://github.com/ipfs/js-ipfs/issues/2506 for discussion. ## [38.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v38.0.0...v38.0.1) (2019-10-04) ### Bug Fixes * pull in preconfigured chai from interface tests ([6a7eb8a](https://github.com/ipfs/js-ipfs-http-client/commit/6a7eb8a)) # [38.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v37.0.3...v38.0.0) (2019-09-25) ## [37.0.3](https://github.com/ipfs/js-ipfs-http-client/compare/v37.0.2...v37.0.3) (2019-09-25) ## [37.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v37.0.1...v37.0.2) (2019-09-20) ### Bug Fixes * only do the big file workaround in node and electron main ([077c997](https://github.com/ipfs/js-ipfs-http-client/commit/077c997)) ## [37.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v37.0.0...v37.0.1) (2019-09-17) # [37.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v36.1.0...v37.0.0) (2019-09-17) ### Bug Fixes * big downloads in electron ([9c9aac8](https://github.com/ipfs/js-ipfs-http-client/commit/9c9aac8)) # [36.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v36.0.0...v36.1.0) (2019-09-17) ### Bug Fixes * fix electron renderer tests and a couple more bugs ([#1105](https://github.com/ipfs/js-ipfs-http-client/issues/1105)) ([a631a21](https://github.com/ipfs/js-ipfs-http-client/commit/a631a21)) # [36.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v35.1.0...v36.0.0) (2019-09-11) # [35.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v35.0.0...v35.1.0) (2019-09-04) ### Features * add config profile endpoint ([#1030](https://github.com/ipfs/js-ipfs-http-client/issues/1030)) ([3aaa3ee](https://github.com/ipfs/js-ipfs-http-client/commit/3aaa3ee)) # [35.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v34.0.0...v35.0.0) (2019-09-04) ### BREAKING CHANGES Kebab case options (e.g. `wrap-with-directory`) are no longer supported in `ipfs.add`. Use camel case instead (e.g. `wrapWithDirectory`). # [34.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v33.1.1...v34.0.0) (2019-08-29) ### Bug Fixes * **package:** update err-code to version 2.0.0 ([#1053](https://github.com/ipfs/js-ipfs-http-client/issues/1053)) ([3515070](https://github.com/ipfs/js-ipfs-http-client/commit/3515070)) ### Features * browser pubsub ([#1059](https://github.com/ipfs/js-ipfs-http-client/issues/1059)) ([3764d06](https://github.com/ipfs/js-ipfs-http-client/commit/3764d06)) * expose pin and preload arguments ([#1079](https://github.com/ipfs/js-ipfs-http-client/issues/1079)) ([e3ed6e9](https://github.com/ipfs/js-ipfs-http-client/commit/e3ed6e9)) * support adding files via async iterator ([#1078](https://github.com/ipfs/js-ipfs-http-client/issues/1078)) ([377042b](https://github.com/ipfs/js-ipfs-http-client/commit/377042b)) ## [33.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v33.1.0...v33.1.1) (2019-07-26) ### Bug Fixes * allow passing timeout option to object stat ([#1055](https://github.com/ipfs/js-ipfs-http-client/issues/1055)) ([92b0594](https://github.com/ipfs/js-ipfs-http-client/commit/92b0594)) # [33.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v33.0.2...v33.1.0) (2019-07-11) ### Bug Fixes * changelog for 33.x does not include breaking change ([cd41a16](https://github.com/ipfs/js-ipfs-http-client/commit/cd41a16)) * invalid multipart/form-data ([#948](https://github.com/ipfs/js-ipfs-http-client/issues/948)) ([9e6dfe7](https://github.com/ipfs/js-ipfs-http-client/commit/9e6dfe7)), closes [/tools.ietf.org/html/rfc7578#section-4](https://github.com//tools.ietf.org/html/rfc7578/issues/section-4) ### Features * add support for js-ipfs dag api and also some tests ([#957](https://github.com/ipfs/js-ipfs-http-client/issues/957)) ([8f378a3](https://github.com/ipfs/js-ipfs-http-client/commit/8f378a3)) ## [33.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v33.0.1...v33.0.2) (2019-07-11) ### Bug Fixes * make findprovs return all responses ([#1041](https://github.com/ipfs/js-ipfs-http-client/issues/1041)) ([63103bd](https://github.com/ipfs/js-ipfs-http-client/commit/63103bd)) ## [33.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v33.0.0...v33.0.1) (2019-07-10) ### Bug Fixes * response for findpeer and findprovs ([#1039](https://github.com/ipfs/js-ipfs-http-client/issues/1039)) ([5252f50](https://github.com/ipfs/js-ipfs-http-client/commit/5252f50)) # [33.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v32.0.1...v33.0.0) (2019-07-10) ### Bug Fixes * link to ipfs.io ([70cdf25](https://github.com/ipfs/js-ipfs-http-client/commit/70cdf25)) * prepare for aegir release ([#1021](https://github.com/ipfs/js-ipfs-http-client/issues/1021)) ([806b206](https://github.com/ipfs/js-ipfs-http-client/commit/806b206)) * sometimes no Addrs element is present in the response ([#1037](https://github.com/ipfs/js-ipfs-http-client/issues/1037)) ([a74b8f7](https://github.com/ipfs/js-ipfs-http-client/commit/a74b8f7)) * **package:** update bignumber.js to version 9.0.0 ([#1024](https://github.com/ipfs/js-ipfs-http-client/issues/1024)) ([a04edac](https://github.com/ipfs/js-ipfs-http-client/commit/a04edac)) ### BREAKING CHANGES `repo.gc` response objects have changed to `{ err, cid }`, where `err` is an `Error` instance and `cid` is a [`CID`](https://github.com/multiformats/js-cid) instance. ## [32.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v32.0.0...v32.0.1) (2019-05-21) ### Bug Fixes * error reporting for non-JSON responses ([#1016](https://github.com/ipfs/js-ipfs-http-client/issues/1016)) ([4251c88](https://github.com/ipfs/js-ipfs-http-client/commit/4251c88)), closes [#912](https://github.com/ipfs/js-ipfs-http-client/issues/912) [#1000](https://github.com/ipfs/js-ipfs-http-client/issues/1000) [#1001](https://github.com/ipfs/js-ipfs-http-client/issues/1001) * send trickle param to trigger trickle dag builder ([#1015](https://github.com/ipfs/js-ipfs-http-client/issues/1015)) ([a28b009](https://github.com/ipfs/js-ipfs-http-client/commit/a28b009)) # [32.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v31.1.0...v32.0.0) (2019-05-21) ### Bug Fixes * handle empty array return value in dht.findProvs ([#1003](https://github.com/ipfs/js-ipfs-http-client/issues/1003)) ([15ab7c5](https://github.com/ipfs/js-ipfs-http-client/commit/15ab7c5)) ### Chores * update ipld formats ([#1010](https://github.com/ipfs/js-ipfs-http-client/issues/1010)) ([a423d7f](https://github.com/ipfs/js-ipfs-http-client/commit/a423d7f)) ### BREAKING CHANGES * The default string encoding for version 1 CIDs has changed to `base32`. IPLD formats have been updated to the latest versions. IPLD nodes returned by `ipfs.dag` and `ipfs.object` commands have significant breaking changes. If you are using these commands in your application you are likely to encounter the following changes to `dag-pb` nodes (the default node type that IPFS creates): * `DAGNode` properties have been renamed as follows: * `data` => `Data` * `links` => `Links` * `size` => `size` (Note: no change) * `DAGLink` properties have been renamed as follows: * `cid` => `Hash` * `name` => `Name` * `size` => `Tsize` See CHANGELOGs for each IPLD format for it's respective changes, you can read more about the [`dag-pb` changes in the CHANGELOG](https://github.com/ipld/js-ipld-dag-pb/blob/master) License: MIT Signed-off-by: Alan Shaw # [31.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v31.0.2...v31.1.0) (2019-05-16) ### Features * add support for File DOM API to files-regular ([#986](https://github.com/ipfs/js-ipfs-http-client/issues/986)) ([7b49f7e](https://github.com/ipfs/js-ipfs-http-client/commit/7b49f7e)) ## [31.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v31.0.1...v31.0.2) (2019-05-16) ### Bug Fixes * error handling for refs/refs local ([#997](https://github.com/ipfs/js-ipfs-http-client/issues/997)) ([391351d](https://github.com/ipfs/js-ipfs-http-client/commit/391351d)) ## [31.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v31.0.0...v31.0.1) (2019-05-15) ### Bug Fixes * config set with number ([#998](https://github.com/ipfs/js-ipfs-http-client/issues/998)) ([4f21bef](https://github.com/ipfs/js-ipfs-http-client/commit/4f21bef)), closes [#881](https://github.com/ipfs/js-ipfs-http-client/issues/881) # [31.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v30.1.4...v31.0.0) (2019-05-13) ### Features * refs endpoint ([#978](https://github.com/ipfs/js-ipfs-http-client/issues/978)) ([a741e10](https://github.com/ipfs/js-ipfs-http-client/commit/a741e10)) ### BREAKING CHANGES * ipfs.refs now returns objects with camelCase properties not PascalCase properties. i.e. `{ ref, err }` not `{ Ref, Err }` ## [30.1.4](https://github.com/ipfs/js-ipfs-http-client/compare/v30.1.3...v30.1.4) (2019-04-29) ### Bug Fixes * uncaught error: stream.push() after EOF ([#980](https://github.com/ipfs/js-ipfs-http-client/issues/980)) ([cc677f0](https://github.com/ipfs/js-ipfs-http-client/commit/cc677f0)), closes [#967](https://github.com/ipfs/js-ipfs-http-client/issues/967) * update Babel in upload-file-via-browser example ([#968](https://github.com/ipfs/js-ipfs-http-client/issues/968)) ([#970](https://github.com/ipfs/js-ipfs-http-client/issues/970)) ([17d49de](https://github.com/ipfs/js-ipfs-http-client/commit/17d49de)) ## [30.1.3](https://github.com/ipfs/js-ipfs-http-client/compare/v30.1.2...v30.1.3) (2019-04-11) ### Bug Fixes * fix missing buffer bundling with browserify ([#966](https://github.com/ipfs/js-ipfs-http-client/issues/966)) ([944a64b](https://github.com/ipfs/js-ipfs-http-client/commit/944a64b)), closes [#964](https://github.com/ipfs/js-ipfs-http-client/issues/964) ## [30.1.2](https://github.com/ipfs/js-ipfs-http-client/compare/v30.1.1...v30.1.2) (2019-04-09) ### Bug Fixes * https multiaddr support in constructor ([#965](https://github.com/ipfs/js-ipfs-http-client/issues/965)) ([5da0bcd](https://github.com/ipfs/js-ipfs-http-client/commit/5da0bcd)) ## [30.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v30.1.0...v30.1.1) (2019-03-28) # [30.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v30.0.0...v30.1.0) (2019-03-15) ### Bug Fixes * dht.findProvs.js handle valid hash but no providers ([#950](https://github.com/ipfs/js-ipfs-http-client/issues/950)) ([c3cde76](https://github.com/ipfs/js-ipfs-http-client/commit/c3cde76)) ### Features * provide access to multicodec ([#954](https://github.com/ipfs/js-ipfs-http-client/issues/954)) ([0c109ab](https://github.com/ipfs/js-ipfs-http-client/commit/0c109ab)) ### Performance Improvements * reduce bundle size ([#915](https://github.com/ipfs/js-ipfs-http-client/issues/915)) ([87dff04](https://github.com/ipfs/js-ipfs-http-client/commit/87dff04)) # [30.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v29.1.1...v30.0.0) (2019-03-13) ### Bug Fixes * windows travis build ([#952](https://github.com/ipfs/js-ipfs-http-client/issues/952)) ([05f2f6c](https://github.com/ipfs/js-ipfs-http-client/commit/05f2f6c)) ### Code Refactoring * export types and utilities statically ([#951](https://github.com/ipfs/js-ipfs-http-client/issues/951)) ([d1e99e7](https://github.com/ipfs/js-ipfs-http-client/commit/d1e99e7)), closes [#902](https://github.com/ipfs/js-ipfs-http-client/issues/902) ### Features * pubsub unsubscribe all ([#956](https://github.com/ipfs/js-ipfs-http-client/issues/956)) ([a57a411](https://github.com/ipfs/js-ipfs-http-client/commit/a57a411)) ### BREAKING CHANGES * `ipfs.util.isIPFS` has moved to a static export and should be accessed via `const { isIPFS } = require('ipfs-http-client')`. The modules available under `ipfs.types.*` have also become static exports. `ipfs.util.crypto` has been removed as it is not a dependency of `ipfs-http-client` so reduces the bundle size. If you need to use libp2p crypto primitives then please see the [js-libp2p-crypto](https://github.com/libp2p/js-libp2p-crypto) project for info on how to use it in your project. Finally `ipfs.util.getEndpointConfig` is now a direct instance method, `ipfs.getEndpointConfig` License: MIT Signed-off-by: Alan Shaw ## [29.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v29.1.0...v29.1.1) (2019-02-13) ### Performance Improvements * use test profile ([#942](https://github.com/ipfs/js-ipfs-http-client/issues/942)) ([2c90620](https://github.com/ipfs/js-ipfs-http-client/commit/2c90620)) # [29.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v29.0.1...v29.1.0) (2019-01-29) ### Bug Fixes * throw on invalid multiaddr to constructor ([#934](https://github.com/ipfs/js-ipfs-http-client/issues/934)) ([bcbf0d2](https://github.com/ipfs/js-ipfs-http-client/commit/bcbf0d2)) ### Features * return protocol from getEndpointConfig ([#935](https://github.com/ipfs/js-ipfs-http-client/issues/935)) ([12ddaa3](https://github.com/ipfs/js-ipfs-http-client/commit/12ddaa3)) ## [29.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v29.0.0...v29.0.1) (2019-01-24) ### Bug Fixes * bundle in meteor ([#931](https://github.com/ipfs/js-ipfs-http-client/issues/931)) ([431c442](https://github.com/ipfs/js-ipfs-http-client/commit/431c442)), closes [#10411](https://github.com/ipfs/js-ipfs-http-client/issues/10411) # [29.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v28.1.2...v29.0.0) (2019-01-15) ### Code Refactoring * switch to bignumber.js ([#927](https://github.com/ipfs/js-ipfs-http-client/issues/927)) ([1a54ae5](https://github.com/ipfs/js-ipfs-http-client/commit/1a54ae5)) ### BREAKING CHANGES * All API methods that returned [`big.js`](https://github.com/MikeMcl/big.js/) instances now return [`bignumber.js`](https://github.com/MikeMcl/bignumber.js/) instances. License: MIT Signed-off-by: Alan Shaw ## [28.1.2](https://github.com/ipfs/js-ipfs-http-client/compare/v28.1.1...v28.1.2) (2019-01-14) ## [28.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v28.1.0...v28.1.1) (2019-01-04) # [28.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v28.0.3...v28.1.0) (2018-12-16) ### Features * add cidBase option to resolve ([#893](https://github.com/ipfs/js-ipfs-http-client/issues/893)) ([ec6285d](https://github.com/ipfs/js-ipfs-http-client/commit/ec6285d)) ## [28.0.3](https://github.com/ipfs/js-ipfs-http-client/compare/v28.0.2...v28.0.3) (2018-12-15) ### Bug Fixes * re-allow passing path to ls ([#914](https://github.com/ipfs/js-ipfs-http-client/issues/914)) ([442bcdd](https://github.com/ipfs/js-ipfs-http-client/commit/442bcdd)) ## [28.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v28.0.1...v28.0.2) (2018-12-14) ## [28.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v28.0.0...v28.0.1) (2018-12-13) ### Bug Fixes * disable just the rule we're breaking ([bed2687](https://github.com/ipfs/js-ipfs-http-client/commit/bed2687)) * properly serialize CID instances ([45b344c](https://github.com/ipfs/js-ipfs-http-client/commit/45b344c)) * skip test that go-ipfs cannot pass ([0e15761](https://github.com/ipfs/js-ipfs-http-client/commit/0e15761)) # [28.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v27.1.0...v28.0.0) (2018-12-11) ### Bug Fixes * case for addFromURL ([#907](https://github.com/ipfs/js-ipfs-http-client/issues/907)) ([99ac7be](https://github.com/ipfs/js-ipfs-http-client/commit/99ac7be)) ### Code Refactoring * dht api ([#890](https://github.com/ipfs/js-ipfs-http-client/issues/890)) ([05a84a4](https://github.com/ipfs/js-ipfs-http-client/commit/05a84a4)) ### BREAKING CHANGES * DHT API methods renamed and return types changed * `ipfs.dht.findprovs` renamed to `ipfs.dht.findProvs` and returns an array of [PeerInfo](https://github.com/libp2p/js-peer-info) * `ipfs.dht.findpeer` renamed to `ipfs.dht.findPeer` and returns a [PeerInfo](https://github.com/libp2p/js-peer-info) * `ipfs.dht.query` now returns an array of [PeerId](https://github.com/libp2p/js-peer-id) * [More info](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md) # [27.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v27.0.0...v27.1.0) (2018-12-05) ### Bug Fixes * add docs for breaking change ([#898](https://github.com/ipfs/js-ipfs-http-client/issues/898)) ([3e794ac](https://github.com/ipfs/js-ipfs-http-client/commit/3e794ac)) ### Features * add files.ls*Stream methods ([#903](https://github.com/ipfs/js-ipfs-http-client/issues/903)) ([705855e](https://github.com/ipfs/js-ipfs-http-client/commit/705855e)) # [27.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v26.1.2...v27.0.0) (2018-11-28) ### Bug Fixes * also retry with misnemed format "dag-cbor" as "cbor" ([#888](https://github.com/ipfs/js-ipfs-http-client/issues/888)) ([348a144](https://github.com/ipfs/js-ipfs-http-client/commit/348a144)) * better input validation for add ([#876](https://github.com/ipfs/js-ipfs-http-client/issues/876)) ([315b7f7](https://github.com/ipfs/js-ipfs-http-client/commit/315b7f7)) * fix log.tail by calling add after listening for events ([#882](https://github.com/ipfs/js-ipfs-http-client/issues/882)) ([da35b0f](https://github.com/ipfs/js-ipfs-http-client/commit/da35b0f)) * handle peer-info validation errors ([#887](https://github.com/ipfs/js-ipfs-http-client/issues/887)) ([6e6d7a2](https://github.com/ipfs/js-ipfs-http-client/commit/6e6d7a2)), closes [#885](https://github.com/ipfs/js-ipfs-http-client/issues/885) * updates ipld-dag-pb dep to version without .cid properties ([#889](https://github.com/ipfs/js-ipfs-http-client/issues/889)) ([ac30a82](https://github.com/ipfs/js-ipfs-http-client/commit/ac30a82)) ### Code Refactoring * object API write methods now return CIDs ([#896](https://github.com/ipfs/js-ipfs-http-client/issues/896)) ([38bed14](https://github.com/ipfs/js-ipfs-http-client/commit/38bed14)) * rename library to ipfs-http-client ([#897](https://github.com/ipfs/js-ipfs-http-client/issues/897)) ([d40cb6c](https://github.com/ipfs/js-ipfs-http-client/commit/d40cb6c)) * updated files API ([#878](https://github.com/ipfs/js-ipfs-http-client/issues/878)) ([39f4733](https://github.com/ipfs/js-ipfs-http-client/commit/39f4733)) ### BREAKING CHANGES * the `ipfs-api` library has been renamed to `ipfs-http-client`. Now install via `npm install ipfs-http-client`. Note that in the browser build the object attached to `window` is now `window.IpfsHttpClient`. License: MIT Signed-off-by: Alan Shaw * Object API refactor. Object API methods that write DAG nodes now return a CID instead of a DAG node. Affected methods: * `ipfs.object.new` * `ipfs.object.patch.addLink` * `ipfs.object.patch.appendData` * `ipfs.object.patch.rmLink` * `ipfs.object.patch.setData` * `ipfs.object.put` Example: ```js // Before const dagNode = await ipfs.object.new() ``` ```js // After const cid = await ipfs.object.new() // now returns a CID const dagNode = await ipfs.object.get(cid) // fetch the DAG node that was created ``` IMPORTANT: `DAGNode` instances, which are part of the IPLD dag-pb format have been refactored. These instances no longer have `multihash`, `cid` or `serialized` properties. This effects the following API methods that return these types of objects: * `ipfs.object.get` * `ipfs.dag.get` See https://github.com/ipld/js-ipld-dag-pb/pull/99 for more information. License: MIT Signed-off-by: Alan Shaw * Files API methods `add*`, `cat*`, `get*` have moved from `files` to the root namespace. Specifically, the following changes have been made: * `ipfs.files.add` => `ipfs.add` * `ipfs.files.addPullStream` => `ipfs.addPullStream` * `ipfs.files.addReadableStream` => `ipfs.addReadableStream` * `ipfs.files.cat` => `ipfs.cat` * `ipfs.files.catPullStream` => `ipfs.catPullStream` * `ipfs.files.catReadableStream` => `ipfs.catReadableStream` * `ipfs.files.get` => `ipfs.get` * `ipfs.files.getPullStream` => `ipfs.getPullStream` * `ipfs.files.getReadableStream` => `ipfs.getReadableStream` Additionally, `addFromFs`, `addFromURL`, `addFromStream` have moved from `util` to the root namespace: * `ipfs.util.addFromFs` => `ipfs.addFromFs` * `ipfs.util.addFromURL` => `ipfs.addFromURL` * `ipfs.util.addFromStream` => `ipfs.addFromStream` License: MIT Signed-off-by: Alan Shaw * Previously `swarm.peers` would throw an uncaught error if any peer in the response could not have its peerId or multiaddr validated. This change catches errors that occur while validating the peer info. The returned array will contain an entry for every peer in the ipfs response. peer-info objects that couldn't be validated, now have an `error` property and a `rawPeerInfo` property. This at least means the count of peers in the response will be accurate, and there the info is available to the caller. This means that callers now have to deal with peer-info objects that may not have a `peer` or `addr` property. Adds `nock` tests to exercice the code under different error conditions. Doing so uncovered a bug in our legacy go-ipfs <= 0.4.4 peer info parsing, which is also fixed. The code was trying to decapusalate the peerId from the multiaddr, but doing so trims the peerId rather than returning it. License: MIT Signed-off-by: Oli Evans ## [26.1.2](https://github.com/ipfs/js-ipfs-http-client/compare/v26.1.0...v26.1.2) (2018-11-03) ### Features * go-ipfs 0.4.18 ([e3e4d6c](https://github.com/ipfs/js-ipfs-http-client/commit/e3e4d6c)) * upload example works with big files ([62b844f](https://github.com/ipfs/js-ipfs-http-client/commit/62b844f)) ## [26.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v26.1.0...v26.1.1) (2018-11-03) ### Features * go-ipfs 0.4.18 ([9178e7d](https://github.com/ipfs/js-ipfs-http-client/commit/9178e7d)) # [26.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v26.0.3...v26.1.0) (2018-10-31) ### Bug Fixes * make ping not mix errors with responses ([#883](https://github.com/ipfs/js-ipfs-http-client/issues/883)) ([80725f2](https://github.com/ipfs/js-ipfs-http-client/commit/80725f2)) ## [26.0.3](https://github.com/ipfs/js-ipfs-http-client/compare/v26.0.2...v26.0.3) (2018-10-31) ## [26.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v26.0.0...v26.0.2) (2018-10-31) ### Bug Fixes * pin.ls ignored opts when hash was present ([#875](https://github.com/ipfs/js-ipfs-http-client/issues/875)) ([0b46750](https://github.com/ipfs/js-ipfs-http-client/commit/0b46750)), closes [/github.com/ipfs-shipyard/ipfs-companion/issues/360#issuecomment-427525801](https://github.com//github.com/ipfs-shipyard/ipfs-companion/issues/360/issues/issuecomment-427525801) ## [26.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v26.0.0...v26.0.1) (2018-10-30) # [26.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v25.0.0...v26.0.0) (2018-10-30) ### Bug Fixes * add missing and remove unused dependencies ([#879](https://github.com/ipfs/js-ipfs-http-client/issues/879)) ([979d8b5](https://github.com/ipfs/js-ipfs-http-client/commit/979d8b5)) ### Chores * remove ipld formats re-export ([#872](https://github.com/ipfs/js-ipfs-http-client/issues/872)) ([c534375](https://github.com/ipfs/js-ipfs-http-client/commit/c534375)) * update to ipld-dag-cbor 0.13 ([0652ac0](https://github.com/ipfs/js-ipfs-http-client/commit/0652ac0)) ### Features * ipns over pubsub ([#846](https://github.com/ipfs/js-ipfs-http-client/issues/846)) ([ef49e95](https://github.com/ipfs/js-ipfs-http-client/commit/ef49e95)) ### BREAKING CHANGES * dag-cbor nodes now represent links as CID objects The API for [dag-cbor](https://github.com/ipld/js-ipld-dag-cbor) changed. Links are no longer represented as JSON objects (`{"/": "base-encoded-cid"}`, but as [CID objects](https://github.com/ipld/js-cid). `ipfs.dag.get()` and now always return links as CID objects. `ipfs.dag.put()` also expects links to be represented as CID objects. The old-style JSON objects representation is still supported, but deprecated. Prior to this change: ```js const cid = new CID('QmXed8RihWcWFXRRmfSRG9yFjEbXNxu1bDwgCFAN8Dxcq5') // Link as JSON object representation const putCid = await ipfs.dag.put({link: {'/': cid.toBaseEncodedString()}}) const result = await ipfs.dag.get(putCid) console.log(result.value) ``` Output: ```js { link: { '/': } } ``` Now: ```js const cid = new CID('QmXed8RihWcWFXRRmfSRG9yFjEbXNxu1bDwgCFAN8Dxcq5') // Link as CID object const putCid = await ipfs.dag.put({link: cid}) const result = await ipfs.dag.get(putCid) console.log(result.value) ``` Output: ```js { link: CID { codec: 'dag-pb', version: 0, multihash: } } ``` See https://github.com/ipld/ipld/issues/44 for more information on why this change was made. * remove `types.dagCBOR` and `types.dagPB` from public API If you need the `ipld-dag-cbor` or `ipld-dag-pb` module in the Browser, you need to bundle them yourself. # [25.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v24.0.2...v25.0.0) (2018-10-15) ### Bug Fixes * >150mb bodies no longer crashing Chromium ([#868](https://github.com/ipfs/js-ipfs-http-client/issues/868)) ([180da77](https://github.com/ipfs/js-ipfs-http-client/commit/180da77)), closes [#654](https://github.com/ipfs/js-ipfs-http-client/issues/654) * add bl module to package dependencies ([#853](https://github.com/ipfs/js-ipfs-http-client/issues/853)) ([#854](https://github.com/ipfs/js-ipfs-http-client/issues/854)) ([834934f](https://github.com/ipfs/js-ipfs-http-client/commit/834934f)) * add lodash dependency ([#873](https://github.com/ipfs/js-ipfs-http-client/issues/873)) ([c510cb7](https://github.com/ipfs/js-ipfs-http-client/commit/c510cb7)), closes [#870](https://github.com/ipfs/js-ipfs-http-client/issues/870) ## [24.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v24.0.1...v24.0.2) (2018-09-21) ### Bug Fixes * block.put options ([#844](https://github.com/ipfs/js-ipfs-http-client/issues/844)) ([e290a38](https://github.com/ipfs/js-ipfs-http-client/commit/e290a38)) ## [24.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v24.0.0...v24.0.1) (2018-08-21) # [24.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v23.0.0...v24.0.0) (2018-08-15) ### Bug Fixes * add test data to IPFS before fetching it ([#832](https://github.com/ipfs/js-ipfs-http-client/issues/832)) ([b2a77d6](https://github.com/ipfs/js-ipfs-http-client/commit/b2a77d6)) * BREAKING CHANGE use data-encoding arg so data is not corrupted ([#806](https://github.com/ipfs/js-ipfs-http-client/issues/806)) ([553c3fb](https://github.com/ipfs/js-ipfs-http-client/commit/553c3fb)) * dag.get return error on missing multicodec ([#831](https://github.com/ipfs/js-ipfs-http-client/issues/831)) ([ff7c7e5](https://github.com/ipfs/js-ipfs-http-client/commit/ff7c7e5)) * remove external urls from addFromURL tests ([#834](https://github.com/ipfs/js-ipfs-http-client/issues/834)) ([7cf7998](https://github.com/ipfs/js-ipfs-http-client/commit/7cf7998)), closes [#803](https://github.com/ipfs/js-ipfs-http-client/issues/803) ### BREAKING CHANGES * Requires go-ipfs 0.4.17 as it allows for specifying the data encoding format when requesting object data. # [23.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v22.3.0...v23.0.0) (2018-08-06) ### Bug Fixes * config get ([#825](https://github.com/ipfs/js-ipfs-http-client/issues/825)) ([ef5a4a3](https://github.com/ipfs/js-ipfs-http-client/commit/ef5a4a3)) ### Features * add resolve cmd ([#826](https://github.com/ipfs/js-ipfs-http-client/issues/826)) ([c7ad0e4](https://github.com/ipfs/js-ipfs-http-client/commit/c7ad0e4)) # [22.3.0](https://github.com/ipfs/js-ipfs-http-client/compare/v22.2.4...v22.3.0) (2018-08-02) ### Bug Fixes * config.set rejects buffer values ([#800](https://github.com/ipfs/js-ipfs-http-client/issues/800)) ([f3e6bf1](https://github.com/ipfs/js-ipfs-http-client/commit/f3e6bf1)) ### Features * compatible with go-ipfs 0.4.16 ([8536ee4](https://github.com/ipfs/js-ipfs-http-client/commit/8536ee4)) * expose mfs files.read*Stream methods ([#823](https://github.com/ipfs/js-ipfs-http-client/issues/823)) ([70c9df1](https://github.com/ipfs/js-ipfs-http-client/commit/70c9df1)) ## [22.2.4](https://github.com/ipfs/js-ipfs-http-client/compare/v22.2.3...v22.2.4) (2018-07-17) ### Bug Fixes * increase browserNoActivityTimeout to account for before ([328e338](https://github.com/ipfs/js-ipfs-http-client/commit/328e338)) * increase timeout for .name after all ([3dc4313](https://github.com/ipfs/js-ipfs-http-client/commit/3dc4313)) * missing debug dependency fixes [#809](https://github.com/ipfs/js-ipfs-http-client/issues/809) ([#810](https://github.com/ipfs/js-ipfs-http-client/issues/810)) ([0f1fe95](https://github.com/ipfs/js-ipfs-http-client/commit/0f1fe95)) ## [22.2.3](https://github.com/ipfs/js-ipfs-http-client/compare/v22.2.2...v22.2.3) (2018-07-10) ### Bug Fixes * Request logging broken in Electron ([#808](https://github.com/ipfs/js-ipfs-http-client/issues/808)) ([52298ae](https://github.com/ipfs/js-ipfs-http-client/commit/52298ae)) ## [22.2.2](https://github.com/ipfs/js-ipfs-http-client/compare/v22.2.1...v22.2.2) (2018-07-05) ### Bug Fixes * ignore response body for some mfs commands ([#805](https://github.com/ipfs/js-ipfs-http-client/issues/805)) ([b604a64](https://github.com/ipfs/js-ipfs-http-client/commit/b604a64)) ### Features * modular interface tests ([#785](https://github.com/ipfs/js-ipfs-http-client/issues/785)) ([2426072](https://github.com/ipfs/js-ipfs-http-client/commit/2426072)), closes [#339](https://github.com/ipfs/js-ipfs-http-client/issues/339) [#802](https://github.com/ipfs/js-ipfs-http-client/issues/802) [#801](https://github.com/ipfs/js-ipfs-http-client/issues/801) ## [22.2.1](https://github.com/ipfs/js-ipfs-http-client/compare/v22.2.0...v22.2.1) (2018-06-29) ### Bug Fixes * res.req only in Node.js, in browser use res.url instead ([#798](https://github.com/ipfs/js-ipfs-http-client/issues/798)) ([e8a5ab9](https://github.com/ipfs/js-ipfs-http-client/commit/e8a5ab9)) # [22.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v22.1.1...v22.2.0) (2018-06-29) ### Features * logs path & querystring for requests ([#796](https://github.com/ipfs/js-ipfs-http-client/issues/796)) ([4e55d19](https://github.com/ipfs/js-ipfs-http-client/commit/4e55d19)) ## [22.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v22.1.0...v22.1.1) (2018-06-25) ### Bug Fixes * get block with empty data ([#789](https://github.com/ipfs/js-ipfs-http-client/issues/789)) ([88edd83](https://github.com/ipfs/js-ipfs-http-client/commit/88edd83)) # [22.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v22.0.2...v22.1.0) (2018-06-18) ### Features * add support for custom headers to send-request ([#741](https://github.com/ipfs/js-ipfs-http-client/issues/741)) ([7fb2e07](https://github.com/ipfs/js-ipfs-http-client/commit/7fb2e07)) * implement bitswap wantlist peer ID param and bitswap unwant ([#761](https://github.com/ipfs/js-ipfs-http-client/issues/761)) ([73a153e](https://github.com/ipfs/js-ipfs-http-client/commit/73a153e)) ## [22.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v22.0.1...v22.0.2) (2018-06-14) ### Bug Fixes * json-loader error in upload-file-via-browser example ([#784](https://github.com/ipfs/js-ipfs-http-client/issues/784)) ([5e7b7c4](https://github.com/ipfs/js-ipfs-http-client/commit/5e7b7c4)) ## [22.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v22.0.0...v22.0.1) (2018-05-30) ### Bug Fixes * configure webpack to not use esmodules in dependencies ([dc14333](https://github.com/ipfs/js-ipfs-http-client/commit/dc14333)) * correctly differentiate pong responses ([4ad25a3](https://github.com/ipfs/js-ipfs-http-client/commit/4ad25a3)) * util.addFromURL with URL-escaped file ([a3bd811](https://github.com/ipfs/js-ipfs-http-client/commit/a3bd811)) # [22.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v21.0.0...v22.0.0) (2018-05-20) ### Bug Fixes * callback from unsub after stream ends ([51a80f2](https://github.com/ipfs/js-ipfs-http-client/commit/51a80f2)) * do not fail stop node if failed start node ([533760f](https://github.com/ipfs/js-ipfs-http-client/commit/533760f)) * **ping:** convert the ping messages to lowercase ([632af40](https://github.com/ipfs/js-ipfs-http-client/commit/632af40)) * more robust ping tests ([fc6d301](https://github.com/ipfs/js-ipfs-http-client/commit/fc6d301)) * remove .only ([0e21c8a](https://github.com/ipfs/js-ipfs-http-client/commit/0e21c8a)) * result.Peers can be null, ensure callback is called ([f5f2e83](https://github.com/ipfs/js-ipfs-http-client/commit/f5f2e83)) * update asserted error message ([17c1f1c](https://github.com/ipfs/js-ipfs-http-client/commit/17c1f1c)) * use async/setImmediate vs process.nextTick ([faa51b4](https://github.com/ipfs/js-ipfs-http-client/commit/faa51b4)) # [21.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v20.2.1...v21.0.0) (2018-05-12) ### Bug Fixes * make pubsub.unsubscribe async and alter pubsub.subscribe signature ([b98f8f3](https://github.com/ipfs/js-ipfs-http-client/commit/b98f8f3)) ### BREAKING CHANGES * pubsub.unsubscribe is now async and argument order for pubsub.subscribe has changed License: MIT Signed-off-by: Alan Shaw ## [20.2.1](https://github.com/ipfs/js-ipfs-http-client/compare/v20.2.0...v20.2.1) (2018-05-06) # [20.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v20.0.1...v20.2.0) (2018-04-30) ### Bug Fixes * adding files by pull stream ([2fa16c5](https://github.com/ipfs/js-ipfs-http-client/commit/2fa16c5)) * handle request errors in addFromURL ([7c5cea5](https://github.com/ipfs/js-ipfs-http-client/commit/7c5cea5)) * increase timeout for name.publish and fix setup code ([ceb1106](https://github.com/ipfs/js-ipfs-http-client/commit/ceb1106)) * ipfs add url wrap doesn't work ([#750](https://github.com/ipfs/js-ipfs-http-client/issues/750)) ([f6f1bf0](https://github.com/ipfs/js-ipfs-http-client/commit/f6f1bf0)) ### Features * Add offset/length arguments to files.cat ([17967c1](https://github.com/ipfs/js-ipfs-http-client/commit/17967c1)) * get it ready for release ([#751](https://github.com/ipfs/js-ipfs-http-client/issues/751)) ([1885af4](https://github.com/ipfs/js-ipfs-http-client/commit/1885af4)) # [20.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v20.0.1...v20.1.0) (2018-04-30) ### Bug Fixes * adding files by pull stream ([2fa16c5](https://github.com/ipfs/js-ipfs-http-client/commit/2fa16c5)) * handle request errors in addFromURL ([7c5cea5](https://github.com/ipfs/js-ipfs-http-client/commit/7c5cea5)) * increase timeout for name.publish and fix setup code ([ceb1106](https://github.com/ipfs/js-ipfs-http-client/commit/ceb1106)) * ipfs add url wrap doesn't work ([#750](https://github.com/ipfs/js-ipfs-http-client/issues/750)) ([f6f1bf0](https://github.com/ipfs/js-ipfs-http-client/commit/f6f1bf0)) ### Features * Add offset/length arguments to files.cat ([17967c1](https://github.com/ipfs/js-ipfs-http-client/commit/17967c1)) * get it ready for release ([#751](https://github.com/ipfs/js-ipfs-http-client/issues/751)) ([1885af4](https://github.com/ipfs/js-ipfs-http-client/commit/1885af4)) ## [20.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v20.0.0...v20.0.1) (2018-04-12) # [20.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v19.0.0...v20.0.0) (2018-04-05) ### Bug Fixes * **dag:** js-ipld format resolver take the raw block ([2683c7e](https://github.com/ipfs/js-ipfs-http-client/commit/2683c7e)) * **dag:** path logic for DAG get was wrong ([d2b203b](https://github.com/ipfs/js-ipfs-http-client/commit/d2b203b)) * **dag:** use SendOneFile for dag put ([9c37213](https://github.com/ipfs/js-ipfs-http-client/commit/9c37213)) ### Features * dag.put ([9463d3a](https://github.com/ipfs/js-ipfs-http-client/commit/9463d3a)) * **dag:** proper get implementation ([7ba0343](https://github.com/ipfs/js-ipfs-http-client/commit/7ba0343)) * **dag:** rebase, use waterfall for put ([ad9eab8](https://github.com/ipfs/js-ipfs-http-client/commit/ad9eab8)) * **dag:** update option names to reflect go-ipfs API ([9bf1c6c](https://github.com/ipfs/js-ipfs-http-client/commit/9bf1c6c)) * Provide access to bundled libraries when in browser ([#732](https://github.com/ipfs/js-ipfs-http-client/issues/732)) ([994bdad](https://github.com/ipfs/js-ipfs-http-client/commit/994bdad)), closes [#406](https://github.com/ipfs/js-ipfs-http-client/issues/406) * public-readonly-method-for-getting-host-and-port ([41d32e3](https://github.com/ipfs/js-ipfs-http-client/commit/41d32e3)), closes [#580](https://github.com/ipfs/js-ipfs-http-client/issues/580) * Wrap with dir ([#730](https://github.com/ipfs/js-ipfs-http-client/issues/730)) ([160860e](https://github.com/ipfs/js-ipfs-http-client/commit/160860e)) # [19.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v18.2.1...v19.0.0) (2018-03-28) ### Bug Fixes * **bitswap:** 0.4.14 returns empty array instead of null ([5e37a54](https://github.com/ipfs/js-ipfs-http-client/commit/5e37a54)) * **ping:** tests were failing and there it was missing to catch when count and n are used at the same time ([2181568](https://github.com/ipfs/js-ipfs-http-client/commit/2181568)) ### Features * streamable ping and optional packet number ([#723](https://github.com/ipfs/js-ipfs-http-client/issues/723)) ([3f3ce8a](https://github.com/ipfs/js-ipfs-http-client/commit/3f3ce8a)) ## [18.2.1](https://github.com/ipfs/js-ipfs-http-client/compare/v18.2.0...v18.2.1) (2018-03-22) ### Features * add ability to files.cat with a cid instance ([aeeb94e](https://github.com/ipfs/js-ipfs-http-client/commit/aeeb94e)) # [18.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v18.1.2...v18.2.0) (2018-03-16) ### Bug Fixes * disable Browser test on Windows ([385a6c3](https://github.com/ipfs/js-ipfs-http-client/commit/385a6c3)) * don't create one webpack bundle for every test file ([3967e96](https://github.com/ipfs/js-ipfs-http-client/commit/3967e96)) * last fixes for green ([#719](https://github.com/ipfs/js-ipfs-http-client/issues/719)) ([658bad2](https://github.com/ipfs/js-ipfs-http-client/commit/658bad2)) * set the FileResultStreamConverter explicitly ([dfad55e](https://github.com/ipfs/js-ipfs-http-client/commit/dfad55e)), closes [#696](https://github.com/ipfs/js-ipfs-http-client/issues/696) * use a different remote server for test ([1fc15a5](https://github.com/ipfs/js-ipfs-http-client/commit/1fc15a5)) ### Features * --only-hash ([#717](https://github.com/ipfs/js-ipfs-http-client/issues/717)) ([1137401](https://github.com/ipfs/js-ipfs-http-client/commit/1137401)), closes [#700](https://github.com/ipfs/js-ipfs-http-client/issues/700) * add support for ipfs files stat --with-local ([#695](https://github.com/ipfs/js-ipfs-http-client/issues/695)) ([b08f21a](https://github.com/ipfs/js-ipfs-http-client/commit/b08f21a)) ## [18.1.2](https://github.com/ipfs/js-ipfs-http-client/compare/v18.1.1...v18.1.2) (2018-03-09) ### Bug Fixes * regression on files.add and update deps ([#709](https://github.com/ipfs/js-ipfs-http-client/issues/709)) ([85cc2a8](https://github.com/ipfs/js-ipfs-http-client/commit/85cc2a8)) * remove argument from .stats.bw* ([#699](https://github.com/ipfs/js-ipfs-http-client/issues/699)) ([f81dce5](https://github.com/ipfs/js-ipfs-http-client/commit/f81dce5)) ## [18.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v18.0.0...v18.1.1) (2018-02-20) ### Features * support recursive ipfs ls ([cfe95f6](https://github.com/ipfs/js-ipfs-http-client/commit/cfe95f6)) # [18.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v18.0.0...v18.1.0) (2018-02-20) ### Features * support recursive ipfs ls ([cfe95f6](https://github.com/ipfs/js-ipfs-http-client/commit/cfe95f6)) # [18.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v17.5.0...v18.0.0) (2018-02-14) ### Bug Fixes * exception when dir is empty ([#680](https://github.com/ipfs/js-ipfs-http-client/issues/680)) ([ec04f6e](https://github.com/ipfs/js-ipfs-http-client/commit/ec04f6e)) * support all the Buffer shims and load fixtures correctly ([066988f](https://github.com/ipfs/js-ipfs-http-client/commit/066988f)) * update stats API ([#684](https://github.com/ipfs/js-ipfs-http-client/issues/684)) ([4f7999d](https://github.com/ipfs/js-ipfs-http-client/commit/4f7999d)) ### Features * (breaking change) stats spec, spec repo, stream to value on files read ([#679](https://github.com/ipfs/js-ipfs-http-client/issues/679)) ([118456e](https://github.com/ipfs/js-ipfs-http-client/commit/118456e)) * **breaking change:** use stream on stats.bw ([#686](https://github.com/ipfs/js-ipfs-http-client/issues/686)) ([895760e](https://github.com/ipfs/js-ipfs-http-client/commit/895760e)) * ipfs.stop ([5091115](https://github.com/ipfs/js-ipfs-http-client/commit/5091115)) # [17.5.0](https://github.com/ipfs/js-ipfs-http-client/compare/v17.3.0...v17.5.0) (2018-01-24) ### Bug Fixes * normalize stats fields ([#669](https://github.com/ipfs/js-ipfs-http-client/issues/669)) ([5803d39](https://github.com/ipfs/js-ipfs-http-client/commit/5803d39)) ### Features * /api/v0/repo/version ([#676](https://github.com/ipfs/js-ipfs-http-client/issues/676)) ([ecf70b9](https://github.com/ipfs/js-ipfs-http-client/commit/ecf70b9)) * integrate new ipfsd-ctl ([2b1820b](https://github.com/ipfs/js-ipfs-http-client/commit/2b1820b)) # [17.4.0](https://github.com/ipfs/js-ipfs-http-client/compare/v17.3.0...v17.4.0) (2018-01-24) ### Bug Fixes * normalize stats fields ([#669](https://github.com/ipfs/js-ipfs-http-client/issues/669)) ([5803d39](https://github.com/ipfs/js-ipfs-http-client/commit/5803d39)) ### Features * integrate new ipfsd-ctl ([2b1820b](https://github.com/ipfs/js-ipfs-http-client/commit/2b1820b)) # [17.3.0](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.7...v17.3.0) (2018-01-12) ### Features * /api/v0/dns ([#665](https://github.com/ipfs/js-ipfs-http-client/issues/665)) ([81016bb](https://github.com/ipfs/js-ipfs-http-client/commit/81016bb)) ## [17.2.7](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.6...v17.2.7) (2018-01-11) ### Bug Fixes * name and key tests ([#661](https://github.com/ipfs/js-ipfs-http-client/issues/661)) ([5ab1d02](https://github.com/ipfs/js-ipfs-http-client/commit/5ab1d02)) ### Features * normalize KEY API ([#659](https://github.com/ipfs/js-ipfs-http-client/issues/659)) ([1b10821](https://github.com/ipfs/js-ipfs-http-client/commit/1b10821)) * normalize NAME API ([#658](https://github.com/ipfs/js-ipfs-http-client/issues/658)) ([9b8ef48](https://github.com/ipfs/js-ipfs-http-client/commit/9b8ef48)) ## [17.2.6](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.5...v17.2.6) (2017-12-28) ### Features * support key/export and key/import ([#653](https://github.com/ipfs/js-ipfs-http-client/issues/653)) ([496f08e](https://github.com/ipfs/js-ipfs-http-client/commit/496f08e)) ## [17.2.5](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.4...v17.2.5) (2017-12-20) ### Bug Fixes * **files.add:** handle weird directory names ([#646](https://github.com/ipfs/js-ipfs-http-client/issues/646)) ([012b86c](https://github.com/ipfs/js-ipfs-http-client/commit/012b86c)) ### Features * add files/flush ([#643](https://github.com/ipfs/js-ipfs-http-client/issues/643)) ([5c254eb](https://github.com/ipfs/js-ipfs-http-client/commit/5c254eb)) * support key/rm and key/rename ([#641](https://github.com/ipfs/js-ipfs-http-client/issues/641)) ([113030a](https://github.com/ipfs/js-ipfs-http-client/commit/113030a)) ## [17.2.4](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.3...v17.2.4) (2017-12-06) ### Bug Fixes * stats/bw uses stream ([#640](https://github.com/ipfs/js-ipfs-http-client/issues/640)) ([c4e922e](https://github.com/ipfs/js-ipfs-http-client/commit/c4e922e)) ## [17.2.3](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.2...v17.2.3) (2017-12-05) ## [17.2.2](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.1...v17.2.2) (2017-12-05) ## [17.2.1](https://github.com/ipfs/js-ipfs-http-client/compare/v17.2.0...v17.2.1) (2017-12-05) ### Features * add the stat commands ([#639](https://github.com/ipfs/js-ipfs-http-client/issues/639)) ([76c3068](https://github.com/ipfs/js-ipfs-http-client/commit/76c3068)) # [17.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v17.1.3...v17.2.0) (2017-12-01) ### Bug Fixes * propagate trailer errors correctly ([#636](https://github.com/ipfs/js-ipfs-http-client/issues/636)) ([62d733e](https://github.com/ipfs/js-ipfs-http-client/commit/62d733e)) ## [17.1.3](https://github.com/ipfs/js-ipfs-http-client/compare/v17.1.2...v17.1.3) (2017-11-23) ## [17.1.2](https://github.com/ipfs/js-ipfs-http-client/compare/v17.1.1...v17.1.2) (2017-11-22) ### Bug Fixes * config.replace ([#634](https://github.com/ipfs/js-ipfs-http-client/issues/634)) ([79d79c5](https://github.com/ipfs/js-ipfs-http-client/commit/79d79c5)), closes [#633](https://github.com/ipfs/js-ipfs-http-client/issues/633) ## [17.1.1](https://github.com/ipfs/js-ipfs-http-client/compare/v17.1.0...v17.1.1) (2017-11-22) ### Bug Fixes * pubsub do not eat error messages ([#632](https://github.com/ipfs/js-ipfs-http-client/issues/632)) ([5a1bf9b](https://github.com/ipfs/js-ipfs-http-client/commit/5a1bf9b)) # [17.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v17.0.1...v17.1.0) (2017-11-20) ### Features * send files HTTP request should stream ([#629](https://github.com/ipfs/js-ipfs-http-client/issues/629)) ([dae62cb](https://github.com/ipfs/js-ipfs-http-client/commit/dae62cb)) ## [17.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v17.0.0...v17.0.1) (2017-11-20) ### Bug Fixes * allow topicCIDs from older peers ([#631](https://github.com/ipfs/js-ipfs-http-client/issues/631)) ([fe7cc22](https://github.com/ipfs/js-ipfs-http-client/commit/fe7cc22)) # [17.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v16.0.0...v17.0.0) (2017-11-17) ### Features * Implementing the new interfaces ([#619](https://github.com/ipfs/js-ipfs-http-client/issues/619)) ([e1b38bf](https://github.com/ipfs/js-ipfs-http-client/commit/e1b38bf)) # [16.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v15.1.0...v16.0.0) (2017-11-16) ### Bug Fixes * pubsub message fields ([#627](https://github.com/ipfs/js-ipfs-http-client/issues/627)) ([470777d](https://github.com/ipfs/js-ipfs-http-client/commit/470777d)) # [15.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v15.0.2...v15.1.0) (2017-11-14) ### Bug Fixes * adapting HTTP API to the interface-ipfs-core spec ([#625](https://github.com/ipfs/js-ipfs-http-client/issues/625)) ([8e58225](https://github.com/ipfs/js-ipfs-http-client/commit/8e58225)) ### Features * windows interop ([#624](https://github.com/ipfs/js-ipfs-http-client/issues/624)) ([40557d0](https://github.com/ipfs/js-ipfs-http-client/commit/40557d0)) ## [15.0.2](https://github.com/ipfs/js-ipfs-http-client/compare/v15.0.1...v15.0.2) (2017-11-13) ## [15.0.1](https://github.com/ipfs/js-ipfs-http-client/compare/v15.0.0...v15.0.1) (2017-10-22) # [15.0.0](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.7...v15.0.0) (2017-10-22) ### Features * update pin API to match interface-ipfs-core ([9102643](https://github.com/ipfs/js-ipfs-http-client/commit/9102643)) ## [14.3.7](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.6...v14.3.7) (2017-10-18) ## [14.3.6](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.5...v14.3.6) (2017-10-18) ### Bug Fixes * pass the config protocol to http requests ([#609](https://github.com/ipfs/js-ipfs-http-client/issues/609)) ([38d7289](https://github.com/ipfs/js-ipfs-http-client/commit/38d7289)) ### Features * avoid doing multiple RPC requests for files.add, fixes [#522](https://github.com/ipfs/js-ipfs-http-client/issues/522) ([#595](https://github.com/ipfs/js-ipfs-http-client/issues/595)) ([0ea5f57](https://github.com/ipfs/js-ipfs-http-client/commit/0ea5f57)) * report progress on ipfs add ([e2d894c](https://github.com/ipfs/js-ipfs-http-client/commit/e2d894c)) ## [14.3.5](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.4...v14.3.5) (2017-09-08) ### Features * Support specify hash algorithm in files.add ([#597](https://github.com/ipfs/js-ipfs-http-client/issues/597)) ([ed68657](https://github.com/ipfs/js-ipfs-http-client/commit/ed68657)) ## [14.3.4](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.3...v14.3.4) (2017-09-07) ## [14.3.3](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.2...v14.3.3) (2017-09-07) ### Features * support options for .add / files.add ([8c717b2](https://github.com/ipfs/js-ipfs-http-client/commit/8c717b2)) ## [14.3.2](https://github.com/ipfs/js-ipfs-http-client/compare/v14.3.1...v14.3.2) (2017-09-04) ### Bug Fixes * new fixed aegir ([93ac472](https://github.com/ipfs/js-ipfs-http-client/commit/93ac472)) ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing ## Setup You should have [node.js] and [npm] installed. ## Linting Linting is done using [eslint] and the rules are based on [standard]. ```bash $ npm run lint ``` ## Tests Tests in node ```bash $ npm run test:node ``` Tests in the browser ```bash $ npm run test:browser ``` ### Writing a new core interface test The core interface tests are kept in a separate repo, because they are used by multiple other projects. To add a core interface test, follow this guide: 1. Clone this project repo and the interface core tests repo: * `git clone https://github.com/ipfs/js-ipfs-http-client.git` * `git clone https://github.com/ipfs/interface-js-ipfs-core.git` 1. Install dependencies and globally [link](https://docs.npmjs.com/cli/link) the interface core tests: * `cd interface-js-ipfs-core` * `npm install` * `npm link` 1. Write your test 1. Install dependencies for this project and link to the interface core tests * `cd ../js-ipfs-http-client` * `npm install` * `npm link interface-ipfs-core` 1. Run the tests: * `npm test` Next: 1. Send a PR to `ipfs/interface-js-ipfs-core` (please also add to the documentation!) 1. This will be reviewed by a core contributor and they will perform the same steps as above 1. When merged, a new version of `interface-ipfs-core` will be released 1. Finally, a PR needs to be created or updated to `ipfs/js-ipfs-http-client` to use the new version ## Building browser version ```bash $ npm run build ``` ## Releases The `release` task will 1. Run a build 2. Commit the build 3. Bump the version in `package.json` 4. Commit the version change 5. Create a git tag 6. Run `git push` to `upstream/master` (You can change this with `--remote my-remote`) ```bash # Major release $ npm run release-major # Minor relase $ npm run release-minor # Patch release $ npm run release ``` [node.js]: https://nodejs.org/ [npm]: http://npmjs.org/ [eslint]: http://eslint.org/ [standard]: https://github.com/feross/standard ================================================ FILE: COPYRIGHT ================================================ This project is transitioning from an MIT-only license to a dual MIT/Apache-2.0 license. Unless otherwise noted, all code contributed prior to 2019-11-26 and not contributed by a user listed in [this signoff issue](https://github.com/ipfs/js-ipfs-http-client/issues/1189) is licensed under MIT-only. All new contributions (and past contributions since 2019-11-29) are licensed under a dual MIT/Apache-2.0 license. ================================================ FILE: LICENSE-APACHE ================================================ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LICENSE-MIT ================================================ The MIT License (MIT) Copyright (c) 2016 Protocol Labs, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # 🔒 Archived The contents of this repo have been merged into [ipfs/js-ipfs](https://github.com/ipfs/js-ipfs). Please open [issues](https://github.com/ipfs/js-ipfs/issues) or submit [PRs](https://github.com/ipfs/js-ipfs/pulls) there.

IPFS http client lib logo

The JavaScript HTTP client library for IPFS implementations.



> A client library for the IPFS HTTP API, implemented in JavaScript. This client library implements the [interface-ipfs-core](https://github.com/ipfs/interface-ipfs-core) enabling applications to change between an embedded js-ipfs node and any remote IPFS node without having to change the code. In addition, this client library implements a set of utility functions. ## Lead Maintainer [Alan Shaw](http://github.com/alanshaw). ## Table of Contents - [Lead Maintainer](#lead-maintainer) - [Table of Contents](#table-of-contents) - [Install](#install) - [Running the daemon with the right port](#running-the-daemon-with-the-right-port) - [Importing the module and usage](#importing-the-module-and-usage) - [Importing a sub-module and usage](#importing-a-sub-module-and-usage) - [In a web browser](#in-a-web-browser) - [CORS](#cors) - [Custom Headers](#custom-headers) - [Global Timeouts](#global-timeouts) - [Usage](#usage) - [API](#api) - [Files](#files) - [Graph](#graph) - [Network](#network) - [Node Management](#node-management) - [Additional Options](#additional-options) - [Instance Utils](#instance-utils) - [Static Types and Utils](#static-types-and-utils) - [Glob source](#glob-source) - [`globSource(path, [options])`](#globsourcepath-options) - [Example](#example) - [URL source](#url-source) - [`urlSource(url)`](#urlsourceurl) - [Example](#example-1) - [Development](#development) - [Testing](#testing) - [Contribute](#contribute) - [Historical context](#historical-context) - [License](#license) ## Install This module uses node.js, and can be installed through npm: ```bash npm install --save ipfs-http-client ``` We support both the Current and Active LTS versions of Node.js. Please see [nodejs.org](https://nodejs.org/) for what these currently are. ### Running the daemon with the right port To interact with the API, you need to have a local daemon running. It needs to be open on the right port. `5001` is the default, and is used in the examples below, but it can be set to whatever you need. ```sh # Show the ipfs config API port to check it is correct > ipfs config Addresses.API /ip4/127.0.0.1/tcp/5001 # Set it if it does not match the above output > ipfs config Addresses.API /ip4/127.0.0.1/tcp/5001 # Restart the daemon after changing the config # Run the daemon > ipfs daemon ``` ### Importing the module and usage ```javascript const ipfsClient = require('ipfs-http-client') // connect to ipfs daemon API server const ipfs = ipfsClient('http://localhost:5001') // (the default in Node.js) // or connect with multiaddr const ipfs = ipfsClient('/ip4/127.0.0.1/tcp/5001') // or using options const ipfs = ipfsClient({ host: 'localhost', port: '5001', protocol: 'http' }) // or specifying a specific API path const ipfs = ipfsClient({ host: '1.1.1.1', port: '80', apiPath: '/ipfs/api/v0' }) ``` ### Importing a sub-module and usage ```javascript const bitswap = require('ipfs-http-client/src/bitswap')('/ip4/127.0.0.1/tcp/5001') const list = await bitswap.wantlist(key) // ... ``` ### In a web browser **through Browserify** Same as in Node.js, you just have to [browserify](http://browserify.org) the code before serving it. See the browserify repo for how to do that. See the example in the [examples folder](/examples/bundle-browserify) to get a boilerplate. **through webpack** See the example in the [examples folder](/examples/bundle-webpack) to get an idea on how to use `js-ipfs-http-client` with webpack. **from CDN** Instead of a local installation (and browserification) you may request a remote copy of IPFS API from [unpkg CDN](https://unpkg.com/). To always request the latest version, use the following: ```html ``` Note: remove the `.min` from the URL to get the human-readable (not minified) version. For maximum security you may also decide to: * reference a specific version of IPFS API (to prevent unexpected breaking changes when a newer latest version is published) * [generate a SRI hash](https://www.srihash.org/) of that version and use it to ensure integrity * set the [CORS settings attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) to make anonymous requests to CDN Example: ```html ``` CDN-based IPFS API provides the `IpfsHttpClient` constructor as a method of the global `window` object. Example: ```js const ipfs = window.IpfsHttpClient({ host: 'localhost', port: 5001 }) ``` If you omit the host and port, the client will parse `window.host`, and use this information. This also works, and can be useful if you want to write apps that can be run from multiple different gateways: ```js const ipfs = window.IpfsHttpClient() ``` ### CORS In a web browser IPFS HTTP client (either browserified or CDN-based) might encounter an error saying that the origin is not allowed. This would be a CORS ("Cross Origin Resource Sharing") failure: IPFS servers are designed to reject requests from unknown domains by default. You can whitelist the domain that you are calling from by changing your ipfs config like this: ```console $ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://example.com"]' $ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "POST", "GET"]' ``` ### Custom Headers If you wish to send custom headers with each request made by this library, for example, the Authorization header. You can use the config to do so: ```js const ipfs = ipfsClient({ host: 'localhost', port: 5001, protocol: 'http', headers: { authorization: 'Bearer ' + TOKEN } }) ``` ### Global Timeouts To set a global timeout for _all_ requests pass a value for the `timeout` option: ```js // Timeout after 10 seconds const ipfs = ipfsClient({ timeout: 10000 }) // Timeout after 2 minutes const ipfs = ipfsClient({ timeout: '2m' }) // see https://www.npmjs.com/package/parse-duration for valid string values ``` ## Usage ### API [![IPFS Core API Compatible](https://cdn.rawgit.com/ipfs/interface-ipfs-core/master/img/badge.svg)](https://github.com/ipfs/interface-ipfs-core) > `js-ipfs-http-client` follows the spec defined by [`interface-ipfs-core`](https://github.com/ipfs/interface-ipfs-core), which concerns the interface to expect from IPFS implementations. This interface is a currently active endeavor. You can use it today to consult the methods available. #### Files - [Regular Files API](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md) - [`ipfs.add(data, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#add) - [`ipfs.cat(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#cat) - [`ipfs.get(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#get) - [`ipfs.ls(ipfsPath)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#ls) - [MFS (mutable file system) specific](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#mutable-file-system) - [`ipfs.files.cp([from, to])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filescp) - [`ipfs.files.flush([path])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesflush) - [`ipfs.files.ls([path], [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesls) - [`ipfs.files.mkdir(path, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesmkdir) - [`ipfs.files.mv([from, to])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesmv) - [`ipfs.files.read(path, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesread) - [`ipfs.files.rm(path, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesrm) - [`ipfs.files.stat(path, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#filesstat) - [`ipfs.files.write(path, content, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#fileswrite) _Explore the Mutable File System through interactive coding challenges in our [ProtoSchool tutorial](https://proto.school/#/mutable-file-system/)._ - [block](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BLOCK.md) - [`ipfs.block.get(cid, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BLOCK.md#blockget) - [`ipfs.block.put(block, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BLOCK.md#blockput) - [`ipfs.block.stat(cid)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BLOCK.md#blockstat) - [refs](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md) - [`ipfs.refs(ipfsPath, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refs) - [`ipfs.refs.local()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REFS.md#refslocal) #### Graph - [dag](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md) - [`ipfs.dag.get(cid, [path], [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md#dagget) - [`ipfs.dag.put(dagNode, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md#dagput) - [`ipfs.dag.tree(cid, [path], [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md#dagtree) _Explore the DAG API through interactive coding challenges in our [ProtoSchool tutorial](https://proto.school/#/basics)._ - [object](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md) - [`ipfs.object.data(multihash, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectdata) - [`ipfs.object.get(multihash, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectget) - [`ipfs.object.links(multihash, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectlinks) - [`ipfs.object.new([template])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectnew) - [`ipfs.object.patch.addLink(multihash, DAGLink, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectpatchaddlink) - [`ipfs.object.patch.appendData(multihash, data, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectpatchappenddata) - [`ipfs.object.patch.rmLink(multihash, DAGLink, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectpatchrmlink) - [`ipfs.object.patch.setData(multihash, data, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectpatchsetdata) - [`ipfs.object.put(obj, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectput) - [`ipfs.object.stat(multihash, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/OBJECT.md#objectstat) - [pin](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PIN.md) - [`ipfs.pin.add(hash, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PIN.md#pinadd) - [`ipfs.pin.ls([hash], [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PIN.md#pinls) - [`ipfs.pin.rm(hash, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PIN.md#pinrm) #### Network - [bootstrap](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BOOTSTRAP.md) - [`ipfs.bootstrap.add(addr, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BOOTSTRAP.md#bootstrapadd) - [`ipfs.bootstrap.list()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BOOTSTRAP.md#bootstraplist) - [`ipfs.bootstrap.rm(addr, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BOOTSTRAP.md#bootstraprm) - [bitswap](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BITSWAP.md) - [`ipfs.bitswap.stat()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BITSWAP.md#bitswapstat) - [`ipfs.bitswap.wantlist([peerId])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BITSWAP.md#bitswapwantlist) - [`ipfs.bitswap.unwant(cid)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/BITSWAP.md#bitswapunwant) - [dht](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md) - [`ipfs.dht.findPeer(peerId)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md#dhtfindpeer) - [`ipfs.dht.findProvs(hash)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md#dhtfindprovs) - [`ipfs.dht.get(key)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md#dhtget) - [`ipfs.dht.provide(cid)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md#dhtprovide) - [`ipfs.dht.put(key, value)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DHT.md#dhtput) - [pubsub](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md) - [`ipfs.pubsub.ls(topic)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md#pubsubls) - [`ipfs.pubsub.peers(topic)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md#pubsubpeers) - [`ipfs.pubsub.publish(topic, data)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md#pubsubpublish) - [`ipfs.pubsub.subscribe(topic, handler, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md#pubsubsubscribe) - [`ipfs.pubsub.unsubscribe(topic, handler)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/PUBSUB.md#pubsubunsubscribe) - [swarm](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/SWARM.md) - [`ipfs.swarm.addrs()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/SWARM.md#swarmaddrs) - [`ipfs.swarm.connect(addr)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/SWARM.md#swarmconnect) - [`ipfs.swarm.disconnect(addr)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/SWARM.md#swarmdisconnect) - [`ipfs.swarm.peers([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/SWARM.md#swarmpeers) - [name](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/NAME.md) - [`ipfs.name.publish(addr, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/NAME.md#namepublish) - [`ipfs.name.pubsub.cancel(arg)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/NAME.md#namepubsubcancel) - [`ipfs.name.pubsub.state()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/NAME.md#namepubsubstate) - [`ipfs.name.pubsub.subs()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/NAME.md#namepubsubsubs) - [`ipfs.name.resolve(addr, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/NAME.md#nameresolve) #### Node Management - [miscellaneous operations](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/MISCELLANEOUS.md) - [`ipfs.dns(domain)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/MISCELLANEOUS.md#dns) - [`ipfs.id()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/MISCELLANEOUS.md#id) - [`ipfs.ping(id, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/MISCELLANEOUS.md#ping) - [`ipfs.stop()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/MISCELLANEOUS.md#stop). Alias to `ipfs.shutdown`. - [`ipfs.version()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/MISCELLANEOUS.md#version) - [config](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/CONFIG.md) - [`ipfs.config.get([key])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/CONFIG.md#configget) - [`ipfs.config.replace(config)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/CONFIG.md#configreplace) - [`ipfs.config.set(key, value)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/CONFIG.md#configset) - [`ipfs.config.profiles.list()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/CONFIG.md#configprofileslist) - [`ipfs.config.profiles.apply(name, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/CONFIG.md#configprofilesapply) - [stats](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/STATS.md) - [`ipfs.stats.bitswap()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/STATS.md#statsbitswap) - [`ipfs.stats.bw([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/STATS.md#statsbw) - [`ipfs.stats.repo([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/STATS.md#statsrepo) - log - `ipfs.log.level(subsystem, level, [options])` - `ipfs.log.ls()` - `ipfs.log.tail()` - [repo](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REPO.md) - [`ipfs.repo.gc([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REPO.md#repogc) - [`ipfs.repo.stat([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REPO.md#repostat) - [`ipfs.repo.version()`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/REPO.md#repoversion) - [key](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md) - [`ipfs.key.export(name, password)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md#keyexport) - [`ipfs.key.gen(name, [options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md#keygen) - [`ipfs.key.import(name, pem, password)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md#keyimport) - [`ipfs.key.list([options])`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md#keylist) - [`ipfs.key.rename(oldName, newName)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md#keyrename) - [`ipfs.key.rm(name)`](https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/KEY.md#keyrm) #### Additional Options All core API methods take _additional_ `options` specific to the HTTP API: * `headers` - An object or [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) instance that can be used to set custom HTTP headers. Note that this option can also be [configured globally](#custom-headers) via the constructor options. * `signal` - An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that can be used to abort the request on demand. * `timeout` - A number or string specifying a timeout for the request. If the timeout is reached before data is received a [`TimeoutError`](https://github.com/sindresorhus/ky/blob/2f37c3f999efb36db9108893b8b3d4b3a7f5ec45/index.js#L127-L132) is thrown. If a number is specified it is interpreted as milliseconds, if a string is passed, it is intepreted according to [`parse-duration`](https://www.npmjs.com/package/parse-duration). Note that this option can also be [configured globally](#global-timeouts) via the constructor options. * `searchParams` - An object or [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) instance that can be used to add additional query parameters to the query string sent with each request. #### Instance Utils - `ipfs.getEndpointConfig()` Call this on your client instance to return an object containing the `host`, `port`, `protocol` and `api-path`. #### Static Types and Utils Aside from the default export, `ipfs-http-client` exports various types and utilities that are included in the bundle: - [`Buffer`](https://www.npmjs.com/package/buffer) - [`multiaddr`](https://www.npmjs.com/package/multiaddr) - [`multibase`](https://www.npmjs.com/package/multibase) - [`multicodec`](https://www.npmjs.com/package/multicodec) - [`multihash`](https://www.npmjs.com/package/multihashes) - [`CID`](https://www.npmjs.com/package/cids) - [`globSource`](https://github.com/ipfs/js-ipfs-utils/blob/master/src/files/glob-source.js) (not available in the browser) - [`urlSource`](https://github.com/ipfs/js-ipfs-utils/blob/master/src/files/url-source.js) These can be accessed like this, for example: ```js const { CID } = require('ipfs-http-client') // ...or from an es-module: import { CID } from 'ipfs-http-client' ``` ##### Glob source A utility to allow files on the file system to be easily added to IPFS. ###### `globSource(path, [options])` - `path`: A path to a single file or directory to glob from - `options`: Optional options - `options.recursive`: If `path` is a directory, use option `{ recursive: true }` to add the directory and all its sub-directories. - `options.ignore`: To exclude file globs from the directory, use option `{ ignore: ['ignore/this/folder/**', 'and/this/file'] }`. - `options.hidden`: Hidden/dot files (files or folders starting with a `.`, for example, `.git/`) are not included by default. To add them, use the option `{ hidden: true }`. Returns an async iterable that yields `{ path, content }` objects suitable for passing to `ipfs.add`. ###### Example ```js const IpfsHttpClient = require('ipfs-http-client') const { globSource } = IpfsHttpClient const ipfs = IpfsHttpClient() for await (const file of ipfs.add(globSource('./docs', { recursive: true }))) { console.log(file) } /* { path: 'docs/assets/anchor.js', cid: CID('QmVHxRocoWgUChLEvfEyDuuD6qJ4PhdDL2dTLcpUy3dSC2'), size: 15347 } { path: 'docs/assets/bass-addons.css', cid: CID('QmPiLWKd6yseMWDTgHegb8T7wVS7zWGYgyvfj7dGNt2viQ'), size: 232 } ... */ ``` ##### URL source A utility to allow content from the internet to be easily added to IPFS. ###### `urlSource(url)` - `url`: A string URL or [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) instance to send HTTP GET request to Returns an async iterable that yields `{ path, content }` objects suitable for passing to `ipfs.add`. ###### Example ```js const IpfsHttpClient = require('ipfs-http-client') const { urlSource } = IpfsHttpClient const ipfs = IpfsHttpClient() for await (const file of ipfs.add(urlSource('https://ipfs.io/images/ipfs-logo.svg'))) { console.log(file) } /* { path: 'ipfs-logo.svg', cid: CID('QmTqZhR6f7jzdhLgPArDPnsbZpvvgxzCZycXK7ywkLxSyU'), size: 3243 } */ ``` ## Development ### Testing We run tests by executing `npm test` in a terminal window. This will run both Node.js and Browser tests, both in Chrome and PhantomJS. To ensure that the module conforms with the [`interface-ipfs-core`](https://github.com/ipfs/interface-ipfs-core) spec, we run the batch of tests provided by the interface module, which can be found [here](https://github.com/ipfs/interface-ipfs-core/tree/master/js/src). ## Contribute The js-ipfs-http-client is a work in progress. As such, there's a few things you can do right now to help out: - **[Check out the existing issues](https://github.com/ipfs/js-ipfs-http-client/issues)**! - **Perform code reviews**. More eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs. - **Add tests**. There can never be enough tests. Note that interface tests exist inside [`interface-ipfs-core`](https://github.com/ipfs/interface-ipfs-core/tree/master/js/src). - **Contribute to the [FAQ repository](https://github.com/ipfs/faq/issues)** with any questions you have about IPFS or any of the relevant technology. A good example would be asking, 'What is a merkledag tree?'. If you don't know a term, odds are, someone else doesn't either. Eventually, we should have a good understanding of where we need to improve communications and teaching together to make IPFS and IPNS better. **Want to hack on IPFS?** [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) ## Historical context This module started as a direct mapping from the go-ipfs cli to a JavaScript implementation, although this was useful and familiar to a lot of developers that were coming to IPFS for the first time, it also created some confusion on how to operate the core of IPFS and have access to the full capacity of the protocol. After much consideration, we decided to create `interface-ipfs-core` with the goal of standardizing the interface of a core implementation of IPFS, and keep the utility functions the IPFS community learned to use and love, such as reading files from disk and storing them directly to IPFS. ## License [MIT](LICENSE) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fipfs%2Fjs-ipfs-http-client.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fipfs%2Fjs-ipfs-http-client?ref=badge_large) ================================================ FILE: examples/browser-pubsub/.gitignore ================================================ bundle.js ================================================ FILE: examples/browser-pubsub/README.md ================================================ # Pubsub in the browser > Use pubsub in the browser! This example is a demo web application that allows you to connect to an IPFS node, subscribe to a pubsub topic and send/receive messages. We'll start two IPFS nodes and two browsers and use the `ipfs-http-client` to instruct each node to listen to a pubsub topic and send/receive pubsub messages to/from each other. We're aiming for something like this: ``` +-----------+ +-----------+ | +-------------------> | | js-ipfs | pubsub | go-ipfs | | <-------------------+ | +-----^-----+ +-----^-----+ | | | HTTP API | HTTP API | | +-------------------+ +-------------------+ +-------------------+ +-------------------+ | | | | | | | | | Browser 1 | | Browser 2 | | | | | | | | | | | | | +-------------------+ +-------------------+ ``` ## 1. Get started With Node.js and git installed, clone the repo and install the project dependencies: ```sh git clone https://github.com/ipfs/js-ipfs-http-client.git cd js-ipfs-http-client npm install # Installs ipfs-http-client dependencies cd examples/browser-pubsub npm install # Installs browser-pubsub app dependencies ``` Start the example application: ```sh npm start ``` You should see something similar to the following in your terminal and the web app should now be available if you navigate to http://127.0.0.1:8888 using your browser: ```sh Starting up http-server, serving ./ Available on: http://127.0.0.1:8888 ``` ## 2. Start two IPFS nodes To demonstrate pubsub we need two nodes running so pubsub messages can be passed between them. Right now the easiest way to do this is to install and start a `js-ipfs` and `go-ipfs` node. There are other ways to do this, see [this document on running multiple nodes](https://github.com/ipfs/js-ipfs/tree/master/examples/running-multiple-nodes) for details. ### Install and start the JS IPFS node ```sh npm install -g ipfs jsipfs init # Configure CORS to allow ipfs-http-client to access this IPFS node jsipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://127.0.0.1:8888"]' # Start the IPFS node, enabling pubsub jsipfs daemon ``` ### Install and start the Go IPFS node Head over to https://dist.ipfs.io/#go-ipfs and hit the "Download go-ipfs" button. Extract the archive and read the instructions to install. After installation: ```sh ipfs init # Configure CORS to allow ipfs-http-client to access this IPFS node ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://127.0.0.1:8888"]' # Start the IPFS node, enabling pubsub ipfs daemon --enable-pubsub-experiment ``` ## 3. Open two browsers and connect to each node Now, open up **two** browser windows. This could be two tabs in the same browser or two completely different browsers, it doesn't matter. Navigate to http://127.0.0.1:8888 in both. In the "API ADDR" field enter `/ip4/127.0.0.1/tcp/5001` in one browser and `/ip4/127.0.0.1/tcp/5002` in the other and hit the "Connect" button. This connects each browser to an IPFS node and now from the comfort of our browser we can instruct each node to listen to a pubsub topic and send/receive pubsub messages to each other. > N.B. Since our two IPFS nodes are running on the same network they should have already found each other by MDNS. So you probably won't need to use the "CONNECT TO PEER" field. If you find your pubsub messages aren't getting through, check the output from your `jsipfs daemon` command and find the first address listed in "Swarm listening on" - it'll look like `/ip4/127.0.0.1/tcp/4002/ipfs/Qm...`. Paste this address into the "CONNECT TO PEER" field for the browser that is connected to your go-ipfs node and hit connect. Finally, use the "SUBSCRIBE TO PUBSUB TOPIC" and "SEND MESSAGE" fields to do some pubsub-ing, you should see messages sent from one browser appear in the log of the other (provided they're both subscribed to the same topic). ================================================ FILE: examples/browser-pubsub/index.html ================================================ Pubsub in the browser

Pubsub

API Addr
Connect to peer
Subscribe to pubsub topic
Send pubsub message
Console
================================================ FILE: examples/browser-pubsub/index.js ================================================ 'use strict' const IpfsHttpClient = require('ipfs-http-client') const { sleep, Logger, onEnterPress, catchAndLog } = require('./util') async function main () { const apiUrlInput = document.getElementById('api-url') const nodeConnectBtn = document.getElementById('node-connect') const peerAddrInput = document.getElementById('peer-addr') const peerConnectBtn = document.getElementById('peer-connect') const topicInput = document.getElementById('topic') const subscribeBtn = document.getElementById('subscribe') const messageInput = document.getElementById('message') const sendBtn = document.getElementById('send') let log = Logger(document.getElementById('console')) let ipfs let topic let peerId async function reset () { if (ipfs && topic) { log(`Unsubscribing from topic ${topic}`) await ipfs.pubsub.unsubscribe(topic) } log = Logger(document.getElementById('console')) topicInput.value = '' topic = null peerId = null ipfs = null } async function nodeConnect (url) { await reset() log(`Connecting to ${url}`) ipfs = IpfsHttpClient(url) const { id, agentVersion } = await ipfs.id() peerId = id log(`Success!`) log(`Version ${agentVersion}`) log(`Peer ID ${id}`) } async function peerConnect (addr) { if (!addr) throw new Error('Missing peer multiaddr') if (!ipfs) throw new Error('Connect to a node first') log(`Connecting to peer ${addr}`) await ipfs.swarm.connect(addr) log(`Success!`) log('Listing swarm peers...') await sleep() const peers = await ipfs.swarm.peers() peers.forEach(peer => { const fullAddr = `${peer.addr}/ipfs/${peer.peer.toB58String()}` log(`${fullAddr}`) }) log(`(${peers.length} peers total)`) } async function subscribe (nextTopic) { if (!nextTopic) throw new Error('Missing topic name') if (!ipfs) throw new Error('Connect to a node first') const lastTopic = topic if (topic) { topic = null log(`Unsubscribing from topic ${lastTopic}`) await ipfs.pubsub.unsubscribe(lastTopic) } log(`Subscribing to ${nextTopic}...`) await ipfs.pubsub.subscribe(nextTopic, msg => { const from = msg.from const seqno = msg.seqno.toString('hex') if (from === peerId) return log(`Ignoring message ${seqno} from self`) log(`Message ${seqno} from ${from}:`) try { log(JSON.stringify(msg.data.toString(), null, 2)) } catch (_) { log(msg.data.toString('hex')) } }, { onError: (err, fatal) => { if (fatal) { console.error(err) log(`${err.message}`) topic = null log('Resubscribing in 5s...') setTimeout(catchAndLog(() => subscribe(nextTopic), log), 5000) } else { console.warn(err) } } }) topic = nextTopic log(`Success!`) } async function send (msg) { if (!msg) throw new Error('Missing message') if (!topic) throw new Error('Subscribe to a topic first') if (!ipfs) throw new Error('Connect to a node first') log(`Sending message to ${topic}...`) await ipfs.pubsub.publish(topic, msg) log(`Success!`) } const onNodeConnectClick = catchAndLog(() => nodeConnect(apiUrlInput.value), log) apiUrlInput.addEventListener('keydown', onEnterPress(onNodeConnectClick)) nodeConnectBtn.addEventListener('click', onNodeConnectClick) const onPeerConnectClick = catchAndLog(() => peerConnect(peerAddrInput.value), log) peerAddrInput.addEventListener('keydown', onEnterPress(onPeerConnectClick)) peerConnectBtn.addEventListener('click', onPeerConnectClick) const onSubscribeClick = catchAndLog(() => subscribe(topicInput.value), log) topicInput.addEventListener('keydown', onEnterPress(onSubscribeClick)) subscribeBtn.addEventListener('click', onSubscribeClick) const onSendClick = catchAndLog(async () => { await send(messageInput.value) messageInput.value = '' }, log) messageInput.addEventListener('keydown', onEnterPress(onSendClick)) sendBtn.addEventListener('click', onSendClick) } main() ================================================ FILE: examples/browser-pubsub/package.json ================================================ { "name": "browser-pubsub-example", "version": "0.0.0", "description": "An example demonstrating pubsub in the browser", "private": true, "main": "index.js", "scripts": { "start": "parcel index.html" }, "author": "Alan Shaw", "license": "MIT", "dependencies": { "ipfs-http-client": "../../" }, "browserslist": [ "last 2 versions and not dead and > 2%" ], "devDependencies": { "parcel-bundler": "^1.12.4" } } ================================================ FILE: examples/browser-pubsub/util.js ================================================ exports.sleep = (ms = 1000) => new Promise(resolve => setTimeout(resolve, ms)) exports.Logger = outEl => { outEl.innerHTML = '' return message => { const container = document.createElement('div') container.innerHTML = message outEl.appendChild(container) outEl.scrollTop = outEl.scrollHeight } } exports.onEnterPress = fn => { return e => { if (event.which == 13 || event.keyCode == 13) { e.preventDefault() fn() } } } exports.catchAndLog = (fn, log) => { return async (...args) => { try { await fn(...args) } catch (err) { console.error(err) log(`${err.message}`) } } } ================================================ FILE: examples/bundle-webpack/.eslintrc ================================================ { "extends": "standard", "rules": { "react/jsx-uses-react": 2, "react/jsx-uses-vars": 2, "react/react-in-jsx-scope": 2 }, "plugins": [ "react" ] } ================================================ FILE: examples/bundle-webpack/.gitignore ================================================ node_modules npm-debug.log .DS_Store dist ================================================ FILE: examples/bundle-webpack/README.md ================================================ # Bundle js-ipfs-http-client with Webpack! > In this example, you will find a boilerplate you can use to guide yourself into bundling js-ipfs-http-client with webpack, so that you can use it in your own web app! ## Setup As for any js-ipfs-http-client example, **you need a running IPFS daemon**, you learn how to do that here: - [Spawn a go-ipfs daemon](https://ipfs.io/docs/getting-started/) - [Spawn a js-ipfs daemon](https://github.com/ipfs/js-ipfs#usage) **Note:** If you load your app from a different domain than the one the daemon is running (most probably), you will need to set up CORS, see https://github.com/ipfs/js-ipfs-http-client#cors to learn how to do that. A quick (and dirty) way to get it done is: ```bash > ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"*\"]" > ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials "[\"true\"]" ``` ## Run this example Once the daemon is on, run the following commands within this folder: ```bash > npm install > npm start ``` Now open your browser at `http://localhost:3000` You should see the following: ![](https://ipfs.io/ipfs/QmZndNLRct3co7h1yVB72S4qfwAwbq7DQghCpWpVQ45jSi/1.png) ================================================ FILE: examples/bundle-webpack/index.html ================================================ Sample App
================================================ FILE: examples/bundle-webpack/package.json ================================================ { "name": "bundle-webpack", "version": "1.0.0", "description": "Bundle js-ipfs-http-client with Webpack", "scripts": { "start": "node server.js" }, "author": "Victor Bjelkholm ", "license": "MIT", "keywords": [], "devDependencies": { "@babel/core": "^7.2.2", "@babel/preset-env": "^7.3.1", "@babel/preset-react": "^7.0.0", "babel-loader": "^8.0.5", "ipfs-http-client": "../../", "react": "~16.8.6", "react-dom": "~16.8.6", "react-hot-loader": "~4.8.4", "webpack": "~4.31.0", "webpack-dev-server": "~3.3.1" }, "browserslist": [ "last 2 versions and not dead and > 2%" ] } ================================================ FILE: examples/bundle-webpack/server.js ================================================ 'use strict' var webpack = require('webpack') var WebpackDevServer = require('webpack-dev-server') var config = require('./webpack.config') new WebpackDevServer(webpack(config), { publicPath: config.output.publicPath, hot: true, historyApiFallback: true }).listen(3000, 'localhost', function (err, result) { if (err) { console.log(err) } console.log('Listening at localhost:3000') }) ================================================ FILE: examples/bundle-webpack/src/App.js ================================================ 'use strict' const React = require('react') const ipfsClient = require('ipfs-http-client') const ipfs = ipfsClient('/ip4/127.0.0.1/tcp/5001') const stringToUse = 'hello world from webpacked IPFS' class App extends React.Component { constructor (props) { super(props) this.state = { id: null, version: null, protocol_version: null, added_file_hash: null, added_file_contents: null } } async componentDidMount () { const id = await ipfs.id() this.setState({ id: id.id, version: id.agentVersion, protocol_version: id.protocolVersion }) const source = ipfs.add(stringToUse) for await (const file of source) { console.log("TCL: App -> forawait -> file", file) const hash = file.path this.setState({ added_file_hash: hash }) const source = ipfs.cat(hash) const data = [] for await (const chunk of source) { data.push(chunk) } this.setState({ added_file_contents: Buffer.concat(data).toString() }) } } render () { return

Everything is working!

Your ID is {this.state.id}

Your IPFS version is {this.state.version}

Your IPFS protocol version is {this.state.protocol_version}

Added a file!
{this.state.added_file_hash}
Contents of this file:
{this.state.added_file_contents}
} } module.exports = App ================================================ FILE: examples/bundle-webpack/src/index.js ================================================ 'use strict' const React = require('react') const ReactDOM = require('react-dom') const App = require('./App') ReactDOM.render(, document.getElementById('root')) ================================================ FILE: examples/bundle-webpack/webpack.config.js ================================================ 'use strict' const path = require('path') const webpack = require('webpack') module.exports = { mode: 'production', devtool: 'eval', entry: [ 'webpack-dev-server/client?http://localhost:3000', 'webpack/hot/only-dev-server', './src/index' ], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', publicPath: '/static/' }, plugins: [ new webpack.HotModuleReplacementPlugin() ], module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env', '@babel/preset-react'] } } } ] }, node: { fs: 'empty', net: 'empty', tls: 'empty' } } ================================================ FILE: examples/files-api/files-api.js ================================================ /* eslint-disable no-console */ 'use strict' // Run `ipfs daemon` in your terminal to start the IPFS daemon // Look for `API server listening on /ip4/127.0.0.1/tcp/5001` const ipfs = require('../../src')('/ip4/127.0.0.1/tcp/5001') const run = async () => { await ipfs.files.write( '/temp/hello-world', Buffer.from('Hello, world!'), { create: true, parents: true } ) const source = ipfs.files.ls('/temp') for await (const file of source) { console.log(file) } } run() ================================================ FILE: examples/name-api/.gitignore ================================================ bundle.js ================================================ FILE: examples/name-api/README.md ================================================ # JS IPFS API - Example Browser - Name ## Setup Install [go-ipfs](https://ipfs.io/docs/install/) and start the daemon. Configure CORS as suggested by the README https://github.com/ipfs/js-ipfs-http-client#cors ```bash > ipfs daemon ``` then in this folder run ```bash > npm install > npm start ``` and open your browser at `http://localhost:8888`. ================================================ FILE: examples/name-api/index.html ================================================ JS IPFS API name example

js-ipfs-http-client

name.publish() and name.resolve()

initializing...

Add a new file to IPFS and publish it.

Publish an existing file or directory from IPFS.


Resolve an IPNS name

================================================ FILE: examples/name-api/index.js ================================================ /* eslint-disable no-console */ 'use strict' const ipfsHttp = require('ipfs-http-client') const ipfs = ipfsHttp('/ip4/127.0.0.1/tcp/5001') const DOM = { status: document.getElementById('status'), buttons: document.getElementsByTagName('button'), publishNew: document.forms[0], publishPath: document.forms[1], resolveName: document.forms[2], publishResultsDiv: document.querySelector('.results--publish'), resolveResultsDiv: document.querySelector('.results--resolve'), publishResult: document.getElementById('publish-result'), resolveResult: document.getElementById('resolve-result'), publishGatewayLink: document.getElementById('publish-gateway-link'), resolveGatewayLink: document.getElementById('resolve-gateway-link') } const COLORS = { active: 'blue', success: 'green', error: 'red' } const IPFS_DOMAIN = 'https://ipfs.io' const showStatus = (text, bg) => { DOM.status.innerText = text DOM.status.style.background = bg } const enableForms = () => { for (const btn of DOM.buttons) { btn.disabled = false } } const init = () => { ipfs.id() .then(res => { showStatus(`daemon active\nid: ${res.id}`, COLORS.success) enableForms() }) .catch(err => { showStatus('daemon inactive', COLORS.error) console.error(err) }) } // Adds a new file to IPFS and publish it const addAndPublish = async (e) => { e.preventDefault() const input = e.target.elements.text const buffer = Buffer.from(input.value) showStatus('adding to IPFS...', COLORS.active) try { for await (const file of ipfs.add(buffer)) { showStatus('success!', COLORS.success) publish(file.path) input.value = '' } } catch (err) { showStatus('failed to add the data', COLORS.error) console.error(err) } } // Publishes an IPFS file or directory under your node's identity const publish = (path) => { showStatus('publishing...', COLORS.active) DOM.publishResultsDiv.classList.add('hidden') ipfs.name.publish(path) .then(res => { const name = res.name showStatus('success!', COLORS.success) DOM.publishResultsDiv.classList.remove('hidden') DOM.publishResult.innerText = `/ipns/${name}` DOM.publishGatewayLink.href = `${IPFS_DOMAIN}/ipns/${name}` }) .catch(err => { showStatus(`error publishing ${path}`, COLORS.error) console.error(err) }) } // Resolves an IPNS name const resolve = async (name) => { showStatus('resolving...', COLORS.active) DOM.resolveResultsDiv.classList.add('hidden') try { for await (const path of ipfs.name.resolve(name)) { showStatus('success!', COLORS.success) DOM.resolveResultsDiv.classList.remove('hidden') DOM.resolveResult.innerText = path DOM.resolveGatewayLink.href = `${IPFS_DOMAIN}${path}` } } catch (err) { showStatus(`error resolving ${name}`, COLORS.error) console.error(err) } } // Event listeners DOM.publishNew.onsubmit = addAndPublish DOM.publishPath.onsubmit = (e) => { e.preventDefault() const input = e.target.elements.path publish(input.value) input.value = '' } DOM.resolveName.onsubmit = (e) => { e.preventDefault() const input = e.target.elements.name resolve(input.value) input.value = '' } init() ================================================ FILE: examples/name-api/package.json ================================================ { "name": "js-ipfs-http-client-example-name-publish-resolve", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "parcel index.html" }, "author": "Tara Vancil ", "license": "MIT", "devDependencies": { "parcel-bundler": "^1.12.4" }, "dependencies": { "ipfs-http-client": "../.." }, "browserslist": [ "last 2 versions and not dead and > 2%" ] } ================================================ FILE: examples/sub-module/bundles-size-KBs.csv ================================================ name, bundled (KBs), minified (KBs) IPFS, 1412.20, 573.44 add, 591.17, 198.23 bitswap, 590.14, 197.96 block, 630.50, 216.31 bootstrap, 590.97, 198.22 cat, 630.78, 216.41 commands, 589.22, 197.59 config, 592.21, 198.93 dht, 593.86, 199.24 diag, 590.31, 198.00 files, 669.07, 235.88 get, 661.57, 233.16 id, 589.65, 197.78 key, 589.93, 197.86 log, 590.74, 198.20 ls, 589.35, 197.63 mount, 589.53, 197.69 name, 589.97, 197.88 object, 833.17, 307.73 pin, 590.86, 198.22 ping, 589.94, 197.73 pubsub, 595.31, 199.76 refs, 589.74, 197.77 repo, 589.91, 197.85 swarm, 1239.42, 498.59 update, 589.79, 197.79 version, 589.55, 197.71 ================================================ FILE: examples/sub-module/complete-module.js ================================================ 'use strict' require('../../src')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/add/add.js ================================================ 'use strict' require('../../../../src/add')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/bitswap/bitswap.js ================================================ 'use strict' require('../../../../src/bitswap')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/block/block.js ================================================ 'use strict' require('../../../../src/block')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/bootstrap/bootstrap.js ================================================ 'use strict' require('../../../../src/bootstrap')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/cat/cat.js ================================================ 'use strict' require('../../../../src/cat')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/commands/commands.js ================================================ 'use strict' require('../../../../src/commands')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/config/config.js ================================================ 'use strict' require('../../../../src/config')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/dht/dht.js ================================================ 'use strict' require('../../../../src/dht')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/diag/diag.js ================================================ 'use strict' require('../../../../src/diag')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/files/files.js ================================================ 'use strict' require('../../../../src/files')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/get/get.js ================================================ 'use strict' require('../../../../src/get')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/id/id.js ================================================ 'use strict' require('../../../../src/id')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/key/key.js ================================================ 'use strict' require('../../../../src/key')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/log/log.js ================================================ 'use strict' require('../../../../src/log')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/ls/ls.js ================================================ 'use strict' require('../../../../src/ls')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/mount/mount.js ================================================ 'use strict' require('../../../../src/mount')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/name/name.js ================================================ 'use strict' require('../../../../src/name')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/object/object.js ================================================ 'use strict' require('../../../../src/object')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/pin/pin.js ================================================ 'use strict' require('../../../../src/pin')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/ping/ping.js ================================================ 'use strict' require('../../../../src/ping')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/pubsub/pubsub.js ================================================ 'use strict' require('../../../../src/pubsub')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/refs/refs.js ================================================ 'use strict' require('../../../../src/refs')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/repo/repo.js ================================================ 'use strict' require('../../../../src/repo')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/swarm/swarm.js ================================================ 'use strict' require('../../../../src/swarm')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/update/update.js ================================================ 'use strict' require('../../../../src/update')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/modules/version/version.js ================================================ 'use strict' require('../../../../src/version')('/ip4/127.0.0.1/tcp/5001') ================================================ FILE: examples/sub-module/package.json ================================================ { "name": "sub-module", "version": "1.0.0", "description": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Nuno Nogueira", "license": "MIT", "devDependencies": { "babel-core": "^6.25.0", "babel-loader": "^8.0.5", "babel-preset-env": "^1.5.2", "babili": "~0.1.4", "webpack": "~4.31.0" } } ================================================ FILE: examples/sub-module/test-modules-size.sh ================================================ #!/bin/sh set -e modules=($(ls modules/)) echo "name, bundled (KBs), minified (KBs)" # Full IPFS module webpack --display none --config webpack.config.js complete-module.js complete-bundle.js babili complete-bundle.js -o complete-bundle-minified.js ipfsBundleSize=($(wc -c < complete-bundle.js | awk '{b=$1/1024; printf "%.2f\n", b}' | sed 's/,/./g')) ipfsMinSize=($(wc -c < complete-bundle-minified.js | awk '{b=$1/1024; printf "%.2f\n", b}' | sed 's/,/./g')) echo IPFS, $ipfsBundleSize, $ipfsMinSize for module in "${modules[@]}" do moduledir="modules/$module" webpack --display none --config webpack.config.js $moduledir/$module.js $moduledir/bundle.js babili $moduledir/bundle.js -o $moduledir/bundle-minified.js bundlesize=($(wc -c < $moduledir/bundle.js | awk '{b=$1/1024; printf "%.2f\n", b}' | sed 's/,/./g')) minsize=($(wc -c < $moduledir/bundle-minified.js | awk '{b=$1/1024; printf "%.2f\n", b}' | sed 's/,/./g')) echo $module, $bundlesize, $minsize done ================================================ FILE: examples/sub-module/webpack.config.js ================================================ 'use strict' module.exports = { module: { loaders: [{ test: /\.js$/, loaders: ['babel-loader'] }] }, node: { fs: 'empty', net: 'empty', tls: 'empty' } } ================================================ FILE: examples/upload-file-via-browser/.gitignore ================================================ node_modules npm-debug.log .DS_Store dist yarn.lock ================================================ FILE: examples/upload-file-via-browser/README.md ================================================ # Upload file to IPFS via browser using js-ipfs-http-client > In this example, you will find a simple React app to upload a file to IPFS via the browser using js-ipfs-http-client and Webpack. ## Setup As for any js-ipfs-http-client example, **you need a running IPFS daemon**, you learn how to do that here: - [Spawn a go-ipfs daemon](https://ipfs.io/docs/getting-started/) - [Spawn a js-ipfs daemon](https://github.com/ipfs/js-ipfs#usage) **Note:** If you load your app from a different domain than the one the daemon is running (most probably), you will need to set up CORS, see https://github.com/ipfs/js-ipfs-http-client#cors to learn how to do that. A quick (and dirty way to get it done) is: ```bash > ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"*\"]" > ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials "[\"true\"]" ``` ## Run this example Once the daemon is on, run the following commands within this folder: ```bash > npm install > npm start ``` After uploading a file (left screen), and opening the uploaded file (right screen), you should see something like: > ![App Screenshot](https://github.com/ipfs/js-ipfs-http-client/raw/master/examples/upload-file-via-browser/screenshot.png) ================================================ FILE: examples/upload-file-via-browser/index.html ================================================ Sample App
================================================ FILE: examples/upload-file-via-browser/package.json ================================================ { "name": "upload-file-via-browser", "version": "1.0.0", "description": "Upload file to IPFS via browser using js-ipfs-http-client with Webpack", "scripts": { "start": "parcel index.html" }, "author": "Harlan T Wood ", "contributors": [ "Victor Bjelkholm " ], "license": "MIT", "dependencies": { "ipfs-http-client": "../.." }, "devDependencies": { "react": "~16.6.3", "react-dom": "~16.6.3", "parcel-bundler": "^1.12.4" }, "browserslist": [ "last 2 versions and not dead and > 2%" ] } ================================================ FILE: examples/upload-file-via-browser/src/App.js ================================================ /* eslint-disable no-console */ 'use strict' const React = require('react') const ipfsClient = require('ipfs-http-client') class App extends React.Component { constructor () { super() this.state = { added_file_hash: null } this.ipfs = ipfsClient('/ip4/127.0.0.1/tcp/5001') // bind methods this.captureFile = this.captureFile.bind(this) this.saveToIpfs = this.saveToIpfs.bind(this) this.handleSubmit = this.handleSubmit.bind(this) } captureFile (event) { event.stopPropagation() event.preventDefault() if (document.getElementById('keepFilename').checked) { this.saveToIpfsWithFilename(event.target.files) } else { this.saveToIpfs(event.target.files) } } // Example #1 // Add file to IPFS and return a CID async saveToIpfs (files) { const source = this.ipfs.add( [...files], { progress: (prog) => console.log(`received: ${prog}`) } ) try { for await (const file of source) { console.log(file) this.setState({ added_file_hash: file.path }) } } catch (err) { console.error(err) } } // Example #2 // Add file to IPFS and wrap it in a directory to keep the original filename async saveToIpfsWithFilename (files) { const file = [...files][0] const fileDetails = { path: file.name, content: file } const options = { wrapWithDirectory: true, progress: (prog) => console.log(`received: ${prog}`) } const source = this.ipfs.add(fileDetails, options) try { for await (const file of source) { console.log(file) this.setState({ added_file_hash: file.cid.toString() }) } } catch (err) { console.error(err) } } handleSubmit (event) { event.preventDefault() } render () { return ( ) } } module.exports = App ================================================ FILE: examples/upload-file-via-browser/src/index.js ================================================ /* eslint-disable no-unused-vars */ 'use strict' const React = require('react') const ReactDOM = require('react-dom') const App = require('./App') ReactDOM.render(, document.getElementById('root')) ================================================ FILE: greenkeeper.json ================================================ { "groups": { "default": { "packages": [ "package.json" ] } } } ================================================ FILE: maintainer.json ================================================ { "repoLeadMaintainer": { "name": "Alan Shaw", "email": "alan.shaw@protocol.ai", "username": "alanshaw" }, "workingGroup": { "name": "JS IPFS", "entryPoint": "https://github.com/ipfs/js-core" } } ================================================ FILE: package.json ================================================ { "name": "ipfs-http-client", "version": "42.0.0", "description": "A client library for the IPFS HTTP API", "keywords": [ "ipfs" ], "homepage": "https://github.com/ipfs/js-ipfs-http-client", "bugs": "https://github.com/ipfs/js-ipfs-http-client/issues", "license": "(Apache-2.0 OR MIT)", "leadMaintainer": "Alan Shaw ", "files": [ "src", "dist" ], "main": "src/index.js", "browser": { "./src/add/form-data.js": "./src/add/form-data.browser.js", "./src/lib/buffer-to-form-data.js": "./src/lib/buffer-to-form-data.browser.js", "ipfs-utils/src/files/glob-source": false }, "repository": "github:ipfs/js-ipfs-http-client", "scripts": { "test": "aegir test", "test:node": "aegir test -t node", "test:browser": "aegir test -t browser", "test:webworker": "aegir test -t webworker", "test:electron-main": "aegir test -t electron-main", "test:electron-renderer": "aegir test -t electron-renderer", "test:chrome": "aegir test -t browser -t webworker -- --browsers ChromeHeadless", "test:firefox": "aegir test -t browser -t webworker -- --browsers FirefoxHeadless", "lint": "aegir lint", "build": "aegir build", "release": "aegir release ", "release-minor": "aegir release --type minor ", "release-major": "aegir release --type major ", "coverage": "npx nyc -r html npm run test:node -- --bail" }, "dependencies": { "abort-controller": "^3.0.0", "bignumber.js": "^9.0.0", "bs58": "^4.0.1", "buffer": "^5.4.2", "cids": "~0.7.1", "debug": "^4.1.0", "form-data": "^3.0.0", "ipfs-block": "~0.8.1", "ipfs-utils": "^0.7.1", "ipld-dag-cbor": "^0.15.1", "ipld-dag-pb": "^0.18.2", "ipld-raw": "^4.0.1", "it-tar": "^1.1.1", "it-to-stream": "^0.1.1", "iterable-ndjson": "^1.1.0", "ky": "^0.15.0", "ky-universal": "^0.3.0", "merge-options": "^2.0.0", "multiaddr": "^7.2.1", "multiaddr-to-uri": "^5.1.0", "multibase": "~0.6.0", "multicodec": "^1.0.0", "multihashes": "~0.4.14", "parse-duration": "^0.1.1", "stream-to-it": "^0.2.0" }, "devDependencies": { "aegir": "^20.4.1", "async": "^3.1.0", "browser-process-platform": "~0.1.1", "go-ipfs-dep": "^0.4.23-3", "interface-ipfs-core": "^0.131.7", "ipfsd-ctl": "^3.0.0", "it-all": "^1.0.1", "it-concat": "^1.0.0", "it-pipe": "^1.1.0", "nock": "^11.7.2" }, "engines": { "node": ">=10.3.0", "npm": ">=3.0.0" }, "contributors": [ "Alan Shaw ", "Alan Shaw ", "Alex Mingoia ", "Alex Potsides ", "Antonio Tenorio-Fornés ", "Bruno Barbieri ", "Clemo ", "Connor Keenan ", "Daniel Constantin ", "Danny ", "David Braun ", "David Dias ", "Dietrich Ayala ", "Diogo Silva ", "Dmitriy Ryajov ", "Dmitry Nikulin ", "Donatas Stundys ", "Fil ", "Filip Š ", "Francisco Baio Dias ", "Friedel Ziegelmayer ", "Gar ", "Gavin McDermott ", "Gopalakrishna Palem ", "Greenkeeper ", "Haad ", "Harlan T Wood ", "Harlan T Wood ", "Henrique Dias ", "Holodisc ", "Hugo Dias ", "Hugo Dias ", "JGAntunes ", "Jacob Heun ", "James Halliday ", "Jason Carver ", "Jason Papakostas ", "Jeff Downie ", "Jeromy ", "Jeromy ", "Jim Pick ", "Joe Turgeon ", "Jonathan ", "Juan Batiz-Benet ", "Kevin Wang ", "Kristoffer Ström ", "Marcin Rataj ", "Matt Bell ", "Matt Ober ", "Maxime Lathuilière ", "Michael Bradley ", "Michael Muré ", "Michael Muré ", "Mikeal Rogers ", "Mitar ", "Mithgol ", "Mohamed Abdulaziz ", "Nitin Patel <31539366+niinpatel@users.noreply.github.com>", "Nuno Nogueira ", "Níckolas Goline ", "Oli Evans ", "Orie Steele ", "Paul Cowgill ", "Pedro Santos ", "Pedro Santos ", "Pedro Teixeira ", "Pete Thomas ", "Richard Littauer ", "Richard Schneider ", "Roman Khafizianov ", "SeungWon ", "Stephen Whitmore ", "Tara Vancil ", "Teri Chadbourne ", "Travis Person ", "Travis Person ", "Vasco Santos ", "Vasco Santos ", "Victor Bjelkholm ", "Volker Mische ", "Zhiyuan Lin ", "dirkmc ", "dmitriy ryajov ", "elsehow ", "ethers ", "greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>", "greenkeeper[bot] ", "haad ", "kumavis ", "leekt216 ", "nginnever ", "noah the goodra ", "phillmac ", "priecint ", "samuli ", "sarthak khandelwal ", "shunkin ", "victorbjelkholm ", "Łukasz Magiera ", "Łukasz Magiera " ] } ================================================ FILE: src/add/form-data.browser.js ================================================ 'use strict' /* eslint-env browser */ const normaliseInput = require('ipfs-utils/src/files/normalise-input') const mtimeToObject = require('../lib/mtime-to-object') exports.toFormData = async input => { const files = normaliseInput(input) const formData = new FormData() let i = 0 for await (const file of files) { const headers = {} if (file.mtime !== undefined && file.mtime !== null) { const mtime = mtimeToObject(file.mtime) if (mtime) { headers.mtime = mtime.secs headers['mtime-nsecs'] = mtime.nsecs } } if (file.mode !== undefined && file.mode !== null) { headers.mode = file.mode.toString(8).padStart(4, '0') } if (file.content) { // In the browser there's _currently_ no streaming upload, buffer up our // async iterator chunks and append a big Blob :( // One day, this will be browser streams const bufs = [] for await (const chunk of file.content) { bufs.push(chunk) } formData.append(`file-${i}`, new Blob(bufs, { type: 'application/octet-stream' }), encodeURIComponent(file.path), { header: headers }) } else { formData.append(`dir-${i}`, new Blob([], { type: 'application/x-directory' }), encodeURIComponent(file.path), { header: headers }) } i++ } return formData } ================================================ FILE: src/add/form-data.js ================================================ 'use strict' const FormData = require('form-data') const { Buffer } = require('buffer') const toStream = require('it-to-stream') const normaliseInput = require('ipfs-utils/src/files/normalise-input') const { isElectronRenderer } = require('ipfs-utils/src/env') const mtimeToObject = require('../lib/mtime-to-object') exports.toFormData = async input => { const files = normaliseInput(input) const formData = new FormData() let i = 0 for await (const file of files) { const headers = {} if (file.mtime !== undefined && file.mtime !== null) { const mtime = mtimeToObject(file.mtime) if (mtime) { headers.mtime = mtime.secs headers['mtime-nsecs'] = mtime.nsecs } } if (file.mode !== undefined && file.mode !== null) { headers.mode = file.mode.toString(8).padStart(4, '0') } if (file.content) { // In Node.js, FormData can be passed a stream so no need to buffer formData.append( `file-${i}`, // FIXME: add a `path` property to the stream so `form-data` doesn't set // a Content-Length header that is only the sum of the size of the // header/footer when knownLength option (below) is null. Object.assign( toStream.readable(file.content), { path: file.path || `file-${i}` } ), { filepath: encodeURIComponent(file.path), contentType: 'application/octet-stream', knownLength: file.content.length, // Send Content-Length header if known header: headers } ) } else { formData.append(`dir-${i}`, Buffer.alloc(0), { filepath: encodeURIComponent(file.path), contentType: 'application/x-directory', header: headers }) } i++ } return formData } // TODO remove this when upstream fix for ky-universal is merged // https://github.com/sindresorhus/ky-universal/issues/9 // also this should only be necessary when nodeIntegration is false in electron renderer if (isElectronRenderer) { exports.toFormData = require('./form-data.browser').toFormData } ================================================ FILE: src/add/index.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const CID = require('cids') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') const { toFormData } = require('./form-data') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async function * add (input, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('stream-channels', true) if (options.chunker) searchParams.set('chunker', options.chunker) if (options.cidVersion) searchParams.set('cid-version', options.cidVersion) if (options.cidBase) searchParams.set('cid-base', options.cidBase) if (options.enableShardingExperiment != null) searchParams.set('enable-sharding-experiment', options.enableShardingExperiment) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.onlyHash != null) searchParams.set('only-hash', options.onlyHash) if (options.pin != null) searchParams.set('pin', options.pin) if (options.progress) searchParams.set('progress', true) if (options.quiet != null) searchParams.set('quiet', options.quiet) if (options.quieter != null) searchParams.set('quieter', options.quieter) if (options.rawLeaves != null) searchParams.set('raw-leaves', options.rawLeaves) if (options.shardSplitThreshold) searchParams.set('shard-split-threshold', options.shardSplitThreshold) if (options.silent) searchParams.set('silent', options.silent) if (options.trickle != null) searchParams.set('trickle', options.trickle) if (options.wrapWithDirectory != null) searchParams.set('wrap-with-directory', options.wrapWithDirectory) if (options.preload != null) searchParams.set('preload', options.preload) if (options.fileImportConcurrency != null) searchParams.set('file-import-concurrency', options.fileImportConcurrency) if (options.blockWriteConcurrency != null) searchParams.set('block-write-concurrency', options.blockWriteConcurrency) const res = await ky.post('add', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: await toFormData(input) }) for await (let file of ndjson(toIterable(res.body))) { file = toCamel(file) if (options.progress && file.bytes) { options.progress(file.bytes) } else { yield toCoreInterface(file) } } } }) function toCoreInterface ({ name, hash, size, mode, mtime, mtimeNsecs }) { const output = { path: name, cid: new CID(hash), size: parseInt(size) } if (mode != null) { output.mode = parseInt(mode, 8) } if (mtime != null) { output.mtime = { secs: mtime, nsecs: mtimeNsecs || 0 } } return output } ================================================ FILE: src/bitswap/index.js ================================================ 'use strict' module.exports = config => ({ wantlist: require('./wantlist')(config), stat: require('./stat')(config), unwant: require('./unwant')(config) }) ================================================ FILE: src/bitswap/stat.js ================================================ 'use strict' const configure = require('../lib/configure') const Big = require('bignumber.js') const CID = require('cids') module.exports = configure(({ ky }) => { return async (options) => { options = options || {} const res = await ky.post('bitswap/stat', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return toCoreInterface(res) } }) function toCoreInterface (res) { return { provideBufLen: res.ProvideBufLen, wantlist: (res.Wantlist || []).map(k => new CID(k['/'])), peers: (res.Peers || []), blocksReceived: new Big(res.BlocksReceived), dataReceived: new Big(res.DataReceived), blocksSent: new Big(res.BlocksSent), dataSent: new Big(res.DataSent), dupBlksReceived: new Big(res.DupBlksReceived), dupDataReceived: new Big(res.DupDataReceived) } } ================================================ FILE: src/bitswap/unwant.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (typeof cid === 'string') { searchParams.set('arg', cid) } else { searchParams.set('arg', new CID(cid).toString()) } const res = await ky.post('bitswap/unwant', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res } }) ================================================ FILE: src/bitswap/wantlist.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (peerId, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (peerId) { if (typeof peerId === 'string') { searchParams.set('peer', peerId) } else { searchParams.set('peer', new CID(peerId).toString()) } } const res = await ky.post('bitswap/wantlist', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return (res.Keys || []).map(k => new CID(k['/'])) } }) ================================================ FILE: src/block/get.js ================================================ 'use strict' const Block = require('ipfs-block') const CID = require('cids') const { Buffer } = require('buffer') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, options) => { cid = new CID(cid) options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${cid}`) const data = await ky.post('block/get', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).arrayBuffer() return new Block(Buffer.from(data), cid) } }) ================================================ FILE: src/block/index.js ================================================ 'use strict' module.exports = config => ({ get: require('./get')(config), stat: require('./stat')(config), put: require('./put')(config), rm: require('./rm')(config) }) ================================================ FILE: src/block/put.js ================================================ 'use strict' const Block = require('ipfs-block') const CID = require('cids') const multihash = require('multihashes') const configure = require('../lib/configure') const toFormData = require('../lib/buffer-to-form-data') module.exports = configure(({ ky }) => { async function put (data, options) { options = options || {} if (Block.isBlock(data)) { const { name, length } = multihash.decode(data.cid.multihash) options = { ...options, format: data.cid.codec, mhtype: name, mhlen: length, version: data.cid.version } data = data.data } else if (options.cid) { const cid = new CID(options.cid) const { name, length } = multihash.decode(cid.multihash) options = { ...options, format: cid.codec, mhtype: name, mhlen: length, version: cid.version } delete options.cid } const searchParams = new URLSearchParams(options.searchParams) if (options.format) searchParams.set('format', options.format) if (options.mhtype) searchParams.set('mhtype', options.mhtype) if (options.mhlen) searchParams.set('mhlen', options.mhlen) if (options.pin != null) searchParams.set('pin', options.pin) if (options.version != null) searchParams.set('version', options.version) let res try { res = await ky.post('block/put', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: toFormData(data) }).json() } catch (err) { // Retry with "protobuf"/"cbor" format for go-ipfs // TODO: remove when https://github.com/ipfs/go-cid/issues/75 resolved if (options.format === 'dag-pb') { return put(data, { ...options, format: 'protobuf' }) } else if (options.format === 'dag-cbor') { return put(data, { ...options, format: 'cbor' }) } throw err } return new Block(data, new CID(res.Key)) } return put }) ================================================ FILE: src/block/rm.js ================================================ 'use strict' const CID = require('cids') const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * rm (cid, options) { options = options || {} if (!Array.isArray(cid)) { cid = [cid] } const searchParams = new URLSearchParams() searchParams.set('stream-channels', true) searchParams.set('force', options.force || false) searchParams.set('quiet', options.quiet || false) cid.forEach(cid => { searchParams.append('arg', new CID(cid).toString()) }) const res = await ky.post('block/rm', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const removed of ndjson(toIterable(res.body))) { yield toCoreInterface(removed) } } }) function toCoreInterface (removed) { const out = { cid: new CID(removed.Hash) } if (removed.Error) { out.error = new Error(removed.Error) } return out } ================================================ FILE: src/block/stat.js ================================================ 'use strict' const CID = require('cids') const { Buffer } = require('buffer') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, options) => { options = options || {} if (Buffer.isBuffer(cid)) { cid = new CID(cid) } const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${cid}`) const res = await ky.post('block/stat', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return { cid: new CID(res.Key), size: res.Size } } }) ================================================ FILE: src/bootstrap/add.js ================================================ 'use strict' const Multiaddr = require('multiaddr') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (addr, options) => { if (addr && typeof addr === 'object' && !Multiaddr.isMultiaddr(addr)) { options = addr addr = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (addr) searchParams.set('arg', `${addr}`) if (options.default != null) searchParams.set('default', options.default) const res = await ky.post('bootstrap/add', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res } }) ================================================ FILE: src/bootstrap/index.js ================================================ 'use strict' module.exports = config => ({ add: require('./add')(config), rm: require('./rm')(config), list: require('./list')(config) }) ================================================ FILE: src/bootstrap/list.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (options) => { options = options || {} const res = await ky.post('bootstrap/list', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return res } }) ================================================ FILE: src/bootstrap/rm.js ================================================ 'use strict' const Multiaddr = require('multiaddr') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (addr, options) => { if (addr && typeof addr === 'object' && !Multiaddr.isMultiaddr(addr)) { options = addr addr = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (addr) searchParams.set('arg', `${addr}`) if (options.all != null) searchParams.set('all', options.all) const res = await ky.post('bootstrap/rm', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res } }) ================================================ FILE: src/cat.js ================================================ 'use strict' const CID = require('cids') const { Buffer } = require('buffer') const configure = require('./lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * cat (path, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (typeof path === 'string') { searchParams.set('arg', path) } else { searchParams.set('arg', new CID(path).toString()) } if (options.offset) searchParams.set('offset', options.offset) if (options.length) searchParams.set('length', options.length) const res = await ky.post('cat', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const chunk of toIterable(res.body)) { yield Buffer.from(chunk) } } }) ================================================ FILE: src/commands.js ================================================ 'use strict' const configure = require('./lib/configure') module.exports = configure(({ ky }) => { return options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.flags != null) searchParams.set('flags', options.flags) return ky.post('commands', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() } }) ================================================ FILE: src/config/get.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (key, options) => { if (key && typeof key === 'object') { options = key key = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (key) searchParams.set('arg', key) const url = key ? 'config' : 'config/show' const data = await ky.post(url, { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return key ? data.Value : data } }) ================================================ FILE: src/config/index.js ================================================ 'use strict' module.exports = config => ({ get: require('./get')(config), set: require('./set')(config), replace: require('./replace')(config), profiles: require('./profiles')(config) }) ================================================ FILE: src/config/profiles/apply.js ================================================ 'use strict' const configure = require('../../lib/configure') module.exports = configure(({ ky }) => { return async (profile, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', profile) if (options.dryRun != null) searchParams.set('dry-run', options.dryRun) const res = await ky.post('config/profile/apply', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return { original: res.OldCfg, updated: res.NewCfg } } }) ================================================ FILE: src/config/profiles/index.js ================================================ 'use strict' module.exports = config => ({ apply: require('./apply')(config), list: require('./list')(config) }) ================================================ FILE: src/config/profiles/list.js ================================================ 'use strict' const configure = require('../../lib/configure') const toCamel = require('../../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (options) => { options = options || {} const res = await ky.post('config/profile/list', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return res.map(profile => toCamel(profile)) } }) ================================================ FILE: src/config/replace.js ================================================ 'use strict' const { Buffer } = require('buffer') const configure = require('../lib/configure') const toFormData = require('../lib/buffer-to-form-data') module.exports = configure(({ ky }) => { return async (config, options) => { options = options || {} const res = await ky.post('config/replace', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams, body: toFormData(Buffer.from(JSON.stringify(config))) }).text() return res } }) ================================================ FILE: src/config/set.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (key, value, options) => { options = options || {} if (typeof key !== 'string') { throw new Error('Invalid key type') } const searchParams = new URLSearchParams(options.searchParams) if (typeof value === 'boolean') { searchParams.set('bool', true) value = value.toString() } else if (typeof value !== 'string') { searchParams.set('json', true) value = JSON.stringify(value) } searchParams.set('arg', key) searchParams.append('arg', value) const res = await ky.post('config', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/dag/get.js ================================================ 'use strict' const dagPB = require('ipld-dag-pb') const dagCBOR = require('ipld-dag-cbor') const raw = require('ipld-raw') const configure = require('../lib/configure') const resolvers = { 'dag-cbor': dagCBOR.resolver, 'dag-pb': dagPB.resolver, raw: raw.resolver } module.exports = config => { const getBlock = require('../block/get')(config) const dagResolve = require('./resolve')(config) return configure(({ ky }) => { return async (cid, path, options) => { if (typeof path === 'object') { options = path path = null } options = options || {} const resolved = await dagResolve(cid, path, options) const block = await getBlock(resolved.cid, options) const dagResolver = resolvers[block.cid.codec] if (!dagResolver) { throw Object.assign( new Error(`Missing IPLD format "${block.cid.codec}"`), { missingMulticodec: cid.codec } ) } return dagResolver.resolve(block.data, resolved.remPath) } })(config) } ================================================ FILE: src/dag/index.js ================================================ 'use strict' module.exports = config => ({ get: require('./get')(config), put: require('./put')(config), resolve: require('./resolve')(config) }) ================================================ FILE: src/dag/put.js ================================================ 'use strict' const dagCBOR = require('ipld-dag-cbor') const CID = require('cids') const multihash = require('multihashes') const configure = require('../lib/configure') const toFormData = require('../lib/buffer-to-form-data') module.exports = configure(({ ky }) => { return async (dagNode, options) => { options = options || {} if (options.hash) { options.hashAlg = options.hash delete options.hash } if (options.cid && (options.format || options.hashAlg)) { throw new Error('Failed to put DAG node. Provide either `cid` OR `format` and `hashAlg` options') } else if ((options.format && !options.hashAlg) || (!options.format && options.hashAlg)) { throw new Error('Failed to put DAG node. Provide `format` AND `hashAlg` options') } if (options.cid) { const cid = new CID(options.cid) options = { ...options, format: cid.codec, hashAlg: multihash.decode(cid.multihash).name } delete options.cid } options = { format: 'dag-cbor', hashAlg: 'sha2-256', inputEnc: 'raw', ...options } let serialized if (options.format === 'dag-cbor') { serialized = dagCBOR.util.serialize(dagNode) } else if (options.format === 'dag-pb') { serialized = dagNode.serialize() } else { // FIXME Hopefully already serialized...can we use IPLD to serialise instead? serialized = dagNode } const searchParams = new URLSearchParams(options.searchParams) searchParams.set('format', options.format) searchParams.set('hash', options.hashAlg) searchParams.set('input-enc', options.inputEnc) if (options.pin != null) searchParams.set('pin', options.pin) const res = await ky.post('dag/put', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: toFormData(serialized) }).json() return new CID(res.Cid['/']) } }) ================================================ FILE: src/dag/resolve.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, path, options) => { if (typeof path === 'object') { options = path path = null } options = options || {} const cidPath = path ? [cid, path].join(path.startsWith('/') ? '' : '/') : `${cid}` const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', cidPath) const res = await ky.post('dag/resolve', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return { cid: new CID(res.Cid['/']), remPath: res.RemPath } } }) ================================================ FILE: src/dht/find-peer.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const multiaddr = require('multiaddr') const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function findPeer (peerId, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(peerId) ? new CID(peerId) : peerId}`) if (options.verbose != null) searchParams.set('verbose', options.verbose) const res = await ky.post('dht/findpeer', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const message of ndjson(toIterable(res.body))) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L388-L389 if (message.Type === 3) { throw new Error(message.Extra) } // 2 = FinalPeer // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 if (message.Type === 2 && message.Responses) { // There will only be 1: // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L395-L396 for (const { ID, Addrs } of message.Responses) { return { id: ID, addrs: (Addrs || []).map(a => multiaddr(a)) } } } } throw new Error('not found') } }) ================================================ FILE: src/dht/find-provs.js ================================================ 'use strict' const CID = require('cids') const multiaddr = require('multiaddr') const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * findProvs (cid, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${new CID(cid)}`) if (options.numProviders) searchParams.set('num-providers', options.numProviders) if (options.verbose != null) searchParams.set('verbose', options.verbose) const res = await ky.post('dht/findprovs', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const message of ndjson(toIterable(res.body))) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/libp2p/go-libp2p-kad-dht/blob/master/routing.go#L525-L526 if (message.Type === 3) { throw new Error(message.Extra) } // 4 = Provider // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L20 if (message.Type === 4 && message.Responses) { for (const { ID, Addrs } of message.Responses) { yield { id: ID, addrs: (Addrs || []).map(a => multiaddr(a)) } } } } } }) ================================================ FILE: src/dht/get.js ================================================ 'use strict' const { Buffer } = require('buffer') const ndjson = require('iterable-ndjson') const toIterable = require('stream-to-it/source') const encodeBufferURIComponent = require('../lib/encode-buffer-uri-component') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async function get (key, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.verbose != null) searchParams.set('verbose', options.verbose) if (!Buffer.isBuffer(key)) { throw new Error('invalid key') } const res = await ky.post(`dht/get?key=${encodeBufferURIComponent(key)}&${searchParams}`, { timeout: options.timeout, signal: options.signal, headers: options.headers }) for await (const message of ndjson(toIterable(res.body))) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L472-L473 if (message.Type === 3) { throw new Error(message.Extra) } // 5 = Value // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L21 if (message.Type === 5) { return message.Extra } } throw new Error('not found') } }) ================================================ FILE: src/dht/index.js ================================================ 'use strict' module.exports = config => ({ get: require('./get')(config), put: require('./put')(config), findProvs: require('./find-provs')(config), findPeer: require('./find-peer')(config), provide: require('./provide')(config), // find closest peerId to given peerId query: require('./query')(config) }) ================================================ FILE: src/dht/provide.js ================================================ 'use strict' const CID = require('cids') const multiaddr = require('multiaddr') const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async function * provide (cids, options) { cids = Array.isArray(cids) ? cids : [cids] options = options || {} const searchParams = new URLSearchParams(options.searchParams) cids.forEach(cid => searchParams.append('arg', `${new CID(cid)}`)) if (options.recursive != null) searchParams.set('recursive', options.recursive) if (options.verbose != null) searchParams.set('verbose', options.verbose) const res = await ky.post('dht/provide', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (let message of ndjson(toIterable(res.body))) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L283-L284 if (message.Type === 3) { throw new Error(message.Extra) } message = toCamel(message) message.id = new CID(message.id) if (message.responses) { message.responses = message.responses.map(({ ID, Addrs }) => ({ id: ID, addrs: (Addrs || []).map(a => multiaddr(a)) })) } else { message.responses = [] } yield message } } }) ================================================ FILE: src/dht/put.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const multiaddr = require('multiaddr') const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') const encodeBufferURIComponent = require('../lib/encode-buffer-uri-component') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async function * put (key, value, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.verbose != null) searchParams.set('verbose', options.verbose) key = Buffer.isBuffer(key) ? encodeBufferURIComponent(key) : encodeURIComponent(key) value = Buffer.isBuffer(value) ? encodeBufferURIComponent(value) : encodeURIComponent(value) const url = `dht/put?arg=${key}&arg=${value}&${searchParams}` const res = await ky.post(url, { timeout: options.timeout, signal: options.signal, headers: options.headers }) for await (let message of ndjson(toIterable(res.body))) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L472-L473 if (message.Type === 3) { throw new Error(message.Extra) } message = toCamel(message) message.id = new CID(message.id) if (message.responses) { message.responses = message.responses.map(({ ID, Addrs }) => ({ id: ID, addrs: (Addrs || []).map(a => multiaddr(a)) })) } yield message } } }) ================================================ FILE: src/dht/query.js ================================================ 'use strict' const CID = require('cids') const ndjson = require('iterable-ndjson') const multiaddr = require('multiaddr') const toIterable = require('stream-to-it/source') const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async function * query (peerId, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(peerId) ? new CID(peerId) : peerId}`) if (options.verbose != null) searchParams.set('verbose', options.verbose) const res = await ky.post('dht/query', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (let message of ndjson(toIterable(res.body))) { message = toCamel(message) message.id = new CID(message.id) message.responses = (message.responses || []).map(({ ID, Addrs }) => ({ id: ID, addrs: (Addrs || []).map(a => multiaddr(a)) })) yield message } } }) ================================================ FILE: src/diag/cmds.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.verbose != null) searchParams.set('verbose', options.verbose) return ky.post('diag/cmds', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() } }) ================================================ FILE: src/diag/index.js ================================================ 'use strict' module.exports = config => ({ net: require('./net')(config), sys: require('./sys')(config), cmds: require('./cmds')(config) }) ================================================ FILE: src/diag/net.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return options => { options = options || {} return ky.post('diag/net', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() } }) ================================================ FILE: src/diag/sys.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return options => { options = options || {} return ky.post('diag/sys', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() } }) ================================================ FILE: src/dns.js ================================================ 'use strict' const configure = require('./lib/configure') module.exports = configure(({ ky }) => { return async (domain, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', domain) if (options.recursive != null) searchParams.set('recursive', options.recursive) const res = await ky.post('dns', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res.Path } }) ================================================ FILE: src/files/chmod.js ================================================ 'use strict' const configure = require('../lib/configure') const modeToString = require('../lib/mode-to-string') module.exports = configure(({ ky }) => { return function chmod (path, mode, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.append('arg', path) searchParams.append('mode', modeToString(mode)) if (options.flush != null) searchParams.set('flush', options.flush) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.parents != null) searchParams.set('parents', options.parents) return ky.post('files/chmod', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/files/cp.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') const { findSources } = require('./utils') module.exports = configure(({ ky }) => { return (...args) => { const { sources, options } = findSources(args) const searchParams = new URLSearchParams(options.searchParams) sources.forEach(src => searchParams.append('arg', CID.isCID(src) ? `/ipfs/${src}` : src)) if (options.flush != null) searchParams.set('flush', options.flush) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.parents != null) searchParams.set('parents', options.parents) if (options.shardSplitThreshold != null) searchParams.set('shardSplitThreshold', options.shardSplitThreshold) return ky.post('files/cp', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/files/flush.js ================================================ 'use strict' const configure = require('../lib/configure') const CID = require('cids') module.exports = configure(({ ky }) => { return async (path, options) => { if (typeof path !== 'string') { options = path path = '/' } options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', path) const res = await ky.post('files/flush', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return new CID(res.Cid) } }) ================================================ FILE: src/files/index.js ================================================ 'use strict' module.exports = config => ({ chmod: require('./chmod')(config), cp: require('./cp')(config), mkdir: require('./mkdir')(config), flush: require('./flush')(config), stat: require('./stat')(config), rm: require('./rm')(config), ls: require('./ls')(config), read: require('./read')(config), touch: require('./touch')(config), write: require('./write')(config), mv: require('./mv')(config) }) ================================================ FILE: src/files/ls.js ================================================ 'use strict' const CID = require('cids') const ndjson = require('iterable-ndjson') const toIterable = require('stream-to-it/source') const configure = require('../lib/configure') const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata') module.exports = configure(({ ky }) => { return async function * ls (path, options) { if (typeof path !== 'string') { options = path path = '/' } options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', CID.isCID(path) ? `/ipfs/${path}` : path) searchParams.set('stream', options.stream == null ? true : options.stream) if (options.cidBase) searchParams.set('cid-base', options.cidBase) searchParams.set('long', options.long == null ? true : options.long) // TODO: remove after go-ipfs 0.5 is released searchParams.set('l', options.long == null ? true : options.long) const res = await ky.post('files/ls', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const result of ndjson(toIterable(res.body))) { // go-ipfs does not yet support the "stream" option if ('Entries' in result) { for (const entry of result.Entries || []) { yield toCoreInterface(toCamelWithMetadata(entry)) } } else { yield toCoreInterface(toCamelWithMetadata(result)) } } } }) function toCoreInterface (entry) { if (entry.hash) entry.cid = new CID(entry.hash) delete entry.hash return entry } ================================================ FILE: src/files/mkdir.js ================================================ 'use strict' const configure = require('../lib/configure') const modeToString = require('../lib/mode-to-string') const mtimeToObject = require('../lib/mtime-to-object') module.exports = configure(({ ky }) => { return (path, options) => { options = options || {} const mtime = mtimeToObject(options.mtime) const searchParams = new URLSearchParams(options.searchParams) searchParams.append('arg', path) if (options.cidVersion != null) searchParams.set('cid-version', options.cidVersion) if (options.flush != null) searchParams.set('flush', options.flush) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.parents != null) searchParams.set('parents', options.parents) if (options.shardSplitThreshold != null) searchParams.set('shardSplitThreshold', options.shardSplitThreshold) if (mtime) { searchParams.set('mtime', mtime.secs) if (mtime.nsecs != null) { searchParams.set('mtimeNsecs', mtime.nsecs) } } if (options.mode != null) searchParams.set('mode', modeToString(options.mode)) return ky.post('files/mkdir', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/files/mv.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') const { findSources } = require('./utils') module.exports = configure(({ ky }) => { return (...args) => { const { sources, options } = findSources(args) const searchParams = new URLSearchParams(options.searchParams) sources.forEach(src => searchParams.append('arg', CID.isCID(src) ? `/ipfs/${src}` : src)) if (options.flush != null) searchParams.set('flush', options.flush) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.parents != null) searchParams.set('parents', options.parents) if (options.shardSplitThreshold != null) searchParams.set('shardSplitThreshold', options.shardSplitThreshold) return ky.post('files/mv', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/files/read.js ================================================ 'use strict' const { Buffer } = require('buffer') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * read (path, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.append('arg', `${path}`) if (options.length != null) searchParams.set('length', options.length) if (options.offset != null) searchParams.set('offset', options.offset) const res = await ky.post('files/read', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const chunk of toIterable(res.body)) { yield Buffer.from(chunk) } } }) ================================================ FILE: src/files/rm.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return (path, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.append('arg', path) if (options.recursive != null) searchParams.set('recursive', options.recursive) if (options.force != null) searchParams.set('force', options.force) if (options.shardSplitThreshold != null) searchParams.set('shardSplitThreshold', options.shardSplitThreshold) return ky.post('files/rm', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/files/stat.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata') module.exports = configure(({ ky }) => { return async (path, options) => { if (typeof path !== 'string') { options = path path = '/' } options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', path) if (options.cidBase) searchParams.set('cid-base', options.cidBase) if (options.hash != null) searchParams.set('hash', options.hash) if (options.size != null) searchParams.set('size', options.size) if (options.withLocal != null) searchParams.set('with-local', options.withLocal) const res = await ky.post('files/stat', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() res.WithLocality = res.WithLocality || false return toCoreInterface(toCamelWithMetadata(res)) } }) function toCoreInterface (entry) { entry.cid = new CID(entry.hash) delete entry.hash return entry } ================================================ FILE: src/files/touch.js ================================================ 'use strict' const configure = require('../lib/configure') const mtimeToObject = require('../lib/mtime-to-object') module.exports = configure(({ ky }) => { return function touch (path, options) { options = options || {} const mtime = mtimeToObject(options.mtime) const searchParams = new URLSearchParams(options.searchParams) searchParams.append('arg', path) if (mtime) { searchParams.set('mtime', mtime.secs) searchParams.set('mtimeNsecs', mtime.nsecs) } if (options.flush != null) searchParams.set('flush', options.flush) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.parents != null) searchParams.set('parents', options.parents) return ky.post('files/touch', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/files/utils.js ================================================ 'use strict' exports.findSources = (args) => { let options = {} let sources = [] if (!Array.isArray(args[args.length - 1]) && typeof args[args.length - 1] === 'object') { options = args.pop() } if (args.length === 1 && Array.isArray(args[0])) { // support ipfs.files.cp([src, dest], opts) sources = args[0] } else { // support ipfs.files.cp(src, dest, opts) and ipfs.files.cp(src1, src2, dest, opts) sources = args } return { sources, options } } ================================================ FILE: src/files/write.js ================================================ 'use strict' const configure = require('../lib/configure') const toFormData = require('../lib/buffer-to-form-data') const modeToString = require('../lib/mode-to-string') const mtimeToObject = require('../lib/mtime-to-object') module.exports = configure(({ ky }) => { return async (path, input, options) => { options = options || {} const mtime = mtimeToObject(options.mtime) const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', path) searchParams.set('stream-channels', true) if (options.cidVersion) searchParams.set('cid-version', options.cidVersion) if (options.create != null) searchParams.set('create', options.create) if (options.hashAlg) searchParams.set('hash', options.hashAlg) if (options.length != null) searchParams.set('length', options.length) if (options.offset != null) searchParams.set('offset', options.offset) if (options.parents != null) searchParams.set('parents', options.parents) if (options.rawLeaves != null) searchParams.set('raw-leaves', options.rawLeaves) if (options.truncate != null) searchParams.set('truncate', options.truncate) if (options.shardSplitThreshold != null) searchParams.set('shardSplitThreshold', options.shardSplitThreshold) if (mtime) { searchParams.set('mtime', mtime.secs) if (mtime.nsecs != null) { searchParams.set('mtimeNsecs', mtime.nsecs) } } const res = await ky.post('files/write', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: toFormData(input, { mode: options.mode != null ? modeToString(options.mode) : undefined, mtime: mtime ? mtime.secs : undefined, mtimeNsecs: mtime ? mtime.nsecs : undefined }) // TODO: support inputs other than buffer as per spec }) return res.text() } }) ================================================ FILE: src/get-endpoint-config.js ================================================ 'use strict' const configure = require('./lib/configure') module.exports = configure(({ apiAddr, apiPath }) => { const url = new URL(apiAddr) return () => ({ host: url.hostname, port: url.port, protocol: url.protocol.split(':')[0], // remove ":" 'api-path': apiPath }) }) ================================================ FILE: src/get.js ================================================ 'use strict' const configure = require('./lib/configure') const Tar = require('it-tar') const { Buffer } = require('buffer') const CID = require('cids') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * get (path, options) { options = options || {} const searchParams = new URLSearchParams() searchParams.set('arg', `${Buffer.isBuffer(path) ? new CID(path) : path}`) if (options.compress !== undefined) { searchParams.set('compress', options.compress) } if (options.compressionLevel !== undefined) { searchParams.set('compression-level', options.compressionLevel) } if (options.offset) { searchParams.set('offset', options.offset) } if (options.length) { searchParams.set('length', options.length) } const res = await ky.post('get', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) const extractor = Tar.extract() for await (const { header, body } of extractor(toIterable(res.body))) { if (header.type === 'directory') { yield { path: header.name } } else { yield { path: header.name, content: body } } } } }) ================================================ FILE: src/id.js ================================================ 'use strict' const configure = require('./lib/configure') const toCamel = require('./lib/object-to-camel') const multiaddr = require('multiaddr') module.exports = configure(({ ky }) => { return async options => { options = options || {} const res = await ky.post('id', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() const output = toCamel(res) if (output.addresses) { output.addresses = output.addresses.map(ma => multiaddr(ma)) } return output } }) ================================================ FILE: src/index.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const multiaddr = require('multiaddr') const multibase = require('multibase') const multicodec = require('multicodec') const multihash = require('multihashes') const globSource = require('ipfs-utils/src/files/glob-source') const urlSource = require('ipfs-utils/src/files/url-source') function ipfsClient (config) { return { add: require('./add')(config), bitswap: require('./bitswap')(config), block: require('./block')(config), bootstrap: require('./bootstrap')(config), cat: require('./cat')(config), commands: require('./commands')(config), config: require('./config')(config), dag: require('./dag')(config), dht: require('./dht')(config), diag: require('./diag')(config), dns: require('./dns')(config), files: require('./files')(config), get: require('./get')(config), getEndpointConfig: require('./get-endpoint-config')(config), id: require('./id')(config), key: require('./key')(config), log: require('./log')(config), ls: require('./ls')(config), mount: require('./mount')(config), name: require('./name')(config), object: require('./object')(config), pin: require('./pin')(config), ping: require('./ping')(config), pubsub: require('./pubsub')(config), refs: require('./refs')(config), repo: require('./repo')(config), resolve: require('./resolve')(config), stats: require('./stats')(config), stop: require('./stop')(config), shutdown: require('./stop')(config), swarm: require('./swarm')(config), version: require('./version')(config) } } Object.assign(ipfsClient, { Buffer, CID, multiaddr, multibase, multicodec, multihash, globSource, urlSource }) module.exports = ipfsClient ================================================ FILE: src/key/export.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return (name, password, options) => { if (typeof password !== 'string') { options = password password = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', name) if (password) searchParams.set('password', password) return ky.post('key/export', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).text() } }) ================================================ FILE: src/key/gen.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (name, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', name) if (options.type) searchParams.set('type', options.type) if (options.size != null) searchParams.set('size', options.size) const res = await ky.post('key/gen', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/key/import.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (name, pem, password, options) => { if (typeof password !== 'string') { options = password password = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', name) searchParams.set('pem', pem) if (password) searchParams.set('password', password) const res = await ky.post('key/import', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/key/index.js ================================================ 'use strict' module.exports = config => ({ gen: require('./gen')(config), list: require('./list')(config), rename: require('./rename')(config), rm: require('./rm')(config), export: require('./export')(config), import: require('./import')(config) }) ================================================ FILE: src/key/list.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async options => { options = options || {} const res = await ky.post('key/list', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return (res.Keys || []).map(k => toCamel(k)) } }) ================================================ FILE: src/key/rename.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (oldName, newName, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', oldName) searchParams.append('arg', newName) if (options.force != null) searchParams.set('force', options.force) const res = await ky.post('key/rename', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/key/rm.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (name, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', name) const res = await ky.post('key/rm', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res.Keys[0]) } }) ================================================ FILE: src/lib/buffer-to-form-data.browser.js ================================================ 'use strict' /* eslint-env browser */ module.exports = buf => { const formData = new FormData() formData.append('file', new Blob([buf], { type: 'application/octet-stream' })) return formData } ================================================ FILE: src/lib/buffer-to-form-data.js ================================================ 'use strict' const FormData = require('form-data') const { isElectronRenderer } = require('ipfs-utils/src/env') module.exports = (buf, { mode, mtime, mtimeNsecs } = {}) => { const headers = {} if (mode != null) { headers.mode = mode } if (mtime != null) { headers.mtime = mtime if (mtimeNsecs != null) { headers['mtime-nsecs'] = mtimeNsecs } } const formData = new FormData() formData.append('file', buf, { header: headers }) return formData } // TODO remove this when upstream fix for ky-universal is merged // https://github.com/sindresorhus/ky-universal/issues/9 // also this should only be necessary when nodeIntegration is false in electron renderer if (isElectronRenderer) { module.exports = require('./buffer-to-form-data.browser') } ================================================ FILE: src/lib/configure.js ================================================ 'use strict' /* eslint-env browser */ const ky = require('ky-universal').default const { isBrowser, isWebWorker } = require('ipfs-utils/src/env') const toUri = require('multiaddr-to-uri') const errorHandler = require('./error-handler') const mergeOptions = require('merge-options').bind({ ignoreUndefined: true }) const parseDuration = require('parse-duration') // Set default configuration and call create function with them module.exports = create => config => { config = config || {} if (typeof config === 'string') { config = { apiAddr: config } } else if (config.constructor && config.constructor.isMultiaddr) { config = { apiAddr: config } } else { config = { ...config } } config.apiAddr = (config.apiAddr || getDefaultApiAddr(config)).toString() config.apiAddr = config.apiAddr.startsWith('/') ? toUri(config.apiAddr) : config.apiAddr config.apiAddr = trimEnd(config.apiAddr, '/') const apiAddrPath = getNonRootPath(config.apiAddr) // Use configured apiPath, or path on the end of apiAddr (if there is one) or default to /api/v0 config.apiPath = config.apiPath || config['api-path'] || apiAddrPath || '/api/v0' config.apiPath = trimEnd(config.apiPath, '/') // If user passed apiAddr with a path, trim it from the end (it is now apiPath) config.apiAddr = apiAddrPath ? trimEnd(config.apiAddr, apiAddrPath) : config.apiAddr const defaults = { prefixUrl: config.apiAddr + config.apiPath, timeout: parseTimeout(config.timeout) || 60000 * 20, headers: config.headers, hooks: { afterResponse: [errorHandler] } } const k = ky.extend(defaults) const client = ['get', 'post', 'put', 'delete', 'patch', 'head'] .reduce((client, key) => { client[key] = wrap(k[key], defaults) return client }, wrap(k, defaults)) return create({ ky: client, ...config }) } function getDefaultApiAddr ({ protocol, host, port }) { if (isBrowser || isWebWorker) { if (!protocol) { protocol = location.protocol.startsWith('http') ? trimEnd(location.protocol, ':') : 'http' } host = host || location.hostname port = port || location.port return `${protocol}://${host}${port ? ':' + port : ''}` } return `${protocol || 'http'}://${host || 'localhost'}:${port || 5001}` } // returns the passed function wrapped in a function that ignores // undefined values in the passed `options` object function wrap (fn, defaults) { return (input, options) => { if (options.timeout) options.timeout = parseTimeout(options.timeout) return fn(input, mergeOptions(defaults, options)) } } function parseTimeout (value) { return typeof value === 'string' ? parseDuration(value) : value } const trimEnd = (str, end) => str.endsWith(end) ? str.slice(0, -end.length) : str // Get the path from a URL is it is not / function getNonRootPath (url) { if (url) { const { pathname } = new URL(url) return pathname === '/' ? null : pathname } } ================================================ FILE: src/lib/encode-buffer-uri-component.js ================================================ 'use strict' // https://github.com/ipfs/js-ipfs-http-client/issues/569 module.exports = function encodeBuffer (buf) { let uriEncoded = '' for (const byte of buf) { // https://tools.ietf.org/html/rfc3986#page-14 // ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E), // underscore (%5F), or tilde (%7E) if ( (byte >= 0x41 && byte <= 0x5A) || (byte >= 0x61 && byte <= 0x7A) || (byte >= 0x30 && byte <= 0x39) || (byte === 0x2D) || (byte === 0x2E) || (byte === 0x5F) || (byte === 0x7E) ) { uriEncoded += String.fromCharCode(byte) } else { uriEncoded += `%${byte.toString(16).padStart(2, '0')}` } } return uriEncoded } ================================================ FILE: src/lib/error-handler.js ================================================ 'use strict' const { HTTPError } = require('ky-universal') const log = require('debug')('ipfs-http-client:lib:error-handler') const { isNode, isElectronMain } = require('ipfs-utils/src/env') function isJsonResponse (res) { return (res.headers.get('Content-Type') || '').startsWith('application/json') } module.exports = async function errorHandler (input, options, response) { if (response.ok) { // FIXME: remove when fixed https://github.com/sindresorhus/ky-universal/issues/8 // // ky clones the response for each handler. In Node.js the response body is // piped to 2 PassThroughs, one becomes the real body and the other is used // in the clone. // // If the body in the clone is not consumed or destroyed the highwater mark // will be reached (for large payloads) and stop the real body from flowing. if (isNode || isElectronMain) response.body.destroy() return } let msg try { if (isJsonResponse(response)) { const data = await response.json() log(data) msg = data.Message || data.message } else { msg = await response.text() } } catch (err) { log('Failed to parse error response', err) // Failed to extract/parse error message from response msg = err.message } const error = new HTTPError(response) // If we managed to extract a message from the response, use it if (msg) { error.message = msg } throw error } ================================================ FILE: src/lib/mode-to-string.js ================================================ 'use strict' module.exports = (mode) => { if (mode === undefined || mode === null) { return undefined } if (typeof mode === 'string' || mode instanceof String) { return mode } return mode.toString(8).padStart(4, '0') } ================================================ FILE: src/lib/mtime-to-object.js ================================================ 'use strict' module.exports = function parseMtime (mtime) { if (mtime == null) { return undefined } // Javascript Date if (mtime instanceof Date) { const ms = mtime.getTime() const secs = Math.floor(ms / 1000) return { secs: secs, nsecs: (ms - (secs * 1000)) * 1000 } } // { secs, nsecs } if (Object.prototype.hasOwnProperty.call(mtime, 'secs')) { return { secs: mtime.secs, nsecs: mtime.nsecs } } // UnixFS TimeSpec if (Object.prototype.hasOwnProperty.call(mtime, 'Seconds')) { return { secs: mtime.Seconds, nsecs: mtime.FractionalNanoseconds } } // process.hrtime() if (Array.isArray(mtime)) { return { secs: mtime[0], nsecs: mtime[1] } } /* TODO: https://github.com/ipfs/aegir/issues/487 // process.hrtime.bigint() if (typeof mtime === 'bigint') { const secs = mtime / BigInt(1e9) const nsecs = mtime - (secs * BigInt(1e9)) return { secs: parseInt(secs), nsecs: parseInt(nsecs) } } */ } ================================================ FILE: src/lib/object-to-camel-with-metadata.js ================================================ 'use strict' const toCamel = require('./object-to-camel') function toCamelWithMetadata (entry) { const file = toCamel(entry) if (Object.prototype.hasOwnProperty.call(file, 'mode')) { file.mode = parseInt(file.mode, 8) } if (Object.prototype.hasOwnProperty.call(file, 'mtime')) { file.mtime = { secs: file.mtime, nsecs: file.mtimeNsecs || 0 } delete file.mtimeNsecs } return file } module.exports = toCamelWithMetadata ================================================ FILE: src/lib/object-to-camel.js ================================================ 'use strict' // Convert object properties to camel case. // NOT recursive! // e.g. // AgentVersion => agentVersion // ID => id module.exports = obj => { if (obj == null) return obj const caps = /^[A-Z]+$/ return Object.keys(obj).reduce((camelObj, k) => { if (caps.test(k)) { // all caps camelObj[k.toLowerCase()] = obj[k] } else if (caps.test(k[0])) { // pascal camelObj[k[0].toLowerCase() + k.slice(1)] = obj[k] } else { camelObj[k] = obj[k] } return camelObj }, {}) } ================================================ FILE: src/log/index.js ================================================ 'use strict' module.exports = config => ({ tail: require('./tail')(config), ls: require('./ls')(config), level: require('./level')(config) }) ================================================ FILE: src/log/level.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (subsystem, level, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', subsystem) searchParams.append('arg', level) const res = await ky.post('log/level', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/log/ls.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async options => { options = options || {} const res = await ky.post('log/ls', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return res.Strings } }) ================================================ FILE: src/log/tail.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * tail (options) { options = options || {} const res = await ky.post('log/tail', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }) yield * ndjson(toIterable(res.body)) } }) ================================================ FILE: src/ls.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const ndjson = require('iterable-ndjson') const toIterable = require('stream-to-it/source') const configure = require('./lib/configure') module.exports = configure(({ ky }) => { return async function * ls (path, options) { options = options || {} const searchParams = new URLSearchParams() searchParams.set('arg', `${Buffer.isBuffer(path) ? new CID(path) : path}`) searchParams.set('stream', options.stream == null ? true : options.stream) if (options.long != null) searchParams.set('long', options.long) if (options.unsorted != null) searchParams.set('unsorted', options.unsorted) if (options.recursive != null) searchParams.set('recursive', options.recursive) const res = await ky.post('ls', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (let result of ndjson(toIterable(res.body))) { result = result.Objects if (!result) { throw new Error('expected .Objects in results') } result = result[0] if (!result) { throw new Error('expected one array in results.Objects') } result = result.Links if (!Array.isArray(result)) { throw new Error('expected one array in results.Objects[0].Links') } for (const link of result) { const entry = { name: link.Name, path: path + '/' + link.Name, size: link.Size, cid: new CID(link.Hash), type: typeOf(link), depth: link.Depth || 1 } if (link.Mode) { entry.mode = parseInt(link.Mode, 8) } if (link.Mtime !== undefined && link.Mtime !== null) { entry.mtime = { secs: link.Mtime } if (link.MtimeNsecs !== undefined && link.MtimeNsecs !== null) { entry.mtime.nsecs = link.MtimeNsecs } } yield entry } } } }) function typeOf (link) { switch (link.Type) { case 1: case 5: return 'dir' case 2: return 'file' default: return 'unknown' } } ================================================ FILE: src/mount.js ================================================ 'use strict' const configure = require('./lib/configure') const toCamel = require('./lib/object-to-camel') module.exports = configure(({ ky }) => { return async options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.ipfsPath != null) searchParams.set('ipfs-path', options.ipfsPath) if (options.ipnsPath != null) searchParams.set('ipns-path', options.ipnsPath) const res = await ky.post('dns', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/name/index.js ================================================ 'use strict' module.exports = config => ({ publish: require('./publish')(config), resolve: require('./resolve')(config), pubsub: require('./pubsub')(config) }) ================================================ FILE: src/name/publish.js ================================================ 'use strict' const configure = require('../lib/configure') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (path, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', path) if (options.allowOffline != null) searchParams.set('allow-offline', options.allowOffline) if (options.key) searchParams.set('key', options.key) if (options.lifetime) searchParams.set('lifetime', options.lifetime) if (options.quieter != null) searchParams.set('quieter', options.quieter) if (options.resolve != null) searchParams.set('resolve', options.resolve) if (options.ttl) searchParams.set('ttl', options.ttl) const res = await ky.post('name/publish', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/name/pubsub/cancel.js ================================================ 'use strict' const configure = require('../../lib/configure') const toCamel = require('../../lib/object-to-camel') module.exports = configure(({ ky }) => { return async (name, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', name) const res = await ky.post('name/pubsub/cancel', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/name/pubsub/index.js ================================================ 'use strict' module.exports = config => ({ cancel: require('./cancel')(config), state: require('./state')(config), subs: require('./subs')(config) }) ================================================ FILE: src/name/pubsub/state.js ================================================ 'use strict' const configure = require('../../lib/configure') const toCamel = require('../../lib/object-to-camel') module.exports = configure(({ ky }) => { return async options => { options = options || {} const res = await ky.post('name/pubsub/state', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return toCamel(res) } }) ================================================ FILE: src/name/pubsub/subs.js ================================================ 'use strict' const configure = require('../../lib/configure') module.exports = configure(({ ky }) => { return async (name, options) => { options = options || {} const res = await ky.post('name/pubsub/subs', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return res.Strings || [] } }) ================================================ FILE: src/name/resolve.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * (path, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', path) searchParams.set('stream', options.stream == null ? true : options.stream) if (options.dhtRecordCount != null) searchParams.set('dht-record-count', options.dhtRecordCount) if (options.dhtTimeout != null) searchParams.set('dht-timeout', options.dhtTimeout) if (options.noCache != null) searchParams.set('nocache', options.noCache) if (options.recursive != null) searchParams.set('recursive', options.recursive) const res = await ky.post('name/resolve', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const result of ndjson(toIterable(res.body))) { yield result.Path } } }) ================================================ FILE: src/object/data.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async function data (cid, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) const data = await ky.post('object/data', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).arrayBuffer() return Buffer.from(data) } }) ================================================ FILE: src/object/get.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const { DAGNode, DAGLink } = require('ipld-dag-pb') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) searchParams.set('data-encoding', 'base64') const res = await ky.post('object/get', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return new DAGNode( Buffer.from(res.Data, 'base64'), (res.Links || []).map(l => new DAGLink(l.Name, l.Size, l.Hash)) ) } }) ================================================ FILE: src/object/index.js ================================================ 'use strict' module.exports = config => ({ data: require('./data')(config), get: require('./get')(config), links: require('./links')(config), new: require('./new')(config), patch: require('./patch')(config), put: require('./put')(config), stat: require('./stat')(config) }) ================================================ FILE: src/object/links.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const { DAGLink } = require('ipld-dag-pb') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) const res = await ky.post('object/links', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return (res.Links || []).map(l => new DAGLink(l.Name, l.Size, l.Hash)) } }) ================================================ FILE: src/object/new.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (template, options) => { if (typeof template !== 'string') { options = template template = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (template) searchParams.set('arg', template) const { Hash } = await ky.post('object/new', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return new CID(Hash) } }) ================================================ FILE: src/object/patch/add-link.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const configure = require('../../lib/configure') module.exports = configure(({ ky }) => { return async (cid, dLink, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) searchParams.append('arg', dLink.Name || dLink.name || null) searchParams.append('arg', (dLink.Hash || dLink.cid || '').toString() || null) const { Hash } = await ky.post('object/patch/add-link', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return new CID(Hash) } }) ================================================ FILE: src/object/patch/append-data.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const configure = require('../../lib/configure') const toFormData = require('../../lib/buffer-to-form-data') module.exports = configure(({ ky }) => { return async (cid, data, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) const { Hash } = await ky.post('object/patch/append-data', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: toFormData(data) }).json() return new CID(Hash) } }) ================================================ FILE: src/object/patch/index.js ================================================ 'use strict' module.exports = config => ({ addLink: require('./add-link')(config), appendData: require('./append-data')(config), rmLink: require('./rm-link')(config), setData: require('./set-data')(config) }) ================================================ FILE: src/object/patch/rm-link.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const configure = require('../../lib/configure') module.exports = configure(({ ky }) => { return async (cid, dLink, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) searchParams.append('arg', dLink.Name || dLink.name || null) const { Hash } = await ky.post('object/patch/rm-link', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return new CID(Hash) } }) ================================================ FILE: src/object/patch/set-data.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const configure = require('../../lib/configure') const toFormData = require('../../lib/buffer-to-form-data') module.exports = configure(({ ky }) => { return async (cid, data, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) const { Hash } = await ky.post('object/patch/set-data', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: toFormData(data) }).json() return new CID(Hash) } }) ================================================ FILE: src/object/put.js ================================================ 'use strict' const CID = require('cids') const { DAGNode } = require('ipld-dag-pb') const { Buffer } = require('buffer') const configure = require('../lib/configure') const toFormData = require('../lib/buffer-to-form-data') module.exports = configure(({ ky }) => { return async (obj, options) => { options = options || {} let tmpObj = { Data: null, Links: [] } if (Buffer.isBuffer(obj)) { if (!options.enc) { tmpObj = { Data: obj.toString(), Links: [] } } } else if (DAGNode.isDAGNode(obj)) { tmpObj = { Data: obj.Data.toString(), Links: obj.Links.map(l => ({ Name: l.Name, Hash: l.Hash.toString(), Size: l.Tsize })) } } else if (typeof obj === 'object') { tmpObj.Data = obj.Data.toString() tmpObj.Links = obj.Links } else { throw new Error('obj not recognized') } let buf if (Buffer.isBuffer(obj) && options.enc) { buf = obj } else { buf = Buffer.from(JSON.stringify(tmpObj)) } const searchParams = new URLSearchParams(options.searchParams) if (options.enc) searchParams.set('inputenc', options.enc) if (options.pin != null) searchParams.set('pin', options.pin) if (options.quiet != null) searchParams.set('quiet', options.quiet) const { Hash } = await ky.post('object/put', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams, body: toFormData(buf) }).json() return new CID(Hash) } }) ================================================ FILE: src/object/stat.js ================================================ 'use strict' const { Buffer } = require('buffer') const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (cid, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${Buffer.isBuffer(cid) ? new CID(cid) : cid}`) let res try { res = await ky.post('object/stat', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() } catch (err) { if (err.name === 'TimeoutError') { err.message = `failed to get block for ${Buffer.isBuffer(cid) ? new CID(cid) : cid}: context deadline exceeded` } throw err } return res } }) ================================================ FILE: src/pin/add.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (paths, options) => { paths = Array.isArray(paths) ? paths : [paths] options = options || {} const searchParams = new URLSearchParams(options.searchParams) paths.forEach(path => searchParams.append('arg', `${path}`)) if (options.recursive != null) searchParams.set('recursive', options.recursive) const res = await ky.post('pin/add', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return (res.Pins || []).map(cid => ({ cid: new CID(cid) })) } }) ================================================ FILE: src/pin/index.js ================================================ 'use strict' module.exports = config => ({ add: require('./add')(config), rm: require('./rm')(config), ls: require('./ls')(config) }) ================================================ FILE: src/pin/ls.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const CID = require('cids') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * ls (path, options) { if (path && path.type) { options = path path = null } path = path || [] path = Array.isArray(path) ? path : [path] options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('stream', options.stream == null ? true : options.stream) path.forEach(p => searchParams.append('arg', `${p}`)) if (options.type) searchParams.set('type', options.type) const res = await ky.post('pin/ls', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const pin of ndjson(toIterable(res.body))) { if (pin.Keys) { // non-streaming response for (const cid of Object.keys(pin.Keys)) { yield { cid: new CID(cid), type: pin.Keys[cid].Type } } return } yield { cid: new CID(pin.Cid), type: pin.Type } } } }) ================================================ FILE: src/pin/rm.js ================================================ 'use strict' const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (path, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${path}`) if (options.recursive != null) searchParams.set('recursive', options.recursive) const res = await ky.post('pin/rm', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return (res.Pins || []).map(cid => ({ cid: new CID(cid) })) } }) ================================================ FILE: src/ping.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const configure = require('./lib/configure') const toIterable = require('stream-to-it/source') const toCamel = require('./lib/object-to-camel') module.exports = configure(({ ky }) => { return async function * ping (peerId, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${peerId}`) if (options.count != null) searchParams.set('count', options.count) const res = await ky.post('ping', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const chunk of ndjson(toIterable(res.body))) { yield toCamel(chunk) } } }) ================================================ FILE: src/pubsub/index.js ================================================ 'use strict' module.exports = config => ({ ls: require('./ls')(config), peers: require('./peers')(config), publish: require('./publish')(config), subscribe: require('./subscribe')(config), unsubscribe: require('./unsubscribe')(config) }) ================================================ FILE: src/pubsub/ls.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (options) => { options = options || {} const { Strings } = await ky.post('pubsub/ls', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return Strings || [] } }) ================================================ FILE: src/pubsub/peers.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (topic, options) => { if (!options && typeof topic === 'object') { options = topic topic = null } options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', topic) const { Strings } = await ky.post('pubsub/peers', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return Strings || [] } }) ================================================ FILE: src/pubsub/publish.js ================================================ 'use strict' const { Buffer } = require('buffer') const configure = require('../lib/configure') const encodeBuffer = require('../lib/encode-buffer-uri-component') module.exports = configure(({ ky }) => { return async (topic, data, options) => { options = options || {} data = Buffer.from(data) const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', topic) const res = await ky.post(`pubsub/pub?${searchParams}&arg=${encodeBuffer(data)}`, { timeout: options.timeout, signal: options.signal, headers: options.headers }).text() return res } }) ================================================ FILE: src/pubsub/subscribe.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const bs58 = require('bs58') const { Buffer } = require('buffer') const log = require('debug')('ipfs-http-client:pubsub:subscribe') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') const SubscriptionTracker = require('./subscription-tracker') module.exports = configure((config) => { const ky = config.ky const subsTracker = SubscriptionTracker.singleton() const publish = require('./publish')(config) return async (topic, handler, options) => { options = options || {} options.signal = subsTracker.subscribe(topic, handler, options.signal) const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', topic) if (options.discover != null) searchParams.set('discover', options.discover) let res // In Firefox, the initial call to fetch does not resolve until some data // is received. If this doesn't happen within 1 second send an empty message // to kickstart the process. const ffWorkaround = setTimeout(async () => { log(`Publishing empty message to "${topic}" to resolve subscription request`) try { await publish(topic, Buffer.alloc(0), options) } catch (err) { log('Failed to publish empty message', err) } }, 1000) try { res = await ky.post('pubsub/sub', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) } catch (err) { // Initial subscribe fail, ensure we clean up subsTracker.unsubscribe(topic, handler) throw err } clearTimeout(ffWorkaround) readMessages(ndjson(toIterable(res.body)), { onMessage: handler, onEnd: () => subsTracker.unsubscribe(topic, handler), onError: options.onError }) } }) async function readMessages (msgStream, { onMessage, onEnd, onError }) { onError = onError || log try { for await (const msg of msgStream) { try { onMessage({ from: bs58.encode(Buffer.from(msg.from, 'base64')).toString(), data: Buffer.from(msg.data, 'base64'), seqno: Buffer.from(msg.seqno, 'base64'), topicIDs: msg.topicIDs }) } catch (err) { err.message = `Failed to parse pubsub message: ${err.message}` onError(err, false, msg) // Not fatal } } } catch (err) { // FIXME: In testing with Chrome, err.type is undefined (should not be!) // Temporarily use the name property instead. if (err.type !== 'aborted' && err.name !== 'AbortError') { onError(err, true) // Fatal } } finally { onEnd() } } ================================================ FILE: src/pubsub/subscription-tracker.js ================================================ 'use strict' const AbortController = require('abort-controller') class SubscriptionTracker { constructor () { this._subs = new Map() } static singleton () { if (SubscriptionTracker.instance) return SubscriptionTracker.instance SubscriptionTracker.instance = new SubscriptionTracker() return SubscriptionTracker.instance } subscribe (topic, handler, signal) { const topicSubs = this._subs.get(topic) || [] if (topicSubs.find(s => s.handler === handler)) { throw new Error(`Already subscribed to ${topic} with this handler`) } // Create controller so a call to unsubscribe can cancel the request const controller = new AbortController() this._subs.set(topic, [{ handler, controller }].concat(topicSubs)) // If there is an external signal, forward the abort event if (signal) { signal.addEventListener('abort', () => this.unsubscribe(topic, handler)) } return controller.signal } unsubscribe (topic, handler) { const subs = this._subs.get(topic) || [] let unsubs if (handler) { this._subs.set(topic, subs.filter(s => s.handler !== handler)) unsubs = subs.filter(s => s.handler === handler) } else { this._subs.set(topic, []) unsubs = subs } unsubs.forEach(s => s.controller.abort()) } } module.exports = SubscriptionTracker ================================================ FILE: src/pubsub/unsubscribe.js ================================================ 'use strict' const configure = require('../lib/configure') const SubscriptionTracker = require('./subscription-tracker') module.exports = configure(({ ky }) => { const subsTracker = SubscriptionTracker.singleton() // eslint-disable-next-line require-await return async (topic, handler) => subsTracker.unsubscribe(topic, handler) }) ================================================ FILE: src/refs/index.js ================================================ 'use strict' const configure = require('../lib/configure') const { Buffer } = require('buffer') const CID = require('cids') const ndjson = require('iterable-ndjson') const toIterable = require('stream-to-it/source') const toCamel = require('../lib/object-to-camel') module.exports = config => { const refs = (configure(({ ky }) => { return async function * refs (args, options) { options = options || {} const searchParams = new URLSearchParams() if (options.format !== undefined) { searchParams.set('format', options.format) } if (options.edges !== undefined) { searchParams.set('edges', options.edges) } if (options.unique !== undefined) { searchParams.set('unique', options.unique) } if (options.recursive !== undefined) { searchParams.set('recursive', options.recursive) } if (options.maxDepth !== undefined) { searchParams.set('max-depth', options.maxDepth) } if (!Array.isArray(args)) { args = [args] } for (const arg of args) { searchParams.append('arg', `${Buffer.isBuffer(arg) ? new CID(arg) : arg}`) } const res = await ky.post('refs', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const file of ndjson(toIterable(res.body))) { yield toCamel(file) } } }))(config) refs.local = require('./local')(config) return refs } ================================================ FILE: src/refs/local.js ================================================ 'use strict' const configure = require('../lib/configure') const ndjson = require('iterable-ndjson') const toIterable = require('stream-to-it/source') const toCamel = require('../lib/object-to-camel') module.exports = configure(({ ky }) => { return async function * refsLocal (options) { options = options || {} const res = await ky.post('refs/local', { timeout: options.timeout, signal: options.signal, headers: options.headers }) for await (const file of ndjson(toIterable(res.body))) { yield toCamel(file) } } }) ================================================ FILE: src/repo/gc.js ================================================ 'use strict' const CID = require('cids') const ndjson = require('iterable-ndjson') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * gc (peerId, options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.streamErrors) searchParams.set('stream-errors', options.streamErrors) const res = await ky.post('repo/gc', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const gcResult of ndjson(toIterable(res.body))) { yield { err: gcResult.Error ? new Error(gcResult.Error) : null, cid: (gcResult.Key || {})['/'] ? new CID(gcResult.Key['/']) : null } } } }) ================================================ FILE: src/repo/index.js ================================================ 'use strict' module.exports = config => ({ gc: require('./gc')(config), stat: require('./stat')(config), version: require('./version')(config) }) ================================================ FILE: src/repo/stat.js ================================================ 'use strict' const Big = require('bignumber.js') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.sizeOnly) searchParams.set('size-only', options.sizeOnly) const res = await ky.post('repo/stat', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return { numObjects: new Big(res.NumObjects), repoSize: new Big(res.RepoSize), repoPath: res.RepoPath, version: res.Version, storageMax: new Big(res.StorageMax) } } }) ================================================ FILE: src/repo/version.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.sizeOnly) searchParams.set('size-only', options.sizeOnly) const res = await ky.post('repo/version', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res.Version } }) ================================================ FILE: src/resolve.js ================================================ 'use strict' const configure = require('./lib/configure') module.exports = configure(({ ky }) => { return async (path, options) => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) searchParams.set('arg', `${path}`) if (options.cidBase) searchParams.set('cid-base', options.cidBase) if (options.dhtRecordCount) searchParams.set('dht-record-count', options.dhtRecordCount) if (options.dhtTimeout) searchParams.set('dht-timeout', options.dhtTimeout) if (options.recursive != null) searchParams.set('recursive', options.recursive) const res = await ky.post('resolve', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res.Path } }) ================================================ FILE: src/stats/bw.js ================================================ 'use strict' const ndjson = require('iterable-ndjson') const Big = require('bignumber.js') const configure = require('../lib/configure') const toIterable = require('stream-to-it/source') module.exports = configure(({ ky }) => { return async function * bw (options) { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.interval) searchParams.set('interval', options.interval) if (options.peer) searchParams.set('peer', options.peer) if (options.poll != null) searchParams.set('poll', options.poll) if (options.proto) searchParams.set('proto', options.proto) const res = await ky.post('stats/bw', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }) for await (const stats of ndjson(toIterable(res.body))) { yield { totalIn: new Big(stats.TotalIn), totalOut: new Big(stats.TotalOut), rateIn: new Big(stats.RateIn), rateOut: new Big(stats.RateOut) } } } }) ================================================ FILE: src/stats/index.js ================================================ 'use strict' module.exports = config => ({ bitswap: require('../bitswap/stat')(config), bw: require('./bw')(config), repo: require('../repo/stat')(config) }) ================================================ FILE: src/stop.js ================================================ 'use strict' const configure = require('./lib/configure') module.exports = configure(({ ky }) => { return options => { options = options || {} return ky.post('shutdown', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).text() } }) ================================================ FILE: src/swarm/addrs.js ================================================ 'use strict' const multiaddr = require('multiaddr') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async options => { options = options || {} const res = await ky.post('swarm/addrs', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return Object.keys(res.Addrs).map(id => ({ id, addrs: (res.Addrs[id] || []).map(a => multiaddr(a)) })) } }) ================================================ FILE: src/swarm/connect.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (addrs, options) => { addrs = Array.isArray(addrs) ? addrs : [addrs] options = options || {} const searchParams = new URLSearchParams(options.searchParams) addrs.forEach(addr => searchParams.append('arg', addr)) const res = await ky.post('swarm/connect', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res.Strings || [] } }) ================================================ FILE: src/swarm/disconnect.js ================================================ 'use strict' const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async (addrs, options) => { addrs = Array.isArray(addrs) ? addrs : [addrs] options = options || {} const searchParams = new URLSearchParams(options.searchParams) addrs.forEach(addr => searchParams.append('arg', `${addr}`)) const res = await ky.post('swarm/disconnect', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return res.Strings || [] } }) ================================================ FILE: src/swarm/index.js ================================================ 'use strict' module.exports = config => ({ addrs: require('./addrs')(config), connect: require('./connect')(config), disconnect: require('./disconnect')(config), localAddrs: require('./localAddrs')(config), peers: require('./peers')(config) }) ================================================ FILE: src/swarm/localAddrs.js ================================================ 'use strict' const multiaddr = require('multiaddr') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.id != null) searchParams.append('id', options.id) const res = await ky.post('swarm/addrs/local', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return (res.Strings || []).map(a => multiaddr(a)) } }) ================================================ FILE: src/swarm/peers.js ================================================ 'use strict' const multiaddr = require('multiaddr') const configure = require('../lib/configure') module.exports = configure(({ ky }) => { return async options => { options = options || {} const searchParams = new URLSearchParams(options.searchParams) if (options.direction != null) searchParams.append('direction', options.direction) if (options.latency != null) searchParams.append('latency', options.latency) if (options.streams != null) searchParams.append('streams', options.streams) if (options.verbose != null) searchParams.append('verbose', options.verbose) const res = await ky.post('swarm/peers', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams }).json() return (res.Peers || []).map(peer => { const info = {} try { info.addr = multiaddr(peer.Addr) info.peer = peer.Peer } catch (error) { info.error = error info.rawPeerInfo = peer } if (peer.Muxer) { info.muxer = peer.Muxer } if (peer.Latency) { info.latency = peer.Latency } if (peer.Streams) { info.streams = peer.Streams } if (peer.Direction != null) { info.direction = peer.Direction } return info }) } }) ================================================ FILE: src/update.js ================================================ 'use strict' const configure = require('./lib/configure') module.exports = configure(({ ky }) => { return options => { options = options || {} return ky.post('update', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).text() } }) ================================================ FILE: src/version.js ================================================ 'use strict' const configure = require('./lib/configure') const toCamel = require('./lib/object-to-camel') module.exports = configure(({ ky }) => { return async options => { options = options || {} const res = await ky.post('version', { timeout: options.timeout, signal: options.signal, headers: options.headers, searchParams: options.searchParams }).json() return toCamel(res) } }) ================================================ FILE: test/commands.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const f = require('./utils/factory')() describe('.commands', function () { this.timeout(60 * 1000) let ipfs before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) it('lists commands', async () => { const res = await ipfs.commands() expect(res).to.exist() }) }) ================================================ FILE: test/constructor.spec.js ================================================ /* eslint-env mocha, browser */ 'use strict' const multiaddr = require('multiaddr') const { expect } = require('interface-ipfs-core/src/utils/mocha') const f = require('./utils/factory')() const ipfsClient = require('../src/index.js') describe('ipfs-http-client constructor tests', () => { describe('parameter permuations', () => { it('none', () => { const ipfs = ipfsClient() if (typeof self !== 'undefined') { const { hostname, port } = self.location expectConfig(ipfs, { host: hostname, port }) } else { expectConfig(ipfs, {}) } }) it('opts', () => { const host = 'wizard.world' const port = '999' const protocol = 'https' const ipfs = ipfsClient({ host, port, protocol }) expectConfig(ipfs, { host, port, protocol }) }) it('multiaddr dns4 string (implicit http)', () => { const host = 'foo.com' const port = '1001' const protocol = 'http' // default to http if not specified in multiaddr const addr = `/dns4/${host}/tcp/${port}` const ipfs = ipfsClient(addr) expectConfig(ipfs, { host, port, protocol }) }) it('multiaddr dns4 string (explicit https)', () => { const host = 'foo.com' const port = '1001' const protocol = 'https' const addr = `/dns4/${host}/tcp/${port}/${protocol}` const ipfs = ipfsClient(addr) expectConfig(ipfs, { host, port, protocol }) }) it('multiaddr ipv4 string (implicit http)', () => { const host = '101.101.101.101' const port = '1001' const protocol = 'http' const addr = `/ip4/${host}/tcp/${port}` const ipfs = ipfsClient(addr) expectConfig(ipfs, { host, port, protocol }) }) it('multiaddr ipv4 string (explicit https)', () => { const host = '101.101.101.101' const port = '1001' const protocol = 'https' const addr = `/ip4/${host}/tcp/${port}/${protocol}` const ipfs = ipfsClient(addr) expectConfig(ipfs, { host, port, protocol }) }) it('multiaddr instance', () => { const host = 'ace.place' const port = '1001' const addr = multiaddr(`/dns4/${host}/tcp/${port}`) const ipfs = ipfsClient(addr) expectConfig(ipfs, { host, port }) }) it('host and port strings', () => { const host = '1.1.1.1' const port = '9999' const ipfs = ipfsClient({ host, port }) expectConfig(ipfs, { host, port }) }) it('host, port and api path', () => { const host = '10.100.100.255' const port = '9999' const apiPath = '/future/api/v1/' const ipfs = ipfsClient({ host, port, apiPath }) expectConfig(ipfs, { host, port, apiPath: apiPath.slice(0, -1) }) }) it('throws on invalid multiaddr', () => { expect(() => ipfsClient('/dns4')).to.throw('invalid address') expect(() => ipfsClient('/hello')).to.throw('no protocol with name') expect(() => ipfsClient('/dns4/ipfs.io')).to.throw() }) }) describe('integration', () => { let ipfsd before(async function () { this.timeout(60 * 1000) // slow CI ipfsd = await f.spawn() }) after(() => f.clean()) it('can connect to an ipfs http api', async () => { await clientWorks(ipfsClient(ipfsd.apiAddr)) }) }) }) async function clientWorks (client) { const id = await client.id() expect(id).to.have.a.property('id') expect(id).to.have.a.property('publicKey') } function expectConfig (ipfs, { host, port, protocol, apiPath }) { const conf = ipfs.getEndpointConfig() expect(conf.host).to.be.oneOf([host, 'localhost', '']) expect(conf.port).to.be.oneOf([port, '5001', '80']) expect(conf.protocol).to.equal(protocol || 'http') expect(conf['api-path']).to.equal(apiPath || '/api/v0') } ================================================ FILE: test/custom-headers.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { isNode } = require('ipfs-utils/src/env') const { expect } = require('interface-ipfs-core/src/utils/mocha') const ipfsClient = require('../src') describe('custom headers', function () { // do not test in browser if (!isNode) { return } let ipfs // initialize ipfs with custom headers before(() => { ipfs = ipfsClient({ host: 'localhost', port: 6001, protocol: 'http', headers: { authorization: 'Bearer ' + 'YOLO' } }) }) it('are supported', (done) => { // spin up a test http server to inspect the requests made by the library const server = require('http').createServer((req, res) => { req.on('data', () => {}) req.on('end', () => { res.writeHead(200) res.write(JSON.stringify({})) res.end() // ensure custom headers are present expect(req.headers.authorization).to.equal('Bearer ' + 'YOLO') server.close() done() }) }) server.listen(6001, () => { ipfs.id((err, res) => { if (err) { throw err } // this call is used to test that headers are being sent. }) }) }) }) ================================================ FILE: test/dag.spec.js ================================================ /* eslint-env mocha */ /* eslint max-nested-callbacks: ["error", 8] */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const { DAGNode } = require('ipld-dag-pb') const CID = require('cids') const f = require('./utils/factory')() let ipfs describe('.dag', function () { this.timeout(20 * 1000) before(async function () { ipfs = (await f.spawn()).api }) after(() => f.clean()) it('should be able to put and get a DAG node with format dag-pb', async () => { const data = Buffer.from('some data') const node = new DAGNode(data) let cid = await ipfs.dag.put(node, { format: 'dag-pb', hashAlg: 'sha2-256' }) cid = cid.toV0() expect(cid.codec).to.equal('dag-pb') cid = cid.toBaseEncodedString('base58btc') // expect(cid).to.equal('bafybeig3t3eugdchignsgkou3ly2mmy4ic4gtfor7inftnqn3yq4ws3a5u') expect(cid).to.equal('Qmd7xRhW5f29QuBFtqu3oSD27iVy35NRB91XFjmKFhtgMr') const result = await ipfs.dag.get(cid) expect(result.value.Data).to.deep.equal(data) }) it('should be able to put and get a DAG node with format dag-cbor', async () => { const cbor = { foo: 'dag-cbor-bar' } let cid = await ipfs.dag.put(cbor, { format: 'dag-cbor', hashAlg: 'sha2-256' }) expect(cid.codec).to.equal('dag-cbor') cid = cid.toBaseEncodedString('base32') expect(cid).to.equal('bafyreic6f672hnponukaacmk2mmt7vs324zkagvu4hcww6yba6kby25zce') const result = await ipfs.dag.get(cid) expect(result.value).to.deep.equal(cbor) }) it('should callback with error when missing DAG resolver for multicodec from requested CID', async () => { const block = await ipfs.block.put(Buffer.from([0, 1, 2, 3]), { cid: new CID('z8mWaJ1dZ9fH5EetPuRsj8jj26pXsgpsr') }) await expect(ipfs.dag.get(block.cid)).to.be.rejectedWith('Missing IPLD format "git-raw"') }) }) ================================================ FILE: test/diag.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const platform = require('browser-process-platform') const f = require('./utils/factory')() describe('.diag', function () { this.timeout(50 * 1000) // go-ipfs does not support these on Windows if (platform === 'win32') { return } let ipfs before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) describe('api API', () => { // Disabled in go-ipfs 0.4.10 it.skip('.diag.net', async () => { const res = await ipfs.diag.net() expect(res).to.exist() }) it('.diag.sys', async () => { const res = await ipfs.diag.sys() expect(res).to.exist() expect(res).to.have.a.property('memory') expect(res).to.have.a.property('diskinfo') }) it('.diag.cmds', async () => { const res = await ipfs.diag.cmds() expect(res).to.exist() }) }) }) ================================================ FILE: test/endpoint-config.spec.js ================================================ /* eslint-env mocha */ /* eslint max-nested-callbacks: ["error", 8] */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const ipfsClient = require('../src') describe('.getEndpointConfig', () => { it('should return the endpoint configuration', function () { const ipfs = ipfsClient('https://127.0.0.1:5501/ipfs/api/') const endpoint = ipfs.getEndpointConfig() expect(endpoint.host).to.equal('127.0.0.1') expect(endpoint.protocol).to.equal('https') expect(endpoint['api-path']).to.equal('/ipfs/api') expect(endpoint.port).to.equal('5501') }) }) ================================================ FILE: test/exports.spec.js ================================================ /* eslint-env mocha, browser */ 'use strict' const CID = require('cids') const multiaddr = require('multiaddr') const multibase = require('multibase') const multicodec = require('multicodec') const multihash = require('multihashes') const { expect } = require('interface-ipfs-core/src/utils/mocha') const IpfsHttpClient = require('../') describe('exports', () => { it('should export the expected types and utilities', () => { expect(IpfsHttpClient.Buffer).to.equal(Buffer) expect(IpfsHttpClient.CID).to.equal(CID) expect(IpfsHttpClient.multiaddr).to.equal(multiaddr) expect(IpfsHttpClient.multibase).to.equal(multibase) expect(IpfsHttpClient.multicodec).to.equal(multicodec) expect(IpfsHttpClient.multihash).to.equal(multihash) }) }) ================================================ FILE: test/files-mfs.spec.js ================================================ /* eslint-env mocha */ /* eslint max-nested-callbacks: ["error", 8] */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const loadFixture = require('aegir/fixtures') const mh = require('multihashes') const all = require('it-all') const pipe = require('it-pipe') const { TimeoutError } = require('ky-universal') const f = require('./utils/factory')() const testfile = loadFixture('test/fixtures/testfile.txt') // TODO: Test against all algorithms Object.keys(mh.names) // This subset is known to work with both go-ipfs and js-ipfs as of 2017-09-05 const HASH_ALGS = [ 'sha1', 'sha2-256', 'sha2-512', // 'keccak-224', // go throws 'keccak-256', // 'keccak-384', // go throws 'keccak-512' ] describe('.files (the MFS API part)', function () { this.timeout(20 * 1000) let ipfs const expectedMultihash = 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP' before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) it('.add file for testing', async () => { const res = await all(ipfs.add(testfile)) expect(res).to.have.length(1) expect(res[0].cid.toString()).to.equal(expectedMultihash) expect(res[0].path).to.equal(expectedMultihash) }) it('.add with Buffer module', async () => { const { Buffer } = require('buffer') const expectedBufferMultihash = 'QmWfVY9y3xjsixTgbd9AorQxH7VtMpzfx2HaWtsoUYecaX' const file = Buffer.from('hello') const res = await all(ipfs.add(file)) expect(res).to.have.length(1) expect(res[0].cid.toString()).to.equal(expectedBufferMultihash) expect(res[0].path).to.equal(expectedBufferMultihash) }) it('.add with empty path and buffer content', async () => { const expectedHash = 'QmWfVY9y3xjsixTgbd9AorQxH7VtMpzfx2HaWtsoUYecaX' const content = Buffer.from('hello') const res = await all(ipfs.add([{ path: '', content }])) expect(res).to.have.length(1) expect(res[0].cid.toString()).to.equal(expectedHash) expect(res[0].path).to.equal(expectedHash) }) it('.add with cid-version=1 and raw-leaves=false', async () => { const expectedCid = 'bafybeifogzovjqrcxvgt7g36y7g63hvwvoakledwk4b2fr2dl4wzawpnny' const options = { cidVersion: 1, rawLeaves: false } const res = await all(ipfs.add(testfile, options)) expect(res).to.have.length(1) expect(res[0].cid.toString()).to.equal(expectedCid) expect(res[0].path).to.equal(expectedCid) }) it('.add with only-hash=true', async () => { const content = String(Math.random() + Date.now()) const files = await all(ipfs.add(Buffer.from(content), { onlyHash: true })) expect(files).to.have.length(1) // 'ipfs.object.get()' should timeout because content wasn't actually added return expect(ipfs.object.get(files[0].cid, { timeout: 2000 })) .to.be.rejectedWith(TimeoutError) }) it('.add with options', async () => { const res = await all(ipfs.add(testfile, { pin: false })) expect(res).to.have.length(1) expect(res[0].cid.toString()).to.equal(expectedMultihash) expect(res[0].path).to.equal(expectedMultihash) }) it('.add pins by default', async () => { const newContent = Buffer.from(String(Math.random())) const initialPins = await all(ipfs.pin.ls()) await all(ipfs.add(newContent)) const pinsAfterAdd = await all(ipfs.pin.ls()) expect(pinsAfterAdd.length).to.eql(initialPins.length + 1) }) it('.add with pin=false', async () => { const newContent = Buffer.from(String(Math.random())) const initialPins = await all(ipfs.pin.ls()) await all(ipfs.add(newContent, { pin: false })) const pinsAfterAdd = await all(ipfs.pin.ls()) expect(pinsAfterAdd.length).to.eql(initialPins.length) }) HASH_ALGS.forEach((name) => { it(`.add with hash=${name} and raw-leaves=false`, async () => { const content = String(Math.random() + Date.now()) const file = { path: content + '.txt', content: Buffer.from(content) } const options = { hashAlg: name, rawLeaves: false } const res = await all(ipfs.add([file], options)) expect(res).to.have.length(1) const { cid } = res[0] expect(mh.decode(cid.multihash).name).to.equal(name) }) }) it('.add file with progress option', async () => { let progress let progressCount = 0 const progressHandler = (p) => { progressCount += 1 progress = p } const res = await all(ipfs.add(testfile, { progress: progressHandler })) expect(res).to.have.length(1) expect(progress).to.be.equal(testfile.byteLength) expect(progressCount).to.be.equal(1) }) it('.add big file with progress option', async () => { let progress = 0 let progressCount = 0 const progressHandler = (p) => { progressCount += 1 progress = p } // TODO: needs to be using a big file const res = await all(ipfs.add(testfile, { progress: progressHandler })) expect(res).to.have.length(1) expect(progress).to.be.equal(testfile.byteLength) expect(progressCount).to.be.equal(1) }) it('.add directory with progress option', async () => { let progress = 0 let progressCount = 0 const progressHandler = (p) => { progressCount += 1 progress = p } // TODO: needs to be using a directory const res = await all(ipfs.add(testfile, { progress: progressHandler })) expect(res).to.have.length(1) expect(progress).to.be.equal(testfile.byteLength) expect(progressCount).to.be.equal(1) }) it('.add without progress options', async () => { const res = await all(ipfs.add(testfile)) expect(res).to.have.length(1) }) HASH_ALGS.forEach((name) => { it(`.add with hash=${name} and raw-leaves=false`, async () => { const content = String(Math.random() + Date.now()) const file = { path: content + '.txt', content: Buffer.from(content) } const options = { hashAlg: name, rawLeaves: false } const res = await all(ipfs.add([file], options)) expect(res).to.have.length(1) const { cid } = res[0] expect(mh.decode(cid.multihash).name).to.equal(name) }) }) it('.add with object chunks and iterable content', async () => { const expectedCid = 'QmRf22bZar3WKmojipms22PkXH1MZGmvsqzQtuSvQE3uhm' const res = await pipe( [{ content: [Buffer.from('test')] }], ipfs.add, all ) expect(res).to.have.length(1) res[0].cid = res[0].cid.toString() expect(res[0]).to.deep.equal({ path: expectedCid, cid: expectedCid, size: 12 }) }) it('.add with iterable', async () => { const expectedCid = 'QmRf22bZar3WKmojipms22PkXH1MZGmvsqzQtuSvQE3uhm' const res = await all(ipfs.add([Buffer.from('test')])) expect(res).to.have.length(1) res[0].cid = res[0].cid.toString() expect(res[0]).to.deep.equal({ path: expectedCid, cid: expectedCid, size: 12 }) }) it('files.mkdir', async () => { await ipfs.files.mkdir('/test-folder') }) it('files.flush', async () => { await ipfs.files.flush('/') }) it('files.cp', async () => { const folder = `/test-folder-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.cp([ '/ipfs/Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', `${folder}/test-file-${Math.random()}` ]) }) it('files.cp with non-array arguments', async () => { const folder = `/test-folder-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.cp( '/ipfs/Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', `${folder}/test-file-${Math.random()}` ) }) it('files.mv', async () => { const folder = `/test-folder-${Math.random()}` const source = `${folder}/test-file-${Math.random()}` const dest = `${folder}/test-file-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.cp( '/ipfs/Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', source ) await ipfs.files.mv([ source, dest ]) }) it('files.mv with non-array arguments', async () => { const folder = `/test-folder-${Math.random()}` const source = `${folder}/test-file-${Math.random()}` const dest = `${folder}/test-file-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.cp( '/ipfs/Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', source ) await ipfs.files.mv( source, dest ) }) it('files.ls', async () => { const folder = `/test-folder-${Math.random()}` const file = `${folder}/test-file-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.write(file, Buffer.from('Hello, world'), { create: true }) const files = await all(ipfs.files.ls(folder)) expect(files.length).to.equal(1) }) it('files.ls mfs root by default', async () => { const folder = `test-folder-${Math.random()}` await ipfs.files.mkdir(`/${folder}`) const files = await all(ipfs.files.ls()) expect(files.find(file => file.name === folder)).to.be.ok() }) it('files.write', async () => { await ipfs.files.write('/test-folder/test-file-2.txt', Buffer.from('hello world'), { create: true }) const buf = Buffer.concat(await all(ipfs.files.read('/test-folder/test-file-2.txt'))) expect(buf.toString()).to.be.equal('hello world') }) it('files.write without options', async () => { await ipfs.files.write('/test-folder/test-file-2.txt', Buffer.from('hello world')) const buf = Buffer.concat(await all(ipfs.files.read('/test-folder/test-file-2.txt'))) expect(buf.toString()).to.be.equal('hello world') }) it('files.stat', async () => { const folder = `/test-folder-${Math.random()}` const file = `${folder}/test-file-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.write(file, testfile, { create: true }) const stats = await ipfs.files.stat(file) stats.cid = stats.cid.toString() expect(stats).to.deep.equal({ cid: 'QmQhouoDPAnzhVM148yCa9CbUXK65wSEAZBtgrLGHtmdmP', size: 12, cumulativeSize: 70, blocks: 1, type: 'file', withLocality: false }) }) it('files.stat file that does not exist()', async () => { await expect(ipfs.files.stat('/test-folder/does-not-exist()')).to.be.rejectedWith({ code: 0, type: 'error' }) }) it('files.read', async () => { const folder = `/test-folder-${Math.random()}` const file = `${folder}/test-file-${Math.random()}` await ipfs.files.mkdir(folder) await ipfs.files.write(file, testfile, { create: true }) const buf = Buffer.concat(await all(ipfs.files.read(file))) expect(Buffer.from(buf)).to.deep.equal(testfile) }) it('files.rm without options', async () => { await ipfs.files.rm('/test-folder/test-file-2.txt') }) it('files.rm', async () => { await ipfs.files.rm('/test-folder', { recursive: true }) }) }) ================================================ FILE: test/fixtures/.gitattributes ================================================ * -text ================================================ FILE: test/fixtures/15mb.random ================================================ [File too large to display: 14.3 MB] ================================================ FILE: test/fixtures/r-config.json ================================================ {} ================================================ FILE: test/fixtures/ssl/cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIDOzCCAiMCCQCVqVeRIp9pFDANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJV UzENMAsGA1UECAwEVXRhaDEOMAwGA1UEBwwFUHJvdm8xIzAhBgNVBAoMGkFDTUUg U2lnbmluZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0x ODA4MTQyMDEzNTdaFw0xOTEyMjcyMDEzNTdaMFgxCzAJBgNVBAYTAlVTMQ0wCwYD VQQIDARVdGFoMQ4wDAYDVQQHDAVQcm92bzEWMBQGA1UECgwNQUNNRSBUZWNoIElu YzESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEA6x6mTXV+rC35QW/sPutT1O1cugtnw+UsJx7EGgzyjh7EoXE3gb7sO96P tOI5zknb0vecckbiVkesmLnAs2iNa1u9EiRr6WHdc+1MfUCxyHRfP731vRZyo0kx bSXerE0qZ2N3M1XyndZF7VMthKDKIg0ZR0TvdjwLqyLYEHAnRBhJLRS0Oy0fC6Of VWCO3gIuk1HkTXH+/ZMA/obqrtlisxY85mMdlRz+1PNdZBMf+NxmrXN59uq+JqUu 8/v1oQ8jH2iU9IWeqyawHDEvPW3aDorfaWGyats5Xd3cT2Ph4xF9tBLT+3PDGU8c oBmTHWDenYn+TCkCseayo1JCO5igJQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCr R7eZxicHjJoRcbsPBDQpzx9uSux3uvpN93pyJrXXHiil/5SE7CYeDqv5+nV2p6HA 6KONUAmpId0iHAEi9u+0/LgPWyYQMzT3sfBhkO8RRaMYI87VuKbk5PFmlZbD843+ Qmg3Se2F7BDnTf88xA6QWR4DCejy+ZHfDRFrh3xfFl4tX1UNgqiTGfjPCzblhWx9 ygzlT+flN2j3NkAlhUEV89pnH4EQWILePMTT4wh2XOQj1VFJ+2ATojHFVUTtNWAJ xrY/Q9cMYsZ++I8i9bHMZoyc1bSUd5CNFpQdfjVzlgMPT9Jj/fzWIQz+wq0KeRLI dLWsa2MZr0GZnTU39YwH -----END CERTIFICATE----- ================================================ FILE: test/fixtures/ssl/privkey.pem ================================================ -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA6x6mTXV+rC35QW/sPutT1O1cugtnw+UsJx7EGgzyjh7EoXE3 gb7sO96PtOI5zknb0vecckbiVkesmLnAs2iNa1u9EiRr6WHdc+1MfUCxyHRfP731 vRZyo0kxbSXerE0qZ2N3M1XyndZF7VMthKDKIg0ZR0TvdjwLqyLYEHAnRBhJLRS0 Oy0fC6OfVWCO3gIuk1HkTXH+/ZMA/obqrtlisxY85mMdlRz+1PNdZBMf+NxmrXN5 9uq+JqUu8/v1oQ8jH2iU9IWeqyawHDEvPW3aDorfaWGyats5Xd3cT2Ph4xF9tBLT +3PDGU8coBmTHWDenYn+TCkCseayo1JCO5igJQIDAQABAoIBAH5fbfFqOpie6T8T wj4bTGbA4bsZkD9JeU7ZiXubA/ABd5xyduwky2JugH0vrvRC3IVrE0qU8OiBA7Le /EUx5/kRSPFsZBf/wwChRiB4WlYsvllLZ76nRxyepZNN7H5dx3Hkk1gjVREi71jd ATUtGxfsRG77DV5WbcshIlLLhT9iaohsalmClAFBmwhqnRMvOXHiQyRbvB0fOX08 uVlObOqo9jLB8N5C/ux+wFEP4wi/AxVqs9ih7Ss7T7+pmOCVWhOnbYcoY2jdaJ11 iLK4F3rv/jQ82OwUpzrWsPedmZUzlOO8xdV3b8hOcPHs/BKvYed7aHSn6b5eVKKT zT8vQoECgYEA+K9pvw9K/7+F810MHG+nZ0gtVWmXJp49jB7zQ6QMIex2sUajY2y9 bEJX8T6rdu3qd+HYU4zl3qt+MUnsVQEBNkLPAn3od0qIWXxu1SL2GF8SDV1xJWK1 Fp0YDe9blaz1JsmSgieNcSoSwqE2V97Wfd/m+EUfyhQt9HX55H5UgAUCgYEA8gkW 0xZKuHhBMYpcES2P5H5q6HN2fcEQycMuS3agAOhrFPYUT1DVNhbfNVmbOvL2NRWI hXixo5SkuEuq2fjmEoeLPTmeKO5LM4IVqovWCYomSftKDpzw4HRn2jvKzi2+mg8J qktIMqRqHu/O1NUIsszCIu4c5DzUdhr4N7GXOaECgYAEd1oF1Wd6al0kfsJN7G9s Om6d/xR43BSs5I1n5JVXMqD7FBKxIW3ReOuNaJu5uhIg7wxsi7ZBJoFQr0wwRqFX 8SE4oTxAkDUcrlBrQYJ785Embkwu6LPp4Q5iia7yZDXO6YXZEo7GvoOxvSV1tInT nubOBKfKgExG/KttQBuSZQKBgAzYOqPdLP35M8yDQTuQJXDE3LuVVRZ7Zn6uowhS NU+XBgfIv28uJQKH2DSmmrxYJITQrbwXmaXKv6sgKOMEeIFHPDZ1llUpwEftgWTZ ovRCpqGKenWoEoh25QQJ5Eto1hKq9aJZ+GznmNIne9yDqcCDaVIdPN9H8yaJa97Y x+PBAoGAOiK6xAbPyJSKDSTGZzdv8+yeOdNeJjRHxKJs+4YsDchmdumrqow83DBP 7ulIJD9pcmsWj+8fntMcsTX5mvzJd5LsKc7Maa5/LtitsLsynu78QFg4Njj8sAKn 3991i8J98DZ9zqmkxJJhGwstCHG+c+Q7lA6kZ1UdbWJwYwIHjAs= -----END RSA PRIVATE KEY----- ================================================ FILE: test/fixtures/test-folder/.hiddenTest.txt ================================================ Aha! You found me! ================================================ FILE: test/fixtures/test-folder/add ================================================ 'use strict' const ipfs = require('../src')('localhost', 5001) const f1 = 'Hello' const f2 = 'World' ipfs.add([new Buffer(f1), new Buffer(f2)], function (err, res) { if (err || !res) return console.log(err) for (let i = 0; i < res.length; i++) { console.log(res[i]) } }) ipfs.add(['./files/hello.txt', './files/ipfs.txt'], function (err, res) { if (err || !res) return console.log(err) for (let i = 0; i < res.length; i++) { console.log(res[i]) } }) ================================================ FILE: test/fixtures/test-folder/cat ================================================ 'use strict' const ipfs = require('../src')('localhost', 5001) const hash = [ 'QmdFyxZXsFiP4csgfM5uPu99AvFiKH62CSPDw5TP92nr7w', 'QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu' ] ipfs.cat(hash, function (err, res) { if (err || !res) return console.log(err) if (res.readable) { res.pipe(process.stdout) } else { console.log(res) } }) ================================================ FILE: test/fixtures/test-folder/files/hello.txt ================================================ Hello ================================================ FILE: test/fixtures/test-folder/files/ipfs.txt ================================================ IPFS ================================================ FILE: test/fixtures/test-folder/ipfs-add ================================================ #!/usr/bin/env node 'use strict' const ipfs = require('../src')('localhost', 5001) const files = process.argv.slice(2) ipfs.add(files, {recursive: true}, function (err, res) { if (err || !res) return console.log(err) for (let i = 0; i < res.length; i++) { console.log('added', res[i].Hash, res[i].Name) } }) ================================================ FILE: test/fixtures/test-folder/ls ================================================ 'use strict' const ipfs = require('../src')('localhost', 5001) const hash = ['QmdbHK6gMiecyjjSoPnfJg6iKMF7v6E2NkoBgGpmyCoevh'] ipfs.ls(hash, function (err, res) { if (err || !res) return console.log(err) res.Objects.forEach(function (node) { console.log(node.Hash) console.log('Links [%d]', node.Links.length) node.Links.forEach(function (link, i) { console.log('[%d]', i, link) }) }) }) ================================================ FILE: test/fixtures/test-folder/version ================================================ 'use strict' const ipfs = require('../src')('localhost', 5001) ipfs.commands(function (err, res) { if (err) throw err console.log(res) }) ================================================ FILE: test/fixtures/testconfig.json ================================================ { "test": "beep boop", "Addresses": { "API": "/ip4/127.0.0.1/tcp/5001", "Gateway": "/ip4/127.0.0.1/tcp/8080", "Swarm": [ "/ip4/0.0.0.0/tcp/4001" ] }, "Bootstrap": [ "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", "/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z", "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLpPVmHKQ4XTPdz8tjDFgdeRFkpV8JgYq8JVJ69RrZm", "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu", "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64", "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd", "/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", "/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx" ], "Datastore": { "Path": "/home/krl/.ipfs/datastore", "Type": "leveldb" }, "Discovery": { "MDNS": { "Enabled": false, "Interval": 0 } }, "Gateway": { "RootRedirect": "", "Writable": false }, "Identity": { "PeerID": "QmTF1MqD5hFgRztwxeosess2S1PpSm6fpy2jt15gZjr39c", "PrivKey": "CAASqxIwggknAgEAAoICAQC8RXr0/XtKFAn5ReHm0AoUQYpMxckyISsdKkDiGTXQW6h3IuU7kypoeSUNPerp+/+dJCXe5dY+GnT9jRXbZBUfVWYuEnMbCCnGHhi28wD71TYgfLuJlZjRKy1eBIz57bOtAVY8CnLM4WPLQLhcwgSZ1G83D6sRTKNOOqZMf37V/I1dxDg2kqi0JujBodlIP0WjDyBJvtPfLUSoytYznIOmJfi/K5ofPm54y5zX81lwuRGAdCXDIwCwcIwFY2EC6gWVECDLTTBnr7ZXITNtTBOWx6tIEmTF8K/hQN3KzazVEkYn7p4r0QL4zJnQCBIr09HhvOuNTK9XdqBeQJsVVm2uM+//acgdlYS8V+F8FQQHCihsaKAEYeDSGuKuzEbLohc8donWy+3k5jn/SwY8jJLaXtBJpux1sGdRG+ko5mFdXViII5e8gbiSdC1JiDKdeYmUSAm27IvuGay4FplK8wczZrComdsS3sxpSvTrS6KulNv7WTZEKPAWtMFGX+58krOpDgOkKg8IvwucmXsrYD5Q+razorP3v4QAVhouhxQNKzVpmOYZemOeJrTf5smUBjTGLIp343N36kWqZVp+bqTjl8x5uRbgn/wthDWHsyV81M6OwCCPtiKLJMKkztlArFnnT5sZ3aG7sFj4AgnxcqHHDo4nUAI+t4tOb9WxcAMR2wIDAQABAoICAG7OxetjNSkIWkZoJujeENCTMn60+hGTC/kCYWWxSMb061YTJ6/EkfUjN/dvNc+5DVzDZbamt02d7LU+UFrrsLLcZGNBYJXMXCnKlOk4ZJ/TgSPlxcrYTTTuoKjxLLf8ev+cBdEYpTCIh1+dG+UcG/Ed4scZZams3YCxbCch8tim590EG8Gi18AQFnXAeE3ZT3cE9A/zTGfSENL3btK5j5I+TwTU+MTizcoyrIE9LKr3gaGEuqT6+PDfjMmvD+3TJq3w7Bw7tf2QoVTuqYHugKkBo4Grsbv+SMXek6tFGi/drYTbICTRw1oDsZOK7Ib3CFRACLMFKz3jB8fxZlVMpQgawiyeBPx0Z4xf6+OG0yUGxGUKBcuATzON29NJWplZxPtfClCozdsm/3VCCPe7ruDUoDEWobhvKh3Ax+8Cdvt9ajoon6qNK3QgJqrm8Xc50MtjaPhZASGw1YweOfxAeQRHVYAJHrkBwblNzOnCMT8F4uKpeI4C9rUjKTOJu53qV1shwIM+zUGtmRvJUTKA15twqnntQrkhO990Dtl3tON/yx/D2ilLYCVuEPqfmW5/xiOrabgS1OlXLZ7teat85JOyqgZ1R/uxFzQljRwVsnCG6DlobkGHbnKwHq4zyV5UXZUDTM1QAgw6ksmkDWOQ9iYJrTk7UTmnNeAb0g4G2tJhAoIBAQDyT8qoPmZkONFktdnUUnjAeuNZ55b2u7pBq+1b2N4ZzhZQjCRCJwK3zxDYpoxWmH9etJcvv1RvZRaWngr9vECMzUEJmklZlCpW2fdxhvprABHaywklFt2heOo8krmfvbZIVMQ6Q9xWRz5sfiV+k3C8q2Y6RIMLuGquYzk8o2pAPG5aD7gHxIeq4JRGCXyJyovgX2AOZmtZe9ttc+dhxKm71V8bI0e88JrusKeapUnVJUm8qgP6PBg+F8MZh1U3kjHczKeB1ND3X9oFQtLP9ypsi4Ngqgvw0O3+CGY/nvJ6c6ynryiVcmURdQY66f+57CtPjjdUVsSoR/bce5S2lrHNAoIBAQDG6C5iHVuX9OplWcA70U7Tc4eiac803QRUL+zdDOC74Qw2Coxr5Nsxjz8/i+6DnOCeWkJBh/uGEaxdYYZHU90LVf/NSA3zguGt6Z0g3MMvMNT9KGgoZl2T5Y/CCgCfrt6gTGRy7PmF+uLtgHcsKQLqCzMarOSVyue35ztxuI6NeRPvlNkoJ54F/+jnY1/6MrMTPXpybHXwf40piyLPuyapqBgyWkRBwPORR9Ucdiz7KhBnZhj7NYpwvut/KgufuU3WF9bkknfFlOJzVp8ZgefxHE8lHPuR0PnWyHwv3v5hgALYwenREcM5MUbRZ7tc18FkWBVOPlvszqSGujq3nMpHAoIBAGV2R9OfHVzF9dgH1Yh0aB+g1WYl9S6neNxa027sJkQD6ZAcvmn8z8SLrfAp/QWdoWfUkqHpqb9jQswarVuF4jmTELKmqiQaSIhJiLU+4cjAJLnK3q9rHa6pZNusTJG47ITpCamkFLUD6/2d7LFNp5043/tyCLV1qSYQYj0j6C+xnjuT7WlDP9OrairRehZwe5WeGiitdjHoDP+N0ss7gB8ov0Qrx7Qzw4xC6Et2/q2DiZa4UiYL19LYPFeKNYKpcruT7mgM5ttOhYpCaueuBVOiL4bgbVOPCLigZ8AoHDxuB1PHomTBm9RtfghZRz1gyNntIPntwzb7u0Cjdqfl/dkCggEAauDxo3jg8ZsBpCoA2GOUtpw6gnPWijJElDQYU4MK8wlvNU6fu44ClfPB6ZR4OjI+o/gd5/Z4mca/VoID1Cnk+aVhSV3xWSq3t2pzKuhU3POhTtK6fRLcL49Hmt0jDqq5J2tFAlgBkBOKglHoN0tmLHqOIERMo4yezDusvmOL/crUgoT51tDK4bBr5oGIXfmGLc14ESnkibEQGgWQVAzdLoaLUesdCDP07NirU5rQerlUjSrYO4u+cuyzv+XIzy+T+nle1/037Gwe7hjabqtWBUHP6UJUjzq6NMYPrO1mxN8zKGOyDsw7mWy3/+d8TtwEJ7YI5L0vSeSTlW1WBblzGwKCAQBeL4WoC0rH7B+MBw/+q3NMozlq4pE1vAC/7L06GhTPQi3Sklz/HHfw1g2aeYYIYpZ3S0Q+QHBs+fpspuPIBE0xTZPNJIAPTOW3WxODCya1rl521Syy8t57m9UPyVG3scSbWrJxH99BQVSPZzVMI3WO2XWGYupUIkVo+MW4WKhBkHd6a8inuJmjExDWm+bwaoQgUxNwE3c1C5OFwkAU1ofhNijE02IJX0EOaYjvNELT0Na3B0T7KZ72Ut4u33byPgEHa9SsPEurctbHy2HdpOGHdpxx4r+7ZQUtg1UZ0ZRQN8B8TyozD05AXQ9V8ybdzU1jSpQYWqbfkXl+LllJBa4E" }, "Log": { "MaxAgeDays": 0, "MaxBackups": 1, "MaxSizeMB": 250 }, "Mounts": { "FuseAllowOther": false, "IPFS": "/ipfs", "IPNS": "/ipns" }, "SupernodeRouting": { "Servers": [ "/ip4/104.236.176.52/tcp/4002/ipfs/QmXdb7tWTxdFEQEFgWBqkuYSrZd3mXrC7HxkD4krGNYx2U", "/ip4/104.236.179.241/tcp/4002/ipfs/QmVRqViDByUxjUMoPnjurjKvZhaEMFDtK35FJXHAM4Lkj6", "/ip4/104.236.151.122/tcp/4002/ipfs/QmSZwGx8Tn8tmcM4PtDJaMeUQNRhNFdBLVGPzRiNaRJtFH", "/ip4/162.243.248.213/tcp/4002/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP", "/ip4/128.199.219.111/tcp/4002/ipfs/Qmb3brdCYmKG1ycwqCbo6LUwWxTuo3FisnJV2yir7oN92R", "/ip4/104.236.76.40/tcp/4002/ipfs/QmdRBCV8Cz2dGhoKLkD3YjPwVFECmqADQkx5ZteF2c6Fy4", "/ip4/178.62.158.247/tcp/4002/ipfs/QmUdiMPci7YoEUBkyFZAh2pAbjqcPr7LezyiPD2artLw3v", "/ip4/178.62.61.185/tcp/4002/ipfs/QmVw6fGNqBixZE4bewRLT2VXX7fAHUHs8JyidDiJ1P7RUN" ] }, "Tour": { "Last": "" }, "Version": { "AutoUpdate": "minor", "Check": "error", "CheckDate": "0001-01-01T00:00:00Z", "CheckPeriod": "172800000000000", "Current": "0.3.0" } } ================================================ FILE: test/fixtures/testfile.txt ================================================ Plz add me! ================================================ FILE: test/get.spec.js ================================================ /* eslint-env mocha */ /* eslint max-nested-callbacks: ["error", 8] */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const loadFixture = require('aegir/fixtures') const all = require('it-all') const concat = require('it-concat') const f = require('./utils/factory')() describe('.get (specific go-ipfs features)', function () { this.timeout(60 * 1000) function fixture (path) { return loadFixture(path, 'interface-ipfs-core') } const smallFile = { cid: 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', data: fixture('test/fixtures/testfile.txt') } let ipfs before(async () => { ipfs = (await f.spawn()).api await all(ipfs.add(smallFile.data)) }) after(() => f.clean()) it('no compression args', async () => { const files = await all(ipfs.get(smallFile.cid)) expect(files).to.be.length(1) const content = await concat(files[0].content) expect(content.toString()).to.contain(smallFile.data.toString()) }) it('archive true', async () => { const files = await all(ipfs.get(smallFile.cid, { archive: true })) expect(files).to.be.length(1) const content = await concat(files[0].content) expect(content.toString()).to.contain(smallFile.data.toString()) }) it('err with out of range compression level', async () => { await expect(all(ipfs.get(smallFile.cid, { compress: true, compressionLevel: 10 }))).to.be.rejectedWith('compression level must be between 1 and 9') }) // TODO Understand why this test started failing it.skip('with compression level', async () => { await all(ipfs.get(smallFile.cid, { compress: true, 'compression-level': 1 })) }) it('add path containing "+"s (for testing get)', async () => { const filename = 'ti,c64x+mega++mod-pic.txt' const subdir = 'tmp/c++files' const expectedCid = 'QmPkmARcqjo5fqK1V1o8cFsuaXxWYsnwCNLJUYS4KeZyff' const path = `${subdir}/${filename}` const files = await all(ipfs.add([{ path, content: Buffer.from(path) }])) expect(files[2].cid.toString()).to.equal(expectedCid) }) it('get path containing "+"s', async () => { const cid = 'QmPkmARcqjo5fqK1V1o8cFsuaXxWYsnwCNLJUYS4KeZyff' const files = await all(ipfs.get(cid)) expect(files).to.be.an('array').with.lengthOf(3) expect(files[0]).to.have.property('path', cid) expect(files[1]).to.have.property('path', `${cid}/c++files`) expect(files[2]).to.have.property('path', `${cid}/c++files/ti,c64x+mega++mod-pic.txt`) }) }) ================================================ FILE: test/interface.spec.js ================================================ /* eslint-env mocha */ 'use strict' const tests = require('interface-ipfs-core') const factory = require('./utils/factory') const isWindows = process.platform && process.platform === 'win32' /** @typedef {import("ipfsd-ctl").ControllerOptions} ControllerOptions */ describe('interface-ipfs-core tests', () => { const commonFactory = factory() tests.root(commonFactory, { skip: [ { name: 'should add with mode as string', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should add with mode as number', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should add with mtime as Date', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should add with mtime as { nsecs, secs }', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should add with mtime as timespec', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should add with mtime as hrtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should export a chunk of a file', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should ls with metadata', reason: 'TODO not implemented in go-ipfs yet' } ] }) tests.bitswap(commonFactory) tests.block(commonFactory, { skip: [{ name: 'should get a block added as CIDv1 with a CIDv0', reason: 'go-ipfs does not support the `version` param' }] }) tests.bootstrap(commonFactory, { skip: [{ name: 'should return a list containing the bootstrap peer when called with a valid arg (ip4)', reason: 'TODO unskip when go-ipfs switches to p2p for libp2p keys' }, { name: 'should prevent duplicate inserts of bootstrap peers', reason: 'TODO unskip when go-ipfs switches to p2p for libp2p keys' }, { name: 'should return a list containing the peer removed when called with a valid arg (ip4)', reason: 'TODO unskip when go-ipfs switches to p2p for libp2p keys' }] }) tests.config(commonFactory, { skip: [ // config.replace { name: 'replace', reason: 'FIXME Waiting for fix on go-ipfs https://github.com/ipfs/js-ipfs-http-client/pull/307#discussion_r69281789 and https://github.com/ipfs/go-ipfs/issues/2927' }, { name: 'should list config profiles', reason: 'TODO: Not implemented in go-ipfs' }, { name: 'should strip private key from diff output', reason: 'TODO: Not implemented in go-ipfs' } ] }) tests.dag(commonFactory, { skip: [ // dag.tree { name: 'tree', reason: 'TODO vmx 2018-02-22: Currently the tree API is not exposed in go-ipfs' }, // dag.get: { name: 'should get a dag-pb node local value', reason: 'FIXME vmx 2018-02-22: Currently not supported in go-ipfs, it might be possible once https://github.com/ipfs/go-ipfs/issues/4728 is done' }, { name: 'should get dag-pb value via dag-cbor node', reason: 'FIXME vmx 2018-02-22: Currently not supported in go-ipfs, it might be possible once https://github.com/ipfs/go-ipfs/issues/4728 is done' }, { name: 'should get by CID string + path', reason: 'FIXME vmx 2018-02-22: Currently not supported in go-ipfs, it might be possible once https://github.com/ipfs/go-ipfs/issues/4728 is done' }, { name: 'should get only a CID, due to resolving locally only', reason: 'FIXME: go-ipfs does not support localResolve option' } ] }) tests.dht(commonFactory) tests.files(commonFactory, { skip: [ { name: 'should ls directory', reason: 'TODO unskip when go-ipfs supports --long https://github.com/ipfs/go-ipfs/pull/6528' }, { name: 'should list a file directly', reason: 'TODO unskip when go-ipfs supports --long https://github.com/ipfs/go-ipfs/pull/6528' }, { name: 'should ls directory and include metadata', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should read from outside of mfs', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should ls from outside of mfs', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should change file mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should change directory mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should change file mode as string', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should change file mode to 0', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should update file mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should update directory mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should respect metadata when copying files', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should respect metadata when copying directories', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should respect metadata when copying from outside of mfs', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should have default mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should set mtime as Date', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should set mtime as { nsecs, secs }', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should set mtime as timespec', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should set mtime as hrtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and have default mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mode as string', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mode as number', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mtime as Date', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mtime as { nsecs, secs }', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mtime as timespec', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should make directory and specify mtime as hrtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mode as a string', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mode as a number', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mtime as Date', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mtime as { nsecs, secs }', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mtime as timespec', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should write file and specify mtime as hrtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should stat file with mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should stat file with mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should stat dir with mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should stat dir with mtime', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should stat sharded dir with mode', reason: 'TODO not implemented in go-ipfs yet' }, { name: 'should stat sharded dir with mtime', reason: 'TODO not implemented in go-ipfs yet' } ] }) tests.key(commonFactory, { skip: [ // key.export { name: 'export', reason: 'TODO not implemented in go-ipfs yet' }, // key.import { name: 'import', reason: 'TODO not implemented in go-ipfs yet' } ] }) tests.miscellaneous(commonFactory) tests.name(factory( { ipfsOptions: { offline: true } } ), { skip: [ { name: 'should resolve a record from peerid as cidv1 in base32', reason: 'TODO not implemented in go-ipfs yet: https://github.com/ipfs/go-ipfs/issues/5287' } ] }) tests.namePubsub(factory( { ipfsOptions: { EXPERIMENTAL: { ipnsPubsub: true } } } ), { skip: [ // name.pubsub.cancel { name: 'should cancel a subscription correctly returning true', reason: 'go-ipfs is really slow for publishing and resolving ipns records, unless in offline mode' }, // name.pubsub.subs { name: 'should get the list of subscriptions updated after a resolve', reason: 'go-ipfs is really slow for publishing and resolving ipns records, unless in offline mode' } ] }) tests.object(commonFactory) tests.pin(commonFactory) tests.ping(commonFactory, { skip: [ { name: 'should fail when pinging a peer that is not available', reason: 'FIXME go-ipfs return success with text: Looking up peer ' } ] }) tests.pubsub(factory({}, { go: { args: ['--enable-pubsub-experiment'] } }), { skip: isWindows ? [ // pubsub.subscribe { name: 'should send/receive 100 messages', reason: 'FIXME https://github.com/ipfs/interface-ipfs-core/pull/188#issuecomment-354673246 and https://github.com/ipfs/go-ipfs/issues/4778' }, { name: 'should receive multiple messages', reason: 'FIXME https://github.com/ipfs/interface-ipfs-core/pull/188#issuecomment-354673246 and https://github.com/ipfs/go-ipfs/issues/4778' } ] : null }) tests.repo(commonFactory) tests.stats(commonFactory) tests.swarm(commonFactory) }) ================================================ FILE: test/key.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const f = require('./utils/factory')() describe('.key', function () { this.timeout(50 * 1000) let ipfs before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) describe('.gen', () => { it('create a new rsa key', async () => { const res = await ipfs.key.gen('foobarsa', { type: 'rsa', size: 2048 }) expect(res).to.exist() }) it('create a new ed25519 key', async () => { const res = await ipfs.key.gen('bazed', { type: 'ed25519' }) expect(res).to.exist() }) }) describe('.list', () => { it('both keys show up + self', async () => { const res = await ipfs.key.list() expect(res).to.exist() expect(res.length).to.equal(3) }) }) }) ================================================ FILE: test/lib.configure.spec.js ================================================ /* eslint-env mocha, browser */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const Multiaddr = require('multiaddr') const { isBrowser, isWebWorker } = require('ipfs-utils/src/env') const configure = require('../src/lib/configure') describe('lib/configure', () => { it('should accept no config', () => { configure(config => { if (isBrowser || isWebWorker) { expect(config.apiAddr).to.eql(location.origin) } else { expect(config.apiAddr).to.eql('http://localhost:5001') } })() }) it('should accept string multiaddr', () => { const input = '/ip4/127.0.0.1/tcp/5001' configure(config => { expect(config.apiAddr).to.eql('http://127.0.0.1:5001') })(input) }) it('should accept string url', () => { const input = 'http://127.0.0.1:5001' configure(config => { expect(config.apiAddr).to.eql('http://127.0.0.1:5001') })(input) }) it('should accept multiaddr instance', () => { const input = Multiaddr('/ip4/127.0.0.1/tcp/5001') configure(config => { expect(config.apiAddr).to.eql('http://127.0.0.1:5001') })(input) }) it('should accept object with protocol, host and port', () => { const input = { protocol: 'https', host: 'ipfs.io', port: 138 } configure(config => { expect(config.apiAddr).to.eql('https://ipfs.io:138') })(input) }) it('should accept object with protocol only', () => { const input = { protocol: 'https' } configure(config => { if (isBrowser || isWebWorker) { expect(config.apiAddr).to.eql(`https://${location.host}`) } else { expect(config.apiAddr).to.eql('https://localhost:5001') } })(input) }) it('should accept object with host only', () => { const input = { host: 'ipfs.io' } configure(config => { if (isBrowser || isWebWorker) { expect(config.apiAddr).to.eql(`http://ipfs.io:${location.port}`) } else { expect(config.apiAddr).to.eql('http://ipfs.io:5001') } })(input) }) it('should accept object with port only', () => { const input = { port: 138 } configure(config => { if (isBrowser || isWebWorker) { expect(config.apiAddr).to.eql(`http://${location.hostname}:138`) } else { expect(config.apiAddr).to.eql('http://localhost:138') } })(input) }) }) ================================================ FILE: test/lib.error-handler.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const { HTTPError } = require('ky-universal') const throwsAsync = require('./utils/throws-async') const errorHandler = require('../src/lib/error-handler') describe('lib/error-handler', () => { it('should parse json error response', async () => { const res = { ok: false, headers: { get: () => 'application/json' }, json: () => Promise.resolve({ Message: 'boom', Code: 0, Type: 'error' }), status: 500 } const err = await throwsAsync(errorHandler(null, null, res)) expect(err instanceof HTTPError).to.be.true() expect(err.message).to.eql('boom') expect(err.response.status).to.eql(500) }) it('should gracefully fail on parse json', async () => { const res = { ok: false, headers: { get: () => 'application/json' }, json: () => 'boom', // not valid json! status: 500 } const err = await throwsAsync(errorHandler(null, null, res)) expect(err instanceof HTTPError).to.be.true() }) it('should gracefully fail on read text', async () => { const res = { ok: false, headers: { get: () => 'text/plain' }, text: () => Promise.reject(new Error('boom')), status: 500 } const err = await throwsAsync(errorHandler(null, null, res)) expect(err instanceof HTTPError).to.be.true() }) }) ================================================ FILE: test/log.spec.js ================================================ /* eslint-env mocha */ /* eslint max-nested-callbacks: ["error", 8] */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const all = require('it-all') const f = require('./utils/factory')() describe('.log', function () { this.timeout(100 * 1000) let ipfs before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) it('.log.tail', async () => { const i = setInterval(async () => { try { await all(ipfs.add(Buffer.from('just adding some data to generate logs'))) } catch (_) { // this can error if the test has finished and we're shutting down the node } }, 1000) for await (const message of ipfs.log.tail()) { clearInterval(i) expect(message).to.be.an('object') break } }) it('.log.ls', async () => { const res = await ipfs.log.ls() expect(res).to.exist() expect(res).to.be.an('array') }) it('.log.level', async () => { const res = await ipfs.log.level('all', 'error') expect(res).to.exist() expect(res).to.be.an('object') expect(res).to.not.have.property('error') expect(res).to.have.property('message') }) }) ================================================ FILE: test/node/swarm.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const nock = require('nock') const ipfsClient = require('../../src') describe('.swarm.peers', function () { this.timeout(50 * 1000) // slow CI const ipfs = ipfsClient('/ip4/127.0.0.1/tcp/5001') const apiUrl = 'http://127.0.0.1:5001' it('handles a peer response', async () => { const response = { Peers: [{ Addr: '/ip4/104.131.131.82/tcp/4001', Peer: 'QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ', Latency: '', Muxer: '', Streams: null }] } const scope = nock(apiUrl) .post('/api/v0/swarm/peers') .query(true) .reply(200, response) const res = await ipfs.swarm.peers() expect(res).to.be.a('array') expect(res.length).to.equal(1) expect(res[0].error).to.not.exist() expect(res[0].addr.toString()).to.equal(response.Peers[0].Addr) expect(res[0].peer.toString()).to.equal(response.Peers[0].Peer) expect(scope.isDone()).to.equal(true) }) it('handles an ip6 quic peer', async () => { const response = { Peers: [{ Addr: '/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/udp/4001/quic', Peer: 'QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC', Latency: '', Muxer: '', Streams: null }] } const scope = nock(apiUrl) .post('/api/v0/swarm/peers') .query(true) .reply(200, response) const res = await ipfs.swarm.peers() expect(res).to.be.a('array') expect(res.length).to.equal(1) expect(res[0].error).to.not.exist() expect(res[0].addr.toString()).to.equal(response.Peers[0].Addr) expect(res[0].peer.toString()).to.equal(response.Peers[0].Peer) expect(scope.isDone()).to.equal(true) }) it('handles unvalidatable peer addr', async () => { const response = { Peers: [{ Addr: '/ip4/104.131.131.82/future-tech', Peer: 'QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC', Latency: '', Muxer: '', Streams: null }] } const scope = nock(apiUrl) .post('/api/v0/swarm/peers') .query(true) .reply(200, response) const res = await ipfs.swarm.peers() expect(res).to.be.a('array') expect(res.length).to.equal(1) expect(res[0].error).to.exist() expect(res[0].rawPeerInfo).to.deep.equal(response.Peers[0]) expect(scope.isDone()).to.equal(true) }) it('handles an error response', async () => { const scope = nock(apiUrl) .post('/api/v0/swarm/peers') .query(true) .replyWithError('something awful happened') await expect(ipfs.swarm.peers()).to.be.rejectedWith('something awful happened') expect(scope.isDone()).to.equal(true) }) }) ================================================ FILE: test/node.js ================================================ 'use strict' require('./node/swarm') ================================================ FILE: test/ping.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const all = require('it-all') const f = require('./utils/factory')() // Determine if a ping response object is a pong, or something else, like a status message function isPong (pingResponse) { return Boolean(pingResponse && pingResponse.success && !pingResponse.text) } describe('.ping', function () { this.timeout(20 * 1000) let ipfs let other let otherId before(async function () { this.timeout(30 * 1000) // slow CI ipfs = (await f.spawn()).api other = (await f.spawn()).api const ma = (await ipfs.id()).addresses[0] await other.swarm.connect(ma) otherId = (await other.id()).id }) after(() => f.clean()) it('.ping with default count', async () => { const res = await all(ipfs.ping(otherId)) expect(res).to.be.an('array') expect(res.filter(isPong)).to.have.lengthOf(10) res.forEach(packet => { expect(packet).to.have.keys('success', 'time', 'text') expect(packet.time).to.be.a('number') }) const resultMsg = res.find(packet => packet.text.includes('Average latency')) expect(resultMsg).to.exist() }) it('.ping with count = 2', async () => { const res = await all(ipfs.ping(otherId, { count: 2 })) expect(res).to.be.an('array') expect(res.filter(isPong)).to.have.lengthOf(2) res.forEach(packet => { expect(packet).to.have.keys('success', 'time', 'text') expect(packet.time).to.be.a('number') }) const resultMsg = res.find(packet => packet.text.includes('Average latency')) expect(resultMsg).to.exist() }) }) ================================================ FILE: test/repo.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const f = require('./utils/factory')() describe('.repo', function () { this.timeout(50 * 1000) // slow CI let ipfs before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) it('.repo.gc', async () => { const res = await ipfs.repo.gc() expect(res).to.exist() }) it('.repo.stat', async () => { const res = await ipfs.repo.stat() expect(res).to.exist() expect(res).to.have.a.property('numObjects') expect(res).to.have.a.property('repoSize') }) it('.repo.version', async () => { const res = await ipfs.repo.version() expect(res).to.exist() }) }) ================================================ FILE: test/request-api.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const { isNode } = require('ipfs-utils/src/env') const ipfsClient = require('../src/index.js') describe('\'deal with HTTP weirdness\' tests', () => { it('does not crash if no content-type header is provided', async function () { if (!isNode) return this.skip() // go-ipfs always (currently) adds a content-type header, even if no content is present, // the standard behaviour for an http-api is to omit this header if no content is present const server = require('http').createServer((req, res) => { // Consume the entire request, before responding. req.on('data', () => {}) req.on('end', () => { res.writeHead(200) res.end() }) }) await new Promise(resolve => server.listen(6001, resolve)) await ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json') server.close() }) }) describe('trailer headers', () => { // TODO: needs fixing https://github.com/ipfs/js-ipfs-http-client/pull/624#issuecomment-344181950 it.skip('should deal with trailer x-stream-error correctly', (done) => { if (!isNode) { return done() } const server = require('http').createServer((req, res) => { res.setHeader('x-chunked-output', '1') res.setHeader('content-type', 'application/json') res.setHeader('Trailer', 'X-Stream-Error') res.addTrailers({ 'X-Stream-Error': JSON.stringify({ Message: 'ups, something went wrong', Code: 500 }) }) res.write(JSON.stringify({ Bytes: 1 })) res.end() }) server.listen(6001, () => { const ipfs = ipfsClient('/ip4/127.0.0.1/tcp/6001') /* eslint-disable */ ipfs.add(Buffer.from('Hello there!'), (err, res) => { // TODO: error's are not being correctly // propagated with Trailer headers yet // expect(err).to.exist() expect(res).to.not.equal(0) server.close(done) }) /* eslint-enable */ }) }) }) describe('error handling', () => { it('should handle plain text error response', async function () { if (!isNode) return this.skip() const server = require('http').createServer((req, res) => { // Consume the entire request, before responding. req.on('data', () => {}) req.on('end', () => { // Write a text/plain response with a 403 (forbidden) status res.writeHead(403, { 'Content-Type': 'text/plain' }) res.write('ipfs method not allowed') res.end() }) }) await new Promise(resolve => server.listen(6001, resolve)) await expect(ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json')) .to.eventually.be.rejectedWith('ipfs method not allowed') .and.to.have.nested.property('response.status').that.equals(403) server.close() }) it('should handle JSON error response', async function () { if (!isNode) return this.skip() const server = require('http').createServer((req, res) => { // Consume the entire request, before responding. req.on('data', () => {}) req.on('end', () => { // Write a application/json response with a 400 (bad request) header res.writeHead(400, { 'Content-Type': 'application/json' }) res.write(JSON.stringify({ Message: 'client error', Code: 1 })) res.end() }) }) await new Promise(resolve => server.listen(6001, resolve)) await expect(ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json')) .to.eventually.be.rejectedWith('client error') .and.to.have.nested.property('response.status').that.equals(400) server.close() }) it('should handle JSON error response with invalid JSON', async function () { if (!isNode) return this.skip() const server = require('http').createServer((req, res) => { // Consume the entire request, before responding. req.on('data', () => {}) req.on('end', () => { // Write a application/json response with a 400 (bad request) header res.writeHead(400, { 'Content-Type': 'application/json' }) res.write('{ Message: ') res.end() }) }) await new Promise(resolve => server.listen(6001, resolve)) await expect(ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json')) .to.eventually.be.rejected() .and.to.have.property('message').that.includes('Unexpected token M in JSON at position 2') server.close() }) }) ================================================ FILE: test/stats.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') const all = require('it-all') const f = require('./utils/factory')() describe('stats', function () { this.timeout(50 * 1000) // slow CI let ipfs before(async () => { ipfs = (await f.spawn()).api }) after(() => f.clean()) it('.stats.bitswap', async () => { const res = await ipfs.stats.bitswap() expect(res).to.exist() expect(res).to.have.a.property('provideBufLen') expect(res).to.have.a.property('wantlist') expect(res).to.have.a.property('peers') expect(res).to.have.a.property('blocksReceived') expect(res).to.have.a.property('dataReceived') expect(res).to.have.a.property('blocksSent') expect(res).to.have.a.property('dataSent') expect(res).to.have.a.property('dupBlksReceived') expect(res).to.have.a.property('dupDataReceived') }) it('.stats.bw', async () => { const res = (await all(ipfs.stats.bw()))[0] expect(res).to.exist() expect(res).to.have.a.property('totalIn') expect(res).to.have.a.property('totalOut') expect(res).to.have.a.property('rateIn') expect(res).to.have.a.property('rateOut') }) it('.stats.repo', async () => { const res = await ipfs.stats.repo() expect(res).to.exist() expect(res).to.have.a.property('numObjects') expect(res).to.have.a.property('repoSize') expect(res).to.have.a.property('repoPath') expect(res).to.have.a.property('version') expect(res).to.have.a.property('storageMax') }) }) ================================================ FILE: test/sub-modules.spec.js ================================================ /* eslint-env mocha */ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') describe('submodules', () => { it('bitswap', () => { const bitswap = require('../src/bitswap')() expect(bitswap.wantlist).to.be.a('function') expect(bitswap.stat).to.be.a('function') expect(bitswap.unwant).to.be.a('function') }) it('block', () => { const block = require('../src/block')() expect(block.get).to.be.a('function') expect(block.stat).to.be.a('function') expect(block.put).to.be.a('function') }) it('bootstrap', () => { const bootstrap = require('../src/bootstrap')() expect(bootstrap.add).to.be.a('function') expect(bootstrap.rm).to.be.a('function') expect(bootstrap.list).to.be.a('function') }) it('config', () => { const cfg = require('../src/config')() expect(cfg.get).to.be.a('function') expect(cfg.set).to.be.a('function') expect(cfg.replace).to.be.a('function') expect(cfg).to.have.a.property('profiles') expect(cfg.profiles.list).to.be.a('function') expect(cfg.profiles.apply).to.be.a('function') }) it('dht', () => { const dht = require('../src/dht')() expect(dht.get).to.be.a('function') expect(dht.put).to.be.a('function') expect(dht.findProvs).to.be.a('function') expect(dht.findPeer).to.be.a('function') expect(dht.provide).to.be.a('function') expect(dht.query).to.be.a('function') }) it('id', () => { const id = require('../src/id')() expect(id).to.be.a('function') }) it('version', () => { const version = require('../src/version')() expect(version).to.be.a('function') }) it('ping', () => { const ping = require('../src')().ping expect(ping).to.be.a('function') }) it('log', () => { const log = require('../src/log')() expect(log.ls).to.be.a('function') expect(log.tail).to.be.a('function') expect(log.level).to.be.a('function') }) it('key', () => { const key = require('../src/key')() expect(key.gen).to.be.a('function') expect(key.list).to.be.a('function') }) it('name', () => { const name = require('../src/name')() expect(name.publish).to.be.a('function') expect(name.resolve).to.be.a('function') }) it('pin', () => { const pin = require('../src/pin')() expect(pin.add).to.be.a('function') expect(pin.rm).to.be.a('function') expect(pin.ls).to.be.a('function') }) it('repo', () => { const repo = require('../src/repo')() expect(repo.gc).to.be.a('function') expect(repo.stat).to.be.a('function') }) it('stats', () => { const stats = require('../src/stats')() expect(stats.bitswap).to.be.a('function') expect(stats.bw).to.be.a('function') expect(stats.repo).to.be.a('function') }) it('swarm', () => { const swarm = require('../src/swarm')() expect(swarm.peers).to.be.a('function') expect(swarm.connect).to.be.a('function') expect(swarm.disconnect).to.be.a('function') expect(swarm.addrs).to.be.a('function') expect(swarm.localAddrs).to.be.a('function') }) it('diag', () => { const diag = require('../src/diag')() expect(diag.net).to.be.a('function') expect(diag.sys).to.be.a('function') expect(diag.cmds).to.be.a('function') }) it('object', () => { const object = require('../src/object')() expect(object.get).to.be.a('function') expect(object.put).to.be.a('function') expect(object.data).to.be.a('function') expect(object.links).to.be.a('function') expect(object.stat).to.be.a('function') expect(object.new).to.be.a('function') expect(object.patch.rmLink).to.be.a('function') expect(object.patch.addLink).to.be.a('function') expect(object.patch.setData).to.be.a('function') expect(object.patch.appendData).to.be.a('function') }) it('pubsub', () => { const pubsub = require('../src/pubsub')() expect(pubsub.subscribe).to.be.a('function') expect(pubsub.unsubscribe).to.be.a('function') expect(pubsub.publish).to.be.a('function') expect(pubsub.ls).to.be.a('function') expect(pubsub.peers).to.be.a('function') }) it('files regular API', () => { const filesRegular = require('../src')() expect(filesRegular.add).to.be.a('function') expect(filesRegular.get).to.be.a('function') expect(filesRegular.cat).to.be.a('function') expect(filesRegular.ls).to.be.a('function') expect(filesRegular.refs).to.be.a('function') expect(filesRegular.refs.local).to.be.a('function') }) it('files MFS API', () => { const files = require('../src/files')() expect(files.cp).to.be.a('function') expect(files.ls).to.be.a('function') expect(files.mkdir).to.be.a('function') expect(files.stat).to.be.a('function') expect(files.rm).to.be.a('function') expect(files.read).to.be.a('function') expect(files.write).to.be.a('function') expect(files.mv).to.be.a('function') }) it('commands', () => { const commands = require('../src/commands')() expect(commands).to.be.a('function') }) it('mount', () => { const mount = require('../src/mount')() expect(mount).to.be.a('function') }) }) ================================================ FILE: test/utils/factory.js ================================================ 'use strict' const { createFactory } = require('ipfsd-ctl') const merge = require('merge-options') const { isNode } = require('ipfs-utils/src/env') const commonOptions = { test: 'true', type: 'go', ipfsHttpModule: require('../../src') } const commonOverrides = { go: { ipfsBin: isNode ? require('go-ipfs-dep').path() : undefined } } const factory = (options = {}, overrides = {}) => createFactory( merge(commonOptions, options), merge(commonOverrides, overrides) ) module.exports = factory ================================================ FILE: test/utils/throws-async.js ================================================ 'use strict' module.exports = async fnOrPromise => { try { await (fnOrPromise.then ? fnOrPromise : fnOrPromise()) } catch (err) { return err } throw new Error('did not throw') }