@helia/verified-fetch 0.0.0-6c88ee1 → 0.0.0-7a7c0c1
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 +24 -0
- package/dist/index.min.js +4 -29
- package/dist/src/index.d.ts +57 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +30 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/utils/get-stream-from-async-iterable.d.ts +10 -0
- package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +1 -0
- package/dist/src/utils/{get-stream-and-content-type.js → get-stream-from-async-iterable.js} +10 -9
- package/dist/src/utils/get-stream-from-async-iterable.js.map +1 -0
- package/dist/src/verified-fetch.d.ts +4 -1
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +46 -18
- package/dist/src/verified-fetch.js.map +1 -1
- package/package.json +15 -14
- package/src/index.ts +62 -8
- package/src/utils/{get-stream-and-content-type.ts → get-stream-from-async-iterable.ts} +9 -8
- package/src/verified-fetch.ts +53 -22
- package/dist/src/utils/get-content-type.d.ts +0 -11
- package/dist/src/utils/get-content-type.d.ts.map +0 -1
- package/dist/src/utils/get-content-type.js +0 -43
- package/dist/src/utils/get-content-type.js.map +0 -1
- package/dist/src/utils/get-stream-and-content-type.d.ts +0 -10
- package/dist/src/utils/get-stream-and-content-type.d.ts.map +0 -1
- package/dist/src/utils/get-stream-and-content-type.js.map +0 -1
- package/src/utils/get-content-type.ts +0 -55
package/src/index.ts
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
* const fetch = await createVerifiedFetch({
|
|
76
76
|
* gateways: ['https://trustless-gateway.link'],
|
|
77
77
|
* routers: ['http://delegated-ipfs.dev']
|
|
78
|
-
*})
|
|
78
|
+
* })
|
|
79
79
|
*
|
|
80
80
|
* const resp = await fetch('ipfs://bafy...')
|
|
81
81
|
*
|
|
@@ -112,6 +112,32 @@
|
|
|
112
112
|
* const json = await resp.json()
|
|
113
113
|
* ```
|
|
114
114
|
*
|
|
115
|
+
* ### Custom content-type parsing
|
|
116
|
+
*
|
|
117
|
+
* By default, `@helia/verified-fetch` sets the `Content-Type` header as `application/octet-stream` - this is because the `.json()`, `.text()`, `.blob()`, and `.arrayBuffer()` methods will usually work as expected without a detailed content type.
|
|
118
|
+
*
|
|
119
|
+
* If you require an accurate content-type you can provide a `contentTypeParser` function as an option to `createVerifiedFetch` to handle parsing the content type.
|
|
120
|
+
*
|
|
121
|
+
* The function you provide will be called with the first chunk of bytes from the file and should return a string or a promise of a string.
|
|
122
|
+
*
|
|
123
|
+
* @example Customizing content-type parsing
|
|
124
|
+
*
|
|
125
|
+
* ```typescript
|
|
126
|
+
* import { createVerifiedFetch } from '@helia/verified-fetch'
|
|
127
|
+
* import { fileTypeFromBuffer } from '@sgtpooki/file-type'
|
|
128
|
+
*
|
|
129
|
+
* const fetch = await createVerifiedFetch({
|
|
130
|
+
* gateways: ['https://trustless-gateway.link'],
|
|
131
|
+
* routers: ['http://delegated-ipfs.dev']
|
|
132
|
+
* }, {
|
|
133
|
+
* contentTypeParser: async (bytes) => {
|
|
134
|
+
* // call to some magic-byte recognition library like magic-bytes, file-type, or your own custom byte recognition
|
|
135
|
+
* const result = await fileTypeFromBuffer(bytes)
|
|
136
|
+
* return result?.mime
|
|
137
|
+
* }
|
|
138
|
+
* })
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
115
141
|
* ## Comparison to fetch
|
|
116
142
|
*
|
|
117
143
|
* This module attempts to act as similarly to the `fetch()` API as possible.
|
|
@@ -242,7 +268,7 @@ import type { ProgressEvent, ProgressOptions } from 'progress-events'
|
|
|
242
268
|
export type Resource = string | CID
|
|
243
269
|
|
|
244
270
|
export interface CIDDetail {
|
|
245
|
-
cid:
|
|
271
|
+
cid: CID
|
|
246
272
|
path: string
|
|
247
273
|
}
|
|
248
274
|
|
|
@@ -257,13 +283,38 @@ export interface VerifiedFetch {
|
|
|
257
283
|
}
|
|
258
284
|
|
|
259
285
|
/**
|
|
260
|
-
* Instead of passing a Helia instance, you can pass a list of gateways and
|
|
286
|
+
* Instead of passing a Helia instance, you can pass a list of gateways and
|
|
287
|
+
* routers, and a HeliaHTTP instance will be created for you.
|
|
261
288
|
*/
|
|
262
|
-
export interface
|
|
289
|
+
export interface CreateVerifiedFetchInit {
|
|
263
290
|
gateways: string[]
|
|
264
291
|
routers?: string[]
|
|
265
292
|
}
|
|
266
293
|
|
|
294
|
+
export interface CreateVerifiedFetchOptions {
|
|
295
|
+
/**
|
|
296
|
+
* A function to handle parsing content type from bytes. The function you
|
|
297
|
+
* provide will be passed the first set of bytes we receive from the network,
|
|
298
|
+
* and should return a string that will be used as the value for the
|
|
299
|
+
* `Content-Type` header in the response.
|
|
300
|
+
*/
|
|
301
|
+
contentTypeParser?: ContentTypeParser
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* A ContentTypeParser attempts to return the mime type of a given file. It
|
|
306
|
+
* receives the first chunk of the file data and the file name, if it is
|
|
307
|
+
* available. The function can be sync or async and if it returns/resolves to
|
|
308
|
+
* `undefined`, `application/octet-stream` will be used.
|
|
309
|
+
*/
|
|
310
|
+
export interface ContentTypeParser {
|
|
311
|
+
/**
|
|
312
|
+
* Attempt to determine a mime type, either via of the passed bytes or the
|
|
313
|
+
* filename if it is available.
|
|
314
|
+
*/
|
|
315
|
+
(bytes: Uint8Array, fileName?: string): Promise<string | undefined> | string | undefined
|
|
316
|
+
}
|
|
317
|
+
|
|
267
318
|
export type BubbledProgressEvents =
|
|
268
319
|
// unixfs
|
|
269
320
|
GetEvents |
|
|
@@ -280,8 +331,9 @@ export type VerifiedFetchProgressEvents =
|
|
|
280
331
|
/**
|
|
281
332
|
* Options for the `fetch` function returned by `createVerifiedFetch`.
|
|
282
333
|
*
|
|
283
|
-
* This
|
|
284
|
-
* listen for
|
|
334
|
+
* This interface contains all the same fields as the [options object](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options)
|
|
335
|
+
* passed to `fetch` in browsers, plus an `onProgress` option to listen for
|
|
336
|
+
* progress events.
|
|
285
337
|
*/
|
|
286
338
|
export interface VerifiedFetchInit extends RequestInit, ProgressOptions<BubbledProgressEvents | VerifiedFetchProgressEvents> {
|
|
287
339
|
}
|
|
@@ -289,7 +341,9 @@ export interface VerifiedFetchInit extends RequestInit, ProgressOptions<BubbledP
|
|
|
289
341
|
/**
|
|
290
342
|
* Create and return a Helia node
|
|
291
343
|
*/
|
|
292
|
-
export async function createVerifiedFetch (init?: Helia |
|
|
344
|
+
export async function createVerifiedFetch (init?: Helia | CreateVerifiedFetchInit, options?: CreateVerifiedFetchOptions): Promise<VerifiedFetch> {
|
|
345
|
+
const contentTypeParser: ContentTypeParser | undefined = options?.contentTypeParser
|
|
346
|
+
|
|
293
347
|
if (!isHelia(init)) {
|
|
294
348
|
init = await createHeliaHTTP({
|
|
295
349
|
blockBrokers: [
|
|
@@ -301,7 +355,7 @@ export async function createVerifiedFetch (init?: Helia | CreateVerifiedFetchWit
|
|
|
301
355
|
})
|
|
302
356
|
}
|
|
303
357
|
|
|
304
|
-
const verifiedFetchInstance = new VerifiedFetchClass({ helia: init })
|
|
358
|
+
const verifiedFetchInstance = new VerifiedFetchClass({ helia: init }, { contentTypeParser })
|
|
305
359
|
async function verifiedFetch (resource: Resource, options?: VerifiedFetchInit): Promise<Response> {
|
|
306
360
|
return verifiedFetchInstance.fetch(resource, options)
|
|
307
361
|
}
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import { CustomProgressEvent } from 'progress-events'
|
|
2
|
-
import { getContentType } from './get-content-type.js'
|
|
3
2
|
import type { VerifiedFetchInit } from '../index.js'
|
|
4
3
|
import type { ComponentLogger } from '@libp2p/interface'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
* Converts an async iterator of Uint8Array bytes to a stream and
|
|
6
|
+
* Converts an async iterator of Uint8Array bytes to a stream and returns the first chunk of bytes
|
|
8
7
|
*/
|
|
9
|
-
export async function
|
|
10
|
-
const log = logger.forComponent('helia:verified-fetch:get-stream-
|
|
8
|
+
export async function getStreamFromAsyncIterable (iterator: AsyncIterable<Uint8Array>, path: string, logger: ComponentLogger, options?: Pick<VerifiedFetchInit, 'onProgress'>): Promise<{ stream: ReadableStream<Uint8Array>, firstChunk: Uint8Array }> {
|
|
9
|
+
const log = logger.forComponent('helia:verified-fetch:get-stream-from-async-iterable')
|
|
11
10
|
const reader = iterator[Symbol.asyncIterator]()
|
|
12
|
-
const { value, done } = await reader.next()
|
|
11
|
+
const { value: firstChunk, done } = await reader.next()
|
|
13
12
|
|
|
14
13
|
if (done === true) {
|
|
15
14
|
log.error('No content found for path', path)
|
|
16
15
|
throw new Error('No content found')
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
const contentType = await getContentType({ bytes: value, path })
|
|
20
18
|
const stream = new ReadableStream({
|
|
21
19
|
async start (controller) {
|
|
22
20
|
// the initial value is already available
|
|
23
21
|
options?.onProgress?.(new CustomProgressEvent<void>('verified-fetch:request:progress:chunk'))
|
|
24
|
-
controller.enqueue(
|
|
22
|
+
controller.enqueue(firstChunk)
|
|
25
23
|
},
|
|
26
24
|
async pull (controller) {
|
|
27
25
|
const { value, done } = await reader.next()
|
|
@@ -40,5 +38,8 @@ export async function getStreamAndContentType (iterator: AsyncIterable<Uint8Arra
|
|
|
40
38
|
}
|
|
41
39
|
})
|
|
42
40
|
|
|
43
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
stream,
|
|
43
|
+
firstChunk
|
|
44
|
+
}
|
|
44
45
|
}
|
package/src/verified-fetch.ts
CHANGED
|
@@ -10,10 +10,10 @@ import { code as dagPbCode } from '@ipld/dag-pb'
|
|
|
10
10
|
import { code as jsonCode } from 'multiformats/codecs/json'
|
|
11
11
|
import { decode, code as rawCode } from 'multiformats/codecs/raw'
|
|
12
12
|
import { CustomProgressEvent } from 'progress-events'
|
|
13
|
-
import {
|
|
13
|
+
import { getStreamFromAsyncIterable } from './utils/get-stream-from-async-iterable.js'
|
|
14
14
|
import { parseResource } from './utils/parse-resource.js'
|
|
15
15
|
import { walkPath, type PathWalkerFn } from './utils/walk-path.js'
|
|
16
|
-
import type { CIDDetail, Resource, VerifiedFetchInit as VerifiedFetchOptions } from './index.js'
|
|
16
|
+
import type { CIDDetail, ContentTypeParser, Resource, VerifiedFetchInit as VerifiedFetchOptions } from './index.js'
|
|
17
17
|
import type { Helia } from '@helia/interface'
|
|
18
18
|
import type { AbortOptions, Logger } from '@libp2p/interface'
|
|
19
19
|
import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
@@ -32,9 +32,8 @@ interface VerifiedFetchComponents {
|
|
|
32
32
|
/**
|
|
33
33
|
* Potential future options for the VerifiedFetch constructor.
|
|
34
34
|
*/
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
36
35
|
interface VerifiedFetchInit {
|
|
37
|
-
|
|
36
|
+
contentTypeParser?: ContentTypeParser
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
interface FetchHandlerFunctionArg {
|
|
@@ -72,6 +71,7 @@ export class VerifiedFetch {
|
|
|
72
71
|
private readonly json: JSON
|
|
73
72
|
private readonly pathWalker: PathWalkerFn
|
|
74
73
|
private readonly log: Logger
|
|
74
|
+
private readonly contentTypeParser: ContentTypeParser | undefined
|
|
75
75
|
|
|
76
76
|
constructor ({ helia, ipns, unixfs, dagJson, json, dagCbor, pathWalker }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
|
|
77
77
|
this.helia = helia
|
|
@@ -87,6 +87,7 @@ export class VerifiedFetch {
|
|
|
87
87
|
this.json = json ?? heliaJson(helia)
|
|
88
88
|
this.dagCbor = dagCbor ?? heliaDagCbor(helia)
|
|
89
89
|
this.pathWalker = pathWalker ?? walkPath
|
|
90
|
+
this.contentTypeParser = init?.contentTypeParser
|
|
90
91
|
this.log.trace('created VerifiedFetch instance')
|
|
91
92
|
}
|
|
92
93
|
|
|
@@ -106,12 +107,12 @@ export class VerifiedFetch {
|
|
|
106
107
|
|
|
107
108
|
private async handleDagJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
108
109
|
this.log.trace('fetching %c/%s', cid, path)
|
|
109
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
110
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
110
111
|
const result = await this.dagJson.get(cid, {
|
|
111
112
|
signal: options?.signal,
|
|
112
113
|
onProgress: options?.onProgress
|
|
113
114
|
})
|
|
114
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
115
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
115
116
|
const response = new Response(JSON.stringify(result), { status: 200 })
|
|
116
117
|
response.headers.set('content-type', 'application/json')
|
|
117
118
|
return response
|
|
@@ -119,12 +120,12 @@ export class VerifiedFetch {
|
|
|
119
120
|
|
|
120
121
|
private async handleJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
121
122
|
this.log.trace('fetching %c/%s', cid, path)
|
|
122
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
123
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
123
124
|
const result: Record<any, any> = await this.json.get(cid, {
|
|
124
125
|
signal: options?.signal,
|
|
125
126
|
onProgress: options?.onProgress
|
|
126
127
|
})
|
|
127
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
128
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
128
129
|
const response = new Response(JSON.stringify(result), { status: 200 })
|
|
129
130
|
response.headers.set('content-type', 'application/json')
|
|
130
131
|
return response
|
|
@@ -132,14 +133,14 @@ export class VerifiedFetch {
|
|
|
132
133
|
|
|
133
134
|
private async handleDagCbor ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
134
135
|
this.log.trace('fetching %c/%s', cid, path)
|
|
135
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
136
|
-
const result = await this.dagCbor.get(cid, {
|
|
136
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
137
|
+
const result = await this.dagCbor.get<Uint8Array>(cid, {
|
|
137
138
|
signal: options?.signal,
|
|
138
139
|
onProgress: options?.onProgress
|
|
139
140
|
})
|
|
140
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
141
|
-
const response = new Response(
|
|
142
|
-
|
|
141
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
142
|
+
const response = new Response(result, { status: 200 })
|
|
143
|
+
await this.setContentType(result, path, response)
|
|
143
144
|
return response
|
|
144
145
|
}
|
|
145
146
|
|
|
@@ -153,7 +154,7 @@ export class VerifiedFetch {
|
|
|
153
154
|
const rootFilePath = 'index.html'
|
|
154
155
|
try {
|
|
155
156
|
this.log.trace('found directory at %c/%s, looking for index.html', cid, path)
|
|
156
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: dirCid
|
|
157
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: dirCid, path: rootFilePath }))
|
|
157
158
|
stat = await this.unixfs.stat(dirCid, {
|
|
158
159
|
path: rootFilePath,
|
|
159
160
|
signal: options?.signal,
|
|
@@ -167,37 +168,63 @@ export class VerifiedFetch {
|
|
|
167
168
|
this.log('error loading path %c/%s', dirCid, rootFilePath, err)
|
|
168
169
|
return new Response('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented', { status: 501 })
|
|
169
170
|
} finally {
|
|
170
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid
|
|
171
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid, path: rootFilePath }))
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: resolvedCID
|
|
175
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: resolvedCID, path: '' }))
|
|
175
176
|
const asyncIter = this.unixfs.cat(resolvedCID, {
|
|
176
177
|
signal: options?.signal,
|
|
177
178
|
onProgress: options?.onProgress
|
|
178
179
|
})
|
|
179
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID
|
|
180
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID, path: '' }))
|
|
180
181
|
this.log('got async iterator for %c/%s', cid, path)
|
|
181
182
|
|
|
182
|
-
const {
|
|
183
|
+
const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, {
|
|
183
184
|
onProgress: options?.onProgress
|
|
184
185
|
})
|
|
185
186
|
const response = new Response(stream, { status: 200 })
|
|
186
|
-
|
|
187
|
+
await this.setContentType(firstChunk, path, response)
|
|
187
188
|
|
|
188
189
|
return response
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
private async handleRaw ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
192
193
|
this.log.trace('fetching %c/%s', cid, path)
|
|
193
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
194
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
194
195
|
const result = await this.helia.blockstore.get(cid)
|
|
195
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
196
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
196
197
|
const response = new Response(decode(result), { status: 200 })
|
|
197
|
-
|
|
198
|
+
await this.setContentType(result, path, response)
|
|
198
199
|
return response
|
|
199
200
|
}
|
|
200
201
|
|
|
202
|
+
private async setContentType (bytes: Uint8Array, path: string, response: Response): Promise<void> {
|
|
203
|
+
let contentType = 'application/octet-stream'
|
|
204
|
+
|
|
205
|
+
if (this.contentTypeParser != null) {
|
|
206
|
+
try {
|
|
207
|
+
let fileName = path.split('/').pop()?.trim()
|
|
208
|
+
fileName = fileName === '' ? undefined : fileName
|
|
209
|
+
const parsed = this.contentTypeParser(bytes, fileName)
|
|
210
|
+
|
|
211
|
+
if (isPromise(parsed)) {
|
|
212
|
+
const result = await parsed
|
|
213
|
+
|
|
214
|
+
if (result != null) {
|
|
215
|
+
contentType = result
|
|
216
|
+
}
|
|
217
|
+
} else if (parsed != null) {
|
|
218
|
+
contentType = parsed
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
this.log.error('Error parsing content type', err)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
response.headers.set('content-type', contentType)
|
|
226
|
+
}
|
|
227
|
+
|
|
201
228
|
/**
|
|
202
229
|
* Determines the format requested by the client, defaults to `null` if no format is requested.
|
|
203
230
|
*
|
|
@@ -321,3 +348,7 @@ export class VerifiedFetch {
|
|
|
321
348
|
await this.helia.stop()
|
|
322
349
|
}
|
|
323
350
|
}
|
|
351
|
+
|
|
352
|
+
function isPromise <T> (p?: any): p is Promise<T> {
|
|
353
|
+
return p?.then != null
|
|
354
|
+
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
interface TestInput {
|
|
2
|
-
bytes: Uint8Array;
|
|
3
|
-
path: string;
|
|
4
|
-
}
|
|
5
|
-
export declare const DEFAULT_MIME_TYPE = "application/octet-stream";
|
|
6
|
-
/**
|
|
7
|
-
* Get the content type from the input based on the tests.
|
|
8
|
-
*/
|
|
9
|
-
export declare function getContentType(input: TestInput): Promise<string>;
|
|
10
|
-
export {};
|
|
11
|
-
//# sourceMappingURL=get-content-type.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-content-type.d.ts","sourceRoot":"","sources":["../../../src/utils/get-content-type.ts"],"names":[],"mappings":"AAEA,UAAU,SAAS;IACjB,KAAK,EAAE,UAAU,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb;AAID,eAAO,MAAM,iBAAiB,6BAA6B,CAAA;AAkC3D;;GAEG;AACH,wBAAsB,cAAc,CAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAQvE"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import mime from 'mime-types';
|
|
2
|
-
export const DEFAULT_MIME_TYPE = 'application/octet-stream';
|
|
3
|
-
const xmlRegex = /^(<\?xml[^>]+>)?[^<^\w]+<svg/ig;
|
|
4
|
-
/**
|
|
5
|
-
* Tests to determine the content type of the input.
|
|
6
|
-
* The order is important on this one.
|
|
7
|
-
*/
|
|
8
|
-
const tests = [
|
|
9
|
-
// svg
|
|
10
|
-
async ({ bytes }) => xmlRegex.test(new TextDecoder().decode(bytes.slice(0, 64)))
|
|
11
|
-
? 'image/svg+xml'
|
|
12
|
-
: undefined,
|
|
13
|
-
// testing file-type from path
|
|
14
|
-
async ({ path }) => {
|
|
15
|
-
const mimeType = mime.lookup(path);
|
|
16
|
-
if (mimeType !== false) {
|
|
17
|
-
return mimeType;
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
];
|
|
22
|
-
const overrides = {
|
|
23
|
-
'video/quicktime': 'video/mp4'
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Override the content type based on overrides.
|
|
27
|
-
*/
|
|
28
|
-
function overrideContentType(type) {
|
|
29
|
-
return overrides[type] ?? type;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Get the content type from the input based on the tests.
|
|
33
|
-
*/
|
|
34
|
-
export async function getContentType(input) {
|
|
35
|
-
for (const test of tests) {
|
|
36
|
-
const type = await test(input);
|
|
37
|
-
if (type !== undefined) {
|
|
38
|
-
return overrideContentType(type);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return DEFAULT_MIME_TYPE;
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=get-content-type.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-content-type.js","sourceRoot":"","sources":["../../../src/utils/get-content-type.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,YAAY,CAAA;AAS7B,MAAM,CAAC,MAAM,iBAAiB,GAAG,0BAA0B,CAAA;AAE3D,MAAM,QAAQ,GAAG,gCAAgC,CAAA;AAEjD;;;GAGG;AACH,MAAM,KAAK,GAA4C;IACrD,MAAM;IACN,KAAK,EAAE,EAAE,KAAK,EAAE,EAAc,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC1F,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,SAAS;IACb,8BAA8B;IAC9B,KAAK,EAAE,EAAE,IAAI,EAAE,EAAc,EAAE;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO,QAAQ,CAAA;QACjB,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;CACF,CAAA;AAED,MAAM,SAAS,GAA2B;IACxC,iBAAiB,EAAE,WAAW;CAC/B,CAAA;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAE,IAAY;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAE,KAAgB;IACpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAA;QAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAA;QAClC,CAAC;IACH,CAAC;IACD,OAAO,iBAAiB,CAAA;AAC1B,CAAC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { VerifiedFetchInit } from '../index.js';
|
|
2
|
-
import type { ComponentLogger } from '@libp2p/interface';
|
|
3
|
-
/**
|
|
4
|
-
* Converts an async iterator of Uint8Array bytes to a stream and attempts to determine the content type of those bytes.
|
|
5
|
-
*/
|
|
6
|
-
export declare function getStreamAndContentType(iterator: AsyncIterable<Uint8Array>, path: string, logger: ComponentLogger, options?: Pick<VerifiedFetchInit, 'onProgress'>): Promise<{
|
|
7
|
-
contentType: string;
|
|
8
|
-
stream: ReadableStream<Uint8Array>;
|
|
9
|
-
}>;
|
|
10
|
-
//# sourceMappingURL=get-stream-and-content-type.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-stream-and-content-type.d.ts","sourceRoot":"","sources":["../../../src/utils/get-stream-and-content-type.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAExD;;GAEG;AACH,wBAAsB,uBAAuB,CAAE,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;CAAE,CAAC,CAmChP"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-stream-and-content-type.js","sourceRoot":"","sources":["../../../src/utils/get-stream-and-content-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAItD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAE,QAAmC,EAAE,IAAY,EAAE,MAAuB,EAAE,OAA+C;IACxK,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,kDAAkD,CAAC,CAAA;IACnF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAA;IAC/C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;IAE3C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,GAAG,CAAC,KAAK,CAAC,2BAA2B,EAAE,IAAI,CAAC,CAAA;QAC5C,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;IACrC,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC;QAChC,KAAK,CAAC,KAAK,CAAE,UAAU;YACrB,yCAAyC;YACzC,OAAO,EAAE,UAAU,EAAE,CAAC,IAAI,mBAAmB,CAAO,uCAAuC,CAAC,CAAC,CAAA;YAC7F,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QACD,KAAK,CAAC,IAAI,CAAE,UAAU;YACpB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;YAE3C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;oBAClB,OAAO,EAAE,UAAU,EAAE,CAAC,IAAI,mBAAmB,CAAO,uCAAuC,CAAC,CAAC,CAAA;oBAC7F,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;gBAC3B,CAAC;gBACD,UAAU,CAAC,KAAK,EAAE,CAAA;gBAClB,OAAM;YACR,CAAC;YAED,OAAO,EAAE,UAAU,EAAE,CAAC,IAAI,mBAAmB,CAAO,uCAAuC,CAAC,CAAC,CAAA;YAC7F,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAA;AAChC,CAAC"}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import mime from 'mime-types'
|
|
2
|
-
|
|
3
|
-
interface TestInput {
|
|
4
|
-
bytes: Uint8Array
|
|
5
|
-
path: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
type TestOutput = Promise<string | undefined>
|
|
9
|
-
|
|
10
|
-
export const DEFAULT_MIME_TYPE = 'application/octet-stream'
|
|
11
|
-
|
|
12
|
-
const xmlRegex = /^(<\?xml[^>]+>)?[^<^\w]+<svg/ig
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Tests to determine the content type of the input.
|
|
16
|
-
* The order is important on this one.
|
|
17
|
-
*/
|
|
18
|
-
const tests: Array<(input: TestInput) => TestOutput> = [
|
|
19
|
-
// svg
|
|
20
|
-
async ({ bytes }): TestOutput => xmlRegex.test(new TextDecoder().decode(bytes.slice(0, 64)))
|
|
21
|
-
? 'image/svg+xml'
|
|
22
|
-
: undefined,
|
|
23
|
-
// testing file-type from path
|
|
24
|
-
async ({ path }): TestOutput => {
|
|
25
|
-
const mimeType = mime.lookup(path)
|
|
26
|
-
if (mimeType !== false) {
|
|
27
|
-
return mimeType
|
|
28
|
-
}
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
const overrides: Record<string, string> = {
|
|
34
|
-
'video/quicktime': 'video/mp4'
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Override the content type based on overrides.
|
|
39
|
-
*/
|
|
40
|
-
function overrideContentType (type: string): string {
|
|
41
|
-
return overrides[type] ?? type
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Get the content type from the input based on the tests.
|
|
46
|
-
*/
|
|
47
|
-
export async function getContentType (input: TestInput): Promise<string> {
|
|
48
|
-
for (const test of tests) {
|
|
49
|
-
const type = await test(input)
|
|
50
|
-
if (type !== undefined) {
|
|
51
|
-
return overrideContentType(type)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return DEFAULT_MIME_TYPE
|
|
55
|
-
}
|