hypercore-fetch 8.6.1 → 9.0.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/.github/workflows/test.yml +1 -1
- package/LICENSE +0 -0
- package/README.md +67 -116
- package/index.js +501 -663
- package/package.json +8 -16
- package/test.js +287 -182
- package/bin.js +0 -27
package/package.json
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore-fetch",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Implementation of Fetch that uses the Dat SDK for loading p2p content",
|
|
5
|
-
"
|
|
6
|
-
"hypercore-fetch": "bin.js"
|
|
7
|
-
},
|
|
5
|
+
"type": "module",
|
|
8
6
|
"main": "index.js",
|
|
9
7
|
"scripts": {
|
|
10
8
|
"test": "node test",
|
|
@@ -25,22 +23,16 @@
|
|
|
25
23
|
},
|
|
26
24
|
"homepage": "https://github.com/RangerMauve/hypercore-fetch#readme",
|
|
27
25
|
"dependencies": {
|
|
28
|
-
"busboy": "^0.3.1",
|
|
29
26
|
"event-iterator": "^2.0.0",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"make-dir": "^3.1.0",
|
|
34
|
-
"make-fetch": "^2.3.3",
|
|
35
|
-
"mime": "^2.4.4",
|
|
27
|
+
"hyperdrive": "^11.0.0-alpha.10",
|
|
28
|
+
"make-fetch": "^3.1.0",
|
|
29
|
+
"mime": "^3.0.0",
|
|
36
30
|
"range-parser": "^1.2.1",
|
|
37
|
-
"
|
|
38
|
-
"sodium-universal": "^3.0.2",
|
|
39
|
-
"streamx": "^2.10.0"
|
|
31
|
+
"streamx": "^2.13.0"
|
|
40
32
|
},
|
|
41
33
|
"devDependencies": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
34
|
+
"@rangermauve/fetch-event-source": "^1.0.3",
|
|
35
|
+
"hyper-sdk": "^4.1.1",
|
|
44
36
|
"standard": "^17.0.0",
|
|
45
37
|
"tape": "^5.2.2"
|
|
46
38
|
}
|
package/test.js
CHANGED
|
@@ -1,282 +1,387 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/* global FormData, Blob */
|
|
2
|
+
import * as SDK from 'hyper-sdk'
|
|
3
|
+
import test from 'tape'
|
|
4
|
+
import createEventSource from '@rangermauve/fetch-event-source'
|
|
5
|
+
import { once } from 'events'
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
import makeHyperFetch from './index.js'
|
|
6
8
|
|
|
7
9
|
const SAMPLE_CONTENT = 'Hello World'
|
|
10
|
+
let count = 0
|
|
11
|
+
function next () {
|
|
12
|
+
return count++
|
|
13
|
+
}
|
|
8
14
|
|
|
9
|
-
async function
|
|
10
|
-
const
|
|
11
|
-
|
|
15
|
+
async function nextURL (t) {
|
|
16
|
+
const createResponse = await fetch(`hyper://localhost/?key=example${next()}`, {
|
|
17
|
+
method: 'post'
|
|
12
18
|
})
|
|
19
|
+
await checkResponse(createResponse, t, 'Created new drive')
|
|
13
20
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
const created = await createResponse.text()
|
|
22
|
+
return created
|
|
23
|
+
}
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
const sdk1 = await SDK.create({ storage: false })
|
|
26
|
+
const sdk2 = await SDK.create({ storage: false })
|
|
27
|
+
|
|
28
|
+
const fetch = await makeHyperFetch({
|
|
29
|
+
sdk: sdk1,
|
|
30
|
+
writable: true
|
|
31
|
+
})
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
const fetch2 = await makeHyperFetch({
|
|
34
|
+
sdk: sdk2,
|
|
35
|
+
writable: true
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test.onFinish(() => {
|
|
39
|
+
sdk1.close()
|
|
40
|
+
sdk2.close()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('Quick check', async (t) => {
|
|
44
|
+
const createResponse = await fetch(`hyper://localhost/?key=example${next()}`, {
|
|
45
|
+
method: 'post'
|
|
26
46
|
})
|
|
27
47
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
await checkResponse(createResponse, t, 'Created new drive')
|
|
49
|
+
|
|
50
|
+
const created = await createResponse.text()
|
|
51
|
+
|
|
52
|
+
t.ok(created.startsWith('hyper://'), 'Created drive has hyper URL')
|
|
53
|
+
|
|
54
|
+
const existsResponse = await fetch(created)
|
|
55
|
+
|
|
56
|
+
await checkResponse(existsResponse, t)
|
|
57
|
+
|
|
58
|
+
t.deepEqual(await existsResponse.json(), [], 'Empty dir on create')
|
|
59
|
+
|
|
60
|
+
const uploadLocation = new URL('./example.txt', created)
|
|
61
|
+
|
|
62
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
63
|
+
method: 'put',
|
|
64
|
+
body: SAMPLE_CONTENT
|
|
31
65
|
})
|
|
32
66
|
|
|
33
|
-
|
|
34
|
-
const archive = Hyperdrive('example1')
|
|
67
|
+
await checkResponse(uploadResponse, t)
|
|
35
68
|
|
|
36
|
-
|
|
37
|
-
const FILE_DATA = '<h1>Hello World!</h1>'
|
|
69
|
+
const uploadedContentResponse = await fetch(uploadLocation)
|
|
38
70
|
|
|
39
|
-
|
|
71
|
+
await checkResponse(uploadedContentResponse, t)
|
|
40
72
|
|
|
41
|
-
|
|
73
|
+
const content = await uploadedContentResponse.text()
|
|
74
|
+
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
42
75
|
|
|
43
|
-
|
|
76
|
+
t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
|
|
77
|
+
t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
const dirResponse = await fetch2(created)
|
|
46
80
|
|
|
47
|
-
|
|
81
|
+
await checkResponse(dirResponse, t)
|
|
48
82
|
|
|
49
|
-
|
|
83
|
+
t.deepEqual(await dirResponse.json(), ['example.txt'], 'File got added')
|
|
84
|
+
})
|
|
50
85
|
|
|
51
|
-
|
|
86
|
+
test('PUT file', async (t) => {
|
|
87
|
+
const created = await nextURL(t)
|
|
52
88
|
|
|
53
|
-
|
|
89
|
+
const uploadLocation = new URL('./example.txt', created)
|
|
54
90
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
92
|
+
method: 'put',
|
|
93
|
+
body: SAMPLE_CONTENT
|
|
58
94
|
})
|
|
59
95
|
|
|
60
|
-
|
|
61
|
-
const response = await fetch('hyper://example/.well-known/dat')
|
|
62
|
-
t.ok(response, 'Got response')
|
|
63
|
-
t.equal(response.status, 200, 'Got OK response code')
|
|
64
|
-
const text = await response.text()
|
|
65
|
-
t.ok(text.startsWith('dat://'), 'Returned dat URL')
|
|
66
|
-
})
|
|
96
|
+
await checkResponse(uploadResponse, t, 'upload successful')
|
|
67
97
|
|
|
68
|
-
|
|
69
|
-
const response = await fetch('hyper://example/.well-known/hyper')
|
|
70
|
-
t.ok(response, 'Got response')
|
|
71
|
-
t.equal(response.status, 200, 'Got OK response code')
|
|
72
|
-
const text = await response.text()
|
|
73
|
-
t.ok(text.startsWith('hyper://'), 'Returned dat URL')
|
|
74
|
-
})
|
|
98
|
+
const uploadedContentResponse = await fetch(uploadLocation)
|
|
75
99
|
|
|
76
|
-
|
|
77
|
-
const response1 = await fetch('hyper://example/checkthis.txt', { method: 'PUT', body: SAMPLE_CONTENT })
|
|
100
|
+
await checkResponse(uploadedContentResponse, t, 'able to load content')
|
|
78
101
|
|
|
79
|
-
|
|
102
|
+
const content = await uploadedContentResponse.text()
|
|
103
|
+
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
80
104
|
|
|
81
|
-
|
|
105
|
+
t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
|
|
106
|
+
t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
|
|
107
|
+
})
|
|
108
|
+
test('PUT FormData', async (t) => {
|
|
109
|
+
const created = await nextURL(t)
|
|
82
110
|
|
|
83
|
-
|
|
111
|
+
const formData = new FormData()
|
|
112
|
+
formData.append('file', new Blob([SAMPLE_CONTENT]), 'example.txt')
|
|
113
|
+
formData.append('file', new Blob([SAMPLE_CONTENT]), 'example2.txt')
|
|
84
114
|
|
|
85
|
-
|
|
115
|
+
const uploadedResponse = await fetch(created, {
|
|
116
|
+
method: 'put',
|
|
117
|
+
body: formData
|
|
86
118
|
})
|
|
87
119
|
|
|
88
|
-
|
|
89
|
-
const form = new FormData()
|
|
90
|
-
|
|
91
|
-
form.append('file', SAMPLE_CONTENT, {
|
|
92
|
-
filename: 'example.txt'
|
|
93
|
-
})
|
|
94
|
-
const body = form.getBuffer()
|
|
95
|
-
const headers = form.getHeaders()
|
|
120
|
+
await checkResponse(uploadedResponse, t)
|
|
96
121
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
headers,
|
|
100
|
-
body
|
|
101
|
-
})
|
|
122
|
+
const file2URL = new URL('/example2.txt', created)
|
|
123
|
+
const file2Response = await fetch(file2URL)
|
|
102
124
|
|
|
103
|
-
|
|
125
|
+
await checkResponse(file2Response, t)
|
|
126
|
+
const file2Content = await file2Response.text()
|
|
104
127
|
|
|
105
|
-
|
|
128
|
+
t.equal(file2Content, SAMPLE_CONTENT, 'file contents got uploaded')
|
|
106
129
|
|
|
107
|
-
|
|
130
|
+
const listDirRequest = await fetch(created)
|
|
131
|
+
await checkResponse(listDirRequest, t)
|
|
132
|
+
const entries = await listDirRequest.json()
|
|
133
|
+
t.deepEqual(entries, ['example.txt', 'example2.txt'], 'new files are listed')
|
|
134
|
+
})
|
|
135
|
+
test('PUT into new directory', async (t) => {
|
|
136
|
+
const created = await nextURL(t)
|
|
108
137
|
|
|
109
|
-
|
|
138
|
+
const uploadLocation = new URL('./subfolder/example.txt', created)
|
|
110
139
|
|
|
111
|
-
|
|
140
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
141
|
+
method: 'put',
|
|
142
|
+
body: SAMPLE_CONTENT
|
|
112
143
|
})
|
|
113
144
|
|
|
114
|
-
|
|
115
|
-
const response1 = await fetch('hyper://example/fizz/buzz/example.txt', { method: 'PUT', body: SAMPLE_CONTENT })
|
|
145
|
+
await checkResponse(uploadResponse, t)
|
|
116
146
|
|
|
117
|
-
|
|
118
|
-
})
|
|
147
|
+
const uploadedContentResponse = await fetch(uploadLocation)
|
|
119
148
|
|
|
120
|
-
|
|
121
|
-
const response1 = await fetch('hyper://example/baz/index.html', { method: 'PUT', body: SAMPLE_CONTENT })
|
|
122
|
-
t.ok(response1.ok)
|
|
123
|
-
const response2 = await fetch('hyper://example/baz/index.html', { method: 'PUT', body: SAMPLE_CONTENT })
|
|
149
|
+
await checkResponse(uploadedContentResponse, t)
|
|
124
150
|
|
|
125
|
-
|
|
126
|
-
|
|
151
|
+
const content = await uploadedContentResponse.text()
|
|
152
|
+
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
127
153
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
t.ok(response1.ok)
|
|
154
|
+
t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
|
|
155
|
+
t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
|
|
131
156
|
|
|
132
|
-
|
|
157
|
+
const listDirRequest = await fetch(created)
|
|
158
|
+
await checkResponse(listDirRequest, t)
|
|
159
|
+
const entries = await listDirRequest.json()
|
|
160
|
+
t.deepEqual(entries, ['subfolder/'], 'new files are listed')
|
|
161
|
+
})
|
|
162
|
+
test('PUT to overwrite a file', async (t) => {
|
|
163
|
+
const created = await nextURL(t)
|
|
133
164
|
|
|
134
|
-
|
|
165
|
+
const uploadLocation = new URL('./example.txt', created)
|
|
135
166
|
|
|
136
|
-
|
|
167
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
168
|
+
method: 'put',
|
|
169
|
+
body: SAMPLE_CONTENT
|
|
170
|
+
})
|
|
171
|
+
await checkResponse(uploadResponse, t)
|
|
137
172
|
|
|
138
|
-
|
|
173
|
+
const SHORTER_CONTENT = 'Hello'
|
|
174
|
+
|
|
175
|
+
const overWriteResponse = await fetch(uploadLocation, {
|
|
176
|
+
method: 'put',
|
|
177
|
+
body: SHORTER_CONTENT
|
|
139
178
|
})
|
|
179
|
+
await checkResponse(overWriteResponse, t)
|
|
180
|
+
|
|
181
|
+
const uploadedContentResponse = await fetch(uploadLocation)
|
|
140
182
|
|
|
141
|
-
|
|
142
|
-
const response1 = await fetch('hyper://example/baz')
|
|
183
|
+
await checkResponse(uploadedContentResponse, t)
|
|
143
184
|
|
|
144
|
-
|
|
185
|
+
const content = await uploadedContentResponse.text()
|
|
186
|
+
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
145
187
|
|
|
146
|
-
|
|
188
|
+
t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
|
|
189
|
+
t.equal(content, SHORTER_CONTENT, 'Got uploaded content back out')
|
|
190
|
+
})
|
|
191
|
+
test('DELETE a file', async (t) => {
|
|
192
|
+
const created = await nextURL(t)
|
|
147
193
|
|
|
148
|
-
|
|
149
|
-
|
|
194
|
+
const formData = new FormData()
|
|
195
|
+
formData.append('file', new Blob([SAMPLE_CONTENT]), 'example.txt')
|
|
196
|
+
formData.append('file', new Blob([SAMPLE_CONTENT]), 'example2.txt')
|
|
150
197
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
198
|
+
const uploadedResponse = await fetch(created, {
|
|
199
|
+
method: 'put',
|
|
200
|
+
body: formData
|
|
154
201
|
})
|
|
202
|
+
await checkResponse(uploadedResponse, t)
|
|
155
203
|
|
|
156
|
-
|
|
157
|
-
|
|
204
|
+
const file2URL = new URL('/example2.txt', created)
|
|
205
|
+
const deleteResponse = await fetch(file2URL, {
|
|
206
|
+
method: 'delete'
|
|
207
|
+
})
|
|
158
208
|
|
|
159
|
-
|
|
160
|
-
t.ok(response2.ok, 'Able to create tag')
|
|
209
|
+
await checkResponse(deleteResponse, t, 'Able to DELETE')
|
|
161
210
|
|
|
162
|
-
|
|
211
|
+
const dirResponse = await fetch(created)
|
|
163
212
|
|
|
164
|
-
|
|
213
|
+
await checkResponse(dirResponse, t)
|
|
165
214
|
|
|
166
|
-
|
|
167
|
-
|
|
215
|
+
t.deepEqual(await dirResponse.json(), ['example.txt'], 'Only one file remains')
|
|
216
|
+
})
|
|
217
|
+
test('DELETE a directory', async (t) => {
|
|
218
|
+
const created = await nextURL(t)
|
|
168
219
|
|
|
169
|
-
|
|
170
|
-
await fetch('hyper://example/notaccessible.txt', { method: 'PUT', body: 'test' })
|
|
220
|
+
const uploadLocation = new URL('./subfolder/example.txt', created)
|
|
171
221
|
|
|
172
|
-
|
|
222
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
223
|
+
method: 'put',
|
|
224
|
+
body: SAMPLE_CONTENT
|
|
225
|
+
})
|
|
226
|
+
await checkResponse(uploadResponse, t)
|
|
173
227
|
|
|
174
|
-
|
|
228
|
+
const deleteResponse = await fetch(created, {
|
|
229
|
+
method: 'delete'
|
|
230
|
+
})
|
|
231
|
+
await checkResponse(deleteResponse, t, 'Able to DELETE')
|
|
232
|
+
|
|
233
|
+
const listDirRequest = await fetch(created)
|
|
234
|
+
await checkResponse(listDirRequest, t)
|
|
235
|
+
const entries = await listDirRequest.json()
|
|
236
|
+
t.deepEqual(entries, [], 'subfolder got deleted')
|
|
237
|
+
})
|
|
238
|
+
test('Read index.html', async (t) => {
|
|
239
|
+
const created = await nextURL(t)
|
|
240
|
+
const uploadLocation = new URL('./index.html', created)
|
|
241
|
+
|
|
242
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
243
|
+
method: 'put',
|
|
244
|
+
body: SAMPLE_CONTENT
|
|
245
|
+
})
|
|
246
|
+
await checkResponse(uploadResponse, t)
|
|
175
247
|
|
|
176
|
-
|
|
248
|
+
const uploadedContentResponse = await fetch(uploadLocation)
|
|
177
249
|
|
|
178
|
-
|
|
250
|
+
await checkResponse(uploadedContentResponse, t)
|
|
179
251
|
|
|
180
|
-
|
|
252
|
+
const content = await uploadedContentResponse.text()
|
|
253
|
+
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
181
254
|
|
|
182
|
-
|
|
255
|
+
t.equal(contentType, 'text/html; charset=utf-8', 'got HTML mime type')
|
|
256
|
+
t.equal(content, SAMPLE_CONTENT, 'loaded index.html content')
|
|
257
|
+
})
|
|
258
|
+
test('Ignore index.html with noResolve', async (t) => {
|
|
259
|
+
const created = await nextURL(t)
|
|
260
|
+
const uploadLocation = new URL('./index.html', created)
|
|
261
|
+
|
|
262
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
263
|
+
method: 'put',
|
|
264
|
+
body: SAMPLE_CONTENT
|
|
183
265
|
})
|
|
266
|
+
await checkResponse(uploadResponse, t)
|
|
184
267
|
|
|
185
|
-
|
|
186
|
-
const response = await fetch('hyper://blog.mauve.moe/')
|
|
268
|
+
const noResolve = created + '?noResolve'
|
|
187
269
|
|
|
188
|
-
|
|
189
|
-
|
|
270
|
+
const listDirRequest = await fetch(noResolve)
|
|
271
|
+
await checkResponse(listDirRequest, t)
|
|
272
|
+
const entries = await listDirRequest.json()
|
|
273
|
+
t.deepEqual(entries, ['index.html'], 'able to list index.html')
|
|
274
|
+
})
|
|
275
|
+
test('Read directory as HTML', async (t) => {
|
|
276
|
+
const created = await nextURL(t)
|
|
190
277
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
278
|
+
const formData = new FormData()
|
|
279
|
+
formData.append('file', new Blob([SAMPLE_CONTENT]), 'example.txt')
|
|
280
|
+
formData.append('file', new Blob([SAMPLE_CONTENT]), 'example2.txt')
|
|
281
|
+
|
|
282
|
+
const uploadedResponse = await fetch(created, {
|
|
283
|
+
method: 'put',
|
|
284
|
+
body: formData
|
|
285
|
+
})
|
|
286
|
+
await checkResponse(uploadedResponse, t)
|
|
197
287
|
|
|
198
|
-
|
|
199
|
-
|
|
288
|
+
const listDirRequest = await fetch(created, {
|
|
289
|
+
headers: {
|
|
290
|
+
Accept: 'text/html'
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
await checkResponse(listDirRequest, t, 'Able to list HTML')
|
|
200
294
|
|
|
201
|
-
|
|
295
|
+
const html = await listDirRequest.text()
|
|
202
296
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
297
|
+
t.equal(listDirRequest.headers.get('Content-Type'), 'text/html; charset=utf-8', 'Returned HTML in mime type')
|
|
298
|
+
t.ok(html.includes('<title'), 'Listing has title')
|
|
299
|
+
t.ok(html.includes('./example.txt'), 'Listing has link to file')
|
|
300
|
+
})
|
|
301
|
+
test('Resolve pretty markdown URLs', async (t) => {
|
|
302
|
+
const created = await nextURL(t)
|
|
207
303
|
|
|
208
|
-
|
|
209
|
-
t.ok(data.value.includes('event:change'), 'Eventsource data represents a change event')
|
|
210
|
-
t.ok(data.value.endsWith('\n\n'), 'Ends with two newlines')
|
|
304
|
+
const uploadLocation = new URL('./example.md', created)
|
|
211
305
|
|
|
212
|
-
|
|
306
|
+
const uploadResponse = await fetch(uploadLocation, {
|
|
307
|
+
method: 'put',
|
|
308
|
+
body: SAMPLE_CONTENT
|
|
213
309
|
})
|
|
310
|
+
await checkResponse(uploadResponse, t)
|
|
214
311
|
|
|
215
|
-
|
|
216
|
-
const domainResponse = await fetch('hyper://example/.well-known/hyper')
|
|
217
|
-
const domain = (await domainResponse.text()).split('\n')[0]
|
|
312
|
+
const resolvedLocation = new URL('/example', created)
|
|
218
313
|
|
|
219
|
-
|
|
220
|
-
const extensionListURL = `${domain}/$/extensions/`
|
|
314
|
+
const uploadedContentResponse = await fetch(resolvedLocation)
|
|
221
315
|
|
|
222
|
-
|
|
223
|
-
await fetch(extensionURL)
|
|
224
|
-
// Load up extension message on peer 2
|
|
225
|
-
await fetch2(extensionURL)
|
|
316
|
+
await checkResponse(uploadedContentResponse, t, 'able to load content')
|
|
226
317
|
|
|
227
|
-
|
|
318
|
+
const content = await uploadedContentResponse.text()
|
|
319
|
+
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
228
320
|
|
|
229
|
-
|
|
230
|
-
|
|
321
|
+
t.equal(content, SAMPLE_CONTENT, 'Got original content out')
|
|
322
|
+
t.equal(contentType, 'text/markdown; charset=utf-8', 'Got markdown mime type')
|
|
323
|
+
})
|
|
231
324
|
|
|
232
|
-
|
|
233
|
-
|
|
325
|
+
test('EventSource extension messages', async (t) => {
|
|
326
|
+
const domain = await nextURL(t)
|
|
234
327
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
await delay(2000)
|
|
328
|
+
const extensionURL = `${domain}$/extensions/example`
|
|
329
|
+
const extensionListURL = `${domain}$/extensions/`
|
|
238
330
|
|
|
239
|
-
|
|
240
|
-
|
|
331
|
+
// Load up extension message on peer 1
|
|
332
|
+
const extensionLoadResponse1 = await fetch(extensionURL)
|
|
333
|
+
await checkResponse(extensionLoadResponse1, t)
|
|
334
|
+
// Load up extension message on peer 2
|
|
335
|
+
const extensionLoadResponse2 = await fetch2(extensionURL)
|
|
336
|
+
await checkResponse(extensionLoadResponse2, t)
|
|
241
337
|
|
|
242
|
-
|
|
338
|
+
const extensionListRequest = await fetch(extensionListURL)
|
|
339
|
+
const extensionList = await extensionListRequest.json()
|
|
243
340
|
|
|
244
|
-
|
|
245
|
-
|
|
341
|
+
// Extension list will always be alphabetically sorted
|
|
342
|
+
t.deepEqual(extensionList, ['example'], 'Got expected list of extensions')
|
|
246
343
|
|
|
247
|
-
|
|
344
|
+
const peerResponse1 = await fetch(extensionURL)
|
|
345
|
+
const peerList1 = await peerResponse1.json()
|
|
248
346
|
|
|
249
|
-
|
|
250
|
-
headers: {
|
|
251
|
-
Accept: 'text/event-stream'
|
|
252
|
-
}
|
|
253
|
-
})
|
|
347
|
+
t.equal(peerList1.length, 1, 'Got one peer for extension message on peer1')
|
|
254
348
|
|
|
255
|
-
|
|
256
|
-
|
|
349
|
+
const peerResponse2 = await fetch2(extensionURL)
|
|
350
|
+
const peerList2 = await peerResponse2.json()
|
|
257
351
|
|
|
258
|
-
|
|
352
|
+
t.equal(peerList2.length, 1, 'Got one peer for extension message on peer2')
|
|
259
353
|
|
|
260
|
-
|
|
354
|
+
const { EventSource } = createEventSource(fetch)
|
|
355
|
+
const source = new EventSource(extensionListURL)
|
|
261
356
|
|
|
262
|
-
|
|
357
|
+
await Promise.race([
|
|
358
|
+
once(source, 'open'),
|
|
359
|
+
once(source, 'error').then(([e]) => { throw e })
|
|
360
|
+
])
|
|
263
361
|
|
|
264
|
-
|
|
362
|
+
const toRead = Promise.race([
|
|
363
|
+
once(source, 'example'),
|
|
364
|
+
once(source, 'error').then(([e]) => { throw e })
|
|
365
|
+
])
|
|
265
366
|
|
|
266
|
-
|
|
367
|
+
const broadcastRequest = await fetch2(extensionURL, { method: 'POST', body: SAMPLE_CONTENT })
|
|
267
368
|
|
|
268
|
-
|
|
369
|
+
t.ok(broadcastRequest.ok, 'Able to broadcast to peers')
|
|
269
370
|
|
|
270
|
-
|
|
271
|
-
t.ok(data.value.includes('event:example\n'), 'EventSource data represents an example event')
|
|
272
|
-
t.ok(data.value.includes('data:Hello World\n'), 'EventSource data contains expected body')
|
|
273
|
-
t.ok(data.value.includes('id:'), 'EventSource data contains an ID')
|
|
274
|
-
t.ok(data.value.endsWith('\n\n'), 'Ends with two newlines')
|
|
371
|
+
const [event] = await toRead
|
|
275
372
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
373
|
+
const { type, data, lastEventId } = event
|
|
374
|
+
|
|
375
|
+
t.equal(data, SAMPLE_CONTENT, 'Got data from event')
|
|
376
|
+
t.equal(type, 'example', 'Event got set to extension message name')
|
|
377
|
+
t.ok(lastEventId, 'Event contained peer ID')
|
|
378
|
+
})
|
|
279
379
|
|
|
280
|
-
function
|
|
281
|
-
|
|
380
|
+
async function checkResponse (response, t, successMessage = 'Response OK') {
|
|
381
|
+
if (!response.ok) {
|
|
382
|
+
const message = await response.text()
|
|
383
|
+
t.fail(new Error(`HTTP Error ${response.status}:\n${message}`))
|
|
384
|
+
} else {
|
|
385
|
+
t.pass(successMessage)
|
|
386
|
+
}
|
|
282
387
|
}
|
package/bin.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const eosp = require('end-of-stream-promise')
|
|
4
|
-
const { Readable } = require('streamx')
|
|
5
|
-
|
|
6
|
-
run()
|
|
7
|
-
.catch((e) => process.nextTick(() => {
|
|
8
|
-
throw e
|
|
9
|
-
}))
|
|
10
|
-
|
|
11
|
-
async function run () {
|
|
12
|
-
const fetch = require('./')()
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const url = process.argv[2]
|
|
16
|
-
|
|
17
|
-
const response = await fetch(url)
|
|
18
|
-
|
|
19
|
-
const stream = Readable.from(response.body)
|
|
20
|
-
|
|
21
|
-
stream.pipe(process.stdout)
|
|
22
|
-
|
|
23
|
-
await eosp(stream)
|
|
24
|
-
} finally {
|
|
25
|
-
fetch.close()
|
|
26
|
-
}
|
|
27
|
-
}
|