bunsenbrowser / bunsen

🔥 Bunsen Browser provides easy to use offline P2P application and file distribution that is verifiable on Android devices. https://bunsenbrowser.github.io/
117 stars 7 forks source link

Support use of dat-polyfill (DatArchive API support) #53

Open rjcorwin opened 6 years ago

rjcorwin commented 6 years ago

@RangerMauve's work on dat-archive-web and dat-polyfill has paved the way for allowing Bunsen to help apps using the polyfill to be backed by Bunsen. In particular the demo for dat-polyfill shows how Bunsen UI can help assist in providing a consistent domain for storage.

rjcorwin commented 6 years ago

@chrisekelley @RangerMauve

Here's a video walking through my fork of dat-archive-web-test (https://github.com/rjsteinert/dat-archive-web-test) which I think can serve as a hello world example at least for now of this feature working. The trick is creating an archive from one archive's context, navigating to another archive and still having write permission on that created archive.

dat://dat-archive-web-test-video-rjsteinert.hashbase.io/dat-archive-web-test.mov

chrisekelley commented 6 years ago

@rjsteinert The video and demo code illustrate the issue very well: without a common place to store the private keys when a dat archive is forked, the user cannot gain access to write to it. Where would we store those keys?

I'm moving towards the react-native option at the moment - I've already seen another dat demo in react-mobile (the dat installer) so there may be some code that could guide us. Going this route would make baby Jesus cry a little because we'd be straying from our "view source" ethos. But the current solution is a bit hacky - stuffing the whole dat into poor indexdb - what happens when it's a really big dat? I would expect that going the react-native route would take bunsen out of the proof-of-concept/test lab/must-wear-your-shop-glasses phase into a something more stable. But since I know almost nothing about react-native, and have only exchanged furtive glances with react, I'd defer to others on the real-world benefits of using react-native.

RangerMauve commented 6 years ago

Nice demo! Can't wait for it to be functional. :D

With regards to key storage, the keys are being persisted in the random-access-storage by Hyperdrive when a new one gets created, so any storage would work. It might be a good idea to add access control restrictions by only giving read access to origins that have created the dat, or have been given access via selectArchive. This can be done by wrapping random-access-storage and redirecting to an invalid file name when we detect the current origin doesn't have write access to that dat. I think for now we can cheat and worry about that later.

I think that the current approach of using an iFrame gives us a lot of benefits for rapidly innovating that we would lose going to RN. If the iframe IndexedDB issue is sorted out, I think going with this will be good for a proof of concept. I don't think most dats are going to be storing huge amounts of data, and if that becomes a problem we can start caching most dats (ie not the ones users created themselves) in memory and clearing the cache periodically.

I think IndexedDB is being used by progressive web apps to do caching, so if it's good enough for those use cases, it should be good enough for many of the sites that will be built on dat. I think the only real worry will be with huge files like high res images and videos.

However, with RN we could store the data right on the filesystem much like how Beaker does it at the moment which I think would be necessary for a finished product. Luckily, a lot of the logic that's being developed in the web version could be ported over to RN. The main thing that will change is the UI, how Node.js gets started, and the random-access-storage. I think the most difficult part of this will be the UI transition. I've been doing React at work for just over a year now, and if you use plain old react with out redux or any of that other fancy crap people use, it's not hard to get started with.

RangerMauve commented 6 years ago

Oh, with regards to the window.location issue, I was thinking that we could modify dat-archive-web to detect redirected URLs and automatically do the decoding. So even if you pass http://rcxwhny359q90p85xzdz47qyn7zj7c0d3gzekbn8029enekc95bg.gateway.mauve.moe:3000, it'll know to convert it to dat://c33bc8d7c32a6e905905efdbf21efea9ff23b00d1c3ee9aea80092eaba6c4957 https://github.com/RangerMauve/dat-archive-web/issues/3

chrisekelley commented 6 years ago

Moving forward on the iframe demo, what is our next step - to use RJ's fork of dat-archive-web-test (https://github.com/rjsteinert/dat-archive-web-test) and apply Mauve's demo for dat-polyfill to it? I can spend some time on this over the weekend.

RangerMauve commented 6 years ago

So, what you'll need to do is implement this line in your iframe, and include a compiled version of dat-polyfill in your child frame.

Basically, you pass in the parent window and child iframe as an arguments, and an object with the random-access-storage instance (I suggest random-access-idb), and a function for addArchive to save the info about newly created archives somewhere locally, and selectArchive which will show some sort of UI for the user to choose from the list of archives and invoke the callback with the URL.

