@helia/verified-fetch 0.0.0-8a5bc6f → 0.0.0-8db7792

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 (50) 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/types.d.ts +2 -0
  12. package/dist/src/types.d.ts.map +1 -0
  13. package/dist/src/types.js +2 -0
  14. package/dist/src/types.js.map +1 -0
  15. package/dist/src/utils/dag-cbor-to-safe-json.d.ts +7 -0
  16. package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +1 -0
  17. package/dist/src/utils/dag-cbor-to-safe-json.js +37 -0
  18. package/dist/src/utils/dag-cbor-to-safe-json.js.map +1 -0
  19. package/dist/src/utils/get-e-tag.d.ts +28 -0
  20. package/dist/src/utils/get-e-tag.d.ts.map +1 -0
  21. package/dist/src/utils/get-e-tag.js +18 -0
  22. package/dist/src/utils/get-e-tag.js.map +1 -0
  23. package/dist/src/utils/get-stream-from-async-iterable.d.ts +10 -0
  24. package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +1 -0
  25. package/dist/src/utils/{get-stream-and-content-type.js → get-stream-from-async-iterable.js} +10 -9
  26. package/dist/src/utils/get-stream-from-async-iterable.js.map +1 -0
  27. package/dist/src/utils/parse-url-string.d.ts +5 -1
  28. package/dist/src/utils/parse-url-string.d.ts.map +1 -1
  29. package/dist/src/utils/parse-url-string.js.map +1 -1
  30. package/dist/src/verified-fetch.d.ts +5 -12
  31. package/dist/src/verified-fetch.d.ts.map +1 -1
  32. package/dist/src/verified-fetch.js +90 -61
  33. package/dist/src/verified-fetch.js.map +1 -1
  34. package/package.json +28 -22
  35. package/src/index.ts +303 -54
  36. package/src/singleton.ts +20 -0
  37. package/src/types.ts +1 -0
  38. package/src/utils/dag-cbor-to-safe-json.ts +44 -0
  39. package/src/utils/get-e-tag.ts +36 -0
  40. package/src/utils/{get-stream-and-content-type.ts → get-stream-from-async-iterable.ts} +9 -8
  41. package/src/utils/parse-url-string.ts +6 -1
  42. package/src/verified-fetch.ts +104 -70
  43. package/dist/src/utils/get-content-type.d.ts +0 -11
  44. package/dist/src/utils/get-content-type.d.ts.map +0 -1
  45. package/dist/src/utils/get-content-type.js +0 -43
  46. package/dist/src/utils/get-content-type.js.map +0 -1
  47. package/dist/src/utils/get-stream-and-content-type.d.ts +0 -10
  48. package/dist/src/utils/get-stream-and-content-type.d.ts.map +0 -1
  49. package/dist/src/utils/get-stream-and-content-type.js.map +0 -1
  50. package/src/utils/get-content-type.ts +0 -55
@@ -1,6 +1,7 @@
1
1
  import { peerIdFromString } from '@libp2p/peer-id'
2
2
  import { CID } from 'multiformats/cid'
3
3
  import { TLRU } from './tlru.js'
4
+ import type { RequestFormatShorthand } from '../types.js'
4
5
  import type { IPNS, IPNSRoutingEvents, ResolveDnsLinkProgressEvents, ResolveProgressEvents, ResolveResult } from '@helia/ipns'
5
6
  import type { ComponentLogger } from '@libp2p/interface'
6
7
  import type { ProgressOptions } from 'progress-events'
@@ -16,11 +17,15 @@ export interface ParseUrlStringOptions extends ProgressOptions<ResolveProgressEv
16
17
 
17
18
  }
18
19
 
20
+ export interface ParsedUrlQuery extends Record<string, string | unknown> {
21
+ format?: RequestFormatShorthand
22
+ }
23
+
19
24
  export interface ParsedUrlStringResults {
20
25
  protocol: string
21
26
  path: string
22
27
  cid: CID
23
- query: Record<string, string>
28
+ query: ParsedUrlQuery
24
29
  }
25
30
 
26
31
  const URL_REGEX = /^(?<protocol>ip[fn]s):\/\/(?<cidOrPeerIdOrDnsLink>[^/$?]+)\/?(?<path>[^$?]*)\??(?<queryString>.*)$/
@@ -1,19 +1,20 @@
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 { getETag } from './utils/get-e-tag.js'
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
+ import type { RequestFormatShorthand } from './types.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'
@@ -23,18 +24,14 @@ interface VerifiedFetchComponents {
23
24
  helia: Helia
24
25
  ipns?: IPNS
25
26
  unixfs?: HeliaUnixFs
26
- dagJson?: DAGJSON
27
- json?: JSON
28
- dagCbor?: DAGCBOR
29
27
  pathWalker?: PathWalkerFn
30
28
  }
31
29
 
32
30
  /**
33
31
  * Potential future options for the VerifiedFetch constructor.
34
32
  */
35
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
36
33
  interface VerifiedFetchInit {
37
-
34
+ contentTypeParser?: ContentTypeParser
38
35
  }
