hypercore-fetch 9.9.1 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +34 -105
- package/package.json +2 -3
- package/test.js +18 -19
package/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { posix } from 'path'
|
|
2
2
|
|
|
3
3
|
import { Readable, pipelinePromise } from 'streamx'
|
|
4
|
-
import Hyperdrive from 'hyperdrive'
|
|
5
4
|
import { makeRoutedFetch } from 'make-fetch'
|
|
6
5
|
import mime from 'mime/index.js'
|
|
7
6
|
import parseRange from 'range-parser'
|
|
@@ -79,9 +78,7 @@ export default async function makeHyperFetch ({
|
|
|
79
78
|
|
|
80
79
|
// Map loaded drive hostnames to their keys
|
|
81
80
|
// TODO: Track LRU + cache clearing
|
|
82
|
-
const drives = new Map()
|
|
83
81
|
const extensions = new Map()
|
|
84
|
-
const cores = new Map()
|
|
85
82
|
|
|
86
83
|
if (extensionMessages) {
|
|
87
84
|
router.get(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`, listExtensions)
|
|
@@ -116,95 +113,6 @@ export default async function makeHyperFetch ({
|
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
async function getCore (hostname) {
|
|
120
|
-
if (cores.has(hostname)) {
|
|
121
|
-
return cores.get(hostname)
|
|
122
|
-
}
|
|
123
|
-
const core = await sdk.get(hostname)
|
|
124
|
-
await core.ready()
|
|
125
|
-
cores.set(core.id, core)
|
|
126
|
-
cores.set(core.url, core)
|
|
127
|
-
return core
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function getDBCoreForName (name) {
|
|
131
|
-
const corestore = sdk.namespace(name)
|
|
132
|
-
const dbCore = corestore.get({ name: 'db' })
|
|
133
|
-
await dbCore.ready()
|
|
134
|
-
|
|
135
|
-
if (!dbCore.discovery) {
|
|
136
|
-
const discovery = sdk.join(dbCore.discoveryKey)
|
|
137
|
-
dbCore.discovery = discovery
|
|
138
|
-
dbCore.once('close', () => {
|
|
139
|
-
discovery.destroy()
|
|
140
|
-
})
|
|
141
|
-
await discovery.flushed()
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return dbCore
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async function getDrive (hostname, errorOnNew = false) {
|
|
148
|
-
if (drives.has(hostname)) {
|
|
149
|
-
return drives.get(hostname)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const core = await getCore(hostname)
|
|
153
|
-
|
|
154
|
-
if (!core.length && errorOnNew) {
|
|
155
|
-
await core.close()
|
|
156
|
-
const e = new Error(ERROR_DRIVE_EMPTY)
|
|
157
|
-
e.statusCode = 404
|
|
158
|
-
throw e
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const corestore = sdk.namespace(core.id)
|
|
162
|
-
const drive = new Hyperdrive(corestore, core.key)
|
|
163
|
-
|
|
164
|
-
await drive.ready()
|
|
165
|
-
|
|
166
|
-
drive.once('close', () => {
|
|
167
|
-
drives.delete(drive.core.id)
|
|
168
|
-
drives.delete(hostname)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
drives.set(drive.core.id, drive)
|
|
172
|
-
drives.set(drive.core.url, drive)
|
|
173
|
-
drives.set(hostname, drive)
|
|
174
|
-
|
|
175
|
-
return drive
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function getDriveFromKey (key, errorOnNew = false) {
|
|
179
|
-
if (drives.has(key)) {
|
|
180
|
-
return drives.get(key)
|
|
181
|
-
}
|
|
182
|
-
const core = await getDBCoreForName(key)
|
|
183
|
-
|
|
184
|
-
if (!core.length && errorOnNew) {
|
|
185
|
-
const e = new Error(ERROR_KEY_NOT_CREATED)
|
|
186
|
-
e.statusCode = 404
|
|
187
|
-
throw e
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const corestore = sdk.namespace(key)
|
|
191
|
-
const drive = new Hyperdrive(corestore)
|
|
192
|
-
|
|
193
|
-
await drive.ready()
|
|
194
|
-
|
|
195
|
-
drive.once('close', () => {
|
|
196
|
-
drives.delete(key)
|
|
197
|
-
drives.delete(drive.url)
|
|
198
|
-
drives.delete(drive.core.id)
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
drives.set(key, drive)
|
|
202
|
-
drives.set(drive.url, drive)
|
|
203
|
-
drives.set(drive.core.id, drive)
|
|
204
|
-
|
|
205
|
-
return drive
|
|
206
|
-
}
|
|
207
|
-
|
|
208
116
|
async function getExtension (core, name) {
|
|
209
117
|
const key = core.url + name
|
|
210
118
|
if (extensions.has(key)) {
|
|
@@ -244,7 +152,7 @@ export default async function makeHyperFetch ({
|
|
|
244
152
|
const { hostname } = new URL(request.url)
|
|
245
153
|
const accept = request.headers.get('Accept') || ''
|
|
246
154
|
|
|
247
|
-
const core = await
|
|
155
|
+
const core = await sdk.get(`hyper://${hostname}/`)
|
|
248
156
|
|
|
249
157
|
if (accept.includes('text/event-stream')) {
|
|
250
158
|
const events = new EventIterator(({ push }) => {
|
|
@@ -298,7 +206,7 @@ export default async function makeHyperFetch ({
|
|
|
298
206
|
const pathname = decodeURI(ensureLeadingSlash(rawPathname))
|
|
299
207
|
const name = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
|
|
300
208
|
|
|
301
|
-
const core = await
|
|
209
|
+
const core = await sdk.get(`hyper://${hostname}/`)
|
|
302
210
|
|
|
303
211
|
await getExtension(core, name)
|
|
304
212
|
|
|
@@ -321,7 +229,7 @@ export default async function makeHyperFetch ({
|
|
|
321
229
|
|
|
322
230
|
const name = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
|
|
323
231
|
|
|
324
|
-
const core = await
|
|
232
|
+
const core = await sdk.get(`hyper://${hostname}/`)
|
|
325
233
|
|
|
326
234
|
const extension = await getExtension(core, name)
|
|
327
235
|
const data = await request.text()
|
|
@@ -337,7 +245,7 @@ export default async function makeHyperFetch ({
|
|
|
337
245
|
const subFolder = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
|
|
338
246
|
const [name, extensionPeer] = subFolder.split('/')
|
|
339
247
|
|
|
340
|
-
const core = await
|
|
248
|
+
const core = await sdk.get(`hyper://${hostname}/`)
|
|
341
249
|
|
|
342
250
|
const extension = await getExtension(core, name)
|
|
343
251
|
const peers = await getExtensionPeers(core, name)
|
|
@@ -363,7 +271,7 @@ export default async function makeHyperFetch ({
|
|
|
363
271
|
}
|
|
364
272
|
|
|
365
273
|
try {
|
|
366
|
-
const drive = await
|
|
274
|
+
const drive = await sdk.getDrive(key)
|
|
367
275
|
|
|
368
276
|
return { body: drive.url }
|
|
369
277
|
} catch (e) {
|
|
@@ -389,7 +297,7 @@ export default async function makeHyperFetch ({
|
|
|
389
297
|
return { status: 400, body: 'Must specify key parameter to resolve' }
|
|
390
298
|
}
|
|
391
299
|
|
|
392
|
-
const drive = await
|
|
300
|
+
const drive = await sdk.getDrive(key)
|
|
393
301
|
|
|
394
302
|
return { body: drive.url }
|
|
395
303
|
}
|
|
@@ -401,7 +309,7 @@ export default async function makeHyperFetch ({
|
|
|
401
309
|
const mtime = Date.parse(request.headers.get('Last-Modified')) || Date.now()
|
|
402
310
|
const isFormData = contentType.includes('multipart/form-data')
|
|
403
311
|
|
|
404
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
312
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
405
313
|
|
|
406
314
|
if (!drive.db.feed.writable) {
|
|
407
315
|
return { status: 403, body: `Cannot PUT file to read-only drive: ${drive.url}`, headers: { Location: request.url } }
|
|
@@ -459,7 +367,7 @@ export default async function makeHyperFetch ({
|
|
|
459
367
|
|
|
460
368
|
async function deleteDrive (request) {
|
|
461
369
|
const { hostname } = new URL(request.url)
|
|
462
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
370
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
463
371
|
|
|
464
372
|
await drive.purge()
|
|
465
373
|
|
|
@@ -470,7 +378,7 @@ export default async function makeHyperFetch ({
|
|
|
470
378
|
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
471
379
|
const pathname = decodeURI(ensureLeadingSlash(rawPathname))
|
|
472
380
|
|
|
473
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
381
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
474
382
|
|
|
475
383
|
if (!drive.db.feed.writable) {
|
|
476
384
|
return { status: 403, body: `Cannot DELETE file in read-only drive: ${drive.url}`, headers: { Location: request.url } }
|
|
@@ -522,7 +430,14 @@ export default async function makeHyperFetch ({
|
|
|
522
430
|
const version = parts[3]
|
|
523
431
|
const realPath = ensureLeadingSlash(parts.slice(4).join('/'))
|
|
524
432
|
|
|
525
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
433
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
434
|
+
|
|
435
|
+
if (!drive.writable && !drive.core.length) {
|
|
436
|
+
return {
|
|
437
|
+
status: 404,
|
|
438
|
+
body: 'Peers Not Found'
|
|
439
|
+
}
|
|
440
|
+
}
|
|
526
441
|
|
|
527
442
|
const snapshot = await drive.checkout(version)
|
|
528
443
|
|
|
@@ -538,7 +453,14 @@ export default async function makeHyperFetch ({
|
|
|
538
453
|
const isRanged = request.headers.get('Range') || ''
|
|
539
454
|
const noResolve = searchParams.has('noResolve')
|
|
540
455
|
|
|
541
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
456
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
457
|
+
|
|
458
|
+
if (!drive.writable && !drive.core.length) {
|
|
459
|
+
return {
|
|
460
|
+
status: 404,
|
|
461
|
+
body: 'Peers Not Found'
|
|
462
|
+
}
|
|
463
|
+
}
|
|
542
464
|
|
|
543
465
|
return serveHead(drive, pathname, { accept, isRanged, noResolve })
|
|
544
466
|
}
|
|
@@ -662,7 +584,7 @@ export default async function makeHyperFetch ({
|
|
|
662
584
|
const version = parts[3]
|
|
663
585
|
const realPath = ensureLeadingSlash(parts.slice(4).join('/'))
|
|
664
586
|
|
|
665
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
587
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
666
588
|
|
|
667
589
|
const snapshot = await drive.checkout(version)
|
|
668
590
|
|
|
@@ -679,7 +601,14 @@ export default async function makeHyperFetch ({
|
|
|
679
601
|
const isRanged = request.headers.get('Range') || ''
|
|
680
602
|
const noResolve = searchParams.has('noResolve')
|
|
681
603
|
|
|
682
|
-
const drive = await getDrive(`hyper://${hostname}
|
|
604
|
+
const drive = await sdk.getDrive(`hyper://${hostname}/`)
|
|
605
|
+
|
|
606
|
+
if (!drive.writable && !drive.core.length) {
|
|
607
|
+
return {
|
|
608
|
+
status: 404,
|
|
609
|
+
body: 'Peers Not Found'
|
|
610
|
+
}
|
|
611
|
+
}
|
|
683
612
|
|
|
684
613
|
return serveGet(drive, pathname, { accept, isRanged, noResolve })
|
|
685
614
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore-fetch",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"description": "Implementation of Fetch that uses the Dat SDK for loading p2p content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
"homepage": "https://github.com/RangerMauve/hypercore-fetch#readme",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"event-iterator": "^2.0.0",
|
|
27
|
-
"hyperdrive": "^11.8.1",
|
|
28
27
|
"make-fetch": "^3.1.1",
|
|
29
28
|
"mime": "^3.0.0",
|
|
30
29
|
"range-parser": "^1.2.1",
|
|
@@ -32,7 +31,7 @@
|
|
|
32
31
|
},
|
|
33
32
|
"devDependencies": {
|
|
34
33
|
"@rangermauve/fetch-event-source": "^1.0.3",
|
|
35
|
-
"hyper-sdk": "^
|
|
34
|
+
"hyper-sdk": "^6.0.0",
|
|
36
35
|
"standard": "^17.0.0",
|
|
37
36
|
"tape": "^5.2.2"
|
|
38
37
|
}
|
package/test.js
CHANGED
|
@@ -3,6 +3,9 @@ import * as SDK from 'hyper-sdk'
|
|
|
3
3
|
import test from 'tape'
|
|
4
4
|
import createEventSource from '@rangermauve/fetch-event-source'
|
|
5
5
|
import { once } from 'events'
|
|
6
|
+
import os from 'os'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
import { rm } from 'fs/promises'
|
|
6
9
|
|
|
7
10
|
import makeHyperFetch from './index.js'
|
|
8
11
|
|
|
@@ -23,8 +26,11 @@ async function nextURL (t) {
|
|
|
23
26
|
return created
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
const
|
|
27
|
-
const
|
|
29
|
+
const tmpSuffix = Math.random().toString().slice(3, 8)
|
|
30
|
+
const tmp = join(os.tmpdir(), `hp-ftch-${tmpSuffix}`)
|
|
31
|
+
|
|
32
|
+
const sdk1 = await SDK.create({ storage: join(tmp, 'sdk1') })
|
|
33
|
+
const sdk2 = await SDK.create({ storage: join(tmp, 'sdk2') })
|
|
28
34
|
|
|
29
35
|
const fetch = await makeHyperFetch({
|
|
30
36
|
sdk: sdk1,
|
|
@@ -36,9 +42,12 @@ const fetch2 = await makeHyperFetch({
|
|
|
36
42
|
writable: true
|
|
37
43
|
})
|
|
38
44
|
|
|
39
|
-
test.onFinish(() => {
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
test.onFinish(async () => {
|
|
46
|
+
await Promise.all([
|
|
47
|
+
sdk1.close(),
|
|
48
|
+
sdk2.close()
|
|
49
|
+
])
|
|
50
|
+
await rm(tmp, { recursive: true })
|
|
42
51
|
})
|
|
43
52
|
|
|
44
53
|
test('Quick check', async (t) => {
|
|
@@ -89,14 +98,6 @@ test('Quick check', async (t) => {
|
|
|
89
98
|
test('GET full url for created keys', async (t) => {
|
|
90
99
|
const keyURL = `hyper://localhost/?key=example${next()}`
|
|
91
100
|
|
|
92
|
-
const nonExistingResponse = await fetch(keyURL)
|
|
93
|
-
|
|
94
|
-
t.notOk(nonExistingResponse.ok, 'response has error before key is created')
|
|
95
|
-
const errorMessage = await nonExistingResponse.text()
|
|
96
|
-
|
|
97
|
-
t.equal(nonExistingResponse.status, 400, 'Got 400 error code')
|
|
98
|
-
t.notOk(errorMessage.startsWith('hyper://'), 'did not return hyper URL')
|
|
99
|
-
|
|
100
101
|
const createResponse = await fetch(keyURL, { method: 'post' })
|
|
101
102
|
await checkResponse(createResponse, t, 'Able to create drive')
|
|
102
103
|
|
|
@@ -302,7 +303,7 @@ test('DELETE a directory', async (t) => {
|
|
|
302
303
|
const entries = await listDirRequest.json()
|
|
303
304
|
t.deepEqual(entries, [], 'subfolder got deleted')
|
|
304
305
|
})
|
|
305
|
-
test('DELETE a drive from storage', async (t) => {
|
|
306
|
+
test.skip('DELETE a drive from storage', async (t) => {
|
|
306
307
|
const created = await nextURL(t)
|
|
307
308
|
|
|
308
309
|
const uploadLocation = new URL('./subfolder/example.txt', created)
|
|
@@ -518,7 +519,7 @@ test('Doing a `GET` on an invalid domain/public key should cause an error', asyn
|
|
|
518
519
|
|
|
519
520
|
const invalidPublicKeyResponse = await fetch('hyper://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/')
|
|
520
521
|
t.notOk(invalidPublicKeyResponse.ok, 'Response errored out due to invalid public key')
|
|
521
|
-
t.equal(invalidPublicKeyResponse.status, 404, 'Invalid public key should
|
|
522
|
+
t.equal(invalidPublicKeyResponse.status, 404, 'Invalid public key should error')
|
|
522
523
|
})
|
|
523
524
|
|
|
524
525
|
test('Old versions in VERSION folder', async (t) => {
|
|
@@ -615,9 +616,8 @@ test('Handle empty string pathname', async (t) => {
|
|
|
615
616
|
await checkResponse(versionedGetResponse, t)
|
|
616
617
|
t.deepEqual(await versionedGetResponse.json(), ['example.txt', 'example2.txt'], 'Returns root directory prior to DELETE')
|
|
617
618
|
|
|
618
|
-
|
|
619
619
|
// DELETE
|
|
620
|
-
await checkResponse(await fetch(urlNoTrailingSlash, { method: 'DELETE' }), t, 'Able to delete root')
|
|
620
|
+
// await checkResponse(await fetch(urlNoTrailingSlash, { method: 'DELETE' }), t, 'Able to delete root')
|
|
621
621
|
})
|
|
622
622
|
|
|
623
623
|
test('Return status 403 Forbidden on attempt to modify read-only hyperdrive', async (t) => {
|
|
@@ -646,8 +646,7 @@ test('Check hyperdrive writability', async (t) => {
|
|
|
646
646
|
const readOnlyHeadersAllow = readOnlyHeadResponse.headers.get('Allow')
|
|
647
647
|
t.equal(readOnlyHeadersAllow, 'HEAD,GET', 'Expected read-only Allows header')
|
|
648
648
|
|
|
649
|
-
const
|
|
650
|
-
const writableHeadResponse = await fetch(writableRootDirectory, { method: 'HEAD' })
|
|
649
|
+
const writableHeadResponse = await fetch(created, { method: 'HEAD' })
|
|
651
650
|
await checkResponse(writableHeadResponse, t, 'Able to load HEAD')
|
|
652
651
|
const writableHeadersAllow = writableHeadResponse.headers.get('Allow')
|
|
653
652
|
t.equal(writableHeadersAllow, 'HEAD,GET,PUT,DELETE', 'Expected writable Allows header')
|