Skip to content

Commit f6fce83

Browse files
authored
feat: make ipfs.get output tarballs (#3785)
`ipfs get` on the cli writes files/folders to disk, also tarballs and gzipped tarballs. Also gzipped individual files. In the API it's very similar to `ipfs.ls`. Make it more like the cli so there's a clear reason to use one or the other. Also adds types to `interface-ipfs-core` to better catch broken types in `ipfs-core-types`. BREAKING CHANGE: the output type of `ipfs.get` has changed and the `recursive` option has been removed from `ipfs.ls` since it was not supported everywhere
1 parent 1b96fd1 commit f6fce83

20 files changed

+86
-173
lines changed

package.json

+2-5
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"test:electron-renderer": "aegir test -t electron-renderer",
3939
"test:chrome": "aegir test -t browser -t webworker -- --browsers ChromeHeadless",
4040
"test:firefox": "aegir test -t browser -t webworker -- --browsers FirefoxHeadless",
41-
"lint": "aegir lint",
41+
"lint": "aegir ts -p check && aegir lint",
4242
"coverage": "npx nyc -r html npm run test:node -- --bail",
4343
"clean": "rimraf ./dist",
4444
"dep-check": "aegir dep-check -i ipfs-core -i rimraf -i ipfs-core-types -i abort-controller"
@@ -56,8 +56,6 @@
5656
"ipfs-utils": "^8.1.4",
5757
"it-first": "^1.0.6",
5858
"it-last": "^1.0.4",
59-
"it-map": "^1.0.4",
60-
"it-tar": "^3.0.0",
6159
"it-to-stream": "^1.0.0",
6260
"merge-options": "^3.0.4",
6361
"multiaddr": "^10.0.0",
@@ -72,9 +70,8 @@
7270
"aegir": "^34.0.2",
7371
"delay": "^5.0.0",
7472
"go-ipfs": "0.8.0",
75-
"ipfsd-ctl": "^9.0.0",
73+
"ipfsd-ctl": "^10.0.3",
7674
"it-all": "^1.0.4",
77-
"it-concat": "^2.0.0",
7875
"it-first": "^1.0.4",
7976
"nock": "^13.0.2",
8077
"p-defer": "^3.0.0",

src/config/replace.js

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ module.exports = configure(api => {
2121
const controller = new AbortController()
2222
const signal = abortSignal(controller.signal, options.signal)
2323

24-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
2524
const res = await api.post('config/replace', {
2625
timeout: options.timeout,
2726
signal,

src/dag/put.js

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ module.exports = (codecs, options) => {
3636
const controller = new AbortController()
3737
const signal = abortSignal(controller.signal, settings.signal)
3838

39-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
4039
const res = await api.post('dag/put', {
4140
timeout: settings.timeout,
4241
signal,

src/dht/put.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const toUrlSearchParams = require('../lib/to-url-search-params')
77
const multipartRequest = require('../lib/multipart-request')
88
const abortSignal = require('../lib/abort-signal')
99
const { AbortController } = require('native-abort-controller')
10+
const uint8ArrayToString = require('uint8arrays/to-string')
1011

1112
/**
1213
* @typedef {import('../types').HTTPClientExtraOptions} HTTPClientExtraOptions
@@ -22,12 +23,11 @@ module.exports = configure(api => {
2223
const controller = new AbortController()
2324
const signal = abortSignal(controller.signal, options.signal)
2425

25-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
2626
const res = await api.post('dht/put', {
2727
timeout: options.timeout,
2828
signal,
2929
searchParams: toUrlSearchParams({
30-
arg: key,
30+
arg: uint8ArrayToString(key),
3131
...options
3232
}),
3333
...(

src/files/write.js

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ module.exports = configure(api => {
2222
const controller = new AbortController()
2323
const signal = abortSignal(controller.signal, options.signal)
2424

25-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
2625
const res = await api.post('files/write', {
2726
timeout: options.timeout,
2827
signal,

src/get.js

+13-27
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
'use strict'
22

3-
// @ts-ignore no types
4-
const Tar = require('it-tar')
53
const { CID } = require('multiformats/cid')
64
const configure = require('./lib/configure')
75
const toUrlSearchParams = require('./lib/to-url-search-params')
8-
const map = require('it-map')
96

107
/**
118
* @typedef {import('./types').HTTPClientExtraOptions} HTTPClientExtraOptions
@@ -17,36 +14,25 @@ module.exports = configure(api => {
1714
* @type {RootAPI["get"]}
1815
*/
1916
async function * get (path, options = {}) {
17+
/** @type {Record<string, any>} */
18+
const opts = {
19+
arg: `${path instanceof Uint8Array ? CID.decode(path) : path}`,
20+
...options
21+
}
22+
23+
if (opts.compressionLevel) {
24+
opts['compression-level'] = opts.compressionLevel
25+
delete opts.compressionLevel
26+
}
27+
2028
const res = await api.post('get', {
2129
timeout: options.timeout,
2230
signal: options.signal,
23-
searchParams: toUrlSearchParams({
24-
arg: `${path instanceof Uint8Array ? CID.decode(path) : path}`,
25-
...options
26-
}),
31+
searchParams: toUrlSearchParams(opts),
2732
headers: options.headers
2833
})
2934

30-
const extractor = Tar.extract()
31-
32-
for await (const { header, body } of extractor(res.iterator())) {
33-
if (header.type === 'directory') {
34-
// @ts-ignore - Missing the following properties from type 'Directory':
35-
// cid, name, size, depthts
36-
yield {
37-
type: 'dir',
38-
path: header.name
39-
}
40-
} else {
41-
// @ts-ignore - Missing the following properties from type 'File':
42-
// cid, name, size, depthts
43-
yield {
44-
type: 'file',
45-
path: header.name,
46-
content: map(body, (chunk) => chunk.slice()) // convert bl to Buffer/Uint8Array
47-
}
48-
}
49-
}
35+
yield * res.iterator()
5036
}
5137

5238
return get

src/index.js

+31-15
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@ const Multihashes = require('ipfs-core-utils/src/multihashes')
1010
const Multibases = require('ipfs-core-utils/src/multibases')
1111
const dagPb = require('@ipld/dag-pb')
1212
const dagCbor = require('@ipld/dag-cbor')
13-
const raw = require('multiformats/codecs/raw')
14-
const json = require('multiformats/codecs/json')
15-
const { sha256, sha512 } = require('multiformats/hashes/sha2')
1613
const { identity } = require('multiformats/hashes/identity')
17-
const { base58btc } = require('multiformats/bases/base58')
14+
const { bases, hashes, codecs } = require('multiformats/basics')
1815

1916
/**
2017
* @typedef {import('./types').EndpointConfig} EndpointConfig
2118
* @typedef {import('./types').Options} Options
2219
* @typedef {import('multiformats/codecs/interface').BlockCodec<any, any>} BlockCodec
20+
* @typedef {import('multiformats/hashes/interface').MultihashHasher} MultihashHasher
21+
* @typedef {import('multiformats/bases/interface').MultibaseCodec<any>} MultibaseCodec
2322
* @typedef {import('./types').IPFSHTTPClient} IPFSHTTPClient
2423
*/
2524

@@ -37,16 +36,33 @@ function create (options = {}) {
3736
decode: (id) => id
3837
}
3938

40-
const bases = new Multibases({
41-
bases: [base58btc].concat(options.ipld && options.ipld.bases ? options.ipld.bases : []),
39+
/** @type {MultibaseCodec[]} */
40+
const multibaseCodecs = Object.values(bases);
41+
42+
(options.ipld && options.ipld.bases ? options.ipld.bases : []).forEach(base => multibaseCodecs.push(base))
43+
44+
const multibases = new Multibases({
45+
bases: multibaseCodecs,
4246
loadBase: options.ipld && options.ipld.loadBase
4347
})
44-
const codecs = new Multicodecs({
45-
codecs: [dagPb, dagCbor, raw, json, id].concat(options.ipld?.codecs || []),
48+
49+
/** @type {BlockCodec[]} */
50+
const blockCodecs = Object.values(codecs);
51+
52+
[dagPb, dagCbor, id].concat((options.ipld && options.ipld.codecs) || []).forEach(codec => blockCodecs.push(codec))
53+
54+
const multicodecs = new Multicodecs({
55+
codecs: blockCodecs,
4656
loadCodec: options.ipld && options.ipld.loadCodec
4757
})
48-
const hashers = new Multihashes({
49-
hashers: [sha256, sha512, identity].concat(options.ipld && options.ipld.hashers ? options.ipld.hashers : []),
58+
59+
/** @type {MultihashHasher[]} */
60+
const multihashHashers = Object.values(hashes);
61+
62+
(options.ipld && options.ipld.hashers ? options.ipld.hashers : []).forEach(hasher => multihashHashers.push(hasher))
63+
64+
const multihashes = new Multihashes({
65+
hashers: multihashHashers,
5066
loadHasher: options.ipld && options.ipld.loadHasher
5167
})
5268

@@ -60,7 +76,7 @@ function create (options = {}) {
6076
cat: require('./cat')(options),
6177
commands: require('./commands')(options),
6278
config: require('./config')(options),
63-
dag: require('./dag')(codecs, options),
79+
dag: require('./dag')(multicodecs, options),
6480
dht: require('./dht')(options),
6581
diag: require('./diag')(options),
6682
dns: require('./dns')(options),
@@ -74,7 +90,7 @@ function create (options = {}) {
7490
ls: require('./ls')(options),
7591
mount: require('./mount')(options),
7692
name: require('./name')(options),
77-
object: require('./object')(codecs, options),
93+
object: require('./object')(multicodecs, options),
7894
pin: require('./pin')(options),
7995
ping: require('./ping')(options),
8096
pubsub: require('./pubsub')(options),
@@ -86,9 +102,9 @@ function create (options = {}) {
86102
stop: require('./stop')(options),
87103
swarm: require('./swarm')(options),
88104
version: require('./version')(options),
89-
bases,
90-
codecs,
91-
hashers
105+
bases: multibases,
106+
codecs: multicodecs,
107+
hashers: multihashes
92108
}
93109

94110
return client

src/lib/multipart-request.browser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// Import browser version otherwise electron-renderer will end up with node
44
// version and fail.
5-
const normaliseInput = require('ipfs-core-utils/src/files/normalise-input/index.browser')
5+
const { normaliseInput } = require('ipfs-core-utils/src/files/normalise-input/index.browser')
66
const modeToString = require('./mode-to-string')
77

88
/**

src/lib/multipart-request.node.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const normaliseInput = require('ipfs-core-utils/src/files/normalise-input')
3+
const { normaliseInput } = require('ipfs-core-utils/src/files/normalise-input')
44
const { nanoid } = require('nanoid')
55
const modeToString = require('./mode-to-string')
66
const merge = require('merge-options').bind({ ignoreUndefined: true })

src/ls.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ module.exports = configure((api, opts) => {
3939
path: pathStr + (link.Name ? `/${link.Name}` : ''),
4040
size: link.Size,
4141
cid: hash,
42-
type: typeOf(link),
43-
depth: link.Depth || 1
42+
type: typeOf(link)
4443
}
4544

4645
if (link.Mode) {

src/name/publish.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = configure(api => {
1818
timeout: options.timeout,
1919
signal: options.signal,
2020
searchParams: toUrlSearchParams({
21-
arg: path,
21+
arg: `${path}`,
2222
...options
2323
}),
2424
headers: options.headers

src/object/patch/append-data.js

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ module.exports = configure(api => {
2121
const controller = new AbortController()
2222
const signal = abortSignal(controller.signal, options.signal)
2323

24-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
2524
const res = await api.post('object/patch/append-data', {
2625
timeout: options.timeout,
2726
signal,

src/object/patch/set-data.js

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ module.exports = configure(api => {
2121
const controller = new AbortController()
2222
const signal = abortSignal(controller.signal, options.signal)
2323

24-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
2524
const res = await api.post('object/patch/set-data', {
2625
timeout: options.timeout,
2726
signal,

src/pin/add-all.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const { CID } = require('multiformats/cid')
44
const configure = require('../lib/configure')
5-
const normaliseInput = require('ipfs-core-utils/src/pins/normalise-input')
5+
const { normaliseInput } = require('ipfs-core-utils/src/pins/normalise-input')
66
const toUrlSearchParams = require('../lib/to-url-search-params')
77

88
/**

src/pin/rm-all.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const { CID } = require('multiformats/cid')
44
const configure = require('../lib/configure')
5-
const normaliseInput = require('ipfs-core-utils/src/pins/normalise-input')
5+
const { normaliseInput } = require('ipfs-core-utils/src/pins/normalise-input')
66
const toUrlSearchParams = require('../lib/to-url-search-params')
77

88
/**

src/pubsub/publish.js

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ module.exports = configure(api => {
2525
const controller = new AbortController()
2626
const signal = abortSignal(controller.signal, options.signal)
2727

28-
// @ts-ignore https://github.com/ipfs/js-ipfs-utils/issues/90
2928
const res = await api.post('pubsub/pub', {
3029
timeout: options.timeout,
3130
signal,

src/pubsub/subscribe.js

+28-30
Original file line numberDiff line numberDiff line change
@@ -41,39 +41,37 @@ module.exports = (options, subsTracker) => {
4141
const ffWorkaround = setTimeout(() => done(), 1000)
4242

4343
// Do this async to not block Firefox
44-
setTimeout(() => {
45-
api.post('pubsub/sub', {
46-
timeout: options.timeout,
47-
signal: options.signal,
48-
searchParams: toUrlSearchParams({
49-
arg: topic,
50-
...options
51-
}),
52-
headers: options.headers
53-
})
54-
.catch((err) => {
55-
// Initial subscribe fail, ensure we clean up
56-
subsTracker.unsubscribe(topic, handler)
44+
api.post('pubsub/sub', {
45+
timeout: options.timeout,
46+
signal: options.signal,
47+
searchParams: toUrlSearchParams({
48+
arg: topic,
49+
...options
50+
}),
51+
headers: options.headers
52+
})
53+
.catch((err) => {
54+
// Initial subscribe fail, ensure we clean up
55+
subsTracker.unsubscribe(topic, handler)
5756

58-
fail(err)
57+
fail(err)
58+
})
59+
.then((response) => {
60+
clearTimeout(ffWorkaround)
61+
62+
if (!response) {
63+
// if there was no response, the subscribe failed
64+
return
65+
}
66+
67+
readMessages(response, {
68+
onMessage: handler,
69+
onEnd: () => subsTracker.unsubscribe(topic, handler),
70+
onError: options.onError
5971
})
60-
.then((response) => {
61-
clearTimeout(ffWorkaround)
62-
63-
if (!response) {
64-
// if there was no response, the subscribe failed
65-
return
66-
}
6772

68-
readMessages(response, {
69-
onMessage: handler,
70-
onEnd: () => subsTracker.unsubscribe(topic, handler),
71-
onError: options.onError
72-
})
73-
74-
done()
75-
})
76-
}, 0)
73+
done()
74+
})
7775

7876
return result
7977
}

src/types.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Agent as HttpAgent } from 'http'
22
import { Agent as HttpsAgent } from 'https'
33
import { Multiaddr } from 'multiaddr'
44
import type { BlockCodec } from 'multiformats/codecs/interface'
5+
import type { MultihashHasher } from 'multiformats/hashes/interface'
6+
import type { MultibaseCodec } from 'multiformats/bases/interface'
57
import type { IPFS } from 'ipfs-core-types'
68

79
export interface Options {
@@ -12,7 +14,7 @@ export interface Options {
1214
timeout?: number | string
1315
apiPath?: string
1416
url?: URL|string|Multiaddr
15-
ipld?: IPLDOptions
17+
ipld?: Partial<IPLDOptions>
1618
agent?: HttpAgent | HttpsAgent
1719
}
1820

0 commit comments

Comments
 (0)