1 | # IPFS Repo JavaScript Implementation <!-- omit in toc -->
|
2 |
|
3 | [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
|
4 | [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
|
5 | [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
|
6 | [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
|
7 | [![Travis CI](https://flat.badgen.net/travis/ipfs/js-ipfs-repo)](https://travis-ci.com/ipfs/js-ipfs-repo)
|
8 | [![codecov](https://codecov.io/gh/ipfs/js-ipfs-repo/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/js-ipfs-repo) [![Dependency Status](https://david-dm.org/ipfs/js-ipfs-repo.svg?style=flat-square)](https://david-dm.org/ipfs/js-ipfs-repo)
|
9 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
|
10 | ![](https://img.shields.io/badge/npm-%3E%3D6.0.0-orange.svg?style=flat-square)
|
11 | ![](https://img.shields.io/badge/Node.js-%3E%3D10.0.0-orange.svg?style=flat-square)
|
12 |
|
13 | > Implementation of the IPFS repo spec (https://github.com/ipfs/specs/blob/master/REPO.md) in JavaScript
|
14 |
|
15 | This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs/blob/master/REPO.md) in JavaScript.
|
16 |
|
17 | ## Lead Maintainer <!-- omit in toc -->
|
18 |
|
19 | [Alex Potsides](https://github.com/achingbrain)
|
20 |
|
21 | ## Table of Contents <!-- omit in toc -->
|
22 |
|
23 | - [Background](#background)
|
24 | - [Install](#install)
|
25 | - [npm](#npm)
|
26 | - [Use in Node.js](#use-in-nodejs)
|
27 | - [Use in a browser with browserify, webpack or any other bundler](#use-in-a-browser-with-browserify-webpack-or-any-other-bundler)
|
28 | - [Use in a browser Using a script tag](#use-in-a-browser-using-a-script-tag)
|
29 | - [Usage](#usage)
|
30 | - [API](#api)
|
31 | - [Setup](#setup)
|
32 | - [`new Repo(path[, options])`](#new-repopath-options)
|
33 | - [`Promise repo.init()`](#promise-repoinit)
|
34 | - [`Promise repo.open()`](#promise-repoopen)
|
35 | - [`Promise repo.close()`](#promise-repoclose)
|
36 | - [`Promise<boolean> repo.exists()`](#promiseboolean-repoexists)
|
37 | - [`Promise<Boolean> repo.isInitialized()`](#promiseboolean-repoisinitialized)
|
38 | - [Repos](#repos)
|
39 | - [`Promise repo.put(key, value:Buffer)`](#promise-repoputkey-valuebuffer)
|
40 | - [`Promise<Buffer> repo.get(key)`](#promisebuffer-repogetkey)
|
41 | - [Blocks](#blocks)
|
42 | - [`Promise<Block> repo.blocks.put(block:Block)`](#promiseblock-repoblocksputblockblock)
|
43 | - [`AsyncIterator<Block> repo.blocks.putMany(source)`](#asynciteratorblock-repoblocksputmanysource)
|
44 | - [`Promise<Buffer> repo.blocks.get(cid)`](#promisebuffer-repoblocksgetcid)
|
45 | - [`AsyncIterable<Buffer> repo.blocks.getMany(source)`](#asynciterablebuffer-repoblocksgetmanysource)
|
46 | - [`Promise<CID> repo.blocks.delete(cid:CID)`](#promisecid-repoblocksdeletecidcid)
|
47 | - [`AsyncIterator<CID> repo.blocks.deleteMany(source)`](#asynciteratorcid-repoblocksdeletemanysource)
|
48 | - [Datastore](#datastore)
|
49 | - [`repo.datastore`](#repodatastore)
|
50 | - [Config](#config)
|
51 | - [`Promise repo.config.set(key:string, value)`](#promise-repoconfigsetkeystring-value)
|
52 | - [`Promise repo.config.replace(value)`](#promise-repoconfigreplacevalue)
|
53 | - [`Promise<?> repo.config.get(key:string)`](#promise-repoconfiggetkeystring)
|
54 | - [`Promise<Object> repo.config.getAll()`](#promiseobject-repoconfiggetall)
|
55 | - [`Promise<boolean> repo.config.exists()`](#promiseboolean-repoconfigexists)
|
56 | - [Version](#version)
|
57 | - [`Promise<Number> repo.version.get()`](#promisenumber-repoversionget)
|
58 | - [`Promise repo.version.set (version:Number)`](#promise-repoversionset-versionnumber)
|
59 | - [API Addr](#api-addr)
|
60 | - [`Promise<String> repo.apiAddr.get()`](#promisestring-repoapiaddrget)
|
61 | - [`Promise repo.apiAddr.set(value)`](#promise-repoapiaddrsetvalue)
|
62 | - [Status](#status)
|
63 | - [`Promise<Object> repo.stat()`](#promiseobject-repostat)
|
64 | - [Lock](#lock)
|
65 | - [`Promise lock.lock(dir)`](#promise-locklockdir)
|
66 | - [`Promise closer.close()`](#promise-closerclose)
|
67 | - [`Promise<boolean> lock.locked(dir)`](#promiseboolean-locklockeddir)
|
68 | - [Notes](#notes)
|
69 | - [Migrations](#migrations)
|
70 | - [Contribute](#contribute)
|
71 | - [License](#license)
|
72 |
|
73 | ## Background
|
74 |
|
75 | Here is the architectural reasoning for this repo:
|
76 |
|
77 | ```bash
|
78 | ┌────────────────────────────────────────┐
|
79 | │ IPFSRepo │
|
80 | └────────────────────────────────────────┘
|
81 | ┌─────────────────┐
|
82 | │ / │
|
83 | ├─────────────────┤
|
84 | │ Datastore │
|
85 | └─────────────────┘
|
86 | ┌───────────┴───────────┐
|
87 | ┌─────────────────┐ ┌─────────────────┐
|
88 | │ /blocks │ │ /datastore │
|
89 | ├─────────────────┤ ├─────────────────┤
|
90 | │ Datastore │ │ LevelDatastore │
|
91 | └─────────────────┘ └─────────────────┘
|
92 |
|
93 | ┌────────────────────────────────────────┐ ┌────────────────────────────────────────┐
|
94 | │ IPFSRepo - Default Node.js │ │ IPFSRepo - Default Browser │
|
95 | └────────────────────────────────────────┘ └────────────────────────────────────────┘
|
96 | ┌─────────────────┐ ┌─────────────────┐
|
97 | │ / │ │ / │
|
98 | ├─────────────────┤ ├─────────────────┤
|
99 | │ FsDatastore │ │ IdbDatastore │
|
100 | └─────────────────┘ └─────────────────┘
|
101 | ┌───────────┴───────────┐ ┌───────────┴───────────┐
|
102 | ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
103 | │ /blocks │ │ /datastore │ │ /blocks │ │ /datastore │
|
104 | ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤
|
105 | │ FlatfsDatastore │ │LevelDBDatastore │ │ IdbDatastore │ │ IdbDatastore │
|
106 | └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
|
107 | ```
|
108 |
|
109 | This provides a well defined interface for creating and interacting with an IPFS repo.
|
110 |
|
111 | ## Install
|
112 |
|
113 | ### npm
|
114 |
|
115 | ```sh
|
116 | > npm install ipfs-repo
|
117 | ```
|
118 |
|
119 | ### Use in Node.js
|
120 |
|
121 | ```js
|
122 | var IPFSRepo = require('ipfs-repo')
|
123 | ```
|
124 |
|
125 | ### Use in a browser with browserify, webpack or any other bundler
|
126 |
|
127 | ```js
|
128 | var IPFSRepo = require('ipfs-repo')
|
129 | ```
|
130 |
|
131 | ### Use in a browser Using a script tag
|
132 |
|
133 | Loading this module through a script tag will make the `IpfsRepo` obj available in the global namespace.
|
134 |
|
135 | ```html
|
136 | <script src="https://unpkg.com/ipfs-repo/dist/index.min.js"></script>
|
137 | <!-- OR -->
|
138 | <script src="https://unpkg.com/ipfs-repo/dist/index.js"></script>
|
139 | ```
|
140 |
|
141 | ## Usage
|
142 |
|
143 | Example:
|
144 |
|
145 | ```js
|
146 | const Repo = require('ipfs-repo')
|
147 | const repo = new Repo('/tmp/ipfs-repo')
|
148 |
|
149 | await repo.init({ cool: 'config' })
|
150 | await repo.open()
|
151 | console.log('repo is ready')
|
152 | ```
|
153 |
|
154 | This now has created the following structure, either on disk or as an in memory representation:
|
155 |
|
156 | ```
|
157 | ├── blocks
|
158 | │ ├── SHARDING
|
159 | │ └── _README
|
160 | ├── config
|
161 | ├── datastore
|
162 | ├── keys
|
163 | └── version
|
164 | ```
|
165 |
|
166 | ## API
|
167 |
|
168 | ### Setup
|
169 |
|
170 | #### `new Repo(path[, options])`
|
171 |
|
172 | Creates an IPFS Repo.
|
173 |
|
174 | Arguments:
|
175 |
|
176 | * `path` (string, mandatory): the path for this repo
|
177 | * `options` (object, optional): may contain the following values
|
178 | * `autoMigrate` (bool, defaults to `true`): controls automatic migrations of repository.
|
179 | * `lock` ([Lock](#lock) or string *Deprecated*): what type of lock to use. Lock has to be acquired when opening. string can be `"fs"` or `"memory"`.
|
180 | * `storageBackends` (object, optional): may contain the following values, which should each be a class implementing the [datastore interface](https://github.com/ipfs/interface-datastore#readme):
|
181 | * `root` (defaults to [`datastore-fs`](https://github.com/ipfs/js-datastore-fs#readme) in Node.js and [`datastore-level`](https://github.com/ipfs/js-datastore-level#readme) in the browser). Defines the back-end type used for gets and puts of values at the root (`repo.set()`, `repo.get()`)
|
182 | * `blocks` (defaults to [`datastore-fs`](https://github.com/ipfs/js-datastore-fs#readme) in Node.js and [`datastore-level`](https://github.com/ipfs/js-datastore-level#readme) in the browser). Defines the back-end type used for gets and puts of values at `repo.blocks`.
|
183 | * `keys` (defaults to [`datastore-fs`](https://github.com/ipfs/js-datastore-fs#readme) in Node.js and [`datastore-level`](https://github.com/ipfs/js-datastore-level#readme) in the browser). Defines the back-end type used for gets and puts of encrypted keys at `repo.keys`
|
184 | * `datastore` (defaults to [`datastore-level`](https://github.com/ipfs/js-datastore-level#readme)). Defines the back-end type used as the key-value store used for gets and puts of values at `repo.datastore`.
|
185 |
|
186 | ```js
|
187 | const repo = new Repo('path/to/repo')
|
188 | ```
|
189 |
|
190 | #### `Promise repo.init()`
|
191 |
|
192 | Creates the necessary folder structure inside the repo
|
193 |
|
194 | #### `Promise repo.open()`
|
195 |
|
196 | [Locks](https://en.wikipedia.org/wiki/Record_locking) the repo to prevent conflicts arising from simultaneous access
|
197 |
|
198 | #### `Promise repo.close()`
|
199 |
|
200 | Unlocks the repo.
|
201 |
|
202 | #### `Promise<boolean> repo.exists()`
|
203 |
|
204 | Tells whether this repo exists or not. Returned promise resolves to a `boolean`
|
205 |
|
206 | #### `Promise<Boolean> repo.isInitialized()`
|
207 |
|
208 | The returned promise resolves to `false` if the repo has not been initialized and `true` if it has
|
209 |
|
210 | ### Repos
|
211 |
|
212 | Root repo:
|
213 |
|
214 | #### `Promise repo.put(key, value:Buffer)`
|
215 |
|
216 | Put a value at the root of the repo
|
217 |
|
218 | * `key` can be a buffer, a string or a [Key][]
|
219 |
|
220 | #### `Promise<Buffer> repo.get(key)`
|
221 |
|
222 | Get a value at the root of the repo
|
223 |
|
224 | * `key` can be a buffer, a string or a [Key][]
|
225 |
|
226 | ### Blocks
|
227 |
|
228 | #### `Promise<Block> repo.blocks.put(block:Block)`
|
229 |
|
230 | * `block` should be of type [Block][]
|
231 |
|
232 | #### `AsyncIterator<Block> repo.blocks.putMany(source)`
|
233 |
|
234 | Put many blocks.
|
235 |
|
236 | * `source` should be an AsyncIterable that yields entries of type [Block][]
|
237 |
|
238 | #### `Promise<Buffer> repo.blocks.get(cid)`
|
239 |
|
240 | Get block.
|
241 |
|
242 | * `cid` is the content id of type [CID][]
|
243 |
|
244 | #### `AsyncIterable<Buffer> repo.blocks.getMany(source)`
|
245 |
|
246 | Get block.
|
247 |
|
248 | * `source` should be an AsyncIterable that yields entries of type [CID][]
|
249 |
|
250 | #### `Promise<CID> repo.blocks.delete(cid:CID)`
|
251 |
|
252 | * `cid` should be of the type [CID][]
|
253 |
|
254 | Delete a block
|
255 |
|
256 | #### `AsyncIterator<CID> repo.blocks.deleteMany(source)`
|
257 |
|
258 | * `source` should be an Iterable or AsyncIterable that yields entries of the type [CID][]
|
259 |
|
260 | Delete many blocks
|
261 |
|
262 | ### Datastore
|
263 |
|
264 | #### `repo.datastore`
|
265 |
|
266 | This contains a full implementation of [the `interface-datastore` API](https://github.com/ipfs/interface-datastore#api).
|
267 |
|
268 | ### Config
|
269 |
|
270 | Instead of using `repo.set('config')` this exposes an API that allows you to set and get a decoded config object, as well as, in a safe manner, change any of the config values individually.
|
271 |
|
272 | #### `Promise repo.config.set(key:string, value)`
|
273 |
|
274 | Set a config value. `value` can be any object that is serializable to JSON.
|
275 |
|
276 | * `key` is a string specifying the object path. Example:
|
277 |
|
278 | ```js
|
279 | await repo.config.set('a.b.c', 'c value')
|
280 | const config = await repo.config.get()
|
281 | assert.equal(config.a.b.c, 'c value')
|
282 | ```
|
283 |
|
284 | #### `Promise repo.config.replace(value)`
|
285 |
|
286 | Set the whole config value. `value` can be any object that is serializable to JSON.
|
287 |
|
288 | #### `Promise<?> repo.config.get(key:string)`
|
289 |
|
290 | Get a config value. Returned promise resolves to the same type that was set before.
|
291 |
|
292 | * `key` is a string specifying the object path. Example:
|
293 |
|
294 | ```js
|
295 | const value = await repo.config.get('a.b.c')
|
296 | console.log('config.a.b.c = ', value)
|
297 | ```
|
298 |
|
299 | #### `Promise<Object> repo.config.getAll()`
|
300 |
|
301 | Get the entire config value.
|
302 |
|
303 | #### `Promise<boolean> repo.config.exists()`
|
304 |
|
305 | Whether the config sub-repo exists.
|
306 |
|
307 | ### Version
|
308 |
|
309 | #### `Promise<Number> repo.version.get()`
|
310 |
|
311 | Gets the repo version (an integer).
|
312 |
|
313 | #### `Promise repo.version.set (version:Number)`
|
314 |
|
315 | Sets the repo version
|
316 |
|
317 | ### API Addr
|
318 |
|
319 | #### `Promise<String> repo.apiAddr.get()`
|
320 |
|
321 | Gets the API address.
|
322 |
|
323 | #### `Promise repo.apiAddr.set(value)`
|
324 |
|
325 | Sets the API address.
|
326 |
|
327 | * `value` should be a [Multiaddr][] or a String representing a valid one.
|
328 |
|
329 | ### Status
|
330 |
|
331 | #### `Promise<Object> repo.stat()`
|
332 |
|
333 | Gets the repo status.
|
334 |
|
335 | Returned promise resolves to an `Object` with the following keys:
|
336 |
|
337 | - `numObjects`
|
338 | - `repoPath`
|
339 | - `repoSize`
|
340 | - `version`
|
341 | - `storageMax`
|
342 |
|
343 | ### Lock
|
344 |
|
345 | IPFS Repo comes with two built in locks: memory and fs. These can be imported via the following:
|
346 |
|
347 | ```js
|
348 | const fsLock = require('ipfs-repo/src/lock') // Default in Node.js
|
349 | const memoryLock = require('ipfs-repo/src/lock-memory') // Default in browser
|
350 | ```
|
351 |
|
352 | You can also provide your own custom Lock. It must be an object with the following interface:
|
353 |
|
354 | #### `Promise lock.lock(dir)`
|
355 |
|
356 | Sets the lock if one does not already exist. If a lock already exists, should throw an error.
|
357 |
|
358 | `dir` is a string to the directory the lock should be created at. The repo typically creates the lock at its root.
|
359 |
|
360 | Returns `closer`, where `closer` has a `close` method for removing the lock.
|
361 |
|
362 | #### `Promise closer.close()`
|
363 |
|
364 | Closes the lock created by `lock.open`
|
365 |
|
366 | If no error was thrown, the lock was successfully removed.
|
367 |
|
368 | #### `Promise<boolean> lock.locked(dir)`
|
369 |
|
370 | Checks the existence of the lock.
|
371 |
|
372 | `dir` is the path to the directory to check for the lock. The repo typically checks for the lock at its root.
|
373 |
|
374 | Returned promise resolves to a `boolean` indicating the existence of the lock.
|
375 |
|
376 | ## Notes
|
377 |
|
378 | - [Explanation of how repo is structured](https://github.com/ipfs/js-ipfs-repo/pull/111#issuecomment-279948247)
|
379 |
|
380 | ### Migrations
|
381 |
|
382 | When there is a new repo migration and the version of repo is increased, don't
|
383 | forget to propagate the changes into the test repo (`test/test-repo`).
|
384 |
|
385 | **For tools that run mainly in the browser environment, be aware that disabling automatic
|
386 | migrations leaves the user with no way to run the migrations because there is no CLI in the browser. In such
|
387 | a case, you should provide a way to trigger migrations manually.**
|
388 |
|
389 | ## Contribute
|
390 |
|
391 | There are some ways you can make this module better:
|
392 |
|
393 | - Consult our [open issues](https://github.com/ipfs/js-ipfs-repo/issues) and take on one of them
|
394 | - Help our tests reach 100% coverage!
|
395 |
|
396 | This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
397 |
|
398 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
|
399 |
|
400 | ## License
|
401 |
|
402 | [MIT](LICENSE)
|
403 |
|
404 | [CID]: https://github.com/multiformats/js-cid
|
405 | [Key]: https://github.com/ipfs/interface-datastore#keys
|
406 | [Block]: https://github.com/ipld/js-ipld-block
|
407 | [Multiaddr]: https://github.com/multiformats/js-multiaddr
|