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.
Files changed (4) hide show
  1. package/README.md +7 -2
  2. package/index.js +3 -3
  3. package/package.json +3 -3
  4. 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 `body`.
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
- Note that this is only available with the `writable: true` flag.
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/lite.js'
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.7.0",
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.0.0-alpha.10",
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.3.0",
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.ok(lastModified, 'Last-Modified header got set')
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(created, {
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.only('DELETE a drive from storage', async (t) => {
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('hyper://blog.mauve.moe/?noResolve')
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
- }), t
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 empty root directory')
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 = 'hyper://blog.mauve.moe/new-file.txt'
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 = 'hyper://blog.mauve.moe/?noResolve'
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')