UNPKG

11.3 kBMarkdownView Raw
1# Hyperdrive
2
3#### *Note*: This is a prerelease version of Hyperdrive that's backed by [Hypertrie](https://github.com/mafintosh/hypertrie)
4#### This version is not yet API-complete.
5
6Hyperdrive is a secure, real time distributed file system
7
8``` js
9npm install hyperdrive@prerelease
10```
11
12[![Build Status](https://travis-ci.org/mafintosh/hyperdrive.svg?branch=master)](https://travis-ci.org/mafintosh/hyperdrive)
13
14## Usage
15
16Hyperdrive aims to implement the same API as Node.js' core fs module.
17
18``` js
19var hyperdrive = require('hyperdrive')
20var archive = hyperdrive('./my-first-hyperdrive') // content will be stored in this folder
21
22archive.writeFile('/hello.txt', 'world', function (err) {
23 if (err) throw err
24 archive.readdir('/', function (err, list) {
25 if (err) throw err
26 console.log(list) // prints ['hello.txt']
27 archive.readFile('/hello.txt', 'utf-8', function (err, data) {
28 if (err) throw err
29 console.log(data) // prints 'world'
30 })
31 })
32})
33```
34
35A big difference is that you can replicate the file system to other computers! All you need is a stream.
36
37``` js
38var net = require('net')
39
40// ... on one machine
41
42var server = net.createServer(function (socket) {
43 socket.pipe(archive.replicate()).pipe(socket)
44})
45
46server.listen(10000)
47
48// ... on another
49
50var clonedArchive = hyperdrive('./my-cloned-hyperdrive', origKey)
51var socket = net.connect(10000)
52
53socket.pipe(clonedArchive.replicate()).pipe(socket)
54```
55
56It also comes with build in versioning and real time replication. See more below.
57
58## API
59
60#### `var archive = hyperdrive(storage, [key], [options])`
61
62Create a new hyperdrive.
63
64The `storage` parameter defines how the contents of the archive will be stored. It can be one of the following, depending on how much control you require over how the archive is stored.
65
66- If you pass in a string, the archive content will be stored in a folder at the given path.
67- You can also pass in a function. This function will be called with the name of each of the required files for the archive, and needs to return a [`random-access-storage`](https://github.com/random-access-storage/) instance.
68- If you require complete control, you can also pass in an object containing a `metadata` and a `content` field. Both of these need to be functions, and are called with the following arguments:
69
70 - `name`: the name of the file to be stored
71 - `opts`
72 - `key`: the [feed key](https://github.com/mafintosh/hypercore#feedkey) of the underlying Hypercore instance
73 - `discoveryKey`: the [discovery key](https://github.com/mafintosh/hypercore#feeddiscoverykey) of the underlying Hypercore instance
74 - `archive`: the current Hyperdrive instance
75
76 The functions need to return a a [`random-access-storage`](https://github.com/random-access-storage/) instance.
77
78Options include:
79
80``` js
81{
82 sparse: true, // only download data on content feed when it is specifically requested
83 sparseMetadata: true // only download data on metadata feed when requested
84 metadataStorageCacheSize: 65536 // how many entries to use in the metadata hypercore's LRU cache
85 contentStorageCacheSize: 65536 // how many entries to use in the content hypercore's LRU cache
86 extensions: [], // The list of extension message types to use
87}
88```
89
90Note that a cloned hyperdrive archive can be "sparse". Usually (by setting `sparse: true`) this means that the content is not downloaded until you ask for it, but the entire metadata feed is still downloaded. If you want a _very_ sparse archive, where even the metadata feed is not downloaded until you request it, then you should _also_ set `sparseMetadata: true`.
91
92#### `var stream = archive.replicate([options])`
93
94Replicate this archive. Options include
95
96``` js
97{
98 live: false, // keep replicating
99 download: true, // download data from peers?
100 upload: true // upload data to peers?
101}
102```
103
104#### `archive.version`
105
106Get the current version of the archive (incrementing number).
107
108#### `archive.key`
109
110The public key identifying the archive.
111
112#### `archive.discoveryKey`
113
114A key derived from the public key that can be used to discovery other peers sharing this archive.
115
116#### `archive.writable`
117
118A boolean indicating whether the archive is writable.
119
120#### `archive.peers`
121
122A list of peers currently replicating with this archive
123
124#### `archive.on('ready')`
125
126Emitted when the archive is fully ready and all properties has been populated.
127
128#### `archive.on('error', err)`
129
130Emitted when a critical error during load happened.
131
132#### `archive.on('update')`
133
134Emitted when there is a new update to the archive.
135
136#### `archive.on('extension', name, message, peer)`
137
138Emitted when a peer has sent you an extension message. The `name` is a string from one of the extension types in the constructor, `message` is a buffer containing the message contents, and `peer` is a reference to the peer that sent the extension. You can send an extension back with `peer.extension(name, message)`.
139
140#### `archive.on('peer-add', peer)`
141
142Emitted when a new peer has been added.
143
144```js
145const archive = Hyperdrive({
146 extension: ['example']
147})
148
149archive.on('extension', (name, message, peer) => {
150 console.log(name, message.toString('utf8'))
151})
152
153archive.on('peer-add', (peer) => {
154 peer.extension('example', Buffer.from('Hello World!', 'utf8'))
155})
156```
157
158#### `archive.on('peer-remove', peer)`
159
160Emitted when a peer has been removed.
161
162#### `archive.on('close')`
163
164Emitted when the archive has been closed.
165
166#### `archive.extension(name, message)`
167
168Broadcasts an extension message to all connected peers. The `name` must be a string for an extension passed in the constructor and the message must be a buffer.
169
170#### `var oldDrive = archive.checkout(version, [opts])`
171
172Checkout a readonly copy of the archive at an old version. Options are used to configure the `oldDrive`:
173
174```js
175{
176 metadataStorageCacheSize: 65536 // how many entries to use in the metadata hypercore's LRU cache
177 contentStorageCacheSize: 65536 // how many entries to use in the content hypercore's LRU cache
178 treeCacheSize: 65536 // how many entries to use in the append-tree's LRU cache
179}
180```
181
182#### `archive.download([path], [callback])`
183
184Download all files in path of current version.
185If no path is specified this will download all files.
186
187You can use this with `.checkout(version)` to download a specific version of the archive.
188
189``` js
190archive.checkout(version).download()
191```
192
193#### `var stream = archive.createReadStream(name, [options])`
194
195Read a file out as a stream. Similar to fs.createReadStream.
196
197Options include:
198
199``` js
200{
201 start: optionalByteOffset, // similar to fs
202 end: optionalInclusiveByteEndOffset, // similar to fs
203 length: optionalByteLength
204}
205```
206
207#### `archive.readFile(name, [options], callback)`
208
209Read an entire file into memory. Similar to fs.readFile.
210
211Options can either be an object or a string
212
213Options include:
214```js
215{
216 encoding: string
217 cached: true|false // default: false
218}
219```
220or a string can be passed as options to simply set the encoding - similar to fs.
221
222If `cached` is set to `true`, this function returns results only if they have already been downloaded.
223
224#### `var stream = archive.createWriteStream(name, [options])`
225
226Write a file as a stream. Similar to fs.createWriteStream.
227If `options.cached` is set to `true`, this function returns results only if they have already been downloaded.
228`options.metadata` is optionally an object with string keys and buffer objects to set metadata on the file entry.
229
230#### `archive.writeFile(name, buffer, [options], [callback])`
231
232Write a file from a single buffer. Similar to fs.writeFile.
233
234#### `archive.unlink(name, [callback])`
235
236Unlinks (deletes) a file. Similar to fs.unlink.
237
238#### `archive.mkdir(name, [options], [callback])`
239
240Explictly create an directory. Similar to fs.mkdir
241
242#### `archive.rmdir(name, [callback])`
243
244Delete an empty directory. Similar to fs.rmdir.
245
246#### `archive.readdir(name, [options], [callback])`
247
248Lists a directory. Similar to fs.readdir.
249
250Options include:
251
252``` js
253{
254 recursive: false, // Recurse into subdirectories and mounts
255 noMount: false // Do not recurse into mounts when recursive: true
256}
257```
258
259#### `archive.stat(name, [options], callback)`
260
261Stat an entry. Similar to fs.stat. Sample output:
262
263```
264Stat {
265 dev: 0,
266 nlink: 1,
267 rdev: 0,
268 blksize: 0,
269 ino: 0,
270 mode: 16877,
271 uid: 0,
272 gid: 0,
273 size: 0,
274 offset: 0,
275 blocks: 0,
276 atime: 2017-04-10T18:59:00.147Z,
277 mtime: 2017-04-10T18:59:00.147Z,
278 ctime: 2017-04-10T18:59:00.147Z,
279 linkname: undefined }
280```
281
282The stat may include a metadata object (string keys, buffer values) with metadata that was passed into `writeFile` or `createWriteStream`.
283
284The output object includes methods similar to fs.stat:
285
286``` js
287var stat = archive.stat('/hello.txt')
288stat.isDirectory()
289stat.isFile()
290```
291
292Options include:
293```js
294{
295 wait: true|false // default: true
296}
297```
298
299If `wait` is set to `true`, this function will wait for data to be downloaded. If false, will return an error.
300
301#### `archive.lstat(name, [options], callback)`
302
303Stat an entry but do not follow symlinks. Similar to fs.lstat.
304
305Options include:
306```js
307{
308 wait: true|false // default: true
309}
310```
311
312If `wait` is set to `true`, this function will wait for data to be downloaded. If false, will return an error.
313
314#### `archive.access(name, [options], callback)`
315
316Similar to fs.access.
317
318Options include:
319```js
320{
321 wait: true|false // default: true
322}
323```
324
325If `wait` is set to `true`, this function will wait for data to be downloaded. If false, will return an error.
326
327#### `archive.open(name, flags, callback)`
328
329Open a file and get a file descriptor back. Similar to fs.open.
330
331Note that currently only read mode is supported in this API.
332
333#### `archive.read(fd, buf, offset, len, position, callback)`
334
335Read from a file descriptor into a buffer. Similar to fs.read.
336
337#### `archive.write(fd, buf, offset, len, pos, cb)`
338
339Write from a buffer into a file descriptor. Similar to fs.write.
340
341#### `archive.symlink(target, linkname, cb)`
342
343Create a symlink from `linkname` to `target`.
344
345#### `archive.mount(name, key, opts, cb)`
346
347Mounts another Hyperdrive at the specified mountpoint.
348
349If a `version` is specified in the options, then the mountpoint will reference a static checkout (it will never update).
350
351Options include:
352```js
353{
354 version: (drive version) // The drive version to checkout.
355}
356```
357
358#### `archive.unmount(name, cb)`
359
360Unmount a previously-mounted Hyperdrive.
361
362#### `archive.createMountStream(opts)`
363
364Create a stream containing content/metadata feeds for all mounted Hyperdrives. Each entry in the stream has the form:
365```js
366{
367 path: '/', // The mountpoint
368 metadata: Hypercore(...), // The mounted metadata feed
369 content: Hypercore(...) // The mounted content feed
370}
371```
372
373#### `archive.getAllMounts(opts, cb)`
374
375Returns a Map of the content/metadata feeds for all mounted Hyperdrives, keyed by their mountpoints. The results will always include the top-level feeds (with key '/').
376
377Options include:
378```js
379{
380 memory: true|false // Only list drives currently cached in memory (default: false).
381}
382```
383
384#### `archive.close(fd, [callback])`
385
386Close a file. Similar to fs.close.
387
388#### `archive.close([callback])`
389
390Closes all open resources used by the archive.
391The archive should no longer be used after calling this.