forked from RangerMauve/hyper-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsdk.js
More file actions
255 lines (203 loc) · 6.17 KB
/
sdk.js
File metadata and controls
255 lines (203 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
const path = require('path')
// This is a dirty hack for browserify to work. 😅
if (!path.posix) path.posix = path
const DatEncoding = require('dat-encoding')
const datDNS = require('dat-dns')
const hyperdrive = require('hyperdrive')
const makeHypercorePromise = require('@geut/hypercore-promise')
const makeHyperdrivePromise = require('@geut/hyperdrive-promise')
const DEFAULT_DRIVE_OPTS = {
sparse: true,
persist: true
}
const DEFAULT_CORE_OPTS = {
sparse: true,
persist: true
}
const DEFAULT_DNS_OPTS = {}
const DEFAULT_APPLICATION_NAME = 'dat-sdk'
const CLOSE_FN = Symbol('close')
const HANDLE_COUNT = Symbol('closeCount')
module.exports = SDK
module.exports.DEFAULT_APPLICATION_NAME = DEFAULT_APPLICATION_NAME
// 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 (opts = {}) {
if (!opts.backend) throw new Error('No backend was passed in')
if (!opts.applicationName) {
opts.applicationName = DEFAULT_APPLICATION_NAME
}
if (opts.persist === undefined) {
opts.persist = true
}
const {
backend,
driveOpts,
coreOpts,
dnsOpts
} = opts
const dns = datDNS(Object.assign({}, DEFAULT_DNS_OPTS, dnsOpts))
const handlers = await backend(opts)
const {
storage,
corestore,
swarm,
deriveSecret,
keyPair
} = handlers
await corestore.ready()
const drives = new Map()
const cores = new Map()
return {
Hyperdrive,
Hypercore,
resolveName,
getIdentity,
deriveSecret,
registerExtension,
close,
get keyPair () { return keyPair },
_storage: storage,
_corestore: corestore,
_swarm: swarm,
_dns: dns
}
function getIdentity () {
console.warn('getIdentity is being deprecated and will be removed in version 3.x.x, please use sdk.keyPair instead')
return keyPair
}
function close (cb) {
for (const drive of drives.values()) {
drive.close()
}
for (const core of cores.values()) {
core.close()
}
if (handlers.close) handlers.close(cb)
else cb()
}
function resolveName (url, cb) {
return dns.resolveName(url, cb)
}
function registerExtension (name, handlers) {
return swarm.registerExtension(name, handlers)
}
function Hyperdrive (nameOrKey, opts) {
if (!nameOrKey) throw new Error('Must give a name or key in the constructor')
opts = Object.assign({}, DEFAULT_DRIVE_OPTS, driveOpts, opts)
const { key, name, id } = resolveNameOrKey(nameOrKey)
if (drives.has(id)) {
const existing = drives.get(id)
existing[HANDLE_COUNT]++
return existing
}
if (name) opts.namespace = name
const drive = hyperdrive(corestore, key, opts)
const wrappedDrive = makeHyperdrivePromise(drive)
drive[HANDLE_COUNT] = 0
drive[CLOSE_FN] = drive.close
drive.close = function (fd, cb) {
if (fd && cb) return this[CLOSE_FN](fd, cb)
const hasHandles = wrappedDrive[HANDLE_COUNT]--
if (hasHandles > 0) setTimeout(fd, 0)
else setTimeout(() => this[CLOSE_FN](fd, cb), 0)
}
drives.set(id, wrappedDrive)
if (!key) {
drive.ready(() => {
const key = drive.key
const stringKey = key.toString('hex')
drives.set(stringKey, wrappedDrive)
})
}
drive.ready(() => {
const {
discoveryKey = drive.discoveryKey,
lookup = true,
announce = true
} = opts
// Don't advertise if we're not looking up or announcing
if (!lookup && !announce) return
swarm.configure(discoveryKey, { lookup, announce })
})
drive.once('close', () => {
const key = drive.key
const stringKey = key.toString('hex')
drives.delete(stringKey)
drives.delete(id)
const { discoveryKey = drive.discoveryKey } = opts
swarm.configure(discoveryKey, { announce: false, lookup: false })
})
return wrappedDrive
}
function Hypercore (nameOrKey, opts) {
if (!nameOrKey) throw new Error('Must give a name or key in the constructor')
opts = Object.assign({}, DEFAULT_CORE_OPTS, coreOpts, opts)
const { key, name, id } = resolveNameOrKey(nameOrKey)
if (cores.has(id)) {
const existing = cores.get(id)
existing[HANDLE_COUNT]++
return existing
}
let core
if (key) {
// If a dat key was provided, get it from the corestore
core = corestore.get({ ...opts, key })
} else {
// If no dat key was provided, but a name was given, use it as a namespace
core = corestore.namespace(name).default(opts)
}
// Wrap with promises
const wrappedCore = makeHypercorePromise(core)
core[HANDLE_COUNT] = 0
core.close = function (cb) {
if (!cb) cb = function noop () {}
const hasHandles = wrappedCore[HANDLE_COUNT]--
if (hasHandles === 0) {
setTimeout(() => {
let promise = core._close(cb)
if (promise && promise.then) promise.then(cb, cb)
}, 0)
} else if (cb) setTimeout(cb, 0)
}
cores.set(id, wrappedCore)
if (!key) {
core.ready(() => {
const key = core.key
const stringKey = key.toString('hex')
cores.set(stringKey, wrappedCore)
})
}
core.ready(() => {
const {
discoveryKey = core.discoveryKey,
lookup = true,
announce = true
} = opts
// Don't advertise if we're not looking up or announcing
if (!lookup && !announce) return
swarm.configure(discoveryKey, { announce, lookup })
})
core.once('close', () => {
const { discoveryKey = core.discoveryKey } = opts
const key = core.key
const stringKey = key.toString('hex')
swarm.configure(discoveryKey, { announce: false, lookup: false })
cores.delete(stringKey)
cores.delete(id)
})
return wrappedCore
}
function resolveNameOrKey (nameOrKey) {
let key, name, id
try {
key = DatEncoding.decode(nameOrKey)
id = key.toString('hex')
// Normalize keys to be hex strings of the key instead of dat URLs
} catch (e) {
// Probably isn't a `dat://` URL, so it must be a name
name = nameOrKey
id = name
}
return { key, name, id }
}
}