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.
- package/index.js +27 -16
- package/package.json +1 -1
- 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[
|
|
451
|
+
resHeaders[HEADER_CONTENT_TYPE] = contentType
|
|
443
452
|
|
|
444
|
-
if (entry
|
|
445
|
-
const date = new Date(entry.metadata.mtime)
|
|
446
|
-
resHeaders[
|
|
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
|
|
560
|
-
const date = new Date(entry.metadata.mtime)
|
|
561
|
-
resHeaders[
|
|
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
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)
|