hypercore-fetch 8.4.0 → 8.6.1
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/.github/workflows/test.yml +22 -0
- package/README.md +9 -0
- package/index.js +50 -1
- package/package.json +7 -3
- package/test.js +24 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Testing
|
|
2
|
+
|
|
3
|
+
on: [ push, pull_request ]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
continue-on-error: true
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
node: [ '16' ]
|
|
11
|
+
os: [ubuntu-latest, windows-latest, macOS-latest]
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
name: Node ${{ matrix.node }}-${{matrix.os}} tests
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v2
|
|
16
|
+
- name: Setup node
|
|
17
|
+
uses: actions/setup-node@v2
|
|
18
|
+
with:
|
|
19
|
+
node-version: ${{ matrix.node }}
|
|
20
|
+
- run: npm install
|
|
21
|
+
- run: npm run lint
|
|
22
|
+
- run: npm test
|
package/README.md
CHANGED
|
@@ -130,6 +130,15 @@ The `body` can be either a `String`, an `ArrayBuffer`, a `Blob`, a WHATWG `Reada
|
|
|
130
130
|
|
|
131
131
|
Your `NAME` will likely be a `name` in most cases to ensure you have a writeable archive.
|
|
132
132
|
|
|
133
|
+
### `fetch('hyper://NAME/folder/', {method: 'PUT', body: new FormData()})`
|
|
134
|
+
|
|
135
|
+
You can add multiple files to a folder using the `PUT` method with a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) body.
|
|
136
|
+
|
|
137
|
+
You can [append](https://developer.mozilla.org/en-US/docs/Web/API/FormData) to a FormData with `formData.append(fieldname, content, 'filename.txt')` where `fieldname` gets ignored (use something like `file`?) the `content` can either be a String, Blob, or some sort of stream.
|
|
138
|
+
The `filename` will be the filename within the directory that gets created.
|
|
139
|
+
|
|
140
|
+
`NAME` can either be the 64 character hex key for an archive, a domain to parse with [dat-dns](https://www.npmjs.com/package/dat-dns), or a name for an archive which allows you to write to it.
|
|
141
|
+
|
|
133
142
|
### `fetch('hyper://NAME/example.txt', {method: 'DELETE'})`
|
|
134
143
|
|
|
135
144
|
You can delete a file in an archive by using the `DELETE` method.
|
package/index.js
CHANGED
|
@@ -7,6 +7,8 @@ const makeDir = require('make-dir')
|
|
|
7
7
|
const { Readable } = require('streamx')
|
|
8
8
|
const makeFetch = require('make-fetch')
|
|
9
9
|
const { EventIterator } = require('event-iterator')
|
|
10
|
+
const Busboy = require('busboy')
|
|
11
|
+
const posixPath = require('path').posix
|
|
10
12
|
|
|
11
13
|
const DEFAULT_TIMEOUT = 5000
|
|
12
14
|
|
|
@@ -380,9 +382,56 @@ module.exports = function makeHyperFetch (opts = {}) {
|
|
|
380
382
|
|
|
381
383
|
if (method === 'PUT') {
|
|
382
384
|
checkWritable(archive)
|
|
385
|
+
const contentType = headers.get('Content-Type') || headers.get('content-type')
|
|
386
|
+
const isFormData = contentType && contentType.includes('multipart/form-data')
|
|
387
|
+
|
|
383
388
|
if (path.endsWith('/')) {
|
|
384
389
|
await makeDir(path, { fs: archive })
|
|
390
|
+
const busboy = new Busboy({ headers: rawHeaders })
|
|
391
|
+
|
|
392
|
+
const toUpload = new EventIterator(({ push, stop, fail }) => {
|
|
393
|
+
busboy.once('error', fail)
|
|
394
|
+
busboy.once('finish', stop)
|
|
395
|
+
|
|
396
|
+
busboy.on('file', async (fieldName, fileData, fileName) => {
|
|
397
|
+
const finalPath = posixPath.join(path, fileName)
|
|
398
|
+
|
|
399
|
+
const source = Readable.from(fileData)
|
|
400
|
+
const destination = archive.createWriteStream(finalPath)
|
|
401
|
+
|
|
402
|
+
source.pipe(destination)
|
|
403
|
+
try {
|
|
404
|
+
Promise.race([
|
|
405
|
+
once(source, 'error').then((e) => { throw e }),
|
|
406
|
+
once(destination, 'error').then((e) => { throw e }),
|
|
407
|
+
once(source, 'end')
|
|
408
|
+
])
|
|
409
|
+
} catch (e) {
|
|
410
|
+
fail(e)
|
|
411
|
+
}
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
// TODO: Does busboy need to be GC'd?
|
|
415
|
+
return () => {}
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
Readable.from(body).pipe(busboy)
|
|
419
|
+
|
|
420
|
+
await Promise.all(await collect(toUpload))
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
statusCode: 200,
|
|
424
|
+
headers: responseHeaders,
|
|
425
|
+
data: intoAsyncIterable(canonical)
|
|
426
|
+
}
|
|
385
427
|
} else {
|
|
428
|
+
if (isFormData) {
|
|
429
|
+
return {
|
|
430
|
+
statusCode: 400,
|
|
431
|
+
headers: responseHeaders,
|
|
432
|
+
data: intoAsyncIterable('FormData only supported for folders (ending with a /)')
|
|
433
|
+
}
|
|
434
|
+
}
|
|
386
435
|
const parentDir = path.split('/').slice(0, -1).join('/')
|
|
387
436
|
if (parentDir) {
|
|
388
437
|
await makeDir(parentDir, { fs: archive })
|
|
@@ -405,7 +454,7 @@ module.exports = function makeHyperFetch (opts = {}) {
|
|
|
405
454
|
return {
|
|
406
455
|
statusCode: 200,
|
|
407
456
|
headers: responseHeaders,
|
|
408
|
-
data: intoAsyncIterable(
|
|
457
|
+
data: intoAsyncIterable(canonical)
|
|
409
458
|
}
|
|
410
459
|
} else if (method === 'DELETE') {
|
|
411
460
|
if (headers.get('x-clear') === 'cache') {
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore-fetch",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.1",
|
|
4
4
|
"description": "Implementation of Fetch that uses the Dat SDK for loading p2p content",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hypercore-fetch": "bin.js"
|
|
7
7
|
},
|
|
8
8
|
"main": "index.js",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "node test"
|
|
10
|
+
"test": "node test",
|
|
11
|
+
"lint": "standard --fix"
|
|
11
12
|
},
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
@@ -24,12 +25,13 @@
|
|
|
24
25
|
},
|
|
25
26
|
"homepage": "https://github.com/RangerMauve/hypercore-fetch#readme",
|
|
26
27
|
"dependencies": {
|
|
28
|
+
"busboy": "^0.3.1",
|
|
27
29
|
"event-iterator": "^2.0.0",
|
|
28
30
|
"fetch-headers": "^2.0.0",
|
|
29
31
|
"hyper-dns": "^0.12.0",
|
|
30
32
|
"hyper-sdk": "^3.0.9",
|
|
31
33
|
"make-dir": "^3.1.0",
|
|
32
|
-
"make-fetch": "^2.
|
|
34
|
+
"make-fetch": "^2.3.3",
|
|
33
35
|
"mime": "^2.4.4",
|
|
34
36
|
"range-parser": "^1.2.1",
|
|
35
37
|
"resolve-dat-path": "^2.0.0",
|
|
@@ -37,7 +39,9 @@
|
|
|
37
39
|
"streamx": "^2.10.0"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
42
|
+
"form-data": "^4.0.0",
|
|
40
43
|
"random-access-memory": "^3.1.1",
|
|
44
|
+
"standard": "^17.0.0",
|
|
41
45
|
"tape": "^5.2.2"
|
|
42
46
|
}
|
|
43
47
|
}
|
package/test.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const SDK = require('hyper-sdk')
|
|
2
2
|
const test = require('tape')
|
|
3
|
+
const FormData = require('form-data')
|
|
3
4
|
|
|
4
5
|
runTests()
|
|
5
6
|
|
|
@@ -84,10 +85,30 @@ async function runTests () {
|
|
|
84
85
|
t.equal(await response2.text(), SAMPLE_CONTENT, 'Read back written data')
|
|
85
86
|
})
|
|
86
87
|
|
|
87
|
-
test('PUT directory', async (t) => {
|
|
88
|
-
const
|
|
88
|
+
test('PUT FormData to directory', async (t) => {
|
|
89
|
+
const form = new FormData()
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
form.append('file', SAMPLE_CONTENT, {
|
|
92
|
+
filename: 'example.txt'
|
|
93
|
+
})
|
|
94
|
+
const body = form.getBuffer()
|
|
95
|
+
const headers = form.getHeaders()
|
|
96
|
+
|
|
97
|
+
const response1 = await fetch('hyper://example/foo/bar/', {
|
|
98
|
+
method: 'PUT',
|
|
99
|
+
headers,
|
|
100
|
+
body
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
t.equal(response1.status, 200, 'Got OK response on directory upload')
|
|
104
|
+
|
|
105
|
+
console.log(await response1.text())
|
|
106
|
+
|
|
107
|
+
const response2 = await fetch('hyper://example/foo/bar/example.txt')
|
|
108
|
+
|
|
109
|
+
t.equal(response2.status, 200, 'Got OK response on read')
|
|
110
|
+
|
|
111
|
+
t.equal(await response2.text(), SAMPLE_CONTENT, 'Read back written data')
|
|
91
112
|
})
|
|
92
113
|
|
|
93
114
|
test('PUT file in new directory', async (t) => {
|