You can pass the gateway URL to the child by appending ?DAT_GATEWAY=http%3A//gateway.mauve.moe%3A3000 to the child URL. If no gateway is supplied, it will attempt to auto-detect it from window.location or fallback to http:localhost:3000

The rest, like providing the global DatArchive API and proxying the storage to the parent, is handled by the polyfill.

chrisekelley commented 6 years ago

Thanks for the tips - I forked dat-polyfill and am trying to understand the flow. I added a function to get the dat url from the input box and fork the archive, but I now think I must do the forking from the child frame - is that correct? Here is the relevant code:

https://github.com/chrisekelley/dat-polyfill/blob/master/demo/server/index.js

document.addEventListener('DOMContentLoaded', (event) => {
  console.log('DOMContentLoaded')
  const searchBox = document.getElementById('search')
  const goButton = document.getElementById('go')
  const frame = document.getElementById('client-frame')
  searchBox.value = wikiDat
  goButton.addEventListener('click', addDat)

  const server = new RPC.Server(window, frame.contentWindow, {
    storage,
    addArchive,
    selectArchive
  })
  window.gatewayServer = server
})

async function addDat () {
  const searchBox = document.getElementById('search')
  const datAddress = searchBox.value
  console.log('added ' + datAddress)
  let forkedArchive = await DatArchive.fork(datAddress)
}

I'm thinking this is the wrong approach because DatArchive is not exposed in the parent (aka server) bundle.js. - my last line throws an error. So - should I postMessage to the child iframe the dat url and DAT_GATEWAY? I'd also need to add an index.js to the client and add an event listener for this message.

chrisekelley commented 6 years ago

I just changed it so that forking is happening on the child iframe: in server/index.js:

async function addDat () {
  const searchBox = document.getElementById('search')
  const datAddress = searchBox.value
  console.log('added ' + datAddress)
  // let forkedArchive = await DatArchive.fork(datAddress)
    frame.contentWindow.postMessage(datAddress, '*');
}

and in client/index.js:

async function receiveMessage (event) {
  // Do we trust the sender of this message?
  // if (event.origin !== "http://example.com:8080")
  //     return;

  console.log(event.data)
  let data = event.data
  if (data.startsWith('dat:')) {
    let datAddress = data
    let forkedArchive = await DatArchive.fork(datAddress)
    console.log('we forked!')
  }
}

window.addEventListener('message', receiveMessage, false)

I now need to check if the message is a string; startsWith is blowing up; After DatArchive.fork is run, event.data is probably returning the forkedArchive :-)

I did a little more work on it, sorted the blowing up, here is what I see in the Application panel:

dat-polyfill-indexdb

The app never makes it to the console.log('we forked!') part - I was hoping to be able to inspect the forkedArchive - but I do think that the polyfill/dat-archive-web are copying the dat and forking it to indexdb.

rjcorwin commented 6 years ago

It might be a good idea to add access control restrictions by only giving read access to origins that have created the dat, or have been given access via selectArchive

@RangerMauve I was thinking the same at first but have realized we might not have to do anything as long as an app can't query for "what private keys do we have?" It's the same security model of Dat itself, if you don't know the public key, you can't guess it.

rjcorwin commented 6 years ago

I think that the current approach of using an iFrame gives us a lot of benefits for rapidly innovating that we would lose going to RN. ... I think IndexedDB is being used by progressive web apps to do caching, so if it's good enough for those use cases, it should be good enough for many of the sites that will be built on dat. ... However, with RN we could store the data right on the filesystem much like how Beaker does it at the moment which I think would be necessary for a finished product. Luckily, a lot of the logic that's being developed in the web version could be ported over to RN.

@RangerMauve Well put, I agree. RN would make a great Bunsen 2.0 (or 1.0) initiative.

I've been doing React at work for just over a year now, and if you use plain old react with out redux or any of that other fancy crap people use, it's not hard to get started with.

Haha I believe it. Luckily Chris and I have been using Redux with Web Components for the past year on the Tangerine project so we have some familiarity with it if that is something we want to go with. Doing Redux with Web Components has made me really want to switch to React. Our code is much more complicated than if we had written with React because you can't just write one easy to understand render function for performance reasons. Luckily there is light at the end of the tunnel for Web Components using Lit HTML!

rjcorwin commented 6 years ago

