diff --git a/examples/browser-browserify/package.json b/examples/browser-browserify/package.json index f886f49d3c..4f05bfb4a5 100644 --- a/examples/browser-browserify/package.json +++ b/examples/browser-browserify/package.json @@ -17,7 +17,7 @@ "browserify": "^16.2.3", "concat-stream": "^2.0.0", "execa": "^4.0.0", - "http-server": "^0.11.1", + "http-server": "^0.12.3", "ipfs": "^0.49.1", "test-ipfs-example": "^2.0.3" }, diff --git a/examples/browser-exchange-files/package.json b/examples/browser-exchange-files/package.json index 506ea0323c..1bfdf439de 100644 --- a/examples/browser-exchange-files/package.json +++ b/examples/browser-exchange-files/package.json @@ -13,7 +13,7 @@ "devDependencies": { "browserify": "^16.2.3", "execa": "^4.0.0", - "http-server": "^0.11.1", + "http-server": "^0.12.3", "ipfs-http-client": "^46.0.1", "uint8arrays": "^1.1.0" }, diff --git a/examples/browser-ipns-publish/.gitignore b/examples/browser-ipns-publish/.gitignore new file mode 100644 index 0000000000..46b007034c --- /dev/null +++ b/examples/browser-ipns-publish/.gitignore @@ -0,0 +1,3 @@ +bundle.js +.cache +/node_modules/ \ No newline at end of file diff --git a/examples/browser-ipns-publish/README.md b/examples/browser-ipns-publish/README.md new file mode 100644 index 0000000000..de26585fb0 --- /dev/null +++ b/examples/browser-ipns-publish/README.md @@ -0,0 +1,85 @@ +# Publish to IPNS from the browser + +> Use ipns from the browser! + +This example is a demo web application that allows you to connect to a go-IPFS node, and publish your IPNS record to the go-DHT network but using your js-ipfs private key. We'll start two IPFS nodes; one in the browser and one on a go-Server. We'll use `ipfs-http-client` to connect to the go-Node to ensure our pubsub messages are getting through, and confirm the IPNS record resolves on the go-Node. We're aiming for something like this: + +``` + +-----------+ websocket +-----------+ + | +-------------------> | + | js-ipfs | pubsub | go-ipfs | + | <-------------------+ | + +-----^-----+ +-----^-----+ + | | + | IPFS in browser | HTTP API + | | ++-------------------------------------------------+ +| Browser | ++-------------------------------------------------+ +| | | | +| | | | +| IPFS direct | | js-http-client | +| a.k.a. ipfsNode | | a.k.a. ipfsAPI | +| | | | ++-------------------------------------------------+ +``` + +## 1. Get started + +With Node.js and git installed, clone the repo and install the project dependencies: + +```sh +git clone https://github.com/ipfs/js-ipfs.git +cd examples/browser-ipns-publish +npm install # Installs browser-pubsub app dependencies +``` + +Start the example application: + +```sh +npm start +``` + +You should see something similar to the following in your terminal and the web app should now be available if you navigate to http://127.0.0.1:1234 using your browser: + +```sh +Starting up http-server, serving ./ +Available on: + http://127.0.0.1:1234 +``` + +## 2. Start two IPFS nodes + +The first node is the js-ipfs made in the browser and the demo does that for you. + +The second is a go-ipfs node on a server. To get our IPNS record to the DHT, we'll need [a server running go-IPFS](https://blog.ipfs.io/22-run-ipfs-on-a-vps/) with the API enabled on port 5001. + +Right now the easiest way to do this is to install and start a `go-ipfs` node. + +### Install and start the Go IPFS node + +Head over to https://dist.ipfs.io/#go-ipfs and hit the "Download go-ipfs" button. Extract the archive and read the instructions to install. + +After installation: + +```sh +ipfs init +# Configure CORS to allow ipfs-http-client to access this IPFS node +ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"*\"]" +# Configure go-ipfs to listen on a websocket address +npx json -I -f ~/.ipfs/config -e "this.Addresses.Swarm.push('/ip4/127.0.0.1/tcp/4003/ws')" +# Start the IPFS node, enabling pubsub and IPNS over pubsub +ipfs daemon --enable-pubsub-experiment --enable-namesys-pubsub +``` + +## 3. Open the demo in a browser and connect to the go-node + +Now, open up the demo in a browser window. + +In the "CONNECT TO GO-IPFS VIA API MULTIADDRESS" field enter `/ip4/YourServerIP/tcp/5001` (where `YourSeverIP` is your server's IP address or use `/dns4/yourdomain.com/tcp/5001`) and click connect. Once it connects, put your go-IPFS websocket address in the next field `/dns4/yourdomain.com/tcp/4003/wss/p2p/QmPeerIDHash` and hit the second "Connect" button. + +This connects the API to the go-Node and connects your js-IPFS node via websocket to the go-IPFS node so pubsub will work. + +You can choose whether to publish this record under the PeerId of the node that is running in the browser ('self') or choose to add a custom key to the IPFS keychain and publish under that instead. Either should work. + +Finally, enter `/ipfs/QmSomeHash` as the content you want to publish to IPNS. You should see the messages sent from the browser to the server appear in the logs below, ending with "Success, resolved" if it all worked. diff --git a/examples/browser-ipns-publish/index.html b/examples/browser-ipns-publish/index.html new file mode 100644 index 0000000000..ce7c5c9187 --- /dev/null +++ b/examples/browser-ipns-publish/index.html @@ -0,0 +1,209 @@ + + + + Publish to IPNS from the browser + + + + +
+ + + +

