Skip to content

Commit

Permalink
Dat-2 support, nodejs-only?
Browse files Browse the repository at this point in the history
  • Loading branch information
RangerMauve committed Feb 7, 2020
1 parent 4fd1769 commit b2af823
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 789 deletions.
229 changes: 132 additions & 97 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ const path = require('path')
// This is a dirty hack for browserify to work. 😅
if (!path.posix) path.posix = path

const discovery = require('hyperdiscovery')
const datStorage = require('universal-dat-storage')
const SwarmNetworker = require('corestore-swarm-networking')
const RAA = require('random-access-application')
const DatEncoding = require('dat-encoding')
const crypto = require('hypercore-crypto')
const RAM = require('random-access-memory')
const fs = require('fs')
const HypercoreProtocol = require('hypercore-protocol')

const datDNS = require('dat-dns')
const hyperdrive = require('hyperdrive')
const hypercore = require('hypercore')
const makeHyperdrive = require('hyperdrive')
const Corestore = require('corestore')
const makeHypercore = require('hypercore')

const DEFAULT_STORAGE_OPTS = {}
const DEFAULT_SWARM_OPTS = {
extensions: []
}
Expand All @@ -27,25 +27,63 @@ const DEFAULT_CORE_OPTS = {
persist: true
}
const DEFAULT_DNS_OPTS = {}
const DEFAULT_CORESTORE_OPTS = {
sparse: true
}

const DEFAULT_APPLICATION_NAME = 'dat-sdk'

module.exports = SDK