In regards to goals for next step, I'm hoping to see get the following scenario working.

  1. Serve up Mauve's dat-polyfill demo and open the server folder in a browser
  2. Enter dat://dat-archive-web-test-rjsteinert.hashbase.io/ into the navigation bar in Mauve's dat-polyfill demo server app and hit go.
  3. After the requested archive loads in the child iframe, click the Click to fork this archive button in the app itself.
  4. The app in the child iframe will now be displaying a dat address of the forked archive. Copy this address and paste it into Mauve's dat polyfill demo address input and click go.
  5. After the archive loads in the child iframe, enter a message in the child iframe's app's input box and click submit.
  6. In Beaker browser, navigate to this forked archive address and see if the message in the box has been updated. If it has been updated, then we successfully navigated to a new archive and wrote to it thanks to the help from the communication between the child iframe with Dat Archive Web Polyfill and Mauve's dat-polyfill demo server app.
rjcorwin commented 6 years ago

At the moment I'm getting stuck trying to navigate to a Dat Archive using Mauve's dat-polyfill demo server app. I enter an address in the address bar, hit go, and then I get a new tab with the same content.

screen shot 2018-05-08 at 9 26 05 am screen shot 2018-05-08 at 9 26 17 am
RangerMauve commented 6 years ago

Hey, sorry. I didn't actually implement the navigation yet. All it does is load the client.html in an iframe.

To test it I open the dev tools and load the iframe context and test the DatArchive API from there.

Ideally if you have something (like the dat gateway) that can load a site that's included the bundled polyfill, you could load that into the iframe instead (my adjusting the source manually)

RangerMauve commented 6 years ago

@chrisekelley Don'y use postframe directly, have the library set up the server instead. (it handles the RPC over postmessage, and adding other messages might mess it up)

Ideally you should do all your dat creation logic within the child frame, and the child frame should have a copy of dat-polyfill loaded up.

chrisekelley commented 6 years ago

@RangerMauve doh! copy/paste error from a different version of this demo. I just cleaned it up.

Dat creation happens in the child iframe. Here is the code form child/index.js:

async function receiveMessage (event) {
  // console.log(event.data)
  let data = event.data
  if (typeof data === 'string' && data.startsWith('dat:')) {
    let datAddress = data
    let forkedArchive = await DatArchive.fork(datAddress)
    console.log('we forked!')
  }
}

window.addEventListener('message', receiveMessage, false)

It waits for a message from the parent, which has the dat archive url to fork. It's just a forkin' machine. But once it is forked, how can we access this forked archive?

RangerMauve commented 6 years ago

@chrisekelley Check out the indexeddb storage in the parent. You should see a single DB that has the contents of all the dats.

This means that when you do the fork, the forked data is saved to indexeddb. That means that a different frame can take the forked URL and create a dat instance from it.

let forkedArchive = await DatArchive.fork(datAddress)
console.log('we forked to', forkedArchive.url)

// Later, copy the URL
const forkedURL = ""; // Paste here
const theArchive = new DatArchive(forkedURL); // Is the same archive
RangerMauve commented 6 years ago

BTW, if you use the master branch of dat-archive-web from github, you will be able to use new DatArchive(window.location) or DatArchive.fork(window.location) for dats served from a gateway. :D

chrisekelley commented 6 years ago

Yeah, cool, I do see the indexdb in the parent. I'm debugging why let forkedArchive = await DatArchive.fork(datAddress) is not returning the forkedArchive, and I turned up something cool in your code:

      archive._archive.key.toString('hex'),
      archive._archive.metadata.secretKey.toString('hex'),

Well that sure is handy!

This is from

await DatArchive._manager.onAddArchive(
      archive._archive.key.toString('hex'),
      archive._archive.metadata.secretKey.toString('hex'),
      {title, description, type, author}
    )
RangerMauve commented 6 years ago

@chrisekelley Yeah, you can set the addArchive handler when you create the server for the polyfill. :D

I wouldn't rely on the archive._archive property if you can. It'll break in regular Beaker apps. :P

What is the return value of .fork()? Are you getting any errors? I haven't tested forking all that much to be honest.

chrisekelley commented 6 years ago

@RangerMauve ahhh yeah, I was just looking at addArchive, and setArchives pops the keys into localstorage - very nice!

It's not making it out of fork - trying to track it down, that's how I found the rpc call to addArchive.

In regards to fork(), I think it is failing somewhere during const destDat = await DatArchive.create(opts) - but it does get as far as hitting addArchive.

RangerMauve commented 6 years ago

Have you tried wrapping everything in a try catch to see if an error is being thrown and ignored because of promises?

