Skip to content

Commit 785116a

Browse files
authored
refactor: make add only work on single items (#3167)
`ipfs.add` now returns single items only. Where a source would result in multiple items returned, only the last item is returned. As a first pass `ipfs.add` still takes multiple items but only accepting single items is documented and considered supported. In the future it may throw if multiple items are passed. `ipfs.addAll` retains the previous behaviour of `ipfs.add`. Examples: ```javascript const { cid, path, mode, mtime } = await ipfs.add('Hello world') ``` ```javascript const { cid } = await ipfs.add(Uint8Array.from([0, 1, 2])) ``` ```javascript const { cid } = await ipfs.add(fs.createReadStream('/path/to/file.txt')) ``` ```javascript const { cid } = await ipfs.add({ path: '/foo/bar/baz.txt', content: 'File content' }) // Creates a DAG with multiple levels of directories. // The returned cid is the CID for the root directory /foo // You can retrieve the file content with an IPFS path for await (const buf of ipfs.cat(`/ipfs/${cid}/bar/baz.txt`)) { ... } // Or get the CID of the nested file with ipfs.files.stat const { cid: fileCid } = await ipfs.files.stat(`/ipfs/${cid}/bar/baz.txt`) // or ipfs.dag.resolve const { cid: fileCid } = await ipfs.dag.resolve(`/ipfs/${cid}/bar/baz.txt`) ``` ```javascript // To have `/foo` included in the ipfs path, wrap it in a directory: const { cid } = await ipfs.add({ path: '/foo/bar/baz.txt', content: 'File content' }, { wrapWithDirectory: true }) for await (const buf of ipfs.cat(`/ipfs/${cid}/foo/bar/baz.txt`)) { ... } ``` BREAKING CHANGES: - `ipfs.add` only works on single items - a Uint8Array, a String, an AsyncIterable<Uint8Array> etc - `ipfs.addAll` works on multiple items
1 parent aaf0c1a commit 785116a

File tree

7 files changed

+83
-67
lines changed

7 files changed

+83
-67
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ const IpfsHttpClient = require('ipfs-http-client')
147147
const { globSource } = IpfsHttpClient
148148
const ipfs = IpfsHttpClient()
149149

150-
for await (const file of ipfs.add(globSource('./docs', { recursive: true }))) {
151-
console.log(file)
152-
}
150+
const file = await ipfs.add(globSource('./docs', { recursive: true }))
151+
console.log(file)
152+
153153
/*
154154
{
155155
path: 'docs/assets/anchor.js',
@@ -182,9 +182,9 @@ const IpfsHttpClient = require('ipfs-http-client')
182182
const { urlSource } = IpfsHttpClient
183183
const ipfs = IpfsHttpClient()
184184

185-
for await (const file of ipfs.add(urlSource('https://ipfs.io/images/ipfs-logo.svg'))) {
186-
console.log(file)
187-
}
185+
const file = await ipfs.add(urlSource('https://ipfs.io/images/ipfs-logo.svg'))
186+
console.log(file)
187+
188188
/*
189189
{
190190
path: 'ipfs-logo.svg',

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"ipld-dag-pb": "^0.18.5",
5454
"ipld-raw": "^5.0.0",
5555
"iso-url": "^0.4.7",
56+
"it-last": "^1.0.1",
5657
"it-tar": "^1.2.2",
5758
"it-to-buffer": "^1.0.0",
5859
"it-to-stream": "^0.1.1",
@@ -72,7 +73,7 @@
7273
"cross-env": "^7.0.0",
7374
"go-ipfs": "^0.6.0",
7475
"interface-ipfs-core": "^0.137.0",
75-
"ipfsd-ctl": "^4.1.1",
76+
"ipfsd-ctl": "^5.0.0",
7677
"it-all": "^1.0.1",
7778
"it-concat": "^1.0.0",
7879
"it-pipe": "^1.1.0",

src/add-all.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use strict'
2+
3+
const CID = require('cids')
4+
const toCamel = require('./lib/object-to-camel')
5+
const configure = require('./lib/configure')
6+
const multipartRequest = require('./lib/multipart-request')
7+
const toUrlSearchParams = require('./lib/to-url-search-params')
8+
const anySignal = require('any-signal')
9+
const AbortController = require('abort-controller')
10+
11+
module.exports = configure((api) => {
12+
return async function * addAll (input, options = {}) {
13+
const progressFn = options.progress
14+
15+
// allow aborting requests on body errors
16+
const controller = new AbortController()
17+
const signal = anySignal([controller.signal, options.signal])
18+
19+
const res = await api.post('add', {
20+
searchParams: toUrlSearchParams({
21+
'stream-channels': true,
22+
...options,
23+
progress: Boolean(progressFn)
24+
}),
25+
timeout: options.timeout,
26+
signal,
27+
...(
28+
await multipartRequest(input, controller, options.headers)
29+
)
30+
})
31+
32+
for await (let file of res.ndjson()) {
33+
file = toCamel(file)
34+
35+
if (progressFn && file.bytes) {
36+
progressFn(file.bytes)
37+
} else {
38+
yield toCoreInterface(file)
39+
}
40+
}
41+
}
42+
})
43+
44+
function toCoreInterface ({ name, hash, size, mode, mtime, mtimeNsecs }) {
45+
const output = {
46+
path: name,
47+
cid: new CID(hash),
48+
size: parseInt(size)
49+
}
50+
51+
if (mode != null) {
52+
output.mode = parseInt(mode, 8)
53+
}
54+
55+
if (mtime != null) {
56+
output.mtime = {
57+
secs: mtime,
58+
nsecs: mtimeNsecs || 0
59+
}
60+
}
61+
62+
return output
63+
}

src/add.js

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,15 @@
11
'use strict'
22

3-
const CID = require('cids')
4-
const toCamel = require('./lib/object-to-camel')
3+
const addAll = require('./add-all')
4+
const last = require('it-last')
55
const configure = require('./lib/configure')
6-
const multipartRequest = require('./lib/multipart-request')
7-
const toUrlSearchParams = require('./lib/to-url-search-params')
8-
const anySignal = require('any-signal')
9-
const AbortController = require('abort-controller')
106

11-
module.exports = configure((api) => {
12-
return async function * add (input, options = {}) {
13-
const progressFn = options.progress
7+
module.exports = (options) => {
8+
const all = addAll(options)
149

15-
// allow aborting requests on body errors
16-
const controller = new AbortController()
17-
const signal = anySignal([controller.signal, options.signal])
18-
19-
const res = await api.post('add', {
20-
searchParams: toUrlSearchParams({
21-
'stream-channels': true,
22-
...options,
23-
progress: Boolean(progressFn)
24-
}),
25-
timeout: options.timeout,
26-
signal,
27-
...(
28-
await multipartRequest(input, controller, options.headers)
29-
)
30-
})
31-
32-
for await (let file of res.ndjson()) {
33-
file = toCamel(file)
34-
35-
if (progressFn && file.bytes) {
36-
progressFn(file.bytes)
37-
} else {
38-
yield toCoreInterface(file)
39-
}
40-
}
41-
}
42-
})
43-
44-
function toCoreInterface ({ name, hash, size, mode, mtime, mtimeNsecs }) {
45-
const output = {
46-
path: name,
47-
cid: new CID(hash),
48-
size: parseInt(size)
49-
}
50-
51-
if (mode != null) {
52-
output.mode = parseInt(mode, 8)
53-
}
54-
55-
if (mtime != null) {
56-
output.mtime = {
57-
secs: mtime,
58-
nsecs: mtimeNsecs || 0
10+
return configure(() => {
11+
return async function add (input, options = {}) { // eslint-disable-line require-await
12+
return last(all(input, options))
5913
}
60-
}
61-
62-
return output
14+
})(options)
6315
}

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const urlSource = require('ipfs-utils/src/files/url-source')
2121
function ipfsClient (options = {}) {
2222
return {
2323
add: require('./add')(options),
24+
addAll: require('./add-all')(options),
2425
bitswap: require('./bitswap')(options),
2526
block: require('./block')(options),
2627
bootstrap: require('./bootstrap')(options),

test/get.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('.get (specific go-ipfs features)', function () {
2727

2828
before(async () => {
2929
ipfs = (await f.spawn()).api
30-
await all(ipfs.add(smallFile.data))
30+
await ipfs.add(smallFile.data)
3131
})
3232

3333
after(() => f.clean())
@@ -65,7 +65,7 @@ describe('.get (specific go-ipfs features)', function () {
6565
const subdir = 'tmp/c++files'
6666
const expectedCid = 'QmPkmARcqjo5fqK1V1o8cFsuaXxWYsnwCNLJUYS4KeZyff'
6767
const path = `${subdir}/${filename}`
68-
const files = await all(ipfs.add([{
68+
const files = await all(ipfs.addAll([{
6969
path,
7070
content: Buffer.from(path)
7171
}]))

test/log.spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
'use strict'
44

55
const { expect } = require('interface-ipfs-core/src/utils/mocha')
6-
const all = require('it-all')
76
const { Buffer } = require('buffer')
87
const f = require('./utils/factory')()
98

@@ -21,7 +20,7 @@ describe('.log', function () {
2120
it('.log.tail', async () => {
2221
const i = setInterval(async () => {
2322
try {
24-
await all(ipfs.add(Buffer.from('just adding some data to generate logs')))
23+
await ipfs.add(Buffer.from('just adding some data to generate logs'))
2524
} catch (_) {
2625
// this can error if the test has finished and we're shutting down the node
2726
}

0 commit comments

Comments
 (0)