hypercore-fetch 9.9.0 → 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.
Files changed (3) hide show
  1. package/index.js +34 -105
  2. package/package.json +2 -3
  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 getCore(`hyper://${hostname}/`)
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 getCore(`hyper://${hostname}/`)
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 getCore(`hyper://${hostname}/`)
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 getCore(`hyper://${hostname}/`)
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 getDriveFromKey(key, true)
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 getDriveFromKey(key, false)
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}/`, true)
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}/`, true)
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}/`, true)
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}/`, true)
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}/`, true)
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}/`, true)
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}/`, true)
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": "9.9.0",
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.6.2",
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": "^4.4.0",
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 sdk1 = await SDK.create({ storage: false })
27
- const sdk2 = await SDK.create({ storage: false })
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
- sdk1.close()
41
- sdk2.close()
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 404')
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 writableRootDirectory = new URL('/', created)
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')