Sadly I think I'm going to be busy this evening but I'll see if I can reproduce it later tonight or tomorrow.

chrisekelley commented 6 years ago

Good idea on the try/catch, but so far nothing is getting thrown. I think an error is happening in this part of DatArchive.js create():

          await DatArchive._manager.onAddArchive(
              archive._archive.key.toString('hex'),
              archive._archive.metadata.secretKey.toString('hex'),
              {title, description, type, author}
          )

It could be happening in addArchives's callback in rpc.js Client:

  addArchive (key, secretKey, options, callback) {
    this._rpc.call('addArchive', key, secretKey, options, callback)
  }

The code for the callback is here:

RPC.prototype._handle = function (msg) {

-- snip --
        var args = msg.arguments.concat(function () {
            self._dstPostMessage({
                protocol: 'frame-rpc',
                version: VERSION,
                response: msg.sequence,
                arguments: [].slice.call(arguments)
            });
 -- snip --
RangerMauve commented 6 years ago

Does your server's addArchive implementation invoke the callback? If you don't invoke the callback it will stall.

chrisekelley commented 6 years ago

No, it's the same code as the one in your master:

function addArchive (key, secretKey, options, callback) {
  const archiveList = getArchives()
  archiveList.push({
    key,
    secretKey,
    details: options
  })
  setArchives(archiveList)
}

So, should I callback() after setArchives(archiveList) ?

chrisekelley commented 6 years ago

Answered the question myself - YES! Things are working :-)

RangerMauve commented 6 years ago

Whoops! I probably should have tested it more. 😅

chrisekelley commented 6 years ago

YES - here is the console output. Forked dat opens in Beaker browser.

DOMContentLoaded
index.js:34 added dat://wysiwywiki-pfrazee.hashbase.io
index.js:54 setting Archives for key: 4db84001b9bc19692fdfd873b015589207e795bbe660e7ee4272cbc305bfd5fe
DatArchive.js:318 finished writing the archive.
index.js:8 we forked to dat://4db84001b9bc19692fdfd873b015589207e795bbe660e7ee4272cbc305bfd5fe

Next step - mess around with the keys that are in LocalStorage. I want to see if the app can open up the forked archive and write to it.

We're getting there!

chrisekelley commented 6 years ago

Here's the data stored in LocalStorage - includes the secret key for the forked archive:

dat-polyfill-demo-localstorage

Also see that in Indexeddb is dat://storage - i'm assuming we would use random-access-idb to browse these.

dat-polyfill-demo-indexeddb

RangerMauve commented 6 years ago

Yeah, I don't think we'd really be browsing it manually, I think it'd be easier to leave it as a general storage for dats. Maybe we'd want to mess with it in order to clear old data, though.

chrisekelley commented 6 years ago

Ahh yes of course - we'd use a higher-level API to access the dats - something like this?

const contents = `
<title>Gateway Test</title>
<p>Hello World!</p>
`
const archive = new DatArchive('dat://4db84001b9bc19692fdfd873b015589207e795bbe660e7ee4272cbc305bfd5fe')

await archive.writeFile('index.html', contents)
  console.log('Wrote index.html')
RangerMauve commented 6 years ago

Yeah!

Eventually you'd want to pair that with DatArchive.selectArchive() to make it more user-friendly.

chrisekelley commented 6 years ago

Cool - I added DatArchive.selectArchive() to my client/index.js, and now the checkbox displays - tweaks in this commit - and I can submit and handleSelected() does its thing.

dat-polyfill-checkbox-ui

I also see that there is some websockets action going on:

dat-polyfill-websockets-action

The UI hides the form w/ checkbox but nothing else happens in the UI. I see that some useful stuff happens in handleSelected:

  if (currentSelection) {
    const input = form.querySelector('input:checked')
    const url = `dat://${input.value}`
    currentSelection.callback(false, url)
    currentSelection = null
  }

On currentSelection.callback(false, url) - I think that loads the archive - but how do I access it?

In the client iframe, should I listen for an event upon the success of currentSelection.callback(false, url) and access the forkedArchive with new DatArchive(window.location)?

RangerMauve commented 6 years ago

On the client side it should look something like

var theArchive = await DatArchive.selectArchive({})

Basically, the call to selectArchive creates a promise then sends an RPC to the parent with a callback to get the URL of the dat to load.

The parent shows the UI for choosing an archive and when the user chooses one, it invokes the callback to send an RPC back to the child frame with the URL

The child frame can now resolve the promise for the URL, invoke new DatURL(theurl) and return it.

Relevant code is:

RangerMauve commented 6 years ago

The goal with the polyfill was to make the client-side code look exactly the same as it would in beaker once the polyfill is included. :D

This forking / selecting archives / creating archives should work exactly the same way as it would in beaker.

Ideally the code you write should be able to load in beaker, skip creating the global DatArchive variable (since Beaker already defines it), and work exactly as it does in the demo with the polyfill, just with Beaker's selectArchive implementation.

RangerMauve commented 6 years ago

By the way, regarding size limits, by default Beaker only allows 100mb of storage for a dat archive.

chrisekelley commented 6 years ago

Now I think I understand much better the promise part - thank . you so much for going through that.

Once I have the result of selectArchive, it is necessary to invoke new DatArchive(url), since selectArchive returns an archive?

I experimented with writing to this archive, but looks like I may need to do some business w/ the secretkey.

Revised code in client/index.js

  let archive = await DatArchive.selectArchive({})
  let url = archive.url
  console.log('primed selectArchive for ' + url)
// necessary to create another archive, when we've just been handed one?
  let nuArchive = await new DatArchive(url)
  const contents = `
        <title>Gateway Test</title>
        <p>Hello World!</p>
        `
  await archive.writeFile('sayHello.html', contents)
  console.log('Wrote sayHello.html to ' + nuArchive.url)

returns ArchiveNotWritableError: Cannot write to this archive ; Not the owner

RangerMauve commented 6 years ago

I don't think you need to create a new archive from the URL since it should just work from the existing one.

It's kinda weird that you can't write to the new archive, though. :/

Try to add an await nuArchive._loadPromise to make sure it's fully initialized.

RangerMauve commented 6 years ago

Make sure that you're selecting the archive that got created as a result of .fork() or .create(). Otherwise you won't have write access to it.

Just to clarify, when you `fork() an archive, you're going to be creating a new one with all the contents copied over. It'll have a different URL and should have the private key stored in the indexeddb storage in the parent.

chrisekelley commented 6 years ago

I tightened up the code and added the archive._loadPromise, but still don't have writable permissions on the archive. I did check if I'm trying to write to the forked version - here are the logs:

added dat://wysiwywiki-pfrazee.hashbase.io
VM5192 bundle.js:55 setting Archives for key: e01e76457c5f6a839e53d75191aba7153a963f524dfadcd1e70814a11b5c2889
VM5272 index.js:8 we forked to dat://e01e76457c5f6a839e53d75191aba7153a963f524dfadcd1e70814a11b5c2889
VM5272 index.js:10 hey!
Navigated to http://127.0.0.1:8080/server/
index.js:24 DOMContentLoaded
index.js:27 DOMContentLoaded in client
index.js:19 dat: dat://e01e76457c5f6a839e53d75191aba7153a963f524dfadcd1e70814a11b5c2889
index.js:31 primed selectArchive for dat://e01e76457c5f6a839e53d75191aba7153a963f524dfadcd1e70814a11b5c2889
index.js:43 Error: ArchiveNotWritableError: Cannot write to this archive ; Not the owner

Here is the localStorage:

screen shot 2018-05-10 at 8 28 06 pm

Here is indexedDB - look at that uuid - shouldn't it be e01e76457c5f6a839e53d75191aba7153a963f524dfadcd1e70814a11b5c2889? It is 46c09171de62c2db83360cb8a81a4d61f4e280c994bf3e2f3a1bafb88eb13979.

screen shot 2018-05-10 at 8 30 20 pm

The raw dat address of Paul's wysiwyg is dat://46c09171de62c2db83360cb8a81a4d61f4e280c994bf3e2f3a1bafb88eb13979/

RangerMauve commented 6 years ago

Are files actually being copied into the fork? Can you view anything? The indexedDB instance will hold all keys, scroll down to see if the forked data is there.

Try changing dat-archive-web around here so that you await srcDat._loadPromise to make sure it's all ready.

Maybe it's forking before the original archive is actually loaded. That wouldn't explain the inability to write.

Would you mind linking me to the latest source? I'm gonna be AFK for a bit but I'll take a look in a few hours. Sadly this has been a super busy week for me. 😂

chrisekelley commented 6 years ago

I am not sure - it's a bunch of Uint8Arrays:

screen shot 2018-05-10 at 8 58 20 pm

I figured this was just the storage format

I'll try out the dat-archive-web change.

I pushed all of my changes to my fork -dat-polyfill/

Hey, thanks a lot for walking through this with me! No rush :-)

RangerMauve commented 6 years ago

You should be able to see keys that start with the forked dat URL. If you're not seeing them then the fork didn't work.

chrisekelley commented 6 years ago

OK - I made one change - pointed DAT_GATEWAY to my instance at localhost instead of to your .moe gateway:

  <iframe id="client-frame" src="../client/index.html?DAT_GATEWAY=http%3A//localhost%3A3000" class="client-frame" seamless="seamless"></iframe>

and now it is populating the correct dat:

screen shot 2018-05-10 at 9 39 20 pm

But I still get this error: index.js:43 Error: ArchiveNotWritableError: Cannot write to this archive ; Not the owner

Update: now that is no longer working, back to 46c09171de62c2db83360cb8a81a4d61f4e280c994bf3e2f3a1bafb88eb13979...

chrisekelley commented 6 years ago

I just checked window.gatewayServer and window.gatewayServer while in the client iframe, and they are uninitialized, whereas they are set in server/index.js. I don't see those being used anywhere, so they may not matter, but it got me thinking....is my fork in client getting created in the correct format or where it should be?

I am doing the forking in client/index.js when I receive a postMessage from the parent providing the url

async function receiveMessage (event) {
// console.log(event.data)
  let data = event.data
  if (typeof data === 'string' && data.startsWith('dat:')) {
    let datAddress = data
    try {
      let forkedArchive = await DatArchive.fork(datAddress)
      console.log('we forked to', forkedArchive.url)
      // let theArchive = await DatArchive.selectArchive({})
      console.log('hey!')
      parent.location.reload()
    } catch (e) {
      console.log('Error: ' + e)
    }
  } else {
-- snip --
  }
}
RangerMauve commented 6 years ago

I'm not 100% sure what's up to be honest. I think we'll need to read up on how it gets the private key and determines that it's the owner.

I'm going to be gone this weekend so I don't think I'll be able to help too much until Monday.

Try bugging the people in the dat gitter or IRC about the writability. Mention that we're using random-access-idb to store the hyperdrive data, but when we open it up again it thinks that it isn't the owner.

RangerMauve commented 6 years ago

Hey, I see nobody in the gitter seemed to get back to you. :(

It seems the error is happening because the archive doesn't have the 'writable' flag set.

That flag is defined in hyperdrive as being a getter for metadata.writable

metadata is a hypercore instance for the key and the folder for storing it.

It seems this property gets set either from opts.writable, which can't be passed into the metadata, or by detecting the secret key when it tries to load the signature when opening.

It will be loading the data from the metadata folder in the storage.

It seems that when the storage is opened it will attempt to load the secret key from the /metadata/secret_key file

There could be a problem at any one of those steps. I think the first thing you can do to make sure it works is to check that the metadata/secret_key file is being written to the IDB storage.

RangerMauve commented 6 years ago

Weird, it works fine with just DatArchiveWeb.

Gonna see if I have time to run the demo locally. :D

RangerMauve commented 6 years ago

Reproduced it, it isn't able to load the secret key for some reason.

Getting the following error:

Error: Not opened
    at Request._openAndNotClosed (http://localhost:3030/client/bundle.js:41764:34)
    at Request._run (http://localhost:3030/client/bundle.js:41792:16)
    at Request._unqueue (http://localhost:3030/client/bundle.js:41752:50)
    at Request.callback (http://localhost:3030/client/bundle.js:41757:8)
    at RPCTransport.NoopTransport._next (http://localhost:3030/client/bundle.js:41571:11)
    at _rpc.call (http://localhost:3030/client/bundle.js:53333:12)
    at RPC._handle (http://localhost:3030/client/bundle.js:24446:20)
    at RPC._onmessage (http://localhost:3030/client/bundle.js:24384:14)

Might be a problem with the random-access-network lib.

RangerMauve commented 6 years ago

Oh man, this is pretty intense. :P

Here's how I reproduced it:

I think something similar is happening with the demo

What we need to do:

Not sure if this is the only approach we can take, and why random-access-network doesn't allow multiple opened instances.

soyuka commented 6 years ago

@RangerMauve don't hesitate to ping me in github I almost didn't see your message :p. Not sure what you meant by "multiple openend instances", I think it should definitely be able to do that. If you have a small reproduction case I might look into it!

Btw about random-access-idb, I remember you had issues but I tried out my version based on random-access-storage and it worked. Just note that it has a slightly different signature in which you have to name the database before getting the ras instance (const rai = require('random-access-idb')('test')).