@helia/verified-fetch 0.0.0-5c0c39c → 0.0.0-75d0a5b
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 +80 -43
- package/dist/index.min.js +4 -29
- package/dist/src/index.d.ts +114 -52
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +87 -48
- 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/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 +75 -33
- package/dist/src/verified-fetch.js.map +1 -1
- package/package.json +19 -14
- package/src/index.ts +120 -53
- package/src/singleton.ts +20 -0
- package/src/utils/{get-stream-and-content-type.ts → get-stream-from-async-iterable.ts} +9 -8
- package/src/verified-fetch.ts +84 -37
- 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
|
@@ -9,11 +9,12 @@ import { code as dagJsonCode } from '@ipld/dag-json'
|
|
|
9
9
|
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
|
+
import { identity } from 'multiformats/hashes/identity'
|
|
12
13
|
import { CustomProgressEvent } from 'progress-events'
|
|
13
|
-
import {
|
|
14
|
+
import { getStreamFromAsyncIterable } from './utils/get-stream-from-async-iterable.js'
|
|
14
15
|
import { parseResource } from './utils/parse-resource.js'
|
|
15
16
|
import { walkPath, type PathWalkerFn } from './utils/walk-path.js'
|
|
16
|
-
import type { CIDDetail, Resource, VerifiedFetchInit as VerifiedFetchOptions } from './index.js'
|
|
17
|
+
import type { CIDDetail, ContentTypeParser, Resource, VerifiedFetchInit as VerifiedFetchOptions } from './index.js'
|
|
17
18
|
import type { Helia } from '@helia/interface'
|
|
18
19
|
import type { AbortOptions, Logger } from '@libp2p/interface'
|
|
19
20
|
import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
@@ -32,9 +33,8 @@ interface VerifiedFetchComponents {
|
|
|
32
33
|
/**
|
|
33
34
|
* Potential future options for the VerifiedFetch constructor.
|
|
34
35
|
*/
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
36
36
|
interface VerifiedFetchInit {
|
|
37
|
-
|
|
37
|
+
contentTypeParser?: ContentTypeParser
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
interface FetchHandlerFunctionArg {
|
|
@@ -63,6 +63,20 @@ function convertOptions (options?: VerifiedFetchOptions): (Omit<VerifiedFetchOpt
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
function okResponse (body?: BodyInit | null): Response {
|
|
67
|
+
return new Response(body, {
|
|
68
|
+
status: 200,
|
|
69
|
+
statusText: 'OK'
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function notSupportedResponse (body?: BodyInit | null): Response {
|
|
74
|
+
return new Response(body, {
|
|
75
|
+
status: 501,
|
|
76
|
+
statusText: 'Not Implemented'
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
export class VerifiedFetch {
|
|
67
81
|
private readonly helia: Helia
|
|
68
82
|
private readonly ipns: IPNS
|
|
@@ -72,6 +86,7 @@ export class VerifiedFetch {
|
|
|
72
86
|
private readonly json: JSON
|
|
73
87
|
private readonly pathWalker: PathWalkerFn
|
|
74
88
|
private readonly log: Logger
|
|
89
|
+
private readonly contentTypeParser: ContentTypeParser | undefined
|
|
75
90
|
|
|
76
91
|
constructor ({ helia, ipns, unixfs, dagJson, json, dagCbor, pathWalker }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
|
|
77
92
|
this.helia = helia
|
|
@@ -87,59 +102,60 @@ export class VerifiedFetch {
|
|
|
87
102
|
this.json = json ?? heliaJson(helia)
|
|
88
103
|
this.dagCbor = dagCbor ?? heliaDagCbor(helia)
|
|
89
104
|
this.pathWalker = pathWalker ?? walkPath
|
|
105
|
+
this.contentTypeParser = init?.contentTypeParser
|
|
90
106
|
this.log.trace('created VerifiedFetch instance')
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
// handle vnd.ipfs.ipns-record
|
|
94
110
|
private async handleIPNSRecord ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
95
|
-
const response =
|
|
111
|
+
const response = notSupportedResponse('vnd.ipfs.ipns-record support is not implemented')
|
|
96
112
|
response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
|
|
97
113
|
return response
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
// handle vnd.ipld.car
|
|
101
117
|
private async handleIPLDCar ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
102
|
-
const response =
|
|
118
|
+
const response = notSupportedResponse('vnd.ipld.car support is not implemented')
|
|
103
119
|
response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
|
|
104
120
|
return response
|
|
105
121
|
}
|
|
106
122
|
|
|
107
123
|
private async handleDagJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
108
124
|
this.log.trace('fetching %c/%s', cid, path)
|
|
109
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
125
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
110
126
|
const result = await this.dagJson.get(cid, {
|
|
111
127
|
signal: options?.signal,
|
|
112
128
|
onProgress: options?.onProgress
|
|
113
129
|
})
|
|
114
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
115
|
-
const response =
|
|
130
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
131
|
+
const response = okResponse(JSON.stringify(result))
|
|
116
132
|
response.headers.set('content-type', 'application/json')
|
|
117
133
|
return response
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
private async handleJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
121
137
|
this.log.trace('fetching %c/%s', cid, path)
|
|
122
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
138
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
123
139
|
const result: Record<any, any> = await this.json.get(cid, {
|
|
124
140
|
signal: options?.signal,
|
|
125
141
|
onProgress: options?.onProgress
|
|
126
142
|
})
|
|
127
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
128
|
-
const response =
|
|
143
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
144
|
+
const response = okResponse(JSON.stringify(result))
|
|
129
145
|
response.headers.set('content-type', 'application/json')
|
|
130
146
|
return response
|
|
131
147
|
}
|
|
132
148
|
|
|
133
149
|
private async handleDagCbor ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
134
150
|
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, {
|
|
151
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
152
|
+
const result = await this.dagCbor.get<Uint8Array>(cid, {
|
|
137
153
|
signal: options?.signal,
|
|
138
154
|
onProgress: options?.onProgress
|
|
139
155
|
})
|
|
140
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
141
|
-
const response =
|
|
142
|
-
|
|
156
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
157
|
+
const response = okResponse(JSON.stringify(result))
|
|
158
|
+
await this.setContentType(result, path, response)
|
|
143
159
|
return response
|
|
144
160
|
}
|
|
145
161
|
|
|
@@ -153,7 +169,7 @@ export class VerifiedFetch {
|
|
|
153
169
|
const rootFilePath = 'index.html'
|
|
154
170
|
try {
|
|
155
171
|
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
|
|
172
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: dirCid, path: rootFilePath }))
|
|
157
173
|
stat = await this.unixfs.stat(dirCid, {
|
|
158
174
|
path: rootFilePath,
|
|
159
175
|
signal: options?.signal,
|
|
@@ -165,39 +181,65 @@ export class VerifiedFetch {
|
|
|
165
181
|
// terminalElement = stat
|
|
166
182
|
} catch (err: any) {
|
|
167
183
|
this.log('error loading path %c/%s', dirCid, rootFilePath, err)
|
|
168
|
-
return
|
|
184
|
+
return notSupportedResponse('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented')
|
|
169
185
|
} finally {
|
|
170
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid
|
|
186
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid, path: rootFilePath }))
|
|
171
187
|
}
|
|
172
188
|
}
|
|
173
189
|
|
|
174
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: resolvedCID
|
|
190
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: resolvedCID, path: '' }))
|
|
175
191
|
const asyncIter = this.unixfs.cat(resolvedCID, {
|
|
176
192
|
signal: options?.signal,
|
|
177
193
|
onProgress: options?.onProgress
|
|
178
194
|
})
|
|
179
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID
|
|
195
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID, path: '' }))
|
|
180
196
|
this.log('got async iterator for %c/%s', cid, path)
|
|
181
197
|
|
|
182
|
-
const {
|
|
198
|
+
const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, {
|
|
183
199
|
onProgress: options?.onProgress
|
|
184
200
|
})
|
|
185
|
-
const response =
|
|
186
|
-
|
|
201
|
+
const response = okResponse(stream)
|
|
202
|
+
await this.setContentType(firstChunk, path, response)
|
|
187
203
|
|
|
188
204
|
return response
|
|
189
205
|
}
|
|
190
206
|
|
|
191
207
|
private async handleRaw ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
|
|
192
208
|
this.log.trace('fetching %c/%s', cid, path)
|
|
193
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid
|
|
209
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
|
|
194
210
|
const result = await this.helia.blockstore.get(cid)
|
|
195
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid
|
|
196
|
-
const response =
|
|
197
|
-
|
|
211
|
+
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
|
|
212
|
+
const response = okResponse(decode(result))
|
|
213
|
+
await this.setContentType(result, path, response)
|
|
198
214
|
return response
|
|
199
215
|
}
|
|
200
216
|
|
|
217
|
+
private async setContentType (bytes: Uint8Array, path: string, response: Response): Promise<void> {
|
|
218
|
+
let contentType = 'application/octet-stream'
|
|
219
|
+
|
|
220
|
+
if (this.contentTypeParser != null) {
|
|
221
|
+
try {
|
|
222
|
+
let fileName = path.split('/').pop()?.trim()
|
|
223
|
+
fileName = fileName === '' ? undefined : fileName
|
|
224
|
+
const parsed = this.contentTypeParser(bytes, fileName)
|
|
225
|
+
|
|
226
|
+
if (isPromise(parsed)) {
|
|
227
|
+
const result = await parsed
|
|
228
|
+
|
|
229
|
+
if (result != null) {
|
|
230
|
+
contentType = result
|
|
231
|
+
}
|
|
232
|
+
} else if (parsed != null) {
|
|
233
|
+
contentType = parsed
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
this.log.error('Error parsing content type', err)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
response.headers.set('content-type', contentType)
|
|
241
|
+
}
|
|
242
|
+
|
|
201
243
|
/**
|
|
202
244
|
* Determines the format requested by the client, defaults to `null` if no format is requested.
|
|
203
245
|
*
|
|
@@ -234,14 +276,14 @@ export class VerifiedFetch {
|
|
|
234
276
|
* These format handlers should adjust the response headers as specified in https://specs.ipfs.tech/http-gateways/path-gateway/#response-headers
|
|
235
277
|
*/
|
|
236
278
|
private readonly formatHandlers: Record<string, FetchHandlerFunction> = {
|
|
237
|
-
raw: async () =>
|
|
279
|
+
raw: async () => notSupportedResponse('application/vnd.ipld.raw support is not implemented'),
|
|
238
280
|
car: this.handleIPLDCar,
|
|
239
281
|
'ipns-record': this.handleIPNSRecord,
|
|
240
|
-
tar: async () =>
|
|
241
|
-
'dag-json': async () =>
|
|
242
|
-
'dag-cbor': async () =>
|
|
243
|
-
json: async () =>
|
|
244
|
-
cbor: async () =>
|
|
282
|
+
tar: async () => notSupportedResponse('application/x-tar support is not implemented'),
|
|
283
|
+
'dag-json': async () => notSupportedResponse('application/vnd.ipld.dag-json support is not implemented'),
|
|
284
|
+
'dag-cbor': async () => notSupportedResponse('application/vnd.ipld.dag-cbor support is not implemented'),
|
|
285
|
+
json: async () => notSupportedResponse('application/json support is not implemented'),
|
|
286
|
+
cbor: async () => notSupportedResponse('application/cbor support is not implemented')
|
|
245
287
|
}
|
|
246
288
|
|
|
247
289
|
private readonly codecHandlers: Record<number, FetchHandlerFunction> = {
|
|
@@ -249,7 +291,8 @@ export class VerifiedFetch {
|
|
|
249
291
|
[dagPbCode]: this.handleDagPb,
|
|
250
292
|
[jsonCode]: this.handleJson,
|
|
251
293
|
[dagCborCode]: this.handleDagCbor,
|
|
252
|
-
[rawCode]: this.handleRaw
|
|
294
|
+
[rawCode]: this.handleRaw,
|
|
295
|
+
[identity.code]: this.handleRaw
|
|
253
296
|
}
|
|
254
297
|
|
|
255
298
|
async fetch (resource: Resource, opts?: VerifiedFetchOptions): Promise<Response> {
|
|
@@ -291,7 +334,7 @@ export class VerifiedFetch {
|
|
|
291
334
|
if (codecHandler != null) {
|
|
292
335
|
response = await codecHandler.call(this, { cid, path, options, terminalElement })
|
|
293
336
|
} else {
|
|
294
|
-
return
|
|
337
|
+
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
338
|
}
|
|
296
339
|
}
|
|
297
340
|
|
|
@@ -321,3 +364,7 @@ export class VerifiedFetch {
|
|
|
321
364
|
await this.helia.stop()
|
|
322
365
|
}
|
|
323
366
|
}
|
|
367
|
+
|
|
368
|
+
function isPromise <T> (p?: any): p is Promise<T> {
|
|
369
|
+
return p?.then != null
|
|
370
|
+
}
|
|
@@ -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
|
-
}
|