hypercore-fetch 9.7.0 → 9.9.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/README.md +7 -2
- package/index.js +3 -3
- package/package.json +3 -3
- package/test.js +25 -17
package/README.md
CHANGED
|
@@ -120,13 +120,18 @@ Note that this is only available with the `writable: true` flag.
|
|
|
120
120
|
|
|
121
121
|
### `fetch('hyper://NAME/example.txt', {method: 'PUT', body: 'Hello World'})`
|
|
122
122
|
|
|
123
|
-
You can add files to archives using a `PUT` method along with a
|
|
123
|
+
You can add files to archives using a `PUT` method along with a
|
|
124
|
+
`body`. Note that this is only available with the `writable: true`
|
|
125
|
+
flag.
|
|
124
126
|
|
|
125
127
|
The `body` can be any of the options supported by the Fetch API such as a `String`, `Blob`, `FormData`, or `ReadableStream`.
|
|
126
128
|
|
|
127
129
|
`NAME` can either be the 52 character [z32 encoded](https://github.com/mafintosh/z32) key for a Hyperdrive or Hypercore , or a domain to parse with the [DNSLink](https://www.dnslink.io/) standard.
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
The mtime metadata is automatically set to the current time when
|
|
132
|
+
uploading. To override this value, pass a `Last-Modified` header with a value
|
|
133
|
+
set to a date string according to [RFC
|
|
134
|
+
7231](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.1.1).
|
|
130
135
|
|
|
131
136
|
An attempt to `PUT` a file to a hyperdrive which is not writable will
|
|
132
137
|
fail with status `403`.
|
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { posix } from 'path'
|
|
|
3
3
|
import { Readable, pipelinePromise } from 'streamx'
|
|
4
4
|
import Hyperdrive from 'hyperdrive'
|
|
5
5
|
import { makeRoutedFetch } from 'make-fetch'
|
|
6
|
-
import mime from 'mime/
|
|
6
|
+
import mime from 'mime/index.js'
|
|
7
7
|
import parseRange from 'range-parser'
|
|
8
8
|
import { EventIterator } from 'event-iterator'
|
|
9
9
|
|
|
@@ -169,6 +169,7 @@ export default async function makeHyperFetch ({
|
|
|
169
169
|
})
|
|
170
170
|
|
|
171
171
|
drives.set(drive.core.id, drive)
|
|
172
|
+
drives.set(drive.core.url, drive)
|
|
172
173
|
drives.set(hostname, drive)
|
|
173
174
|
|
|
174
175
|
return drive
|
|
@@ -397,6 +398,7 @@ export default async function makeHyperFetch ({
|
|
|
397
398
|
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
398
399
|
const pathname = decodeURI(ensureLeadingSlash(rawPathname))
|
|
399
400
|
const contentType = request.headers.get('Content-Type') || ''
|
|
401
|
+
const mtime = Date.parse(request.headers.get('Last-Modified')) || Date.now()
|
|
400
402
|
const isFormData = contentType.includes('multipart/form-data')
|
|
401
403
|
|
|
402
404
|
const drive = await getDrive(`hyper://${hostname}/`, true)
|
|
@@ -405,8 +407,6 @@ export default async function makeHyperFetch ({
|
|
|
405
407
|
return { status: 403, body: `Cannot PUT file to read-only drive: ${drive.url}`, headers: { Location: request.url } }
|
|
406
408
|
}
|
|
407
409
|
|
|
408
|
-
const mtime = Date.now()
|
|
409
|
-
|
|
410
410
|
if (isFormData) {
|
|
411
411
|
// It's a form! Get the files out and process them
|
|
412
412
|
const formData = await request.formData()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore-fetch",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.9.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,7 @@
|
|
|
24
24
|
"homepage": "https://github.com/RangerMauve/hypercore-fetch#readme",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"event-iterator": "^2.0.0",
|
|
27
|
-
"hyperdrive": "^11.
|
|
27
|
+
"hyperdrive": "^11.6.2",
|
|
28
28
|
"make-fetch": "^3.1.1",
|
|
29
29
|
"mime": "^3.0.0",
|
|
30
30
|
"range-parser": "^1.2.1",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@rangermauve/fetch-event-source": "^1.0.3",
|
|
35
|
-
"hyper-sdk": "^4.
|
|
35
|
+
"hyper-sdk": "^4.4.0",
|
|
36
36
|
"standard": "^17.0.0",
|
|
37
37
|
"tape": "^5.2.2"
|
|
38
38
|
}
|
package/test.js
CHANGED
|
@@ -7,6 +7,7 @@ import { once } from 'events'
|
|
|
7
7
|
import makeHyperFetch from './index.js'
|
|
8
8
|
|
|
9
9
|
const SAMPLE_CONTENT = 'Hello World'
|
|
10
|
+
const DNS_DOMAIN = 'blog.mauve.moe'
|
|
10
11
|
let count = 0
|
|
11
12
|
function next () {
|
|
12
13
|
return count++
|
|
@@ -142,9 +143,13 @@ test('PUT file', async (t) => {
|
|
|
142
143
|
|
|
143
144
|
const uploadLocation = new URL('./example.txt', created)
|
|
144
145
|
|
|
146
|
+
const fakeDate = new Date(Date.parse(0)).toUTCString()
|
|
145
147
|
const uploadResponse = await fetch(uploadLocation, {
|
|
146
148
|
method: 'put',
|
|
147
|
-
body: SAMPLE_CONTENT
|
|
149
|
+
body: SAMPLE_CONTENT,
|
|
150
|
+
headers: {
|
|
151
|
+
'Last-Modified': fakeDate
|
|
152
|
+
}
|
|
148
153
|
})
|
|
149
154
|
|
|
150
155
|
await checkResponse(uploadResponse, t, 'upload successful')
|
|
@@ -159,7 +164,7 @@ test('PUT file', async (t) => {
|
|
|
159
164
|
|
|
160
165
|
t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
|
|
161
166
|
t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
|
|
162
|
-
t.
|
|
167
|
+
t.equal(lastModified, fakeDate, 'Last-Modified header was set to value of Date header')
|
|
163
168
|
})
|
|
164
169
|
test('PUT FormData', async (t) => {
|
|
165
170
|
const created = await nextURL(t)
|
|
@@ -287,7 +292,7 @@ test('DELETE a directory', async (t) => {
|
|
|
287
292
|
})
|
|
288
293
|
await checkResponse(uploadResponse, t)
|
|
289
294
|
|
|
290
|
-
const deleteResponse = await fetch(
|
|
295
|
+
const deleteResponse = await fetch(uploadLocation, {
|
|
291
296
|
method: 'delete'
|
|
292
297
|
})
|
|
293
298
|
await checkResponse(deleteResponse, t, 'Able to DELETE')
|
|
@@ -297,7 +302,7 @@ test('DELETE a directory', async (t) => {
|
|
|
297
302
|
const entries = await listDirRequest.json()
|
|
298
303
|
t.deepEqual(entries, [], 'subfolder got deleted')
|
|
299
304
|
})
|
|
300
|
-
test
|
|
305
|
+
test('DELETE a drive from storage', async (t) => {
|
|
301
306
|
const created = await nextURL(t)
|
|
302
307
|
|
|
303
308
|
const uploadLocation = new URL('./subfolder/example.txt', created)
|
|
@@ -494,11 +499,17 @@ test('EventSource extension messages', async (t) => {
|
|
|
494
499
|
})
|
|
495
500
|
|
|
496
501
|
test('Resolve DNS', async (t) => {
|
|
497
|
-
const loadResponse = await fetch(
|
|
502
|
+
const loadResponse = await fetch(`hyper://${DNS_DOMAIN}/?noResolve`)
|
|
498
503
|
|
|
499
504
|
const entries = await loadResponse.json()
|
|
500
505
|
|
|
501
506
|
t.ok(entries.length, 'Loaded contents with some files present')
|
|
507
|
+
|
|
508
|
+
const rawLink = loadResponse.headers.get('Link').match(/<(.+)>/)[1]
|
|
509
|
+
const loadRawURLResponse = await fetch(rawLink + '?noResolve')
|
|
510
|
+
|
|
511
|
+
const rawLinkEntries = await loadRawURLResponse.json()
|
|
512
|
+
t.deepEqual(rawLinkEntries, entries, 'Raw link resolves to same content as DNS domain.')
|
|
502
513
|
})
|
|
503
514
|
|
|
504
515
|
test('Doing a `GET` on an invalid domain/public key should cause an error', async (t) => {
|
|
@@ -585,17 +596,10 @@ test('Handle empty string pathname', async (t) => {
|
|
|
585
596
|
await fetch(urlNoTrailingSlash, {
|
|
586
597
|
method: 'put',
|
|
587
598
|
body: formData
|
|
588
|
-
}),
|
|
599
|
+
}),
|
|
600
|
+
t
|
|
589
601
|
)
|
|
590
602
|
|
|
591
|
-
// DELETE
|
|
592
|
-
await checkResponse(await fetch(urlNoTrailingSlash, { method: 'DELETE' }), t)
|
|
593
|
-
|
|
594
|
-
// HEAD
|
|
595
|
-
const headResponse = await fetch(urlNoTrailingSlash, { method: 'HEAD' })
|
|
596
|
-
await checkResponse(headResponse, t)
|
|
597
|
-
t.deepEqual(headResponse.headers.get('Etag'), '5', 'HEAD request returns correct Etag')
|
|
598
|
-
|
|
599
603
|
// HEAD (versioned)
|
|
600
604
|
const versionedHeadResponse = await fetch(versionedURLNoTrailingSlash, { method: 'HEAD' })
|
|
601
605
|
await checkResponse(versionedHeadResponse, t)
|
|
@@ -604,16 +608,20 @@ test('Handle empty string pathname', async (t) => {
|
|
|
604
608
|
// GET
|
|
605
609
|
const getResponse = await fetch(urlNoTrailingSlash)
|
|
606
610
|
await checkResponse(getResponse, t)
|
|
607
|
-
t.deepEqual(await getResponse.json(), [], 'Returns
|
|
611
|
+
t.deepEqual(await getResponse.json(), ['example.txt', 'example2.txt'], 'Returns directory listing')
|
|
608
612
|
|
|
609
613
|
// GET (versioned)
|
|
610
614
|
const versionedGetResponse = await fetch(versionedURLNoTrailingSlash)
|
|
611
615
|
await checkResponse(versionedGetResponse, t)
|
|
612
616
|
t.deepEqual(await versionedGetResponse.json(), ['example.txt', 'example2.txt'], 'Returns root directory prior to DELETE')
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
// DELETE
|
|
620
|
+
await checkResponse(await fetch(urlNoTrailingSlash, { method: 'DELETE' }), t, 'Able to delete root')
|
|
613
621
|
})
|
|
614
622
|
|
|
615
623
|
test('Return status 403 Forbidden on attempt to modify read-only hyperdrive', async (t) => {
|
|
616
|
-
const readOnlyURL =
|
|
624
|
+
const readOnlyURL = `hyper://${DNS_DOMAIN}/new-file.txt`
|
|
617
625
|
const putResponse = await fetch(readOnlyURL, { method: 'PUT', body: SAMPLE_CONTENT })
|
|
618
626
|
if (putResponse.ok) {
|
|
619
627
|
throw new Error('PUT file to read-only drive should have failed')
|
|
@@ -632,7 +640,7 @@ test('Return status 403 Forbidden on attempt to modify read-only hyperdrive', as
|
|
|
632
640
|
test('Check hyperdrive writability', async (t) => {
|
|
633
641
|
const created = await nextURL(t)
|
|
634
642
|
|
|
635
|
-
const readOnlyRootDirectory =
|
|
643
|
+
const readOnlyRootDirectory = `hyper://${DNS_DOMAIN}/?noResolve`
|
|
636
644
|
const readOnlyHeadResponse = await fetch(readOnlyRootDirectory, { method: 'HEAD' })
|
|
637
645
|
await checkResponse(readOnlyHeadResponse, t, 'Able to load HEAD')
|
|
638
646
|
const readOnlyHeadersAllow = readOnlyHeadResponse.headers.get('Allow')
|