@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.
Files changed (31) hide show
  1. package/README.md +80 -43
  2. package/dist/index.min.js +4 -29
  3. package/dist/src/index.d.ts +114 -52
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +87 -48
  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/get-stream-from-async-iterable.d.ts +10 -0
  12. package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +1 -0
  13. package/dist/src/utils/{get-stream-and-content-type.js → get-stream-from-async-iterable.js} +10 -9
  14. package/dist/src/utils/get-stream-from-async-iterable.js.map +1 -0
  15. package/dist/src/verified-fetch.d.ts +4 -1
  16. package/dist/src/verified-fetch.d.ts.map +1 -1
  17. package/dist/src/verified-fetch.js +75 -33
  18. package/dist/src/verified-fetch.js.map +1 -1
  19. package/package.json +19 -14
  20. package/src/index.ts +120 -53
  21. package/src/singleton.ts +20 -0
  22. package/src/utils/{get-stream-and-content-type.ts → get-stream-from-async-iterable.ts} +9 -8
  23. package/src/verified-fetch.ts +84 -37
  24. package/dist/src/utils/get-content-type.d.ts +0 -11
  25. package/dist/src/utils/get-content-type.d.ts.map +0 -1
  26. package/dist/src/utils/get-content-type.js +0 -43
  27. package/dist/src/utils/get-content-type.js.map +0 -1
  28. package/dist/src/utils/get-stream-and-content-type.d.ts +0 -10
  29. package/dist/src/utils/get-stream-and-content-type.d.ts.map +0 -1
  30. package/dist/src/utils/get-stream-and-content-type.js.map +0 -1
  31. package/src/utils/get-content-type.ts +0 -55
@@ -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 { getStreamAndContentType } from './utils/get-stream-and-content-type.js'
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 = new Response('vnd.ipfs.ipns-record support is not implemented', { status: 501 })
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 = new Response('vnd.ipld.car support is not implemented', { status: 501 })
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: cid.toString(), path }))
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: cid.toString(), path }))
115
- const response = new Response(JSON.stringify(result), { status: 200 })
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: cid.toString(), path }))
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: cid.toString(), path }))
128
- const response = new Response(JSON.stringify(result), { status: 200 })
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: cid.toString(), path }))
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: cid.toString(), path }))
141
- const response = new Response(JSON.stringify(result), { status: 200 })
142
- response.headers.set('content-type', 'application/json')
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.toString(), path: rootFilePath }))
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 new Response('Unable to find index.html for directory at given path. Support for directories with implicit root is not implemented', { status: 501 })
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.toString(), path: rootFilePath }))
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.toString(), path: '' }))
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.toString(), path: '' }))
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 { contentType, stream } = await getStreamAndContentType(asyncIter, path ?? '', this.helia.logger, {
198
+ const { stream, firstChunk } = await getStreamFromAsyncIterable(asyncIter, path ?? '', this.helia.logger, {
183
199
  onProgress: options?.onProgress
184
200
  })
185
- const response = new Response(stream, { status: 200 })
186
- response.headers.set('content-type', contentType)
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: cid.toString(), path }))
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: cid.toString(), path }))
196
- const response = new Response(decode(result), { status: 200 })
197
- response.headers.set('content-type', 'application/octet-stream')
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 () => new Response('application/vnd.ipld.raw support is not implemented', { status: 501 }),
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 () => 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 })
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 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 })
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
- }