# 🔦 Highlights
One giant leap forward
# 🦁 Async Await and Async Iterables
🎶 In the jungle, the mighty jungle the lion sleeps tonight!
🎶 async await, async await, async await, async await...
We've completed a HUGE refactor (opens new window) to js-ipfs internals 🥳, switching to using Promises and async/await over Callbacks and using async iterables (opens new window) instead of Node.js Streams (opens new window) and Pull Streams (opens new window). Ok, I tell a lie, it's actually not just js-ipfs internals, it's the whole stack, including libp2p, IPLD and multiformats, you might call it a ground up re-write, but, you know, we don't like to brag.
🚨 Oh, wait, serious note - this release brings big breaking changes to the core API so please consult the "API Changes" section below for all the information.
It's been a long and emotional voyage but this refactor brings a plethora of incredible immediate and future benefits to consumers, contributors and core developers that make all the work and effort worthwhile. To summarise, we've:
- Switched to streaming APIs by default to reduce memory pressure
- Reduced API surface area by removing buffering, Node.js and Pull Stream APIs
- Reduced the amount of code in the code base and number of dependencies we depend on (for smaller browser bundles and faster install times)
- Switched to using async iterables to stream data to help make streaming more approachable
- Switched to using
async/awaitso we’ll get better error stack traces and improved readability and maintainability
Using async/await in JavaScript is gaining a lot of traction in the ecosystem and is rapidly becoming the de facto way of writing idiomatic JS. We want js-ipfs to move with the times and continue to be attractive to contributors by using modern JS features, techniques and practices. The big idea with these changes is for the code to be easier to contribute to, easier to understand, easier to maintain, and be faster and smaller than ever.
This change is so big, and so significant, we wrote a whole blog post (opens new window) about it to explain the motivations behind the changes...in excruciating delightful depth 🤣!
We've also compiled some stats on this refactor for your viewing pleasure:
- 27 direct dependencies were removed from our
package.json - 214 fewer modules end up in our browser bundle
- 155 kB lighter browser bundle (unpkg.com/ipfs@0.40.0 (opens new window) vs unpkg.com/ipfs@0.41.0-rc.0 (opens new window)) - that's 18% smaller!
- 124 kB lighter
ipfs-http-clientbrowser bundle (unpkg.com/ipfs-http-client@41.0.1 (opens new window) vs unpkg.com/ipfs-http-client@42.0.0 (opens new window)) - that's 60% smaller! - ~2,600 lines of code removed (net)
- ~360 lines of code removed from
ipfs-http-client(net) - ~10 minutes shorter CI run times
...and a lot of those stats are just for js-ipfs and js-ipfs-http-client - the tip of the iceberg! We saw changes similar to this for between 60-70 dependencies (opens new window) across IPFS, libp2p, IPLD and multiformats.
# 🌗 UnixFS v1.5
Turns out, it's really important for package managers to retain file metadata, particularly last modified time (mtime). File mtime allows them to selectively sync only data that has changed. Up until now if you wanted to host a large data set on IPFS, like a package manager's repository, it would be difficult to update.
"What about the permanent web?" I hear you cry. Well, this absolutely doesn't prevent a particular snapshot of a package manager's repository from being permanently available. Metadata just enables diffs to be imported, instead of the whole thing. So, when I say "difficult to update", like I did up there, I mean slow and/or impossible. When you have Terabytes (or more) of package data and someone publishes a new package, it's kinda inconvenient to import everything again, when only a little part changed. File mtime is a really good indicator of which things have changed, so you can use it in IPFS now! 🥳
For example, there's two new options to jsipfs add that allow mode and mtime to be preserved as the file is added to IPFS:
$ jsipfs add -r --preserve-mtime --preserve-mode ~/Desktop/gif
added QmT6WX9McZyx5ZoisRgpsjYKDBWnYpMnBLpfAgjW5kavBA gif/yesthisisdog.jpg
added QmXMrFfZ9zHLZKN7xP2dX76YFFhvBJsQkd4fLnTDkyR31Q gif
Ok, no big changes there aside from the new options, buuut, now when you list directory contents you get Mode and Mtime info:
$ jsipfs ls QmXMrFfZ9zHLZKN7xP2dX76YFFhvBJsQkd4fLnTDkyR31Q -v
Mode Mtime Hash Size Name
-rw-r--r-- Apr 16, 2018, 12:20:33 PM GMT+1 QmT6WX9McZyx5ZoisRgpsjYKDBWnYpMnBLpfAgjW5kavBA 87779 yesthisisdog.jpg
Rad right!?
Persisting the file mode is also super rad, because it opens up NFS type use cases that weren't possible before. Imagine your node_modules directory is backed by IPFS and mounted on your file system - the file mode will allow everything in node_modules/.bin (opens new window) to be executable as you'd expect.
The coolest thing about all of this is that it's completely backwards compatible. The CID for a given file/directory only changes if you opt in to metadata, otherwise the CIDs remain the same. Hooray!
There's a bunch of changes that add metadata capability to the CLI, HTTP and core API both for inputs and outputs. There's also a couple of new MFS commands touch and chmod which allow you to change the metadata whenever you like! Magic 🧙♂️.
See the API Changes sections below for details of all the new UnixFS v1.5 stuffs.
# 🏗 API Changes
# Core API
There are significant and breaking core API changes in this release. Please see the migration guide (opens new window).
IPFS is not a class that can be instantiated - use
IPFS.create. An IPFS node instance is not an event emitter.The
initoption passed toIPFS.createwill now not take any initialization steps if it is set tofalse. Previously, the repo would be initialized if it already existed. This is no longer the case. If you wish to initialize a node but only if the repo exists, passinit: { allowNew: false }to the constructor.Instance
.readyproperty has been removed. Please useIPFS.createinstead.IPFS.createNodehas been removed, please useIPFS.createinstead.Callbacks are no longer supported on any API methods. Please use a utility such as
callbackify(opens new window) on API methods that return Promises to emulate previous behaviour. See the migration guide (opens new window) for more info.Delegated peer and content routing modules are no longer included as part of core (but are still available if starting a js-ipfs daemon from the command line). If you wish to use delegated routing and are creating your node programmatically in Node.js or the browser you must
npm install libp2p-delegated-content-routingand/ornpm install libp2p-delegated-peer-routingand provide configured instances of them inoptions.libp2p(opens new window). See the module repos for further instructions:- https://github.com/libp2p/js-libp2p-delegated-content-routing
- https://github.com/libp2p/js-libp2p-delegated-peer-routing
addnow returns an async iterable.addnow acceptsmodeandmtimeoptions on inputs to allow setting mode and mtime metadata for added files. See the core interface docs (opens new window) for more info.addresults now contain acidproperty (a CID instance (opens new window)) instead of a stringhashproperty.🆕
addresults now includemodeandmtimeproperties if they were set.addReadableStream,addPullStreamhave been removed. Please see the migration guide (opens new window) for more info.addFromStreamhas been removed. Useaddinstead.addFromFshas been removed. Please use the exportedglobSourceutility and pass the result toadd. See the glob source documentation (opens new window) for more details and an example.addFromURLhas been removed. Please use the exportedurlSourceutility and pass the result toadd. See the URL source documentation (opens new window) for more details and an example.bitswap.statresult has changed -wantlistand values are now an array of CID (opens new window) instances andpeersis now astring[]of peer IDs.bitswap.wantlistnow returns an array of CID (opens new window) instances.block.rmnow returns an async iterable.block.rmnow yields objects of{ cid: CID, error: Error }.block.statresult now contains acidproperty (whose value is a CID instance (opens new window)) instead of akeyproperty.dht.findProvs,dht.provide,dht.putanddht.querynow all return an async iterable.dht.findPeer,dht.findProvs,dht.provide,dht.putanddht.querynow yield/return an object{ id: string, addrs: Multiaddr[] }instead of aPeerInfoinstance(s).🆕
files.chmodhas been added. See the core interface docs (opens new window) for info.🆕
files.flushnow returns the root CID for the path that was flushed (/by default)files.lsPullStreamandfiles.lsReadableStreamhave been removed. Please see the migration guide (opens new window) for more info.files.lsnow returns an async iterable.files.lsresults now contain acidproperty (whose value is a CID instance (opens new window)) instead of ahashproperty.🆕
files.lsresults now includemodeandmtimeproperties if they were set. See the core interface docs (opens new window) for more info.files.lsno longer takes alongoption (in core) - you will receive all data by default.🆕
files.mkdirnow acceptsmodeandmtimeoptions to allow setting mode and mtime metadata. See the core interface docs (opens new window) for more info.files.readPullStreamandfiles.readReadableStreamhave been removed. Please see the migration guide (opens new window) for more info.files.readnow returns an async iterable.files.statresult now contains acidproperty (whose value is a CID instance (opens new window)) instead of ahashproperty.🆕
files.statresult now includesmodeandmtimeproperties if they were set. See the core interface docs (opens new window) for more info.🆕
files.touchhas been added. See the core interface docs (opens new window) for info.🆕
files.writenow acceptsmodeandmtimeoptions to allow setting mode and mtime metadata. See the core interface docs (opens new window) for more info.getnow returns an async iterable. Thecontentproperty value for objects yielded from the iterator is now an async iterable that yieldsBufferList(opens new window) objects.idresult has changed, theaddressesproperty is now aMultiaddr[]name.resolvenow 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 useit-lastto extract it like so:const last = require('it-last') await last(ipfs.name.resolve('/ipns/QmHash'))🆕
object.getnow accepts atimeoutoption. It will cause the method to throw with aTimeoutErrorif no data is received within the timeout window. It can be passed as anumberor astring. If anumberis passed it is interpreted as milliseconds, if a string is passed it is interpreted as a human readable duration (opens new window).lsnow returns an async iterable.lsresults now contain acidproperty (whose value is a CID instance (opens new window)) instead of ahashproperty.🆕
lsresults now includemodeandmtimeproperties if they were set. See the core interface docs (opens new window) for more info.pin.addresults now contain acidproperty (a CID instance (opens new window)) instead of a stringhashproperty.🆕
pin.addnow accepts atimeoutoption. It will cause the method to throw with aTimeoutErrorif no data is received within the timeout window. It can be passed as anumberor astring. If anumberis passed it is interpreted as milliseconds, if a string is passed it is interpreted as a human readable duration (opens new window).pin.lsnow returns an async iterable.pin.lsresults now contain acidproperty (a CID instance (opens new window)) instead of a stringhashproperty.pin.rmresults now contain acidproperty (a CID instance (opens new window)) instead of a stringhashproperty.pingnow returns an async iterable.refsandrefs.localnow return an async iterable.🆕
refsnow accepts atimeoutoption. It will cause the method to throw with aTimeoutErrorif no data is received within the timeout window. It can be passed as anumberor astring. If anumberis passed it is interpreted as milliseconds, if a string is passed it is interpreted as a human readable duration (opens new window).repo.gcnow returns an async iterable.stats.bwnow returns an async iterable.swarm.peersnow returns an array of objects with apeerproperty that is astring, instead of aPeerIdinstance.swarm.addrsnow returns an array of objects{ id: string, addrs: Multiaddr[] }instead ofPeerInfoinstances.
# HTTP API
- 🆕
/api/v0/addnow supports the following additional multipart headers to allowmodeandmtimemetadata to be set on individual files:modefile mode to apply to created UnixFS entries[string]mtimemodification time in seconds before or since the Unix Epoch to apply to created UnixFS entries[number]mtime-nsecsmodification time fraction in nanoseconds[number]
- 🆕
/api/v0/addnow returns fileMode,MtimeandMtimeNsecsif set. /api/v0/file/lshas been removed, please use/api/v0/lsinstead.- 🆕
/api/v0/files/chmodhas been added and supports the following query string args:argpath of file to apply mode to[string]modefile mode to apply[string]
- 🆕
/api/v0/files/lsnow returns fileMode,MtimeandMtimeNsecsif set. - 🆕
/api/v0/files/mkdirnow supports supports the following additional query string args:modefile mode to apply[string]mtimemodification time in seconds before or since the Unix Epoch to apply[number]
- 🆕
/api/v0/files/statnow returns fileMode,MtimeandMtimeNsecsif set. - 🆕
/api/v0/files/touchhas been added and supports the following query string args:argpath of file to apply mode to[string]mtimemodification time in seconds before or since the Unix Epoch to apply[number]
- 🆕
/api/v0/files/writenow supports the following additional multipart headers:modefile mode to apply to created UnixFS entries[string]mtimemodification time in seconds before or since the Unix Epoch to apply to created UnixFS entries[number]
- 🆕
/api/v0/lsnow returns fileMode,MtimeandMtimeNsecsif set.
# CLI
- 🆕
jsipfs add [file...]now supports the following flags to respect and applymodeandmtimemetadata of files added from the file system or explicitly set them:--preserve-modeautomatically apply permissions to created UnixFS entries from the file system[boolean] [default: false]--preserve-mtimeautomatically apply modification time to created UnixFS entries from the file system[boolean] [default: false]--modefile mode to apply to created UnixFS entries[string]--mtimemodification time in seconds before or since the Unix Epoch to apply to created UnixFS entries[number]--mtime-nsecsmodification time fraction in nanoseconds[number]
jsipfs file lshas been removed, please usejsipfs lsinstead.- 🆕
jsipfs files chmod [mode] [path]has been added. - 🆕
jsipfs files lsnow prints filemodeandmtime. - 🆕
jsipfs files mkdirnow supports the following flags:--modefile mode to apply to created UnixFS entries[string]--mtimemodification time in seconds before or since the Unix Epoch to apply to created UnixFS entries[number]
- 🆕
jsipfs files statnow prints filemodeandmtime. - 🆕
jsipfs files touch [path]has been added and supports the following flags:--mtimemodification time in seconds before or since the Unix Epoch to apply to created UnixFS entries[number]
- 🆕
jsipfs files writenow supports the following flags:--modefile mode to apply to created UnixFS entries[string]--mtimemodification time in seconds before or since the Unix Epoch to apply to created UnixFS entries[number]
- 🆕
jsipfs lsnow prints filemodeandmtime.
# Other changes
- libp2p has been upgraded to 0.27, which also includes breaking changes to it's core API. Please see the release announcement post for more info:
- https://blog.ipfs.tech/2020-02-07-js-libp2p-0-27/
- 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.
# ❤️ Huge thank you to everyone that made this release possible
- @achingbrain (opens new window) (159 commits, 69 PRs, 10 issues, 121 comments)
- @adamsoffer (opens new window) (2 comments)
- @AdityaNambiar (opens new window) (1 issue, 1 comment)
- @AgentJ-WR (opens new window) (1 PR, 1 issue)
- @agowa338 (opens new window) (1 issue)
- @alanhoff (opens new window) (1 comment)
- @alanshaw (opens new window) (85 commits, 88 PRs, 12 issues, 177 comments)
- @AliMirlou (opens new window) (1 comment)
- @andylim0221 (opens new window) (1 comment)
- @AuHau (opens new window) (1 issue, 3 comments)
- @autonome (opens new window) (4 comments)
- @blakebyrnes (opens new window) (1 issue, 1 comment)
- @bonedaddy (opens new window) (1 comment)
- @bugeats (opens new window) (1 comment)
- @calikevuche (opens new window) (1 issue, 1 comment)
- @carsonfarmer (opens new window) (14 commits, 9 PRs, 4 issues, 27 comments)
- @chriamue (opens new window) (1 PR)
- @claassistantio (opens new window) (3 comments)
- @codecov-io (opens new window) (9 comments)
- @coryschwartz (opens new window) (1 PR, 1 comment)
- @DallasC (opens new window) (1 issue)
- @DaniellMesquita (opens new window) (3 comments)
- @dannyid (opens new window) (1 comment)
- @daviddias (opens new window) (6 commits, 3 PRs, 2 issues, 21 comments)
- @DieMyst (opens new window) (1 issue)
- @dignifiedquire (opens new window) (2 comments)
- @dirkmc (opens new window) (11 commits, 5 PRs, 9 comments)
- @dostu (opens new window) (1 comment)
- @ekkis (opens new window) (1 issue, 6 comments)
- @elmariachi111 (opens new window) (1 comment)
- @emclab (opens new window) (4 issues, 7 comments)
- @eordano (opens new window) (1 PR)
- @folex (opens new window) (1 comment)
- @ggarri (opens new window) (1 commit, 1 PR, 3 issues, 5 comments)
- @hacdias (opens new window) (9 commits, 4 PRs, 1 issue, 15 comments)
- @hiddentao (opens new window) (1 comment)
- @hugomrdias (opens new window) (48 commits, 10 PRs, 3 issues, 41 comments)
- @iameddieyayaya (opens new window) (1 comment)
- @ilirhushi (opens new window) (2 PRs, 2 comments)
- @inspiraluna (opens new window) (2 comments)
- @jacobheun (opens new window) (123 commits, 28 PRs, 3 issues, 55 comments)
- @jimpick (opens new window) (1 commit, 1 PR)
- @joequant (opens new window) (1 issue)
- @john-osullivan (opens new window) (1 issue, 4 comments)
- @Jorropo (opens new window) (1 PR)
- @kawmaiparis (opens new window) (1 issue)
- @khartig (opens new window) (1 issue)
- @kottackalsulvin (opens new window) (2 issues)
- @kuabhish (opens new window) (4 comments)
- @kumavis (opens new window) (1 PR, 1 issue, 6 comments)
- @leandroyalet (opens new window) (2 comments)
- @lidel (opens new window) (6 commits, 3 PRs, 12 comments)
- @Maj0rT (opens new window) (2 issues, 1 comment)
- @mariotaku (opens new window) (1 issue, 2 comments)
- @mburns (opens new window) (5 PRs, 1 comment)
- @mcclure (opens new window) (1 issue, 2 comments)
- @michaelsbradleyjr (opens new window) (1 comment)
- @mikeal (opens new window) (5 commits, 3 PRs, 3 issues, 18 comments)
- @mohe2015 (opens new window) (1 comment)
- @momack2 (opens new window) (1 comment)
- @mpetrunic (opens new window) (1 issue, 1 comment)
- @mqklin (opens new window) (1 issue, 1 comment)
- @My9Bot (opens new window) (1 comment)
- @nhynes (opens new window) (2 comments)
- @npfoss (opens new window) (1 comment)
- @oed (opens new window) (1 comment)
- @olizilla (opens new window) (2 commits, 1 PR, 11 comments)
- @parkan (opens new window) (1 comment)
- @pcowgill (opens new window) (1 commit, 2 PRs, 7 issues, 41 comments)
- @PedroMiguelSS (opens new window) (6 commits, 6 PRs, 3 comments)
- @peter-donovan (opens new window) (1 comment)
- @posix4e (opens new window) (1 issue)
- @Prabhakar-Poudel (opens new window) (2 comments)
- @pvsmounish (opens new window) (1 comment)
- @qu37zal (opens new window) (1 issue)
- @raulk (opens new window) (1 comment)
- @raullaprida (opens new window) (1 issue, 1 comment)
- @ribasushi (opens new window) (1 issue, 2 comments)
- @ridenaio (opens new window) (2 issues)
- @Rkrushanovskij (opens new window) (1 issue, 1 comment)
- @robertkiel (opens new window) (3 issues, 2 comments)
- @ruifortes (opens new window) (3 issues)
- @rvagg (opens new window) (3 commits, 4 PRs, 5 comments)
- @SahidMiller (opens new window) (1 comment)
- @Schwartz10 (opens new window) (1 issue)
- @shamb0t (opens new window) (1 issue, 1 comment)
- @shresthagrawal (opens new window) (1 commit, 1 PR, 1 issue, 7 comments)
- @Stebalien (opens new window) (4 commits, 3 PRs, 1 issue, 10 comments)
- @SvenMeyer (opens new window) (1 issue, 4 comments)
- @tapaswenipathak (opens new window) (1 commit)
- @terichadbourne (opens new window) (3 comments)
- @travisperson (opens new window) (1 comment)
- @tuyennhv (opens new window) (1 comment)
- @uchetron (opens new window) (1 PR)
- @uluhonolulu (opens new window) (1 comment)
- @vasco-santos (opens new window) (92 commits, 42 PRs, 5 issues, 56 comments)
- @Velenir (opens new window) (1 issue)
- @vmx (opens new window) (22 commits, 1 PR, 1 issue, 25 comments)
- @vweevers (opens new window) (1 comment)
- @wehriam (opens new window) (1 comment)
- @wemeetagain (opens new window) (16 commits, 4 PRs, 5 comments)
- @xmaysonnave (opens new window) (1 issue, 1 comment)
- @zhoreeq (opens new window) (1 comment)
- @zicmama (opens new window) (1 comment)
- @zot (opens new window) (2 comments)
# 🙌🏽 Want to contribute?
Would you like to contribute to the IPFS project and don't know how? Well, there are a few places you can get started:
- Check the issues with the
help wantedlabel in the js-ipfs repo (opens new window) - Join an IPFS All Hands, introduce yourself and let us know where you would like to contribute - https://github.com/ipfs/team-mgmt/#weekly-ipfs-all-hands
- Hack with IPFS and show us what you made! The All Hands call is also the perfect venue for demos, join in and show us what you built
- Join the discussion at https://discuss.ipfs.tech/ and help users finding their answers.
- Join the 🚀 IPFS Core Implementations Weekly Sync 🛰 (opens new window) and be part of the action!
# ⁉️ Do you have questions?
The best place to ask your questions about IPFS, how it works and what you can do with it is at discuss.ipfs.tech (opens new window). We are also available at the #ipfs channel on Freenode.