@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.
Files changed (36) hide show
  1. package/README.md +280 -44
  2. package/dist/index.min.js +4 -29
  3. package/dist/src/index.d.ts +297 -53
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +270 -49
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/singleton.d.ts +3 -0
  8. package/dist/src/singleton.d.ts.map +1 -0
  9. package/dist/src/singleton.js +15 -0
  10. package/dist/src/singleton.js.map +1 -0
  11. package/dist/src/utils/dag-cbor-to-safe-json.d.ts +7 -0
  12. package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +1 -0
  13. package/dist/src/utils/dag-cbor-to-safe-json.js +37 -0
  14. package/dist/src/utils/dag-cbor-to-safe-json.js.map +1 -0
  15. package/dist/src/utils/get-stream-from-async-iterable.d.ts +10 -0
  16. package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +1 -0
  17. package/dist/src/utils/{get-stream-and-content-type.js → get-stream-from-async-iterable.js} +10 -9
  18. package/dist/src/utils/get-stream-from-async-iterable.js.map +1 -0
  19. package/dist/src/verified-fetch.d.ts +5 -12
  20. package/dist/src/verified-fetch.d.ts.map +1 -1
  21. package/dist/src/verified-fetch.js +88 -60
  22. package/dist/src/verified-fetch.js.map +1 -1
  23. package/package.json +25 -18
  24. package/src/index.ts +303 -54
  25. package/src/singleton.ts +20 -0
  26. package/src/utils/dag-cbor-to-safe-json.ts +44 -0
  27. package/src/utils/{get-stream-and-content-type.ts → get-stream-from-async-iterable.ts} +9 -8
  28. package/src/verified-fetch.ts +99 -67
  29. package/dist/src/utils/get-content-type.d.ts +0 -11
  30. package/dist/src/utils/get-content-type.d.ts.map +0 -1
  31. package/dist/src/utils/get-content-type.js +0 -43
  32. package/dist/src/utils/get-content-type.js.map +0 -1
  33. package/dist/src/utils/get-stream-and-content-type.d.ts +0 -10
  34. package/dist/src/utils/get-stream-and-content-type.d.ts.map +0 -1
  35. package/dist/src/utils/get-stream-and-content-type.js.map +0 -1
  36. package/src/utils/get-content-type.ts +0 -55
@@ -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 { decode, code as rawCode } from 'multiformats/codecs/raw'
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 { getStreamAndContentType } from './utils/get-stream-and-content-type.js'
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, dagJson, json, dagCbor, pathWalker }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
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 = new Response('vnd.ipfs.ipns-record support is not implemented', { status: 501 })
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 = new Response('vnd.ipld.car support is not implemented', { status: 501 })
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: cid.toString(), path }))
123
- const result: Record<any, any> = await this.json.get(cid, {
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
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: cid.toString(), path }))
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: cid.toString(), path }))
136
- const result = await this.dagCbor.get(cid, {
137
- signal: options?.signal,
138
- onProgress: options?.onProgress
139
- })
140
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: cid.toString(), path }))
141
- const response = new Response(JSON.stringify(result), { status: 200 })
142
- response.headers.set('content-type', 'application/json')
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.toString(), path: rootFilePath }))
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 new Response('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented', { status: 501 })
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.toString(), path: rootFilePath }))
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.toString(), path: '' }))
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 { contentType, stream } = await getStreamAndContentType(asyncIter, path ?? '', this.helia.logger, {
180
+ const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, {
183
181
  onProgress: options?.onProgress
184
182
  })
185
- const response = new Response(stream, { status: 200 })
186
- response.headers.set('content-type', contentType)
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: cid.toString(), path }))
193
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
194
194
  const result = await this.helia.blockstore.get(cid)
195
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: cid.toString(), path }))
196
- const response = new Response(decode(result), { status: 200 })
197
- response.headers.set('content-type', 'application/octet-stream')
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 () => new Response('application/vnd.ipld.raw support is not implemented', { status: 501 }),
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 () => new Response('application/x-tar support is not implemented', { status: 501 }),
241
- 'dag-json': async () => new Response('application/vnd.ipld.dag-json support is not implemented', { status: 501 }),
242
- 'dag-cbor': async () => new Response('application/vnd.ipld.dag-cbor support is not implemented', { status: 501 }),
243
- json: async () => new Response('application/json support is not implemented', { status: 501 }),
244
- cbor: async () => new Response('application/cbor support is not implemented', { status: 501 })
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 new Response(`Support for codec with code ${cid.code} is not yet implemented. Please open an issue at https://github.com/ipfs/helia/issues/new`, { status: 501 })
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-cotrol', 'public, max-age=29030400, immutable')
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
- }