@helia/verified-fetch 0.0.0-5c0c39c → 0.0.0-754c7af
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 +280 -44
- package/dist/index.min.js +4 -29
- package/dist/src/index.d.ts +297 -53
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +270 -49
- package/dist/src/index.js.map +1 -1
- package/dist/src/singleton.d.ts +3 -0
- package/dist/src/singleton.d.ts.map +1 -0
- package/dist/src/singleton.js +15 -0
- package/dist/src/singleton.js.map +1 -0
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts +7 -0
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +1 -0
- package/dist/src/utils/dag-cbor-to-safe-json.js +37 -0
- package/dist/src/utils/dag-cbor-to-safe-json.js.map +1 -0
- 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 +5 -12
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +88 -60
- package/dist/src/verified-fetch.js.map +1 -1
- package/package.json +25 -18
- package/src/index.ts +303 -54
- package/src/singleton.ts +20 -0
- package/src/utils/dag-cbor-to-safe-json.ts +44 -0
- package/src/utils/{get-stream-and-content-type.ts → get-stream-from-async-iterable.ts} +9 -8
- package/src/verified-fetch.ts +99 -67
- 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/verified-fetch.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import { dagCbor as heliaDagCbor, type DAGCBOR } from '@helia/dag-cbor'
|
|
2
|
-
import { dagJson as heliaDagJson, type DAGJSON } from '@helia/dag-json'
|
|
3
1
|
import { ipns as heliaIpns, type IPNS } from '@helia/ipns'
|
|
4
2
|
import { dnsJsonOverHttps } from '@helia/ipns/dns-resolvers'
|
|
5
|
-
import { json as heliaJson, type JSON } from '@helia/json'
|
|
6
3
|
import { unixfs as heliaUnixFs, type UnixFS as HeliaUnixFs, type UnixFSStats } from '@helia/unixfs'
|
|
7
4
|
import { code as dagCborCode } from '@ipld/dag-cbor'
|
|
8
5
|
import { code as dagJsonCode } from '@ipld/dag-json'
|
|
9
6
|
import { code as dagPbCode } from '@ipld/dag-pb'
|
|
10
7
|
import { code as jsonCode } from 'multiformats/codecs/json'
|
|
11
|
-
import {
|
|
8
|
+
import { code as rawCode } from 'multiformats/codecs/raw'
|
|
9
|
+
import { identity } from 'multiformats/hashes/identity'
|
|
12
10
|
import { CustomProgressEvent } from 'progress-events'
|
|
13
|
-
import {
|
|
11
|
+
import { dagCborToSafeJSON } from './utils/dag-cbor-to-safe-json.js'
|
|
12
|
+
import { getStreamFromAsyncIterable } from './utils/get-stream-from-async-iterable.js'
|
|
14
13
|
import { parseResource } from './utils/parse-resource.js'
|
|
15
14
|
import { walkPath, type PathWalkerFn } from './utils/walk-path.js'
|
|
16
|
-
import type { CIDDetail, Resource, VerifiedFetchInit as VerifiedFetchOptions } from './index.js'
|
|
15
|
+
import type { CIDDetail, ContentTypeParser, Resource, VerifiedFetchInit as VerifiedFetchOptions } from './index.js'
|
|
17
16
|
import type { Helia } from '@helia/interface'
|
|
18
17
|
import type { AbortOptions, Logger } from '@libp2p/interface'
|
|
19
18
|
import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
@@ -23,18 +22,14 @@ interface VerifiedFetchComponents {
|
|
|
23
22
|
helia: Helia
|
|
24
23
|
ipns?: IPNS
|
|
25
24
|
unixfs?: HeliaUnixFs
|
|
26
|
-
dagJson?: DAGJSON
|
|
27
|
-
json?: JSON
|
|
28
|
-
dagCbor?: DAGCBOR
|
|
29
25
|
pathWalker?: PathWalkerFn
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
/**
|
|
33
29
|
* Potential future options for the VerifiedFetch constructor.
|
|
34
30
|
*/
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
36
31
|
interface VerifiedFetchInit {
|
|
37
|
-
|
|
32
|
+
contentTypeParser?: ContentTypeParser
|
|
38
33
|
}
|
|
39
34
|
|
|
40
35
|
interface FetchHandlerFunctionArg {
|
|
@@ -63,17 +58,29 @@ function convertOptions (options?: VerifiedFetchOptions): (Omit<VerifiedFetchOpt
|
|
|
63
58
|
}
|
|
64
59
|
}
|
|
65
60
|
|
|
61
|
+
function okResponse (body?: BodyInit | null): Response {
|
|
62
|
+
return new Response(body, {
|
|
63
|
+
status: 200,
|
|
64
|
+
statusText: 'OK'
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function notSupportedResponse (body?: BodyInit | null): Response {
|
|
69
|
+
return new Response(body, {
|
|
70
|
+
status: 501,
|
|
71
|
+
statusText: 'Not Implemented'
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
66
75
|
export class VerifiedFetch {
|
|
67
76
|
private readonly helia: Helia
|
|
68
77
|
private readonly ipns: IPNS
|
|
69
78
|
private readonly unixfs: HeliaUnixFs
|
|
70
|
-
private readonly dagJson: DAGJSON
|
|
71
|
-
private readonly dagCbor: DAGCBOR
|
|
72
|
-
private readonly json: JSON
|
|
73
79
|
private readonly pathWalker: PathWalkerFn
|
|
74
80
|
private readonly log: Logger
|
|
81
|
+
private readonly contentTypeParser: ContentTypeParser | undefined
|
|
75
82
|
|
|
76
|
-
constructor ({ helia, ipns, unixfs,
|
|
83
|
+
constructor ({ helia, ipns, unixfs, pathWalker }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
|
|
77
84
|
this.helia = helia
|
|
78
85
|
this.log = helia.logger.forComponent('helia:verified-fetch')
|
|
79
86
|
this.ipns = ipns ?? heliaIpns(helia, {
|
|
@@ -83,63 +90,55 @@ export class VerifiedFetch {
|
|
|
83
90
|
]
|
|
84
91
|
})
|
|
85
92
|
this.unixfs = unixfs ?? heliaUnixFs(helia)
|
|
86
|
-
this.dagJson = dagJson ?? heliaDagJson(helia)
|
|
87
|
-
this.json = json ?? heliaJson(helia)
|
|
88
|
-
this.dagCbor = dagCbor ?? heliaDagCbor(helia)
|
|
89
93
|
this.pathWalker = pathWalker ?? walkPath
|
|
94
|
+
this.contentTypeParser = init?.contentTypeParser
|
|
90
95
|
this.log.trace('created VerifiedFetch instance')
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
// handle vnd.ipfs.ipns-record
|
|
94
99
|
private async handleIPNSRecord ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
95
|
-
const response =
|
|
100
|
+
const response = notSupportedResponse('vnd.ipfs.ipns-record support is not implemented')
|
|
96
101
|
response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
|
|
97
102
|
return response
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
// handle vnd.ipld.car
|
|
101
106
|
private async handleIPLDCar ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
102
|
-
const response =
|
|
107
|
+
const response = notSupportedResponse('vnd.ipld.car support is not implemented')
|
|
103
108
|
response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
|
|
104
109
|
return response
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
private async handleDagJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
108
|
-
this.log.trace('fetching %c/%s', cid, path)
|
|
109
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: cid.toString(), path }))
|
|
110
|
-
const result = await this.dagJson.get(cid, {
|
|
111
|
-
signal: options?.signal,
|
|
112
|
-
onProgress: options?.onProgress
|
|
113
|
-
})
|
|
114
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: cid.toString(), path }))
|
|
115
|
-
const response = new Response(JSON.stringify(result), { status: 200 })
|
|
116
|
-
response.headers.set('content-type', 'application/json')
|
|
117
|
-
return response
|
|
118
|
-
}
|
|
119
|
-
|
|
120
112
|
private async handleJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
121
113
|
this.log.trace('fetching %c/%s', cid, path)
|
|
122
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
123
|
-
const result
|
|
114
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
115
|
+
const result = await this.helia.blockstore.get(cid, {
|
|
124
116
|
signal: options?.signal,
|
|
125
117
|
onProgress: options?.onProgress
|
|
126
118
|
})
|
|
127
|
-
|
|
128
|
-
const response = new Response(JSON.stringify(result), { status: 200 })
|
|
119
|
+
const response = okResponse(result)
|
|
129
120
|
response.headers.set('content-type', 'application/json')
|
|
121
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
130
122
|
return response
|
|
131
123
|
}
|
|
132
124
|
|
|
133
125
|
private async handleDagCbor ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
134
126
|
this.log.trace('fetching %c/%s', cid, path)
|
|
135
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
127
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
128
|
+
// return body as binary
|
|
129
|
+
const block = await this.helia.blockstore.get(cid)
|
|
130
|
+
let body: string | Uint8Array
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
body = dagCborToSafeJSON(block)
|
|
134
|
+
} catch (err) {
|
|
135
|
+
this.log('could not decode DAG-CBOR as JSON-safe, falling back to `application/octet-stream`', err)
|
|
136
|
+
body = block
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const response = okResponse(body)
|
|
140
|
+
response.headers.set('content-type', body instanceof Uint8Array ? 'application/octet-stream' : 'application/json')
|
|
141
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
143
142
|
return response
|
|
144
143
|
}
|
|
145
144
|
|
|
@@ -153,7 +152,7 @@ export class VerifiedFetch {
|
|
|
153
152
|
const rootFilePath = 'index.html'
|
|
154
153
|
try {
|
|
155
154
|
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
|
|
155
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: dirCid, path: rootFilePath }))
|
|
157
156
|
stat = await this.unixfs.stat(dirCid, {
|
|
158
157
|
path: rootFilePath,
|
|
159
158
|
signal: options?.signal,
|
|
@@ -165,39 +164,67 @@ export class VerifiedFetch {
|
|
|
165
164
|
// terminalElement = stat
|
|
166
165
|
} catch (err: any) {
|
|
167
166
|
this.log('error loading path %c/%s', dirCid, rootFilePath, err)
|
|
168
|
-
return
|
|
167
|
+
return notSupportedResponse('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented')
|
|
169
168
|
} finally {
|
|
170
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid
|
|
169
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid, path: rootFilePath }))
|
|
171
170
|
}
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: resolvedCID
|
|
173
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: resolvedCID, path: '' }))
|
|
175
174
|
const asyncIter = this.unixfs.cat(resolvedCID, {
|
|
176
175
|
signal: options?.signal,
|
|
177
176
|
onProgress: options?.onProgress
|
|
178
177
|
})
|
|
179
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID.toString(), path: '' }))
|
|
180
178
|
this.log('got async iterator for %c/%s', cid, path)
|
|
181
179
|
|
|
182
|
-
const {
|
|
180
|
+
const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, {
|
|
183
181
|
onProgress: options?.onProgress
|
|
184
182
|
})
|
|
185
|
-
const response =
|
|
186
|
-
|
|
183
|
+
const response = okResponse(stream)
|
|
184
|
+
await this.setContentType(firstChunk, path, response)
|
|
185
|
+
|
|
186
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID, path: '' }))
|
|
187
187
|
|
|
188
188
|
return response
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
private async handleRaw ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
192
192
|
this.log.trace('fetching %c/%s', cid, path)
|
|
193
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
193
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
194
194
|
const result = await this.helia.blockstore.get(cid)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
const response = okResponse(result)
|
|
196
|
+
await this.setContentType(result, path, response)
|
|
197
|
+
|
|
198
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
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
|
*
|
|
@@ -234,22 +261,23 @@ export class VerifiedFetch {
|
|
|
234
261
|
* These format handlers should adjust the response headers as specified in https://specs.ipfs.tech/http-gateways/path-gateway/#response-headers
|
|
235
262
|
*/
|
|
236
263
|
private readonly formatHandlers: Record<string, FetchHandlerFunction> = {
|
|
237
|
-
raw: async () =>
|
|
264
|
+
raw: async () => notSupportedResponse('application/vnd.ipld.raw support is not implemented'),
|
|
238
265
|
car: this.handleIPLDCar,
|
|
239
266
|
'ipns-record': this.handleIPNSRecord,
|
|
240
|
-
tar: async () =>
|
|
241
|
-
'dag-json': async () =>
|
|
242
|
-
'dag-cbor': async () =>
|
|
243
|
-
json: async () =>
|
|
244
|
-
cbor: async () =>
|
|
267
|
+
tar: async () => notSupportedResponse('application/x-tar support is not implemented'),
|
|
268
|
+
'dag-json': async () => notSupportedResponse('application/vnd.ipld.dag-json support is not implemented'),
|
|
269
|
+
'dag-cbor': async () => notSupportedResponse('application/vnd.ipld.dag-cbor support is not implemented'),
|
|
270
|
+
json: async () => notSupportedResponse('application/json support is not implemented'),
|
|
271
|
+
cbor: async () => notSupportedResponse('application/cbor support is not implemented')
|
|
245
272
|
}
|
|
246
273
|
|
|
247
274
|
private readonly codecHandlers: Record<number, FetchHandlerFunction> = {
|
|
248
|
-
[dagJsonCode]: this.handleDagJson,
|
|
249
275
|
[dagPbCode]: this.handleDagPb,
|
|
276
|
+
[dagJsonCode]: this.handleJson,
|
|
250
277
|
[jsonCode]: this.handleJson,
|
|
251
278
|
[dagCborCode]: this.handleDagCbor,
|
|
252
|
-
[rawCode]: this.handleRaw
|
|
279
|
+
[rawCode]: this.handleRaw,
|
|
280
|
+
[identity.code]: this.handleRaw
|
|
253
281
|
}
|
|
254
282
|
|
|
255
283
|
async fetch (resource: Resource, opts?: VerifiedFetchOptions): Promise<Response> {
|
|
@@ -291,12 +319,12 @@ export class VerifiedFetch {
|
|
|
291
319
|
if (codecHandler != null) {
|
|
292
320
|
response = await codecHandler.call(this, { cid, path, options, terminalElement })
|
|
293
321
|
} else {
|
|
294
|
-
return
|
|
322
|
+
return notSupportedResponse(`Support for codec with code ${cid.code} is not yet implemented. Please open an issue at https://github.com/ipfs/helia/issues/new`)
|
|
295
323
|
}
|
|
296
324
|
}
|
|
297
325
|
|
|
298
326
|
response.headers.set('etag', cid.toString()) // https://specs.ipfs.tech/http-gateways/path-gateway/#etag-response-header
|
|
299
|
-
response.headers.set('cache-
|
|
327
|
+
response.headers.set('cache-control', 'public, max-age=29030400, immutable')
|
|
300
328
|
response.headers.set('X-Ipfs-Path', resource.toString()) // https://specs.ipfs.tech/http-gateways/path-gateway/#x-ipfs-path-response-header
|
|
301
329
|
|
|
302
330
|
if (ipfsRoots != null) {
|
|
@@ -321,3 +349,7 @@ export class VerifiedFetch {
|
|
|
321
349
|
await this.helia.stop()
|
|
322
350
|
}
|
|
323
351
|
}
|
|
352
|
+
|
|
353
|
+
function isPromise <T> (p?: any): p is Promise<T> {
|
|
354
|
+
return p?.then != null
|
|
355
|
+
}
|
|
@@ -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
|
-
}
|