+ IPNS Publish from JS to Go Peer +

+
+
+
+ IPNS PUBLISH SET-UP STEPS +
+
+
+
+

+ Code repo for this demo + on Github +

+

+ The idea is to publish from js-ipfs so you control your own + private keys, but publish to go-ipfs to benefit from the DHT. + Since the DHT in js-ipfs is still in the experimental phase, we need to use PubSub + and have a go-ipfs node subscribed to that PubSub to get our IPNS + record onto the DHT. In order to use PubSub between these two + nodes, you'll need a websocket to connect them. +

+
+ To make this demo work, you're going to need: +
    +
  • + 1. Access to a go-ipfs node and it's API, a-la +
    /dns4/domain.com/tcp/5001
    +
  • +
  • + This is how the demo talks to the server, to ensure things + like: +
  • +
      +
    • + A) pubsub is enabled, { EXPERIMENTAL: { ipnsPubsub: true } } + and --enable-pubsub-experiment +
    • +
    • + B) go-ipfs is connected to the js-ipfs node in the browser, + via node.swarm.peers(), +
    • +
    • + C) the pubsub messages are getting through to the go node, + via node.pubsub.subscribe(). +
    • +
    +
+
    +
  • + 2. Access to a go-ipfs Websocket port, a-la +
    /dns4/domain.com/tcp/4003/wss/p2p/QmTheDomainPeerId
    +
  • +
  • + Since we need PubSub for IPNS to reach the go-IPFS node (and + further replicate through the go-DHT network) we need to + connect our pubsub enabled JS-IPFS node in the browser to our + go-IPFS node on the server. The way we connect is via + Websocket. See + this example + for reference. +
  • +
+

+ Once we can talk to go-IPFS and we're connected via Websocket, + then we can publish in our browser node, have the pubsub push it + to the go-IPFS server, and then check with the server that it's + confirmed as published. Once it's on the go-IPFS node it should + replicate throughout the rest of the DHT amongst the go-Nodes. +

