Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit 6c17081

Browse files
author
Alan Shaw
committed
feat: enable pubsub in the browser
License: MIT Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
1 parent b65bba3 commit 6c17081

19 files changed

+450
-303
lines changed

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"browser": {
1818
"glob": false,
1919
"fs": false,
20-
"stream": "readable-stream"
20+
"stream": "readable-stream",
21+
"./src/lib/configure.js": "./src/lib/configure.browser.js"
2122
},
2223
"repository": "github:ipfs/js-ipfs-http-client",
2324
"scripts": {
@@ -33,6 +34,7 @@
3334
"coverage": "npx nyc -r html npm run test:node -- --bail"
3435
},
3536
"dependencies": {
37+
"abort-controller": "^3.0.0",
3638
"async": "^2.6.1",
3739
"bignumber.js": "^9.0.0",
3840
"bl": "^3.0.0",
@@ -44,6 +46,7 @@
4446
"detect-node": "^2.0.4",
4547
"end-of-stream": "^1.4.1",
4648
"err-code": "^1.1.2",
49+
"explain-error": "^1.0.4",
4750
"flatmap": "0.0.3",
4851
"glob": "^7.1.3",
4952
"ipfs-block": "~0.8.1",
@@ -56,6 +59,7 @@
5659
"is-stream": "^2.0.0",
5760
"iso-stream-http": "~0.1.2",
5861
"iso-url": "~0.4.6",
62+
"iterable-ndjson": "^1.1.0",
5963
"just-kebab-case": "^1.1.0",
6064
"just-map-keys": "^1.1.0",
6165
"kind-of": "^6.0.2",
@@ -65,6 +69,7 @@
6569
"multicodec": "~0.5.1",
6670
"multihashes": "~0.4.14",
6771
"ndjson": "github:hugomrdias/ndjson#feat/readable-stream3",
72+
"node-fetch": "^2.6.0",
6873
"once": "^1.4.0",
6974
"peer-id": "~0.12.2",
7075
"peer-info": "~0.15.1",
@@ -74,6 +79,7 @@
7479
"pull-to-stream": "~0.1.1",
7580
"pump": "^3.0.0",
7681
"qs": "^6.5.2",
82+
"querystring": "^0.2.0",
7783
"readable-stream": "^3.1.1",
7884
"stream-to-pull-stream": "^1.7.2",
7985
"tar-stream": "^2.0.1",

src/lib/callbackify.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict'
2+
3+
module.exports = (fn, opts) => {
4+
opts = opts || {}
5+
// Min number of non-callback args
6+
opts.minArgs = opts.minArgs == null ? 0 : opts.minArgs
7+
8+
return (...args) => {
9+
const cb = args[args.length - 1]
10+
11+
if (typeof cb !== 'function' || args.length === opts.minArgs) {
12+
return fn(...args)
13+
}
14+
15+
fn(...args.slice(0, -1)).then(res => cb(null, res), cb)
16+
}
17+
}

src/lib/configure.browser.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict'
2+
/* eslint-env browser */
3+
4+
const { toUri } = require('./multiaddr')
5+
6+
// Set default configuration and call create function with them
7+
module.exports = create => config => {
8+
config = config || {}
9+
10+
if (typeof config === 'string') {
11+
config = { apiAddr: config }
12+
}
13+
14+
// Multiaddr instance
15+
if (config.constructor && config.constructor.isMultiaddr) {
16+
config = { apiAddr: config }
17+
}
18+
19+
config.fetch = config.fetch || require('./fetch').fetch
20+
config.apiAddr = (config.apiAddr || getDefaultApiAddr(config)).toString()
21+
config.apiAddr = config.apiAddr.startsWith('/')
22+
? toUri(config.apiAddr)
23+
: config.apiAddr
24+
config.apiPath = config.apiPath || config['api-path'] || '/api/v0'
25+
26+
if (config.apiPath.endsWith('/')) {
27+
config.apiPath = config.apiPath.slice(0, -1)
28+
}
29+
30+
config.headers = new Headers(config.headers)
31+
32+
return create(config)
33+
}
34+
35+
function getDefaultApiAddr ({ protocol, host, port }) {
36+
if (!protocol) {
37+
protocol = location.protocol.startsWith('http')
38+
? location.protocol.split(':')[0]
39+
: 'http'
40+
}
41+
42+
host = host || location.hostname
43+
port = port || location.port
44+
45+
return `${protocol}://${host}${port ? ':' + port : ''}`
46+
}

src/lib/configure.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict'
2+
3+
const { Headers } = require('node-fetch')
4+
const { toUri } = require('./multiaddr')
5+
const pkg = require('../../package.json')
6+
7+
// Set default configuration and call create function with them
8+
module.exports = create => config => {
9+
config = config || {}
10+
11+
if (typeof config === 'string') {
12+
config = { apiAddr: config }
13+
}
14+
15+
// Multiaddr instance
16+
if (config.constructor && config.constructor.isMultiaddr) {
17+
config = { apiAddr: config }
18+
}
19+
20+
config.fetch = config.fetch || require('./fetch').fetch
21+
22+
if (config.protocol || config.host || config.port) {
23+
const port = config.port ? `:${config.port}` : ''
24+
config.apiAddr = `${config.protocol || 'http'}://${config.host || 'localhost'}${port}`
25+
}
26+
27+
config.apiAddr = (config.apiAddr || 'http://localhost:5001').toString()
28+
config.apiAddr = config.apiAddr.startsWith('/')
29+
? toUri(config.apiAddr)
30+
: config.apiAddr
31+
config.apiPath = config.apiPath || config['api-path'] || '/api/v0'
32+
33+
if (config.apiPath.endsWith('/')) {
34+
config.apiPath = config.apiPath.slice(0, -1)
35+
}
36+
37+
config.headers = new Headers(config.headers)
38+
39+
if (!config.headers.has('User-Agent')) {
40+
config.headers.append('User-Agent', `${pkg.name}/${pkg.version}`)
41+
}
42+
43+
return create(config)
44+
}

src/lib/fetch.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict'
2+
3+
const explain = require('explain-error')
4+
5+
exports.fetch = require('node-fetch')
6+
7+
// Ensure fetch response is ok (200)
8+
// and if not, attempt to JSON parse body, extract error message and throw
9+
exports.ok = async res => {
10+
res = await res
11+
12+
if (!res.ok) {
13+
const { status } = res
14+
const defaultMsg = `unexpected status ${status}`
15+
let msg
16+
try {
17+
let data = await res.text()
18+
try {
19+
data = JSON.parse(data)
20+
msg = data.message || data.Message
21+
} catch (err) {
22+
msg = data
23+
}
24+
} catch (err) {
25+
throw Object.assign(explain(err, defaultMsg), { status })
26+
}
27+
throw Object.assign(new Error(msg || defaultMsg), { status })
28+
}
29+
30+
return res
31+
}
32+
33+
exports.toIterable = body => {
34+
if (body[Symbol.asyncIterator]) return body
35+
36+
if (body.getReader) {
37+
return (async function * () {
38+
const reader = body.getReader()
39+
40+
try {
41+
while (true) {
42+
const { done, value } = await reader.read()
43+
if (done) return
44+
yield value
45+
}
46+
} finally {
47+
reader.releaseLock()
48+
}
49+
})()
50+
}
51+
52+
throw new Error('unknown stream')
53+
}

src/lib/multiaddr.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Convert a multiaddr to a URI
2+
// Assumes multiaddr is in a format that can be converted to a HTTP(s) URI
3+
exports.toUri = ma => {
4+
const parts = `${ma}`.split('/')
5+
const port = getPort(parts)
6+
return `${getProtocol(parts)}://${parts[2]}${port == null ? '' : ':' + port}`
7+
}
8+
9+
function getProtocol (maParts) {
10+
return maParts.indexOf('https') === -1 ? 'http' : 'https'
11+
}
12+
13+
function getPort (maParts) {
14+
const tcpIndex = maParts.indexOf('tcp')
15+
return tcpIndex === -1 ? null : maParts[tcpIndex + 1]
16+
}

src/lib/querystring.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict'
2+
3+
const QueryString = require('querystring')
4+
5+
// Convert an object to a query string INCLUDING leading ?
6+
// Excludes null/undefined values
7+
exports.objectToQuery = obj => {
8+
if (!obj) return ''
9+
10+
const qs = Object.entries(obj).reduce((obj, [key, value]) => {
11+
if (value != null) obj[key] = value
12+
return obj
13+
}, {})
14+
15+
return Object.keys(qs).length ? `?${QueryString.stringify(qs)}` : ''
16+
}

0 commit comments

Comments
 (0)