hypercore-fetch 10.1.0 → 10.2.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 +20 -4
  2. package/index.js +110 -20
  3. package/package.json +1 -1
  4. package/test.js +1 -1
package/README.md CHANGED
@@ -11,11 +11,13 @@ import * as SDK from 'hyper-sdk'
11
11
  // Create in-memory hyper-sdk instance
12
12
  const sdk = await SDK.create({storage: false})
13
13
 
14
+ // Init
14
15
  const fetch = await makeFetch({
15
16
  sdk: true,
16
17
  writable: true
17
18
  })
18
19
 
20
+ // Download
19
21
  const someURL = `hyper://blog.mauve.moe/`
20
22
 
21
23
  const response = await fetch(someURL)
@@ -23,6 +25,16 @@ const response = await fetch(someURL)
23
25
  const data = await response.text()
24
26
 
25
27
  console.log(data)
28
+
29
+ const response = await fetch('hyper://localhost/example.txt', {
30
+ method: 'PUT',
31
+ body: 'Hello World'
32
+ })
33
+
34
+ // This is where the data got uploaded
35
+ const location = response.headers.get('Location')
36
+ // Clear the response body or else electron will flip out
37
+ await response.text()
26
38
  ```
27
39
 
28
40
  ## API
@@ -104,6 +116,8 @@ In order to create a writable Hyperdrive with its own URL, you must first genera
104
116
 
105
117
  `NAME` can be any alphanumeric string which can be used for key generation in [Corestore](https://github.com/holepunchto/corestore).
106
118
 
119
+ If you omit the `NAME`, it will use the name `default`.
120
+
107
121
  The response body will contain a `hyper://` URL with the new Hyperdrive.
108
122
 
109
123
  You can then use this with `PUT`/`DELETE` requests.
@@ -116,6 +130,8 @@ If you want to resolve the public key URL of a previously created Hyperdrive, yo
116
130
 