39
36
 
40
37
  interface FetchHandlerFunctionArg {
@@ -63,17 +60,29 @@ function convertOptions (options?: VerifiedFetchOptions): (Omit<VerifiedFetchOpt
63
60
  }
64
61
  }
65
62
 
63
+ function okResponse (body?: BodyInit | null): Response {
64
+ return new Response(body, {
65
+ status: 200,
66
+ statusText: 'OK'
67
+ })
68
+ }
69
+
70
+ function notSupportedResponse (body?: BodyInit | null): Response {
71
+ return new Response(body, {
72
+ status: 501,
73
+ statusText: 'Not Implemented'
74
+ })
75
+ }
76
+
66
77
  export class VerifiedFetch {
67
78
  private readonly helia: Helia
68
79
  private readonly ipns: IPNS
69
80
  private readonly unixfs: HeliaUnixFs
70
- private readonly dagJson: DAGJSON
71
- private readonly dagCbor: DAGCBOR
72
- private readonly json: JSON
73
81
  private readonly pathWalker: PathWalkerFn
74
82
  private readonly log: Logger
83
+ private readonly contentTypeParser: ContentTypeParser | undefined
75
84
 
76
- constructor ({ helia, ipns, unixfs, dagJson, json, dagCbor, pathWalker }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
85
+ constructor ({ helia, ipns, unixfs, pathWalker }: VerifiedFetchComponents, init?: VerifiedFetchInit) {
77
86
  this.helia = helia
78
87
  this.log = helia.logger.forComponent('helia:verified-fetch')
79
88
  this.ipns = ipns ?? heliaIpns(helia, {
@@ -83,63 +92,55 @@ export class VerifiedFetch {
83
92
  ]
84
93
  })
85
94
  this.unixfs = unixfs ?? heliaUnixFs(helia)
86
- this.dagJson = dagJson ?? heliaDagJson(helia)
87
- this.json = json ?? heliaJson(helia)
88
- this.dagCbor = dagCbor ?? heliaDagCbor(helia)
89
95
  this.pathWalker = pathWalker ?? walkPath
96
+ this.contentTypeParser = init?.contentTypeParser
90
97
  this.log.trace('created VerifiedFetch instance')
91
98
  }
92
99
 
93
100
  // handle vnd.ipfs.ipns-record
94
101
  private async handleIPNSRecord ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
95
- const response = new Response('vnd.ipfs.ipns-record support is not implemented', { status: 501 })
102
+ const response = notSupportedResponse('vnd.ipfs.ipns-record support is not implemented')
96
103
  response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
97
104
  return response
98
105
  }
99
106
 
100
107
  // handle vnd.ipld.car
101
108
  private async handleIPLDCar ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
102
- const response = new Response('vnd.ipld.car support is not implemented', { status: 501 })
109
+ const response = notSupportedResponse('vnd.ipld.car support is not implemented')
103
110
  response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
104
111
  return response
105
112
  }
106
113
 
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
114
  private async handleJson ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
121
115
  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, {
116
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
117
+ const result = await this.helia.blockstore.get(cid, {
124
118
  signal: options?.signal,
125
119
  onProgress: options?.onProgress
126
120
  })
127
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: cid.toString(), path }))
128
- const response = new Response(JSON.stringify(result), { status: 200 })
121
+ const response = okResponse(result)
129
122
  response.headers.set('content-type', 'application/json')
123
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
130
124
  return response
131
125
  }
132
126
 
133
127
  private async handleDagCbor ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
134
128
  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')
129
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
130
+ // return body as binary
131
+ const block = await this.helia.blockstore.get(cid)
132
+ let body: string | Uint8Array
133
+
134
+ try {
135
+ body = dagCborToSafeJSON(block)
136
+ } catch (err) {
137
+ this.log('could not decode DAG-CBOR as JSON-safe, falling back to `application/octet-stream`', err)
138
+ body = block
139
+ }
140
+
141
+ const response = okResponse(body)
142
+ response.headers.set('content-type', body instanceof Uint8Array ? 'application/octet-stream' : 'application/json')
143
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
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.toString(), path: rootFilePath }))
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,
@@ -165,47 +166,75 @@ export class VerifiedFetch {
165
166
  // terminalElement = stat
166
167
  } catch (err: any) {
167
168
  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 })
169
+ return notSupportedResponse('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented')
169
170
  } finally {
170
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid.toString(), path: rootFilePath }))
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.toString(), path: '' }))
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.toString(), path: '' }))
180
180
  this.log('got async iterator for %c/%s', cid, path)
181
181
 
182
- const { contentType, stream } = await getStreamAndContentType(asyncIter, path ?? '', this.helia.logger, {
182
+ const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, {
183
183
  onProgress: options?.onProgress
184
184
  })
185
- const response = new Response(stream, { status: 200 })
186
- response.headers.set('content-type', contentType)
185
+ const response = okResponse(stream)
186
+ await this.setContentType(firstChunk, path, response)
187
+
188
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: resolvedCID, path: '' }))
187
189
 
