hypercore-fetch 9.0.5 → 9.0.7

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 +27 -16
  2. package/package.json +1 -1
  3. package/test.js +30 -3
package/index.js CHANGED
@@ -22,6 +22,7 @@ const MIME_TEXT_HTML = 'text/html; charset=utf-8'
22
22
  const MIME_EVENT_STREAM = 'text/event-stream; charset=utf-8'
23
23
 
24
24
  const HEADER_CONTENT_TYPE = 'Content-Type'
25
+ const HEADER_LAST_MODIFIED = 'Last-Modified'
25
26
 
26
27
  export const ERROR_KEY_NOT_CREATED = 'Must create key with POST before reading'
27
28
 
@@ -208,7 +209,8 @@ export default async function makeHyperFetch ({
208
209
  }
209
210
  })
210
211
  router.get(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/*`, async function listenExtension (request) {
211
- const { hostname, pathname } = new URL(request.url)
212
+ const { hostname, pathname: rawPathname } = new URL(request.url)
213
+ const pathname = decodeURI(rawPathname)
212
214
  const name = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
213
215
 
214
216
  const core = await getCore(`hyper://${hostname}/`)
@@ -228,7 +230,9 @@ export default async function makeHyperFetch ({
228
230
  }
229
231
  })
230
232
  router.post(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/*`, async function broadcastExtension (request) {
231
- const { hostname, pathname } = new URL(request.url)
233
+ const { hostname, pathname: rawPathname } = new URL(request.url)
234
+ const pathname = decodeURI(rawPathname)
235
+
232
236
  const name = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
233
237
 
234
238
  const core = await getCore(`hyper://${hostname}/`)
@@ -240,7 +244,9 @@ export default async function makeHyperFetch ({
240
244
  return { status: 200 }
241
245
  })
242
246
  router.post(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/*/*`, async function extensionToPeer (request) {
243
- const { hostname, pathname } = new URL(request.url)
247
+ const { hostname, pathname: rawPathname } = new URL(request.url)
248
+ const pathname = decodeURI(rawPathname)
249
+
244
250
  const subFolder = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
245
251
  const [name, extensionPeer] = subFolder.split('/')
246
252
 
@@ -303,7 +309,8 @@ export default async function makeHyperFetch ({
303
309
  })
304
310
 
305
311
  router.put('hyper://*/**', async function putFiles (request) {
306
- const { hostname, pathname } = new URL(request.url)
312
+ const { hostname, pathname: rawPathname } = new URL(request.url)
313
+ const pathname = decodeURI(rawPathname)
307
314
  const contentType = request.headers.get('Content-Type') || ''
308
315
  const isFormData = contentType.includes('multipart/form-data')
309
316
 
@@ -338,7 +345,8 @@ export default async function makeHyperFetch ({
338
345
  return { status: 201, headers: { Location: request.url } }
339
346
  })
340
347
  router.delete('hyper://*/**', async function putFiles (request) {
341
- const { hostname, pathname } = new URL(request.url)
348
+ const { hostname, pathname: rawPathname } = new URL(request.url)
349
+ const pathname = decodeURI(rawPathname)
342
350
 
343
351
  const drive = await getDrive(`hyper://${hostname}`)
344
352
 
@@ -367,7 +375,9 @@ export default async function makeHyperFetch ({
367
375
 
368
376
  router.head('hyper://*/**', async function headFiles (request) {
369
377
  const url = new URL(request.url)
370
- const { hostname, pathname, searchParams } = url
378
+ const { hostname, pathname: rawPathname, searchParams } = url
379
+ const pathname = decodeURI(rawPathname)
380
+
371
381
  const accept = request.headers.get('Accept') || ''
372
382
  const isRanged = request.headers.get('Range') || ''
373
383
  const noResolve = searchParams.has('noResolve')
@@ -434,16 +444,15 @@ export default async function makeHyperFetch ({
434
444
  return { status: 404, body: 'Not Found' }
435
445
  }
436
446
 
437
- resHeaders.Link = new URL(path, drive.core.url).href
438
-
439
447
  resHeaders.ETag = `${entry.seq}`
448
+ resHeaders['Content-Length'] = `${entry.value.blob.byteLength}`
440
449
 
441
450
  const contentType = getMimeType(path)
442
- resHeaders['Content-Type'] = contentType
451
+ resHeaders[HEADER_CONTENT_TYPE] = contentType
443
452
 
444
- if (entry.metadata?.mtime) {
445
- const date = new Date(entry.metadata.mtime)
446
- resHeaders['Last-Modified'] = date.toUTCString()
453
+ if (entry?.value?.metadata?.mtime) {
454
+ const date = new Date(entry.value.metadata.mtime)
455
+ resHeaders[HEADER_LAST_MODIFIED] = date.toUTCString()
447
456
  }
448
457
 
449
458
  const size = entry.value.byteLength
@@ -474,7 +483,9 @@ export default async function makeHyperFetch ({
474
483
  // TODO: Redirect on directories without trailing slash
475
484
  router.get('hyper://*/**', async function getFiles (request) {
476
485
  const url = new URL(request.url)
477
- const { hostname, pathname, searchParams } = url
486
+ const { hostname, pathname: rawPathname, searchParams } = url
487
+ const pathname = decodeURI(rawPathname)
488
+
478
489
  const accept = request.headers.get('Accept') || ''
479
490
  const noResolve = searchParams.has('noResolve')
480
491
  const isDirectory = pathname.endsWith('/')
@@ -556,9 +567,9 @@ async function serveFile (headers, drive, pathname) {
556
567
  Link: `<${fullURL}>; rel="canonical"`
557
568
  }
558
569
 
559
- if (entry.metadata?.mtime) {
560
- const date = new Date(entry.metadata.mtime)
561
- resHeaders['Last-Modified'] = date.toUTCString()
570
+ if (entry?.value?.metadata?.mtime) {
571
+ const date = new Date(entry.value.metadata.mtime)
572
+ resHeaders[HEADER_LAST_MODIFIED] = date.toUTCString()
562
573
  }
563
574
 
564
575
  const size = entry.value.blob.byteLength
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore-fetch",
3
- "version": "9.0.5",
3
+ "version": "9.0.7",
4
4
  "description": "Implementation of Fetch that uses the Dat SDK for loading p2p content",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/test.js CHANGED
@@ -57,7 +57,7 @@ test('Quick check', async (t) => {
57
57
 
58
58
  t.deepEqual(await existsResponse.json(), [], 'Empty dir on create')
59
59
 
60
- const uploadLocation = new URL('./example.txt', created)
60
+ const uploadLocation = new URL('./example .txt', created)
61
61
 
62
62
  const uploadResponse = await fetch(uploadLocation, {
63
63
  method: 'put',
@@ -74,7 +74,7 @@ test('Quick check', async (t) => {
74
74
  const contentType = uploadedContentResponse.headers.get('Content-Type')
75
75
  const contentLink = uploadedContentResponse.headers.get('Link')
76
76
 
77
- t.match(contentLink, /^<hyper:\/\/[0-9a-z]{52}\/example.txt>; rel="canonical"$/, 'Link header includes both public key and path.')
77
+ t.match(contentLink, /^<hyper:\/\/[0-9a-z]{52}\/example%20.txt>; rel="canonical"$/, 'Link header includes both public key and path.')
78
78
  t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
79
79
  t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
80
80
 
@@ -82,7 +82,7 @@ test('Quick check', async (t) => {
82
82
 
83
83
  await checkResponse(dirResponse, t)
84
84
 
85
- t.deepEqual(await dirResponse.json(), ['example.txt'], 'File got added')
85
+ t.deepEqual(await dirResponse.json(), ['example .txt'], 'File got added')
86
86
  })
87
87
 
88
88
  test('GET full url for created keys', async (t) => {
@@ -111,6 +111,31 @@ test('GET full url for created keys', async (t) => {
111
111
  t.equal(existingURL, createdURL, 'URL same as in initial create')
112
112
  })
113
113
 
114
+ test('HEAD request', async (t) => {
115
+ const created = await nextURL(t)
116
+ const uploadLocation = new URL('./example.txt', created)
117
+ await fetch(uploadLocation, { method: 'put', body: SAMPLE_CONTENT })
118
+
119
+ const headResponse = await fetch(uploadLocation, { method: 'head' })
120
+
121
+ await checkResponse(headResponse, t, 'Able to load HEAD')
122
+
123
+ const headersEtag = headResponse.headers.get('Etag')
124
+ const headersContentType = headResponse.headers.get('Content-Type')
125
+ const headersContentLength = headResponse.headers.get('Content-Length')
126
+ const headersAcceptRanges = headResponse.headers.get('Accept-Ranges')
127
+ const headersLastModified = headResponse.headers.get('Last-Modified')
128
+ const headersLink = headResponse.headers.get('Link')
129
+
130
+ // Version at which the file was added
131
+ t.equal(headersEtag, '1', 'Headers got expected etag')
132
+ t.equal(headersContentType, 'text/plain; charset=utf-8', 'Headers got expected mime type')
133
+ t.ok(headersContentLength, "Headers have 'Content-Length' set.")
134
+ t.ok(headersLastModified, "Headers have 'Last-Modified' set.")
135
+ t.equal(headersAcceptRanges, 'bytes')
136
+ t.match(headersLink, /^<hyper:\/\/[0-9a-z]{52}\/example.txt>; rel="canonical"$/, 'Link header includes both public key and path.')
137
+ })
138
+
114
139
  test('PUT file', async (t) => {
115
140
  const created = await nextURL(t)
116
141
 
@@ -129,9 +154,11 @@ test('PUT file', async (t) => {
129
154
 
130
155
  const content = await uploadedContentResponse.text()
131
156
  const contentType = uploadedContentResponse.headers.get('Content-Type')
157
+ const lastModified = uploadedContentResponse.headers.get('Last-Modified')
132
158
 
133
159
  t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
134
160
  t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
161
+ t.ok(lastModified, 'Last-Modified header got set')
135
162
  })
136
163
  test('PUT FormData', async (t) => {
137
164
  const created = await nextURL(t)