function SDK ({ storageOpts, swarmOpts, driveOpts, coreOpts, dnsOpts } = {}) {
const storage = datStorage(Object.assign({}, DEFAULT_STORAGE_OPTS, storageOpts))
const swarm = discovery(Object.assign({}, DEFAULT_SWARM_OPTS, swarmOpts))
// TODO: Set up Promise API based on Beaker https://github.com/beakerbrowser/beaker/blob/blue-hyperdrive10/app/bg/web-apis/fg/hyperdrive.js

async function SDK ({
storage,
corestore,
applicationName = DEFAULT_APPLICATION_NAME,
swarmOpts,
driveOpts,
coreOpts,
dnsOpts,
corestoreOpts
} = {}
) {
// Derive storage if it isn't provided
// Don't derive if corestore was provided
if (!storage && !corestore) storage = RAA(applicationName)

if (!corestore) {
corestore = new Corestore(
storage,
Object.assign({}, DEFAULT_CORESTORE_OPTS, corestoreOpts)
)
}

const swarm = new SwarmNetworker(corestore, Object.assign({}, DEFAULT_SWARM_OPTS, swarmOpts))
const dns = datDNS(Object.assign({}, DEFAULT_DNS_OPTS, dnsOpts))

// Track list of hyperdrives
const drives = new Map()
const cores = new Map()

function addExtensions (extensions) {
if (!extensions || !extensions.length) return
// TODO: This has code smell
const currentExtensions = swarm._opts.extensions || []
const finalSet = new Set([...currentExtensions, ...extensions])
await corestore.ready()

// I think this is used to create a persisted identity?
const noiseSeed = corestore._deriveSecret(applicationName, 'replication-keypair')
swarm.listen({
keyPair: HypercoreProtocol.keyPair(noiseSeed)
})

swarm._opts.extensions = [...finalSet]
return {
Hyperdrive,
Hypercore,
resolveName,
deleteStorage,
destroy,
_storage: storage,
_corestore: corestore,
_swarm: swarm,
_dns: dns
}

function destroy (cb) {
Expand All @@ -68,137 +106,134 @@ function SDK ({ storageOpts, swarmOpts, driveOpts, coreOpts, dnsOpts } = {}) {
storage.delete(key, cb)
}

function Hyperdrive (location, opts) {
opts = Object.assign({}, DEFAULT_DRIVE_OPTS, driveOpts, opts)
function Hyperdrive (nameOrKey, opts) {
if (!nameOrKey) throw new Error('Must give a name or key in the constructor')

addExtensions(opts.extensions)
opts = Object.assign({}, DEFAULT_DRIVE_OPTS, driveOpts, opts)

let key = null

if (!location) {
const { publicKey, secretKey } = crypto.keyPair()
key = publicKey
location = DatEncoding.encode(publicKey)
opts.secretKey = secretKey
}

try {
key = DatEncoding.decode(location)
key = DatEncoding.decode(nameOrKey)
// Normalize keys to be hex strings of the key instead of dat URLs
nameOrKey = key.toString('hex')
} catch (e) {
// Location must be relative path
// Probably isn't a `dat://` URL, so it must be a name
}

const stringKey = key.toString('hex')
if (drives.has(nameOrKey)) return drives.get(nameOrKey)

if (drives.has(stringKey)) return drives.get(stringKey)
opts.namespace = nameOrKey

const { persist } = opts

let driveStorage = null
try {
if (!persist) {
driveStorage = RAM
} else if (opts.storage) {
driveStorage = opts.storage(location)
} else {
driveStorage = storage.getDrive(location)
}
} catch (e) {
if (e.message !== 'Unable to create storage') throw e

// If the folder isn't a dat archive. Turn it into one.
const { publicKey, secretKey } = crypto.keyPair()
fs.writeFileSync(path.join(location, '.dat'), publicKey)
key = publicKey
location = DatEncoding.encode(publicKey)
opts.secretKey = secretKey

if (opts.storage) {
driveStorage = opts.storage(location)
} else {
driveStorage = storage.getDrive(location)
}
let driveStorage = corestore
if (!persist) {
driveStorage = RAM
} else if (opts.storage) {
driveStorage = opts.storage(key)
} else {
driveStorage = corestore
}

const drive = hyperdrive(driveStorage, key, opts)
const drive = makeHyperdrive(driveStorage, key, opts)

drives.set(stringKey, drive)
drives.set(nameOrKey, drive)
if (!key) {
drive.ready(() => {
const key = drive.key
const stringKey = key.toString('hex')
drives.set(stringKey, drive)
})
}

drive.ready(() => {
swarm.add(drive)
swarm.seed(drive.discoveryKey)
})

drive.once('close', () => {
const discoveryKey = DatEncoding.encode(drive.discoveryKey)
swarm.leave(discoveryKey)
swarm._replicatingFeeds.delete(discoveryKey)
swarm.unseed(drive.discoveryKey)

const key = drive.key
const stringKey = key.toString('hex')

drives.delete(stringKey)
drives.delete(nameOrKey)
})

return drive
}

function Hypercore (location, opts) {
opts = Object.assign({}, DEFAULT_CORE_OPTS, coreOpts, opts)
function Hypercore (nameOrKey, opts) {
if (!nameOrKey) throw new Error('Must give a name or key in the constructor')

addExtensions(opts.extensions)
opts = Object.assign({}, DEFAULT_CORE_OPTS, driveOpts, opts)

let key = null

if (!location) {
const { publicKey, secretKey } = crypto.keyPair()
key = publicKey
location = DatEncoding.encode(publicKey)
opts.secretKey = secretKey
}

try {
key = DatEncoding.decode(location)
key = DatEncoding.decode(nameOrKey)
// Normalize keys to be hex strings of the key instead of dat URLs
nameOrKey = key.toString('hex')
} catch (e) {
// Location must be relative path
// Probably isn't a `dat://` URL, so it must be a name
}

const stringKey = location.toString('hex')

if (cores.has(stringKey)) return cores.get(stringKey)
if (cores.has(nameOrKey)) return cores.get(nameOrKey)

const { persist } = opts
let coreStorage = null
try {
if (!persist) {
coreStorage = RAM
} else if (opts.storage) {
coreStorage = opts.storage(location)

if (!persist) {
coreStorage = RAM
} else if (opts.storage) {
coreStorage = opts.storage(key)
}

let core = null

// If sotrage was passed in the opts, use it. Else use the corestore
if (coreStorage) {
// We only want to generate keys if we have a custom storage
// Else the corestore does fancy key storage for us
if (!key) {
const { publicKey, secretKey } = crypto.keyPair()
key = publicKey
opts.secretKey = secretKey
}
core = makeHypercore(coreStorage, key, opts)
} else {
if (key) {
// If a dat key was provided, get it from the corestore
core = corestore.get({ key, ...opts })
} else {
coreStorage = storage.getCore(location)
// If no dat key was provided, but a name was given, use it as a namespace
core = corestore.namespace(nameOrKey).default(opts)
}
} catch (e) {
if (e.message !== 'Unable to create storage') throw e
}

const core = hypercore(coreStorage, key, opts)

cores.set(stringKey, core)
cores.set(nameOrKey, core)
if (!key) {
core.ready(() => {
const key = core.key
const stringKey = key.toString('hex')
cores.set(stringKey, core)
})
}

core.ready(() => {
swarm.add(core)
swarm.seed(core.discoveryKey)
})

core.once('close', () => {
const discoveryKey = DatEncoding.encode(core.discoveryKey)
swarm.leave(discoveryKey)
swarm._replicatingFeeds.delete(discoveryKey)
const key = core.key
const stringKey = key.toString('hex')

swarm.unseed(core.discoveryKey)
cores.delete(stringKey)
cores.delete(nameOrKey)
})

return core
}

return {
Hyperdrive,
Hypercore,
resolveName,
deleteStorage,
destroy
}
}
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "The official Dat SDK",
"main": "index.js",
"scripts": {
"test": "node test && node test-promise",
"test": "node test",
"build-test": "browserify -d test.js > test-bundle.js",
"build-test-promise": "browserify -d test-promise.js > test-bundle.js"
},
Expand All @@ -26,25 +26,27 @@
},
"browser": {
"dat-dns": "./dns-web.js",
"hyperswarm": "hyperswarm-web",
"./localstorage": "./localstorage-web.js"
},
"homepage": "https://github.com/datproject/sdk#readme",
"dependencies": {
"concat-stream": "^2.0.0",
"corestore": "^5.0.8",
"corestore-swarm-networking": "^4.0.3",
"dat-dns": "^4.0.0",
"dat-encoding": "^5.0.1",
"dom-event-target": "^1.0.0",
"global": "^4.4.0",
"hex-to-32": "^2.0.0",
"hypercore": "^7.5.0",
"hypercore": "^8.7.0",
"hypercore-crypto": "^1.0.0",
"hyperdiscovery": "^10.2.0",
"hyperdrive": "github:mafintosh/hyperdrive#v9",
"node-dat-archive": "^2.2.0",
"hypercore-protocol": "^7.10.0",
"hyperdrive": "^10.8.10",
"hyperswarm-web": "^1.0.1",
"node-localstorage": "^2.1.5",
"pauls-dat-api": "^8.1.0",
"random-access-application": "^1.0.0",
"random-access-memory": "^3.1.1",
"universal-dat-storage": "^1.3.1",
"universal-localstorage": "^1.0.2",
"universal-prompt": "^1.0.0",
"url-parse": "^1.4.7"
Expand Down
Loading

0 comments on commit b2af823

Please sign in to comment.