188
190
  return response
189
191
  }
190
192
 
191
193
  private async handleRaw ({ cid, path, options }: FetchHandlerFunctionArg): Promise<Response> {
192
194
  this.log.trace('fetching %c/%s', cid, path)
193
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid: cid.toString(), path }))
195
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:start', { cid, path }))
194
196
  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')
197
+ const response = okResponse(result)
198
+ await this.setContentType(result, path, response)
199
+
200
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid, path }))
198
201
  return response
199
202
  }
200
203
 
204
+ private async setContentType (bytes: Uint8Array, path: string, response: Response): Promise<void> {
205
+ let contentType = 'application/octet-stream'
206
+
207
+ if (this.contentTypeParser != null) {
208
+ try {
209
+ let fileName = path.split('/').pop()?.trim()
210
+ fileName = fileName === '' ? undefined : fileName
211
+ const parsed = this.contentTypeParser(bytes, fileName)
212
+
213
+ if (isPromise(parsed)) {
214
+ const result = await parsed
215
+
216
+ if (result != null) {
217
+ contentType = result
218
+ }
219
+ } else if (parsed != null) {
220
+ contentType = parsed
221
+ }
222
+ } catch (err) {
223
+ this.log.error('Error parsing content type', err)
224
+ }
225
+ }
226
+
227
+ response.headers.set('content-type', contentType)
228
+ }
229
+
201
230
  /**
202
231
  * Determines the format requested by the client, defaults to `null` if no format is requested.
203
232
  *
204
233
  * @see https://specs.ipfs.tech/http-gateways/path-gateway/#format-request-query-parameter
205
234
  * @default 'raw'
206
235
  */
207
- private getFormat ({ headerFormat, queryFormat }: { headerFormat: string | null, queryFormat: string | null }): string | null {
208
- const formatMap: Record<string, string> = {
236
+ private getFormat ({ headerFormat, queryFormat }: { headerFormat: string | null, queryFormat: RequestFormatShorthand | null }): RequestFormatShorthand | null {
237
+ const formatMap: Record<string, RequestFormatShorthand> = {
209
238
  'vnd.ipld.raw': 'raw',
210
239
  'vnd.ipld.car': 'car',
211
240
  'application/x-tar': 'tar',
@@ -234,22 +263,23 @@ export class VerifiedFetch {
234
263
  * These format handlers should adjust the response headers as specified in https://specs.ipfs.tech/http-gateways/path-gateway/#response-headers
235
264
  */
236
265
  private readonly formatHandlers: Record<string, FetchHandlerFunction> = {
237
- raw: async () => new Response('application/vnd.ipld.raw support is not implemented', { status: 501 }),
266
+ raw: async () => notSupportedResponse('application/vnd.ipld.raw support is not implemented'),
238
267
  car: this.handleIPLDCar,
239
268
  '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 })
269
+ tar: async () => notSupportedResponse('application/x-tar support is not implemented'),
270
+ 'dag-json': async () => notSupportedResponse('application/vnd.ipld.dag-json support is not implemented'),
271
+ 'dag-cbor': async () => notSupportedResponse('application/vnd.ipld.dag-cbor support is not implemented'),
272
+ json: async () => notSupportedResponse('application/json support is not implemented'),
273
+ cbor: async () => notSupportedResponse('application/cbor support is not implemented')
245
274
  }
246
275
 
247
276
  private readonly codecHandlers: Record<number, FetchHandlerFunction> = {
248
- [dagJsonCode]: this.handleDagJson,
249
277
  [dagPbCode]: this.handleDagPb,
278
+ [dagJsonCode]: this.handleJson,
250
279
  [jsonCode]: this.handleJson,
251
280
  [dagCborCode]: this.handleDagCbor,
252
- [rawCode]: this.handleRaw
281
+ [rawCode]: this.handleRaw,
282
+ [identity.code]: this.handleRaw
253
283
  }
254
284
 
255
285
  async fetch (resource: Resource, opts?: VerifiedFetchOptions): Promise<Response> {
@@ -291,12 +321,12 @@ export class VerifiedFetch {
291
321
  if (codecHandler != null) {
292
322
  response = await codecHandler.call(this, { cid, path, options, terminalElement })
293
323
  } 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 })
324
+ 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
325
  }
296
326
  }
297
327
 
298
- 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')
328
+ response.headers.set('etag', getETag({ cid, reqFormat: format ?? undefined, weak: false }))
329
+ response.headers.set('cache-control', 'public, max-age=29030400, immutable')
300
330
  response.headers.set('X-Ipfs-Path', resource.toString()) // https://specs.ipfs.tech/http-gateways/path-gateway/#x-ipfs-path-response-header
301
331
 
302
332
  if (ipfsRoots != null) {
@@ -321,3 +351,7 @@ export class VerifiedFetch {
321
351
  await this.helia.stop()
322
352
  }
323
353
  }
354
+
355
+ function isPromise <T> (p?: any): p is Promise<T> {
356
+ return p?.then != null
357
+ }
@@ -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
- }