What
I think it'd be useful to have a set of APIs for interacting with the p2p primitives provided by the extension. I've been building apps for a while and here's some of the stuff I've found useful (after adding it to the dat-sdk)
- Being able to read / write files from hyperdrives
- Creating hyperdrives within an application
- Able to read / write metadata in hypertrie of a hyperdrive
- Being able to listen on updates
- Being able to listen on / emit extension messages
- Being able to listen on network changes (added / removed peers) - important for making use of extensions
- Having 'named' hyperdrives in an application so you don't need to save the key for later use. E.g
hyperdrive('My profile')
- Being able to read / write blocks from hypercores
- read/write ranges or streams from hypercores
- all the same networking options / extensions as hyperdrive
Hyperdrive
I think that since we're targeting the web and register protocol handlers, it'd be cool to use fetch() and http-like interfaces for doing all this stuff.
For example fetch('hyper://domain/example.txt') should already be handled for reading. Maybe we can extend that to support uploads like so:
fetch('hyper://domain/example.txt', {
method: 'PUT',
body: 'Hello World!'
})
What's cool here is that we can get modern streaming from all this by providing a ReadableStream for the body, or using the ReadableStream returned in the response.
The application specific archive name thing could be useful for creating archives using the namespace method of Corestore. For example you could have something like this.
const ARCHIVE_NAME = 'Main archive'
const {url, version} = (await fetch(`hyper://${ARCHIVE_NAME}/`, {method: 'HEAD'})
alert(`Your archive is ${url} and is at version ${version}`)
The application knows that it can always use Main archive to generate the same archive under the hood, and can get the public URL for sharing with others from there. The namespace could probably calculated with corestore.namespace(origin + name) to make scoping easy.
Changes / Events
Listening for changes / getting updates / extensions is a bit of a question mark for me, since I'm not sure what support the protocol handler code has, but maybe it could be done by opening Websockets, or Server Sent Events. The types of events to subscribe on could be specified in the query string. You could subscribe to multiple extensions by including them multiple times. For example.
const source = new EventSource('hyper://domain?change&extension=example&extension=example2`)
source.onmessage = ({data}) => {
const {type} = JSON.parse(data)
if(type === 'change') console.log('change', data.path, data.version)
if(type === 'extension') console.log('extension message', data.extension, data.message)
}
fetch('hyper://domain`, {
method: 'post',
body: 'Example',
headers: {
'x-hypercore-protocol-extension': 'example'
}
})
Hypercore
Similarly to the hyperdrive use case, we could support hypercores. This has been something that's been talked about a lot with regards to brave support and support for apps like cabal.
For example, we could create a hypercore by adding a parameter the first time we reference it.
const {url} = fetch('hyper://my core/`, {
method: 'HEAD',
headers: {
'x-hypercore-protocol-structure': 'core'
}
})
// Or when we first write to it
const response = await fetch('hyper://my core/`, {
method: 'POST',
body: 'Example',
headers: {
'x-hypercore-protocol-structure': 'core'
}
})
const version = response.headers.get('x-hypercore-protocol-version')
const response = await fetch('hyper://my core/0`)
const data = await response.text()
One thing that would be important is to get ranges of data out of a hypercore. Not sure what would be best here. Maybe a Range header? Maybe server-sent events again? Maybe something in the URL like hyper://core/4..20.
Benefits of approach
One of the advantages of doing this over injecting JS APIs is that it'll be available in all worker contexts. For example, you'd be able to easily use this stuff inside service workers or extensions without having to fuss around with injecting APIs into those contexts. This has been a bit of a pain when making Beaker apps since it was hard to get the DatArchive API within iframes or Worker threads. As far as I know every JS context has access to fetch() so code could be reused anywhere the same way.
These APIs are a bit more low level than what Beaker provides, but I think that's a good thing because it gives applications more flexibility. I could also see somebody building up the Beaker specific APIs on top of this without too much hassle.
I could also see a nice JS wrapper over top of the fetch API being made in something like the dat-sdk or other projects. Or even intercepting window.fetch to provide these abilities with a pure JS implementation for cases where dat-webext or an equivalent isn't installed. It being an async API helps a lot in that regard.
This could also be useful as a foundation for a HTTP proxy for interacting with all this Dat stuff. There's been a lot of talk in the community and we could standardize around it. Instead of using the built in hyper:// support, an application could instead use http://localhost:4200/domain/path with all the other HTTP-isms unchanged.
Having hypercore be a first class citizen of the API will also help progress efforts in getting kappa-core based apps working in the browser with proxy-less p2p support. Though I think they'd be happier with having access to the p2p networking in the short term. :P
Also, archive authors could mess with access control using CORS with the index.json file.
TODOs
- Could this apply to the brave integration folks have been talking about?
- Hypercore stuff should be thought about more with regards to ranges
Roadmap
Here's a rough roadmap I made up on the spot which could yield useful APIs for applications at each step
What
I think it'd be useful to have a set of APIs for interacting with the p2p primitives provided by the extension. I've been building apps for a while and here's some of the stuff I've found useful (after adding it to the dat-sdk)
hyperdrive('My profile')Hyperdrive
I think that since we're targeting the web and register protocol handlers, it'd be cool to use
fetch()and http-like interfaces for doing all this stuff.For example
fetch('hyper://domain/example.txt')should already be handled for reading. Maybe we can extend that to support uploads like so:What's cool here is that we can get modern streaming from all this by providing a ReadableStream for the body, or using the ReadableStream returned in the response.
The
application specific archive namething could be useful for creating archives using the namespace method of Corestore. For example you could have something like this.The application knows that it can always use
Main archiveto generate the same archive under the hood, and can get the public URL for sharing with others from there. The namespace could probably calculated withcorestore.namespace(origin + name)to make scoping easy.Changes / Events
Listening for changes / getting updates / extensions is a bit of a question mark for me, since I'm not sure what support the protocol handler code has, but maybe it could be done by opening Websockets, or Server Sent Events. The types of events to subscribe on could be specified in the query string. You could subscribe to multiple extensions by including them multiple times. For example.
Hypercore
Similarly to the hyperdrive use case, we could support hypercores. This has been something that's been talked about a lot with regards to brave support and support for apps like cabal.
For example, we could create a hypercore by adding a parameter the first time we reference it.
One thing that would be important is to get ranges of data out of a hypercore. Not sure what would be best here. Maybe a
Rangeheader? Maybe server-sent events again? Maybe something in the URL likehyper://core/4..20.Benefits of approach
One of the advantages of doing this over injecting JS APIs is that it'll be available in all worker contexts. For example, you'd be able to easily use this stuff inside service workers or extensions without having to fuss around with injecting APIs into those contexts. This has been a bit of a pain when making Beaker apps since it was hard to get the DatArchive API within iframes or Worker threads. As far as I know every JS context has access to
fetch()so code could be reused anywhere the same way.These APIs are a bit more low level than what Beaker provides, but I think that's a good thing because it gives applications more flexibility. I could also see somebody building up the Beaker specific APIs on top of this without too much hassle.
I could also see a nice JS wrapper over top of the fetch API being made in something like the
dat-sdkor other projects. Or even interceptingwindow.fetchto provide these abilities with a pure JS implementation for cases where dat-webext or an equivalent isn't installed. It being an async API helps a lot in that regard.This could also be useful as a foundation for a HTTP proxy for interacting with all this Dat stuff. There's been a lot of talk in the community and we could standardize around it. Instead of using the built in
hyper://support, an application could instead usehttp://localhost:4200/domain/pathwith all the other HTTP-isms unchanged.Having hypercore be a first class citizen of the API will also help progress efforts in getting kappa-core based apps working in the browser with proxy-less p2p support. Though I think they'd be happier with having access to the p2p networking in the short term. :P
Also, archive authors could mess with access control using CORS with the index.json file.
TODOs
Roadmap
Here's a rough roadmap I made up on the spot which could yield useful APIs for applications at each step
GETHEADand custom headersPOSTwith names / write files using PUT