117
131
  `NAME` can be any alphanumeric string which can be used for key generation in [Corestore](https://github.com/holepunchto/corestore).
118
132
 
133
+ If you omit the `NAME`, it will use the name `default`.
134
+
119
135
  The response body will contain a `hyper://` URL with the new Hyperdrive.
120
136
 
121
137
  You can then use this with `PUT`/`DELETE` requests.
@@ -130,7 +146,7 @@ flag.
130
146
 
131
147
  The `body` can be any of the options supported by the Fetch API such as a `String`, `Blob`, `FormData`, or `ReadableStream`.
132
148
 
133
- `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.
149
+ `NAME` can either be the 52 character [z32 encoded](https://github.com/mafintosh/z32) key for a Hyperdrive, `localhost` for the `default` drive, or a domain to parse with the [DNSLink](https://www.dnslink.io/) standard.
134
150
 
135
151
  The mtime metadata is automatically set to the current time when
136
152
  uploading. To override this value, pass a `Last-Modified` header with a value
@@ -149,7 +165,7 @@ The `filename` will be the filename within the directory that gets created.
149
165
 
150
166
  Note that you must use the name `file` for uploaded files.
151
167
 
152
- `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.
168
+ `NAME` can either be the 52 character [z32 encoded](https://github.com/mafintosh/z32) key for a Hyperdrive, `localhost` for the `default` drive, or a domain to parse with the [DNSLink](https://www.dnslink.io/) standard.
153
169
 
154
170
  ### `fetch('hyper://NAME/', {method: 'DELETE'})`
155
171
 
@@ -159,13 +175,13 @@ If this is a writable drive, your data will get fully clearned and trying to wri
159
175
 
160
176
  If you try to load this drive again data will be loaded from scratch.
161
177
 
162
- `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.
178
+ `NAME` can either be the 52 character [z32 encoded](https://github.com/mafintosh/z32) key for a Hyperdrive, `localhost` for the `default` drive, or a domain to parse with the [DNSLink](https://www.dnslink.io/) standard.
163
179
 
164
180
  ### `fetch('hyper://NAME/example.txt', {method: 'DELETE'})`
165
181
 
166
182
  You can delete a file or directory tree in a Hyperdrive by using the `DELETE` method.
167
183
 
168
- `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.
184
+ `NAME` can either be the 52 character [z32 encoded](https://github.com/mafintosh/z32) key for a Hyperdrive, `localhost` for the `default` drive, or a domain to parse with the [DNSLink](https://www.dnslink.io/) standard.
169
185
 
170
186
  Note that this is only available with the `writable: true` flag.
171
187
 
package/index.js CHANGED
@@ -15,6 +15,7 @@ import { EventIterator } from 'event-iterator'
15
15
  /** @typedef {(url: URL) => void} OnDeleteHandler */
16
16
 
17
17
  const DEFAULT_TIMEOUT = 5000
18
+ const DEFAULT_DRIVE = 'default'
18
19
 
19
20
  const SPECIAL_DOMAIN = 'localhost'
20
21
  const SPECIAL_FOLDER = '$'
@@ -83,6 +84,7 @@ function noop () {}
83
84
  * @param {boolean} [options.writable]
84
85
  * @param {boolean} [options.extensionMessages]
85
86
  * @param {number} [options.timeout]
87
+ * @param {string} [options.defaultDrive]
86
88
  * @param {typeof DEFAULT_RENDER_INDEX} [options.renderIndex]
87
89
  * @param {OnLoadHandler} [options.onLoad]
88
90
  * @param {OnDeleteHandler} [options.onDelete]
@@ -93,6 +95,7 @@ export default async function makeHyperFetch ({
93
95
  writable = false,
94
96
  extensionMessages = writable,
95
97
  timeout = DEFAULT_TIMEOUT,
98
+ defaultDrive = DEFAULT_DRIVE,
96
99
  renderIndex = DEFAULT_RENDER_INDEX,
97
100
  onLoad = noop,
98
101
  onDelete = noop
@@ -115,6 +118,10 @@ export default async function makeHyperFetch ({
115
118
  if (writable) {
116
119
  router.get(`hyper://${SPECIAL_DOMAIN}/`, getKey)
117
120
  router.post(`hyper://${SPECIAL_DOMAIN}/`, createKey)
121
+ router.put(`hyper://${SPECIAL_DOMAIN}/`, putFilesDefault)
122
+ router.delete('hyper://SPECIAL_DOMAIN/**', deleteFilesDefault)
123
+ router.get(`hyper://${SPECIAL_DOMAIN}/**`, getFilesDefault)
124
+ router.head(`hyper://${SPECIAL_DOMAIN}/**`, headFilesDefault)
118
125
 
119
126
  router.put(`hyper://*/${SPECIAL_FOLDER}/${VERSION_FOLDER_NAME}/**`, putFilesVersioned)
120
127
  router.put('hyper://*/**', putFiles)
@@ -345,10 +352,7 @@ export default async function makeHyperFetch ({
345
352
  * @returns
346
353
  */
347
354
  async function getKey (request) {
348
- const key = new URL(request.url).searchParams.get('key')
349
- if (!key) {
350
- return { status: 400, body: 'Must specify key parameter to resolve' }
351
- }
355
+ const key = new URL(request.url).searchParams.get('key') || defaultDrive
352
356
 
353
357
  try {
354
358
  const drive = await sdk.getDrive(key)
@@ -378,10 +382,7 @@ export default async function makeHyperFetch ({
378
382
  // Maybe specify a seed to use for generating the blobs?
379
383
  // Else we'd need to specify the blobs keys and metadata keys
380
384
 
381
- const key = new URL(request.url).searchParams.get('key')
382
- if (!key) {
383
- return { status: 400, body: 'Must specify key parameter to resolve' }
384
- }
385
+ const key = new URL(request.url).searchParams.get('key') || defaultDrive
385
386
 
386
387
  const drive = await sdk.getDrive(key)
387
388
  onLoad(new URL('/', drive.url), drive.writable, key)
@@ -389,12 +390,30 @@ export default async function makeHyperFetch ({
389
390
  return { body: drive.url }
390
391
  }
391
392
 
393
+ /**
394
+ * @param {Request} request
395
+ */
396
+ async function putFilesDefault (request) {
397
+ const finalURL = await resolveInDefault(request.url)
398
+
399
+ return putTo(finalURL, request)
400
+ }
401
+
392
402
  /**
393
403
  * @param {Request} request
394
404
  * @returns
395
405
  */
396
406
  async function putFiles (request) {
397
- const { hostname, pathname: rawPathname } = new URL(request.url)
407
+ return putTo(request.url, request)
408
+ }
409
+
410
+ /**
411
+ * @param {string} url
412
+ * @param {Request} request
413
+ * @returns
414
+ */
415
+ async function putTo (url, request) {
416
+ const { hostname, pathname: rawPathname } = new URL(url)
398
417
  const pathname = decodeURI(ensureLeadingSlash(rawPathname))
399
418
  const contentType = request.headers.get('Content-Type') || ''
400
419
  const lastModified = request.headers.get('Last-Modified')
@@ -407,7 +426,7 @@ export default async function makeHyperFetch ({
407
426
  return { status: 403, body: `Cannot PUT file to read-only drive: ${drive.url}`, headers: { Location: request.url } }
408
427
  }
409
428
 
410
- onLoad(new URL('/', request.url), drive.writable)
429
+ onLoad(new URL('/', url), drive.writable)
411
430
 
412
431
  if (isFormData) {
413
432
  // It's a form! Get the files out and process them
@@ -427,7 +446,7 @@ export default async function makeHyperFetch ({
427
446
  }
428
447
  } else {
429
448
  if (pathname.endsWith('/')) {
430
- return { status: 405, body: 'Cannot PUT file with trailing slash', headers: { Location: request.url } }
449
+ return { status: 405, body: 'Cannot PUT file with trailing slash', headers: { Location: url } }
431
450
  } else {
432
451
  await pipelinePromise(
433
452
  Readable.from(request.body),
@@ -478,20 +497,39 @@ export default async function makeHyperFetch ({
478
497
  return { status: 200 }
479
498
  }
480
499
 
500
+ /**
501
+ * @param {Request} request
502
+ * @returns
503
+ */
504
+ async function deleteFilesDefault (request) {
505
+ const finalURL = await resolveInDefault(request.url)
506
+
507
+ return deleteFilesTo(finalURL, request)
508
+ }
509
+
481
510
  /**
482
511
  * @param {Request} request
483
512
  * @returns
484
513
  */
485
514
  async function deleteFiles (request) {
486
- const { hostname, pathname: rawPathname } = new URL(request.url)
515
+ return deleteFilesTo(request.url, request)
516
+ }
517
+
518
+ /**
519
+ * @param {string} url
520
+ * @param {Request} request
521
+ * @returns
522
+ */
523
+ async function deleteFilesTo (url, request) {
524
+ const { hostname, pathname: rawPathname } = new URL(url)
487
525
  const pathname = decodeURI(ensureLeadingSlash(rawPathname))
488
526
 
489
527
  const drive = await sdk.getDrive(`hyper://${hostname}/`)
490
528
  if (!drive.writable) {
491
- return { status: 403, body: `Cannot DELETE file in read-only drive: ${drive.url}`, headers: { Location: request.url } }
529
+ return { status: 403, body: `Cannot DELETE file in read-only drive: ${drive.url}`, headers: { Location: url } }
492
530
  }
493
531
 
494
- onLoad(new URL('/', request.url), drive.writable)
532
+ onLoad(new URL('/', url), drive.writable)
495
533
 
496
534
  if (pathname.endsWith('/')) {
497
535
  let didDelete = false
@@ -561,12 +599,31 @@ export default async function makeHyperFetch ({
561
599
  return serveHead(snapshot, realPath, { accept, isRanged, noResolve })
562
600
  }
563
601
 
602
+ /**
603
+ * @param {Request} request
604
+ * @returns
605
+ */
606
+ async function headFilesDefault (request) {
607
+ const finalURL = await resolveInDefault(request.url)
608
+
609
+ return headFilesFrom(finalURL, request)
610
+ }
611
+
564
612
  /**
565
613
  * @param {Request} request
566
614
  * @returns
567
615
  */
568
616
  async function headFiles (request) {
569
- const url = new URL(request.url)
617
+ return headFilesFrom(request.url, request)
618
+ }
619
+
620
+ /**
621
+ * @param {string} _url
622
+ * @param {Request} request
623
+ * @returns
624
+ */
625
+ async function headFilesFrom (_url, request) {
626
+ const url = new URL(_url)
570
627
  const { hostname, pathname: rawPathname, searchParams } = url
571
628
  const pathname = decodeURI(ensureLeadingSlash(rawPathname))
572
629
 
@@ -738,12 +795,31 @@ export default async function makeHyperFetch ({
738
795
  }
739
796
 
740
797
  /**
741
- * @param {Request} request
742
- * @returns
743
- */
798
+ * @param {Request} request
799
+ * @returns
800
+ */
801
+ async function getFilesDefault (request) {
802
+ const finalURL = await resolveInDefault(request.url)
803
+
804
+ return getFilesFrom(finalURL, request)
805
+ }
806
+
807
+ /**
808
+ * @param {Request} request
809
+ * @returns
810
+ */
744
811
  async function getFiles (request) {
812
+ return getFilesFrom(request.url, request)
813
+ }
814
+
815
+ /**
816
+ * @param {string} _url
817
+ * @param {Request} request
818
+ * @returns
819
+ */
820
+ async function getFilesFrom (_url, request) {
745
821
  // TODO: Redirect on directories without trailing slash
746
- const url = new URL(request.url)
822
+ const url = new URL(_url)
747
823
  const { hostname, pathname: rawPathname, searchParams } = url
748
824
  const pathname = decodeURI(ensureLeadingSlash(rawPathname))
749
825
 
@@ -760,7 +836,7 @@ export default async function makeHyperFetch ({
760
836
  }
761
837
  }
762
838
 
763
- onLoad(new URL('/', request.url), drive.writable)
839
+ onLoad(new URL('/', url), drive.writable)
764
840
 
765
841
  return serveGet(drive, pathname, { accept, isRanged, noResolve })
766
842
  }
@@ -836,6 +912,20 @@ export default async function makeHyperFetch ({
836
912
  return serveFile(drive, path, isRanged)
837
913
  }
838
914
 
915
+ /**
916
+ * Resolve a URL with a pathname to be within the default drive
917
+ * @param {string} url
918
+ * @returns {Promise<string>}
919
+ */
920
+ async function resolveInDefault (url) {
921
+ const { pathname } = new URL(url)
922
+
923
+ const drive = await sdk.getDrive(defaultDrive)
924
+ const finalURL = new URL(pathname, drive.url).href
925
+
926
+ return finalURL
927
+ }
928
+
839
929
  return fetch
840
930
  }
841
931
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore-fetch",
3
- "version": "10.1.0",
3
+ "version": "10.2.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",
package/test.js CHANGED
@@ -669,7 +669,7 @@ test('Check hyperdrive writability', async (t) => {
669
669
  t.equal(writableHeadersAllow, 'HEAD,GET,PUT,DELETE', 'Expected writable Allows header')
670
670
  })
671
671
 
672
- test.only('onLoad and onDelete handlers', async (t) => {
672
+ test('onLoad and onDelete handlers', async (t) => {
673
673
  /** @type {Parameters<OnLoadHandler> | Parameters<OnDeleteHandler> | null} */
674
674
  let args = null
675
675