+
+
+
+
+
+
+
+ 1. Connect to Go-IPFS via API MultiAddress +
+ + +
+
+
+ 2. Connect to Go-IPFS via Websocket MultiAddress (for PubSub to work) +
+ + +
+ +
+
+ 3. Choose a key: +
+
+ +
+
+
+
+ 4. IPFS hash to publish +
+ + +
+ +
+
Browser Console
+
+
+
+
Server Console
+
+
+ + + diff --git a/examples/browser-ipns-publish/index.js b/examples/browser-ipns-publish/index.js new file mode 100644 index 0000000000..7e916c5940 --- /dev/null +++ b/examples/browser-ipns-publish/index.js @@ -0,0 +1,261 @@ +"use strict"; + +const IpfsHttpClient = require("ipfs-http-client"); +const ipns = require("ipns"); +const IPFS = require("ipfs"); +const pRetry = require("p-retry"); +const last = require("it-last"); +const cryptoKeys = require("human-crypto-keys"); // { getKeyPairFromSeed } +const uint8ArrayToString = require('uint8arrays/to-string') + +const { sleep, Logger, onEnterPress, catchAndLog } = require("./util"); + +async function main() { + const apiUrlInput = document.getElementById("api-url"); + const nodeConnectBtn = document.getElementById("node-connect"); + + const peerAddrInput = document.getElementById("peer-addr"); + const wsConnectBtn = document.getElementById("peer-connect"); + + const ipnsInput = document.getElementById("topic"); + const publishBtn = document.getElementById("publish"); + + const namespace = "/record/"; + const retryOptions = { + retries: 5, + }; + + let log = Logger(document.getElementById("console")); + let sLog = Logger(document.getElementById("server-console")); + let keyName = "self"; + + let ipfsAPI; // remote server IPFS node + let ipfsBrowser; // local browser IPFS node + let peerId; + + // init local browser node right away + log(`Browser IPFS getting ready...`); + ipfsBrowser = await IPFS.create({ + pass: "01234567890123456789", + EXPERIMENTAL: { ipnsPubsub: true }, + }); + const { id } = await ipfsBrowser.id(); + log(`Browser IPFS ready! Node id: ${id}`); + document.getElementById("api-url").disabled = false; + document.getElementById("node-connect").disabled = false; + + async function nodeConnect(url) { + log(`Connecting to ${url}`); + ipfsAPI = IpfsHttpClient(url); + const { id, agentVersion } = await ipfsAPI.id(); + peerId = id; + log(`Success!`); + log(`Version ${agentVersion}`); + log(`Peer ID ${id}`); + document.getElementById("peer-addr").disabled = false; + document.getElementById("peer-connect").disabled = false; + } + + async function wsConnect(addr) { + if (!addr) throw new Error("Missing peer multiaddr"); + if (!ipfsBrowser) + throw new Error("Wait for the local IPFS node to start first"); + log(`Connecting to peer ${addr}`); + await ipfsBrowser.swarm.connect(addr); + log(`Success!`); + log("Listing swarm peers..."); + await sleep(); + const peers = await ipfsBrowser.swarm.peers(); + peers.forEach((peer) => { + //console.log(`peer: ${JSON.stringify(peer, null, 2)}`); + const fullAddr = `${peer.addr}/ipfs/${peer.peer}`; + log( + `${fullAddr}` + ); + }); + log(`(${peers.length} peers total)`); + document.getElementById("topic").disabled = false; + document.getElementById("publish").disabled = false; + } + + // Wait until a peer subscribes a topic + const waitForPeerToSubscribe = async (daemon, topic) => { + await pRetry(async () => { + const res = await daemon.pubsub.ls(); + + if (!res || !res.length || !res.includes(topic)) { + throw new Error("Could not find subscription"); + } + + return res[0]; + }, retryOptions); + }; + + // wait until a peer know about other peer to subscribe a topic + const waitForNotificationOfSubscription = (daemon, topic, peerId) => + pRetry(async () => { + const res = await daemon.pubsub.peers(topic); + + if (!res || !res.length || !res.includes(peerId)) { + throw new Error("Could not find peer subscribing"); + } + }, retryOptions); + + async function subs(node, topic, tLog) { + tLog(`Subscribing to ${topic}`); + await node.pubsub.subscribe( + topic, + (msg) => { + const from = msg.from; + const seqno = msg.seqno.toString("hex"); + + tLog( + `${new Date( + Date.now() + ).toLocaleTimeString()}\n Message ${seqno} from ${from}` + ); + + let regex = "/record/"; + if (topic.match(regex) ? topic.match(regex).length > 0 : false) { + tLog( + "\n#" + + ipns.unmarshal(msg.data).sequence.toString() + + ") Topic: " + + msg.topicIDs[0].toString() + ); + tLog("Value:\n" + ipns.unmarshal(msg.data).value.toString()); + } else { + try { + tLog(JSON.stringify(msg.data.toString(), null, 2)); + } catch (_) { + tLog(msg.data.toString("hex")); + } + } + } + ); + } + + async function createKey(keyName) { + return new Promise(async (resolve, reject) => { + try { + // quick and dirty key gen, don't do this in real life + const key = await IPFS.multihashing.digest( + Buffer.from(keyName + Math.random().toString(36).substring(2)), + "sha2-256" + ); + const keyPair = await cryptoKeys.getKeyPairFromSeed(key, "rsa"); + + // put it on the browser IPNS keychain and name it + await ipfsBrowser.key.import(keyName, keyPair.privateKey); + // now this key can be used to publish to this ipns publicKey + resolve(true); + } catch (err) { + console.log(`Error creating Key ${keyName}: \n ${err}`); + reject(false); + } + }); + } + + async function publish(content) { + if (!content) throw new Error("Missing ipns content to publish"); + if (!ipfsAPI) throw new Error("Connect to a go-server node first"); + if (!ipfsAPI.name.pubsub.state() || !ipfsBrowser.name.pubsub.state()) + throw new Error( + "IPNS Pubsub must be enabled on bother peers, use --enable-namesys-pubsub" + ); + + log(`Publish to IPNS`); // subscribes the server to our IPNS topic + + let browserNode = await ipfsBrowser.id(); + let serverNode = await ipfsAPI.id(); + + // get which key this will be publish under, self or an imported custom key + keyName = document.querySelector('input[name="keyName"]:checked').value; + let keys = { name: "self", id: browserNode.id }; // default init + + if (keyName != "self") { + if (!(await ipfsBrowser.key.list()).find((k) => k.name == keyName)) + // skip if custom key exists already + await createKey(keyName); + let r = await ipfsBrowser.key.list(); + keys = r.find((k) => k.name == keyName); + log(JSON.stringify(keys)); + } + + log(`Initial Resolve ${keys.id}`); // subscribes the server to our IPNS topic + last(ipfsAPI.name.resolve(keys.id, { stream: false })); // save the pubsub topic to the server to make them listen + + // set up the topic from ipns key + let b58 = await IPFS.multihash.fromB58String(keys.id); + const ipnsKeys = ipns.getIdKeys(b58); + const topic = `${namespace}${uint8ArrayToString(ipnsKeys.routingKey, 'base64url')}`; + + // subscribe and log on both nodes + await subs(ipfsBrowser, topic, log); // browserLog + await subs(ipfsAPI, topic, sLog); // serverLog + + // confirm they are subscribed + await waitForPeerToSubscribe(ipfsAPI, topic); // confirm topic is on THEIR list // API + await waitForNotificationOfSubscription(ipfsBrowser, topic, serverNode.id); // confirm they are on OUR list + + let remList = await ipfsAPI.pubsub.ls(); // API + if (!remList.includes(topic)) + sLog(`[Fail] !Pubsub.ls ${topic}`); + else sLog(`[Pass] Pubsub.ls`); + + let remListSubs = await ipfsAPI.name.pubsub.subs(); // API + if (!remListSubs.includes(`/ipns/${keys.id}`)) + sLog(`[Fail] !Name.Pubsub.subs ${keys.id}`); + else sLog(`[Pass] Name.Pubsub.subs`); + + // publish will send a pubsub msg to the server to update their ipns record + log(`Publishing ${content} to ${keys.name} /ipns/${keys.id}`); + const results = await ipfsBrowser.name.publish(content, { + resolve: false, + key: keyName, + }); + log(`Published ${results.name} to ${results.value}`); // + + log(`Try resolve ${keys.id} on server through API`); + + let name = await last( + ipfsAPI.name.resolve(keys.id, { + stream: false, + }) + ); + log(`Resolved: ${name}`); + if (name == content) { + log(`IPNS Publish Success!`); + log( + `Look at that! /ipns/${keys.id} resolves to ${content}` + ); + } else { + log( + `Error, resolve did not match ${name} !== ${content}` + ); + } + } + + const onNodeConnectClick = catchAndLog( + () => nodeConnect(apiUrlInput.value), + log + ); + + apiUrlInput.addEventListener("keydown", onEnterPress(onNodeConnectClick)); + nodeConnectBtn.addEventListener("click", onNodeConnectClick); + + const onwsConnectClick = catchAndLog( + () => wsConnect(peerAddrInput.value), + log + ); + peerAddrInput.addEventListener("keydown", onEnterPress(onwsConnectClick)); + wsConnectBtn.addEventListener("click", onwsConnectClick); + + const onPublishClick = catchAndLog(() => publish(ipnsInput.value), log); + ipnsInput.addEventListener("keydown", onEnterPress(onPublishClick)); + publishBtn.addEventListener("click", onPublishClick); +} + +main(); diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json new file mode 100644 index 0000000000..761bb9e81e --- /dev/null +++ b/examples/browser-ipns-publish/package.json @@ -0,0 +1,44 @@ +{ + "name": "browser-ipns-publish", + "version": "1.0.0", + "description": "An example demonstrating publishing to IPNS in the browser", + "private": true, + "main": "index.js", + "scripts": { + "build": "parcel build index.html --public-url ./", + "start": "parcel index.html", + "test": "test-ipfs-example" + }, + "author": "Doug Anderson", + "license": "MIT", + "dependencies": { + "human-crypto-keys": "^0.1.4", + "ipfs": "^0.49.1", + "ipfs-http-client": "^46.0.1", + "ipfs-utils": "^3.0.0", + "ipns": "^0.8.0", + "it-last": "^1.0.2", + "p-retry": "^4.2.0", + "uint8arrays": "^1.1.0" + }, + "browserslist": [ + "last 2 versions and not dead and > 2%" + ], + "devDependencies": { + "delay": "^4.3.0", + "execa": "^4.0.0", + "ipfsd-ctl": "^7.0.0", + "go-ipfs": "^0.6.0", + "parcel-bundler": "^1.12.4", + "path": "^0.12.7", + "test-ipfs-example": "^2.0.3" + }, + "repository": { + "type": "git", + "url": "https://github.com/ipfs/js-ipfs/examples" + }, + "keywords": [ + "IPNS", + "Publish" + ] +} diff --git a/examples/browser-ipns-publish/test.js b/examples/browser-ipns-publish/test.js new file mode 100644 index 0000000000..2b6dece1ff --- /dev/null +++ b/examples/browser-ipns-publish/test.js @@ -0,0 +1,138 @@ +"use strict"; + +const path = require("path"); +const execa = require("execa"); +const { createFactory } = require("ipfsd-ctl"); +const df = createFactory({ + ipfsHttpModule: require("ipfs-http-client"), + ipfsBin: require("go-ipfs").path(), + args: [ + "--enable-pubsub-experiment", + '--enable-namesys-pubsub' + ], + test: true +}); +const { startServer } = require("test-ipfs-example/utils"); +const pkg = require("./package.json"); + +async function testUI(url, apiAddr, peerAddr, topic) { + const proc = execa( + require.resolve("test-ipfs-example/node_modules/.bin/nightwatch"), + [ + "--config", + require.resolve("test-ipfs-example/nightwatch.conf.js"), + path.join(__dirname, "test.js"), + ], + { + cwd: path.resolve(__dirname, "../"), + env: { + ...process.env, + CI: true, + IPFS_EXAMPLE_TEST_URL: url, + IPFS_API_ADDRESS: apiAddr, + IPFS_PEER_ADDRESS: peerAddr, + IPFS_TOPIC: topic, + }, + all: true, + } + ); + proc.all.on("data", (data) => { + process.stdout.write(data); + }); + + await proc; +} + +async function runTest() { + const app = await startServer(__dirname); + const go = await df.spawn({ + ipfsOptions: { + config: { + Addresses: { + API: "/ip4/127.0.0.1/tcp/0", + Swarm: [ + "/ip4/127.0.0.1/tcp/0/ws" + ] + }, + API: { + HTTPHeaders: { + "Access-Control-Allow-Origin": [app.url], + }, + }, + }, + }, + }); + + const go2 = await df.spawn(); + await go.api.swarm.connect(go2.api.peerId.addresses[0]); + + const { cid } = await go.api.add(`Some data ${Date.now()}`) + const topic = `/ipfs/${cid}`; + + const peerAddr = go.api.peerId.addresses + .map(addr => addr.toString()) + .filter(addr => addr.includes("/ws/p2p/")) + .pop() + + try { + await testUI( + app.url, + go.apiAddr, + peerAddr, + topic + ); + } finally { + await app.stop(); + await df.clean(); + } +} + +module.exports = runTest; + +module.exports[pkg.name] = function (browser) { + const apiSelector = "#api-url:enabled"; + + // connect to the API + browser + .url(process.env.IPFS_EXAMPLE_TEST_URL) + .waitForElementVisible(apiSelector) + .clearValue(apiSelector) + .setValue(apiSelector, process.env.IPFS_API_ADDRESS) + .pause(1000) + .click("#node-connect"); + + browser.expect + .element("#console") + .text.to.contain(`Connecting to ${process.env.IPFS_API_ADDRESS}\nSuccess!`); + + // connect via websocket + const peerAddrSelector = "#peer-addr:enabled"; + browser + .waitForElementVisible(peerAddrSelector) + .clearValue(peerAddrSelector) + .setValue(peerAddrSelector, process.env.IPFS_PEER_ADDRESS) + .pause(1000) + .click("#peer-connect"); + + browser.expect + .element("#console") + .text.to.contain( + `Connecting to peer ${process.env.IPFS_PEER_ADDRESS}\nSuccess!` + ); + + // publish to IPNS + const publishSelector = "#topic:enabled"; + browser + .waitForElementVisible(publishSelector) + .clearValue(publishSelector) + .setValue(publishSelector, process.env.IPFS_TOPIC) + .pause(1000) + .click("#publish"); + + browser.expect.element("#console").text.to.contain('Publish to IPNS'); + browser.expect.element("#console").text.to.contain('Initial Resolve'); + browser.expect.element("#console").text.to.contain('Published'); + browser.expect.element("#console").text.to.contain(`IPNS Publish Success!`); + + browser.end(); +}; diff --git a/examples/browser-ipns-publish/util.js b/examples/browser-ipns-publish/util.js new file mode 100644 index 0000000000..e6aada61f8 --- /dev/null +++ b/examples/browser-ipns-publish/util.js @@ -0,0 +1,31 @@ +exports.sleep = (ms = 1000) => new Promise(resolve => setTimeout(resolve, ms)) + +exports.Logger = outEl => { + outEl.innerHTML = '' + return message => { + const container = document.createElement('div') + container.innerHTML = message + outEl.appendChild(container) + outEl.scrollTop = outEl.scrollHeight + } +} + +exports.onEnterPress = fn => { + return e => { + if (event.which == 13 || event.keyCode == 13) { + e.preventDefault() + fn() + } + } +} + +exports.catchAndLog = (fn, log) => { + return async (...args) => { + try { + await fn(...args) + } catch (err) { + console.error(err) + log(`${err.message}`) + } + } +} diff --git a/examples/browser-mfs/package.json b/examples/browser-mfs/package.json index 2435bf829b..e2f6c73c4b 100644 --- a/examples/browser-mfs/package.json +++ b/examples/browser-mfs/package.json @@ -14,7 +14,7 @@ "license": "ISC", "devDependencies": { "html-webpack-plugin": "^3.2.0", - "http-server": "^0.11.1", + "http-server": "^0.12.3", "terser-webpack-plugin": "^1.2.1", "test-ipfs-example": "^2.0.3", "webpack": "^4.43.0", diff --git a/examples/browser-readablestream/package.json b/examples/browser-readablestream/package.json index 6568cc4f23..2255250b0c 100644 --- a/examples/browser-readablestream/package.json +++ b/examples/browser-readablestream/package.json @@ -14,7 +14,7 @@ "license": "ISC", "devDependencies": { "html-webpack-plugin": "^3.2.0", - "http-server": "^0.11.1", + "http-server": "^0.12.3", "terser-webpack-plugin": "^1.2.1", "test-ipfs-example": "^2.0.3", "webpack": "^4.43.0" diff --git a/examples/browser-script-tag/package.json b/examples/browser-script-tag/package.json index 28b65626c5..86d945819e 100644 --- a/examples/browser-script-tag/package.json +++ b/examples/browser-script-tag/package.json @@ -12,7 +12,7 @@ "keywords": [], "license": "MIT", "devDependencies": { - "http-server": "^0.11.1", + "http-server": "^0.12.3", "test-ipfs-example": "^2.0.3" }, "dependencies": { diff --git a/examples/browser-video-streaming/package.json b/examples/browser-video-streaming/package.json index dd9e675d15..7cb4f48a24 100644 --- a/examples/browser-video-streaming/package.json +++ b/examples/browser-video-streaming/package.json @@ -12,7 +12,7 @@ "keywords": [], "license": "MIT", "devDependencies": { - "http-server": "^0.11.1", + "http-server": "^0.12.3", "test-ipfs-example": "^2.0.3" }, "dependencies": { diff --git a/examples/test-ipfs-example/nightwatch.conf.js b/examples/test-ipfs-example/nightwatch.conf.js index fa7def7b23..524dc76a6b 100644 --- a/examples/test-ipfs-example/nightwatch.conf.js +++ b/examples/test-ipfs-example/nightwatch.conf.js @@ -2,6 +2,7 @@ const { ephemeralPort } = require('./utils') const path = require('path') +const chrome = require('chromedriver') // added to work on windows const WEBRIVER_PORT = ephemeralPort() @@ -11,7 +12,7 @@ module.exports = { webdriver: { start_process: true, - server_path: require.resolve(path.resolve(__dirname, 'node_modules/.bin/chromedriver')), + server_path: chrome.path, //require.resolve(path.resolve(__dirname, 'node_modules/.bin/chromedriver')), // this wasn't working on windows port: WEBRIVER_PORT, cli_args: [ `--port=${WEBRIVER_PORT}` diff --git a/examples/test-ipfs-example/utils.js b/examples/test-ipfs-example/utils.js index 5835daaf5d..489da88397 100644 --- a/examples/test-ipfs-example/utils.js +++ b/examples/test-ipfs-example/utils.js @@ -1,4 +1,4 @@ -'use strict' +"use strict"; const fs = require('fs-extra') const path = require('path') @@ -6,8 +6,8 @@ const execa = require('execa') const which = require('which') const uint8ArrayToString = require('uint8arrays/to-string') -async function startServer (dir) { - async function serveFrom (appDir) { +async function startServer(dir) { + async function serveFrom(appDir) { return new Promise((resolve, reject) => { let output = '' const port = ephemeralPort() @@ -24,130 +24,140 @@ async function startServer (dir) { if (output.includes('Hit CTRL-C to stop the server')) { resolve({ stop: () => { - console.info('Stopping server') - proc.kill('SIGINT', { - forceKillAfterTimeout: 2000 - }) + console.info("Stopping server"); + proc.kill("SIGINT", { + forceKillAfterTimeout: 2000, + }); }, - url: `http://127.0.0.1:${port}` - }) + url: `http://127.0.0.1:${port}`, + }); } - }) + }); - proc.then(() => {}, (err) => reject(err)) - }) + proc.then( + () => {}, + (err) => reject(err) + ); + }); } // start something.. const serverPaths = [ - path.join(dir, 'build'), - path.join(dir, 'dist'), - path.join(dir, 'public') - ] + path.join(dir, "build"), + path.join(dir, "dist"), + path.join(dir, "public"), + ]; for (const p of serverPaths) { if (fs.existsSync(p)) { - return serveFrom(p) + return serveFrom(p); } } // running a bare index.html file - const files = [ - path.join(dir, 'index.html') - ] + const files = [path.join(dir, "index.html")]; for (const f of files) { if (fs.existsSync(f)) { - console.info('Found bare file', f) + console.info("Found bare file", f); return Promise.resolve({ url: `file://${f}`, - stop: () => {} - }) + stop: () => {}, + }); } } - throw new Error('Browser examples must contain a `public`, `dist` or `build` folder or an `index.html` file') + throw new Error( + "Browser examples must contain a `public`, `dist` or `build` folder or an `index.html` file" + ); } -function ephemeralPort (min = 49152, max = 65535) { - return Math.floor(Math.random() * (max - min + 1) + min) +function ephemeralPort(min = 49152, max = 65535) { + return Math.floor(Math.random() * (max - min + 1) + min); } -async function isExecutable (command) { +async function isExecutable(command) { try { - await fs.access(command, fs.constants.X_OK) + await fs.access(command, fs.constants.X_OK); - return true + return true; } catch (err) { - if (err.code === 'ENOENT') { - return isExecutable(await which(command)) + if (err.code === "ENOENT") { + return isExecutable(await which(command)); } - if (err.code === 'EACCES') { - return false + if (err.code === "EACCES") { + return false; } - throw err + throw err; } } -async function waitForOutput (expectedOutput, command, args = [], opts = {}) { - if (!await isExecutable(command)) { - args.unshift(command) - command = 'node' +async function waitForOutput(expectedOutput, command, args = [], opts = {}) { + if (!(await isExecutable(command))) { + args.unshift(command); + command = "node"; } - const proc = execa(command, args, { ...opts, all: true }) - let output = '' - const time = 120000 + const proc = execa(command, args, { ...opts, all: true }); + let output = ""; + const time = 120000; - let foundExpectedOutput = false - let cancelTimeout + let foundExpectedOutput = false; + let cancelTimeout; const timeoutPromise = new Promise((resolve, reject) => { const timeout = setTimeout(() => { - reject(new Error(`Did not see "${expectedOutput}" in output from "${[command].concat(args).join(' ')}" after ${time / 1000}s`)) + reject( + new Error( + `Did not see "${expectedOutput}" in output from "${[command] + .concat(args) + .join(" ")}" after ${time / 1000}s` + ) + ); setTimeout(() => { - proc.kill() - }, 100) - }, time) + proc.kill(); + }, 100); + }, time); cancelTimeout = () => { - clearTimeout(timeout) - resolve() - } - }) + clearTimeout(timeout); + resolve(); + }; + }); proc.all.on('data', (data) => { process.stdout.write(data) output += uint8ArrayToString(data) if (output.includes(expectedOutput)) { - foundExpectedOutput = true - proc.kill() - cancelTimeout() + foundExpectedOutput = true; + proc.kill(); + cancelTimeout(); } - }) + }); try { - await Promise.race([ - proc, - timeoutPromise - ]) + await Promise.race([proc, timeoutPromise]); } catch (err) { if (!err.killed) { - throw err + throw err; } } if (!foundExpectedOutput) { - throw new Error(`Did not see "${expectedOutput}" in output from "${[command].concat(args).join(' ')}"`) + throw new Error( + `Did not see "${expectedOutput}" in output from "${[command] + .concat(args) + .join(" ")}"` + ); } } module.exports = { startServer, ephemeralPort, - waitForOutput -} + waitForOutput, +}; diff --git a/packages/interface-ipfs-core/package.json b/packages/interface-ipfs-core/package.json index 93dde85de4..b4a91ddd3a 100644 --- a/packages/interface-ipfs-core/package.json +++ b/packages/interface-ipfs-core/package.json @@ -48,7 +48,7 @@ "iso-random-stream": "^1.1.1", "it-all": "^1.0.1", "it-drain": "^1.0.1", - "it-last": "^1.0.1", + "it-last": "^1.0.2", "it-map": "^1.0.2", "it-pushable": "^1.3.1", "libp2p-crypto": "^0.18.0", diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index 253edefe7f..06a29f0337 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -53,7 +53,7 @@ "ipld-dag-pb": "^0.20.0", "ipld-raw": "^6.0.0", "iso-url": "^0.4.7", - "it-last": "^1.0.1", + "it-last": "^1.0.2", "it-map": "^1.0.2", "it-tar": "^1.2.2", "it-to-buffer": "^1.0.0", diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index 4a01ed800e..e5d7c2a811 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -118,7 +118,7 @@ "it-drain": "^1.0.1", "it-first": "^1.0.1", "it-glob": "0.0.8", - "it-last": "^1.0.1", + "it-last": "^1.0.2", "it-map": "^1.0.2", "it-multipart": "^1.0.1", "it-pipe": "^1.1.0",