@helia/verified-fetch 5.1.1 → 6.0.0

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 (54) hide show
  1. package/dist/index.min.js +52 -52
  2. package/dist/index.min.js.map +4 -4
  3. package/dist/src/index.d.ts +20 -4
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js.map +1 -1
  6. package/dist/src/plugins/plugin-handle-unixfs.d.ts.map +1 -1
  7. package/dist/src/plugins/plugin-handle-unixfs.js +6 -6
  8. package/dist/src/plugins/plugin-handle-unixfs.js.map +1 -1
  9. package/dist/src/url-resolver.d.ts.map +1 -1
  10. package/dist/src/url-resolver.js +1 -2
  11. package/dist/src/url-resolver.js.map +1 -1
  12. package/dist/src/utils/error-to-response.d.ts +1 -1
  13. package/dist/src/utils/error-to-response.d.ts.map +1 -1
  14. package/dist/src/utils/error-to-response.js +13 -11
  15. package/dist/src/utils/error-to-response.js.map +1 -1
  16. package/dist/src/utils/get-range-header.d.ts +2 -1
  17. package/dist/src/utils/get-range-header.d.ts.map +1 -1
  18. package/dist/src/utils/get-range-header.js.map +1 -1
  19. package/dist/src/utils/parse-resource.d.ts +10 -0
  20. package/dist/src/utils/parse-resource.d.ts.map +1 -0
  21. package/dist/src/utils/parse-resource.js +52 -0
  22. package/dist/src/utils/parse-resource.js.map +1 -0
  23. package/dist/src/utils/responses.d.ts +14 -14
  24. package/dist/src/utils/responses.d.ts.map +1 -1
  25. package/dist/src/utils/responses.js +8 -3
  26. package/dist/src/utils/responses.js.map +1 -1
  27. package/dist/src/verified-fetch.d.ts +1 -0
  28. package/dist/src/verified-fetch.d.ts.map +1 -1
  29. package/dist/src/verified-fetch.js +94 -101
  30. package/dist/src/verified-fetch.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/index.ts +20 -4
  33. package/src/plugins/plugin-handle-unixfs.ts +9 -8
  34. package/src/url-resolver.ts +1 -2
  35. package/src/utils/error-to-response.ts +14 -11
  36. package/src/utils/get-range-header.ts +2 -1
  37. package/src/utils/parse-resource.ts +62 -0
  38. package/src/utils/responses.ts +24 -19
  39. package/src/verified-fetch.ts +105 -109
  40. package/dist/src/utils/ipfs-path-to-url.d.ts +0 -16
  41. package/dist/src/utils/ipfs-path-to-url.d.ts.map +0 -1
  42. package/dist/src/utils/ipfs-path-to-url.js +0 -45
  43. package/dist/src/utils/ipfs-path-to-url.js.map +0 -1
  44. package/dist/src/utils/parse-url-string.d.ts +0 -23
  45. package/dist/src/utils/parse-url-string.d.ts.map +0 -1
  46. package/dist/src/utils/parse-url-string.js +0 -120
  47. package/dist/src/utils/parse-url-string.js.map +0 -1
  48. package/dist/src/utils/resource-to-cache-key.d.ts +0 -15
  49. package/dist/src/utils/resource-to-cache-key.d.ts.map +0 -1
  50. package/dist/src/utils/resource-to-cache-key.js +0 -27
  51. package/dist/src/utils/resource-to-cache-key.js.map +0 -1
  52. package/src/utils/ipfs-path-to-url.ts +0 -54
  53. package/src/utils/parse-url-string.ts +0 -165
  54. package/src/utils/resource-to-cache-key.ts +0 -30
@@ -1,8 +1,9 @@
1
1
  import itToBrowserReadableStream from 'it-to-browser-readablestream'
2
2
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
3
+ import { errorToObject } from './error-to-object.ts'
3
4
  import { rangeToOffsetAndLength } from './get-offset-and-length.ts'
4
5
  import { getContentRangeHeader } from './response-headers.ts'
5
- import type { SupportedBodyTypes, ContentType } from '../index.js'
6
+ import type { SupportedBodyTypes, ContentType, Resource } from '../index.js'
6
7
  import type { Range, RangeHeader } from './get-range-header.ts'
7
8
 
8
9
  function setField (response: Response, name: string, value: string | boolean): void {
@@ -20,7 +21,7 @@ function setType (response: Response, value: 'basic' | 'cors' | 'error' | 'opaqu
20
21
  }
21
22
  }
22
23
 
23
- function setUrl (response: Response, value: string | URL): void {
24
+ function setUrl (response: Response, value: Resource): void {
24
25
  value = value.toString()
25
26
  const fragmentStart = value.indexOf('#')
26
27
 
@@ -41,7 +42,7 @@ export interface ResponseOptions extends ResponseInit {
41
42
  redirected?: boolean
42
43
  }
43
44
 
44
- export function okResponse (url: string, body?: SupportedBodyTypes, init?: ResponseOptions): Response {
45
+ export function okResponse (url: Resource, body?: SupportedBodyTypes, init?: ResponseOptions): Response {
45
46
  const response = new Response(body, {
46
47
  ...(init ?? {}),
47
48
  status: 200,
@@ -58,13 +59,17 @@ export function okResponse (url: string, body?: SupportedBodyTypes, init?: Respo
58
59
  return response
59
60
  }
60
61
 
61
- export function internalServerErrorResponse (url: string, body?: SupportedBodyTypes, init?: ResponseInit): Response {
62
- const response = new Response(body, {
62
+ export function internalServerErrorResponse (url: Resource, err: Error, init?: ResponseInit): Response {
63
+ const response = new Response(JSON.stringify({
64
+ error: errorToObject(err)
65
+ }), {
63
66
  ...(init ?? {}),
64
67
  status: 500,
65
68
  statusText: 'Internal Server Error'
66
69
  })
67
70
  response.headers.set('X-Content-Type-Options', 'nosniff') // see https://specs.ipfs.tech/http-gateways/path-gateway/#x-content-type-options-response-header
71
+ response.headers.set('content-type', 'application/json')
72
+ response.headers.set('x-error-message', btoa(err.message))
68
73
 
69
74
  setType(response, 'basic')
70
75
  setUrl(response, url)
@@ -75,7 +80,7 @@ export function internalServerErrorResponse (url: string, body?: SupportedBodyTy
75
80
  /**
76
81
  * A 504 Gateway Timeout for when a request made to an upstream server timed out
77
82
  */
78
- export function gatewayTimeoutResponse (url: string, body?: SupportedBodyTypes, init?: ResponseInit): Response {
83
+ export function gatewayTimeoutResponse (url: Resource, body?: SupportedBodyTypes, init?: ResponseInit): Response {
79
84
  const response = new Response(body, {
80
85
  ...(init ?? {}),
81
86
  status: 504,
@@ -92,7 +97,7 @@ export function gatewayTimeoutResponse (url: string, body?: SupportedBodyTypes,
92
97
  * A 502 Bad Gateway is for when an invalid response was received from an
93
98
  * upstream server.
94
99
  */
95
- export function badGatewayResponse (url: string, body?: SupportedBodyTypes, init?: ResponseInit): Response {
100
+ export function badGatewayResponse (url: Resource, body?: SupportedBodyTypes, init?: ResponseInit): Response {
96
101
  const response = new Response(body, {
97
102
  ...(init ?? {}),
98
103
  status: 502,
@@ -105,7 +110,7 @@ export function badGatewayResponse (url: string, body?: SupportedBodyTypes, init
105
110
  return response
106
111
  }
107
112
 
108
- export function notImplementedResponse (url: string, body?: SupportedBodyTypes, init?: ResponseInit): Response {
113
+ export function notImplementedResponse (url: Resource, body?: SupportedBodyTypes, init?: ResponseInit): Response {
109
114
  const response = new Response(body, {
110
115
  ...(init ?? {}),
111
116
  status: 501,
@@ -119,7 +124,7 @@ export function notImplementedResponse (url: string, body?: SupportedBodyTypes,
119
124
  return response
120
125
  }
121
126
 
122
- export function notAcceptableResponse (url: string | URL, requested: Array<Pick<ContentType, 'mediaType'>>, acceptable: Array<Pick<ContentType, 'mediaType'>>, init?: ResponseInit): Response {
127
+ export function notAcceptableResponse (url: Resource, requested: Array<Pick<ContentType, 'mediaType'>>, acceptable: Array<Pick<ContentType, 'mediaType'>>, init?: ResponseInit): Response {
123
128
  const headers = new Headers(init?.headers)
124
129
  headers.set('content-type', 'application/json')
125
130
 
@@ -139,7 +144,7 @@ export function notAcceptableResponse (url: string | URL, requested: Array<Pick<
139
144
  return response
140
145
  }
141
146
 
142
- export function notFoundResponse (url: string, body?: SupportedBodyTypes, init?: ResponseInit): Response {
147
+ export function notFoundResponse (url: Resource, body?: SupportedBodyTypes, init?: ResponseInit): Response {
143
148
  const response = new Response(body, {
144
149
  ...(init ?? {}),
145
150
  status: 404,
@@ -156,7 +161,7 @@ function isArrayOfErrors (body: unknown | Error | Error[]): body is Error[] {
156
161
  return Array.isArray(body) && body.every(e => e instanceof Error)
157
162
  }
158
163
 
159
- export function badRequestResponse (url: string, errors: Error | Error[], init?: ResponseInit): Response {
164
+ export function badRequestResponse (url: Resource, errors: Error | Error[], init?: ResponseInit): Response {
160
165
  // stacktrace of the single error, or the stacktrace of the last error in the array
161
166
  let stack: string | undefined
162
167
  let convertedErrors: Array<{ message: string, stack: string }> | undefined
@@ -190,7 +195,7 @@ export function badRequestResponse (url: string, errors: Error | Error[], init?:
190
195
  return response
191
196
  }
192
197
 
193
- export function movedPermanentlyResponse (url: string, location: string, init?: ResponseInit): Response {
198
+ export function movedPermanentlyResponse (url: Resource, location: string, init?: ResponseInit): Response {
194
199
  const response = new Response(null, {
195
200
  ...(init ?? {}),
196
201
  status: 301,
@@ -207,7 +212,7 @@ export function movedPermanentlyResponse (url: string, location: string, init?:
207
212
  return response
208
213
  }
209
214
 
210
- export function notModifiedResponse (url: string, headers: Headers, init?: ResponseInit): Response {
215
+ export function notModifiedResponse (url: Resource, headers: Headers, init?: ResponseInit): Response {
211
216
  const response = new Response(null, {
212
217
  ...(init ?? {}),
213
218
  status: 304,
@@ -248,7 +253,7 @@ export interface PartialContent {
248
253
  (offset: number, length: number): AsyncGenerator<Uint8Array>
249
254
  }
250
255
 
251
- export function partialContentResponse (url: string, getSlice: PartialContent, range: RangeHeader, documentSize: number | bigint, init?: ResponseOptions): Response {
256
+ export function partialContentResponse (url: Resource, getSlice: PartialContent, range: RangeHeader, documentSize: number | bigint, init?: ResponseOptions): Response {
252
257
  let response: Response
253
258
 
254
259
  if (range.ranges.length === 1) {
@@ -269,7 +274,7 @@ export function partialContentResponse (url: string, getSlice: PartialContent, r
269
274
  return response
270
275
  }
271
276
 
272
- function singleRangeResponse (url: string, getSlice: PartialContent, range: Range, documentSize: number | bigint, init?: ResponseOptions): Response {
277
+ function singleRangeResponse (url: Resource, getSlice: PartialContent, range: Range, documentSize: number | bigint, init?: ResponseOptions): Response {
273
278
  try {
274
279
  // create headers object with any initial headers from init
275
280
  const headers = new Headers(init?.headers)
@@ -293,14 +298,14 @@ function singleRangeResponse (url: string, getSlice: PartialContent, range: Rang
293
298
  return notSatisfiableResponse(url, documentSize, init)
294
299
  }
295
300
 
296
- return internalServerErrorResponse(url, '', init)
301
+ return internalServerErrorResponse(url, err, init)
297
302
  }
298
303
  }
299
304
 
300
305
  /**
301
306
  * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests
302
307
  */
303
- function multiRangeResponse (url: string, getSlice: PartialContent, range: RangeHeader, documentSize: number | bigint, init?: ResponseOptions): Response {
308
+ function multiRangeResponse (url: Resource, getSlice: PartialContent, range: RangeHeader, documentSize: number | bigint, init?: ResponseOptions): Response {
304
309
  // create headers object with any initial headers from init
305
310
  const headers = new Headers(init?.headers)
306
311
 
@@ -375,7 +380,7 @@ function multiRangeResponse (url: string, getSlice: PartialContent, range: Range
375
380
  * - The range is invalid
376
381
  * - The range is not supported for the given type
377
382
  */
378
- export function notSatisfiableResponse (url: string, documentSize?: number | bigint | string, init?: ResponseInit): Response {
383
+ export function notSatisfiableResponse (url: Resource, documentSize?: number | bigint | string, init?: ResponseInit): Response {
379
384
  const headers = new Headers(init?.headers)
380
385
 
381
386
  if (documentSize != null) {
@@ -402,7 +407,7 @@ export function notSatisfiableResponse (url: string, documentSize?: number | big
402
407
  *
403
408
  * @see https://specs.ipfs.tech/http-gateways/path-gateway/#412-precondition-failed
404
409
  */
405
- export function preconditionFailedResponse (url: string, init?: ResponseInit): Response {
410
+ export function preconditionFailedResponse (url: Resource, init?: ResponseInit): Response {
406
411
  const headers = new Headers(init?.headers)
407
412
 
408
413
  const response = new Response('Precondition Failed', {
@@ -1,6 +1,6 @@
1
1
  import { dnsLink } from '@helia/dnslink'
2
2
  import { ipnsResolver } from '@helia/ipns'
3
- import { AbortError } from '@libp2p/interface'
3
+ import { AbortError, isPeerId, isPublicKey } from '@libp2p/interface'
4
4
  import { CID } from 'multiformats/cid'
5
5
  import { CustomProgressEvent } from 'progress-events'
6
6
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
@@ -17,11 +17,11 @@ import { errorToObject } from './utils/error-to-object.ts'
17
17
  import { errorToResponse } from './utils/error-to-response.ts'
18
18
  import { getETag, ifNoneMatches } from './utils/get-e-tag.js'
19
19
  import { getRangeHeader } from './utils/get-range-header.ts'
20
- import { parseURLString } from './utils/parse-url-string.ts'
20
+ import { stringToIpfsUrl } from './utils/parse-resource.ts'
21
21
  import { setCacheControlHeader } from './utils/response-headers.js'
22
- import { badRequestResponse, internalServerErrorResponse, notAcceptableResponse, notImplementedResponse, notModifiedResponse } from './utils/responses.js'
22
+ import { internalServerErrorResponse, notAcceptableResponse, notImplementedResponse, notModifiedResponse } from './utils/responses.js'
23
23
  import { ServerTiming } from './utils/server-timing.js'
24
- import type { AcceptHeader, CIDDetail, ContentTypeParser, CreateVerifiedFetchOptions, ResolveURLResult, Resource, ResourceDetail, VerifiedFetchInit as VerifiedFetchOptions, VerifiedFetchPlugin, PluginContext, PluginOptions } from './index.js'
24
+ import type { AcceptHeader, CIDDetail, ContentTypeParser, CreateVerifiedFetchOptions, Resource, ResourceDetail, VerifiedFetchInit as VerifiedFetchOptions, VerifiedFetchPlugin, PluginContext, PluginOptions } from './index.js'
25
25
  import type { DNSLink } from '@helia/dnslink'
26
26
  import type { Helia } from '@helia/interface'
27
27
  import type { IPNSResolver } from '@helia/ipns'
@@ -138,127 +138,117 @@ export class VerifiedFetch {
138
138
  async fetch (resource: Resource, opts?: VerifiedFetchOptions): Promise<Response> {
139
139
  this.log('fetch %s %s', opts?.method ?? 'GET', resource)
140
140
 
141
- if (opts?.method === 'OPTIONS') {
142
- return this.handleFinalResponse(new Response(null, {
143
- status: 200
144
- }))
145
- }
146
-
147
- const options = convertOptions(opts)
148
- const headers = new Headers(options?.headers)
149
- const serverTiming = new ServerTiming()
150
-
151
- options?.onProgress?.(new CustomProgressEvent<ResourceDetail>('verified-fetch:request:start', { resource }))
141
+ try {
142
+ if (opts?.method === 'OPTIONS') {
143
+ return this.handleFinalResponse(new Response(null, {
144
+ status: 200
145
+ }))
146
+ }
152
147
 
153
- const range = getRangeHeader(resource.toString(), headers)
148
+ const options = convertOptions(opts)
149
+ const headers = new Headers(options?.headers)
150
+ const serverTiming = new ServerTiming()
154
151
 
155
- if (range instanceof Response) {
156
- // invalid range request
157
- return this.handleFinalResponse(range)
158
- }
152
+ options?.onProgress?.(new CustomProgressEvent<ResourceDetail>('verified-fetch:request:start', { resource }))
159
153
 
160
- let url: URL
154
+ const range = getRangeHeader(resource, headers)
161
155
 
162
- try {
163
- url = parseURLString(typeof resource === 'string' ? resource : `ipfs://${resource}`)
164
- } catch (err: any) {
165
- return this.handleFinalResponse(badRequestResponse(resource.toString(), err))
166
- }
156
+ if (range instanceof Response) {
157
+ // invalid range request
158
+ return this.handleFinalResponse(range)
159
+ }
167
160
 
168
- if (url.protocol === 'ipfs:' && url.pathname === '') {
169
- // if we don't need to resolve an IPNS names or traverse a DAG, we can
170
- // check the if-none-match header and maybe return a 304 without needing
171
- // to load any blocks
172
- if (ifNoneMatches(`"${url.hostname}"`, headers)) {
173
- return notModifiedResponse(resource.toString(), new Headers({
174
- etag: `"${url.hostname}"`,
175
- 'cache-control': 'public, max-age=29030400, immutable'
176
- }))
161
+ const url = this.parseResource(resource)
162
+
163
+ if (url.protocol === 'ipfs:' && url.pathname === '') {
164
+ // if we don't need to resolve an IPNS names or traverse a DAG, we can
165
+ // check the if-none-match header and maybe return a 304 without needing
166
+ // to load any blocks
167
+ if (ifNoneMatches(`"${url.hostname}"`, headers)) {
168
+ return notModifiedResponse(resource, new Headers({
169
+ etag: `"${url.hostname}"`,
170
+ 'cache-control': 'public, max-age=29030400, immutable'
171
+ }))
172
+ }
177
173
  }
178
- }
179
174
 
180
- const requestedMimeTypes = getRequestedMimeTypes(url, headers.get('accept'))
175
+ const requestedMimeTypes = getRequestedMimeTypes(url, headers.get('accept'))
181
176
 
182
- let parsedResult: ResolveURLResult
177
+ // if a raw IPNS record has been requested, don't try to load the block
178
+ // the record points to or do any recursive IPNS resolving
179
+ if (isIPNSRecordRequest(headers)) {
180
+ if (url.protocol !== 'ipns:') {
181
+ return notAcceptableResponse(url, requestedMimeTypes, [
182
+ CONTENT_TYPE_IPNS
183
+ ])
184
+ }
183
185
 
184
- // if just an IPNS record has been requested, don't try to load the block
185
- // the record points to or do any recursive IPNS resolving
186
- if (isIPNSRecordRequest(headers)) {
187
- if (url.protocol !== 'ipns:') {
188
- return notAcceptableResponse(url, requestedMimeTypes, [
189
- CONTENT_TYPE_IPNS
190
- ])
191
- }
186
+ // @ts-expect-error ipnsRecordPlugin may not be of type IpnsRecordPlugin
187
+ const ipnsRecordPlugin: IpnsRecordPlugin | undefined = this.plugins.find(plugin => plugin.id === 'ipns-record-plugin')
192
188
 
193
- // @ts-expect-error ipnsRecordPlugin may not be of type IpnsRecordPlugin
194
- const ipnsRecordPlugin: IpnsRecordPlugin | undefined = this.plugins.find(plugin => plugin.id === 'ipns-record-plugin')
189
+ if (ipnsRecordPlugin == null) {
190
+ // IPNS record was requested but no IPNS Record plugin is configured?!
191
+ return notAcceptableResponse(resource, requestedMimeTypes, [])
192
+ }
195
193
 
196
- if (ipnsRecordPlugin == null) {
197
- // IPNS record was requested but no IPNS Record plugin is configured?!
198
- return notAcceptableResponse(url, requestedMimeTypes, [])
194
+ return this.handleFinalResponse(await ipnsRecordPlugin.handle({
195
+ range,
196
+ url,
197
+ resource,
198
+ options
199
+ }))
199
200
  }
200
201
 
201
- return this.handleFinalResponse(await ipnsRecordPlugin.handle({
202
- range,
203
- url,
204
- resource: resource.toString(),
205
- options
206
- }))
207
- } else {
208
- try {
209
- parsedResult = await this.urlResolver.resolve(url, serverTiming, {
210
- ...options,
211
- isRawBlockRequest: isRawBlockRequest(headers),
212
- onlyIfCached: headers.get('cache-control') === 'only-if-cached'
213
- })
214
- } catch (err: any) {
215
- options?.signal?.throwIfAborted()
202
+ const resolveResult = await this.urlResolver.resolve(url, serverTiming, {
203
+ ...options,
204
+ isRawBlockRequest: isRawBlockRequest(headers),
205
+ onlyIfCached: headers.get('cache-control') === 'only-if-cached'
206
+ })
216
207
 
217
- this.log.error('error parsing resource %s - %e', resource, err)
218
- return this.handleFinalResponse(errorToResponse(resource, err))
219
- }
220
- }
208
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:resolve', {
209
+ cid: resolveResult.terminalElement.cid,
210
+ path: resolveResult.url.pathname
211
+ }))
221
212
 
222
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:resolve', {
223
- cid: parsedResult.terminalElement.cid,
224
- path: parsedResult.url.pathname
225
- }))
213
+ const accept = this.getAcceptHeader(resolveResult.url, requestedMimeTypes, resolveResult.terminalElement.cid)
226
214
 
227
- const accept = this.getAcceptHeader(parsedResult.url, requestedMimeTypes, parsedResult.terminalElement.cid)
215
+ if (accept instanceof Response) {
216
+ this.log('allowed media types for requested CID did not contain anything the client can understand')
228
217
 
229
- if (accept instanceof Response) {
230
- this.log('allowed media types for requested CID did not contain anything the client can understand')
218
+ // invalid accept header
219
+ return this.handleFinalResponse(accept)
220
+ }
231
221
 
232
- // invalid accept header
233
- return this.handleFinalResponse(accept)
234
- }
222
+ const context: PluginContext = {
223
+ ...resolveResult,
224
+ resource,
225
+ accept,
226
+ range,
227
+ options,
228
+ onProgress: options?.onProgress,
229
+ serverTiming,
230
+ headers,
231
+ requestedMimeTypes
232
+ }
235
233
 
236
- const context: PluginContext = {
237
- ...parsedResult,
238
- resource: resource.toString(),
239
- accept,
240
- range,
241
- options,
242
- onProgress: options?.onProgress,
243
- serverTiming,
244
- headers,
245
- requestedMimeTypes
246
- }
234
+ this.log.trace('finding handler for cid code "0x%s" and response content types %s', resolveResult.terminalElement.cid.code.toString(16), accept.map(header => header.contentType.mediaType).join(', '))
247
235
 
248
- this.log.trace('finding handler for cid code "0x%s" and response content types %s', parsedResult.terminalElement.cid.code.toString(16), accept.map(header => header.contentType.mediaType).join(', '))
236
+ const response = await this.runPluginPipeline(context)
249
237
 
250
- const response = await this.runPluginPipeline(context)
238
+ options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', {
239
+ cid: resolveResult.terminalElement.cid,
240
+ path: resolveResult.url.pathname
241
+ }))
251
242
 
252
- options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', {
253
- cid: parsedResult.terminalElement.cid,
254
- path: parsedResult.url.pathname
255
- }))
243
+ if (response == null) {
244
+ this.log.error('no plugin could handle request for %s', resource)
245
+ }
256
246
 
257
- if (response == null) {
258
- this.log.error('no plugin could handle request for %s', resource)
247
+ return this.handleFinalResponse(response, Boolean(options?.withServerTiming) || Boolean(this.withServerTiming), context)
248
+ } catch (err: any) {
249
+ this.log.error('error fetching resource %s - %e', resource, err)
250
+ return this.handleFinalResponse(errorToResponse(resource, err, opts))
259
251
  }
260
-
261
- return this.handleFinalResponse(response, Boolean(options?.withServerTiming) || Boolean(this.withServerTiming), context)
262
252
  }
263
253
 
264
254
  /**
@@ -315,6 +305,18 @@ export class VerifiedFetch {
315
305
  return acceptable
316
306
  }
317
307
 
308
+ private parseResource (resource: Resource): URL {
309
+ if (isPeerId(resource) || isPublicKey(resource)) {
310
+ resource = `/ipns/${resource}`
311
+ }
312
+
313
+ if (CID.asCID(resource) === resource || resource instanceof CID) {
314
+ resource = `/ipfs/${resource}`
315
+ }
316
+
317
+ return new URL(stringToIpfsUrl(resource.toString()))
318
+ }
319
+
318
320
  /**
319
321
  * The last place a Response touches in verified-fetch before being returned
320
322
  * to the user. This is where we add the Server-Timing header to the response
@@ -345,7 +347,7 @@ export class VerifiedFetch {
345
347
  const decodedPath = decodeURI(context?.url.pathname)
346
348
  const path = uint8ArrayToString(uint8ArrayFromString(decodedPath), 'ascii')
347
349
 
348
- response.headers.set('x-ipfs-path', `/${context.url.protocol === 'ipfs:' ? 'ipfs' : 'ipns'}/${context?.url.hostname}${path}`)
350
+ response.headers.set('x-ipfs-path', `/${context.url.protocol === 'ipfs:' ? 'ipfs' : 'ipns'}/${context?.url.hostname}${path === '/' ? '' : path}`)
349
351
  }
350
352
 
351
353
  // set CORS headers. If hosting your own gateway with verified-fetch behind
@@ -427,13 +429,7 @@ export class VerifiedFetch {
427
429
 
428
430
  this.log.error('error in plugin %s - %e', plugin.id, err)
429
431
 
430
- return internalServerErrorResponse(context.resource, JSON.stringify({
431
- error: errorToObject(err)
432
- }), {
433
- headers: {
434
- 'content-type': 'application/json'
435
- }
436
- })
432
+ return internalServerErrorResponse(context.resource, err)
437
433
  }
438
434
 
439
435
  if (finalResponse != null) {
@@ -1,16 +0,0 @@
1
- /**
2
- * Turns an IPFS or IPNS path into a HTTP URL. Path gateway syntax is used to
3
- * preserve any case sensitivity
4
- *
5
- * - `/ipfs/cid` -> `https://example.org/ipfs/cid`
6
- * - `/ipns/name` -> `https://example.org/ipns/name`
7
- */
8
- export declare function ipfsPathToUrl(path: string): string;
9
- /**
10
- * Turns an IPFS or IPNS URL into a HTTP URL. Path gateway syntax is used to
11
- * preserve and case sensitivity
12
- *
13
- * `ipfs://cid` -> `https://example.org/ipfs/cid`
14
- */
15
- export declare function ipfsUrlToUrl(url: string): string;
16
- //# sourceMappingURL=ipfs-path-to-url.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ipfs-path-to-url.d.ts","sourceRoot":"","sources":["../../../src/utils/ipfs-path-to-url.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA2BnD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CASjD"}
@@ -1,45 +0,0 @@
1
- import { InvalidParametersError } from '@libp2p/interface';
2
- /**
3
- * Turns an IPFS or IPNS path into a HTTP URL. Path gateway syntax is used to
4
- * preserve any case sensitivity
5
- *
6
- * - `/ipfs/cid` -> `https://example.org/ipfs/cid`
7
- * - `/ipns/name` -> `https://example.org/ipns/name`
8
- */
9
- export function ipfsPathToUrl(path) {
10
- if (!path.startsWith('/ipfs/') && !path.startsWith('/ipns/')) {
11
- throw new InvalidParametersError(`Path ${path} did not start with /ipfs/ or /ipns/`);
12
- }
13
- // trim fragment
14
- const fragmentIndex = path.indexOf('#');
15
- let fragment = '';
16
- if (fragmentIndex > -1) {
17
- fragment = path.substring(fragmentIndex);
18
- path = path.substring(0, fragmentIndex);
19
- }
20
- // trim query
21
- const queryIndex = path.indexOf('?');
22
- let query = '';
23
- if (queryIndex > -1) {
24
- query = path.substring(queryIndex);
25
- path = path.substring(0, queryIndex);
26
- }
27
- const type = path.substring(1, 5);
28
- const rest = path.substring(6);
29
- return `https://example.org/${type}/${rest}${query}${fragment}`;
30
- }
31
- /**
32
- * Turns an IPFS or IPNS URL into a HTTP URL. Path gateway syntax is used to
33
- * preserve and case sensitivity
34
- *
35
- * `ipfs://cid` -> `https://example.org/ipfs/cid`
36
- */
37
- export function ipfsUrlToUrl(url) {
38
- if (!url.startsWith('ipfs://') && !url.startsWith('ipns://')) {
39
- throw new InvalidParametersError(`URL ${url} did not start with ipfs:// or ipns://`);
40
- }
41
- const type = url.substring(0, 4);
42
- const rest = url.substring(7);
43
- return `https://example.org/${type}/${rest}`;
44
- }
45
- //# sourceMappingURL=ipfs-path-to-url.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ipfs-path-to-url.js","sourceRoot":"","sources":["../../../src/utils/ipfs-path-to-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAE,IAAY;IACzC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,sBAAsB,CAAC,QAAQ,IAAI,sCAAsC,CAAC,CAAA;IACtF,CAAC;IAED,gBAAgB;IAChB,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACvC,IAAI,QAAQ,GAAG,EAAE,CAAA;IAEjB,IAAI,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC;QACvB,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QACxC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,aAAa,CAAC,CAAA;IACzC,CAAC;IAED,aAAa;IACb,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,KAAK,GAAG,EAAE,CAAA;IAEd,IAAI,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC;QACpB,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;QAClC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;IACtC,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IAE9B,OAAO,uBAAuB,IAAI,IAAI,IAAI,GAAG,KAAK,GAAG,QAAQ,EAAE,CAAA;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAE,GAAW;IACvC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,sBAAsB,CAAC,OAAO,GAAG,wCAAwC,CAAC,CAAA;IACtF,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IAE7B,OAAO,uBAAuB,IAAI,IAAI,IAAI,EAAE,CAAA;AAC9C,CAAC"}
@@ -1,23 +0,0 @@
1
- export declare const SUBDOMAIN_GATEWAY_REGEX: RegExp;
2
- export interface ParsedURL {
3
- url: URL;
4
- protocol: 'ipfs' | 'ipns';
5
- cidOrPeerIdOrDnsLink: string;
6
- path: string[];
7
- query: Record<string, any>;
8
- fragment: string;
9
- }
10
- /**
11
- * Accepts the following url strings:
12
- *
13
- * - /ipfs/Qmfoo/path
14
- * - /ipns/Qmfoo/path
15
- * - ipfs://cid/path
16
- * - ipns://name/path
17
- * - http://cid.ipfs.example.com/path
18
- * - http://name.ipns.example.com/path
19
- * - http://example.com/ipfs/cid/path
20
- * - http://example.com/ipns/name/path
21
- */
22
- export declare function parseURLString(urlString: string): URL;
23
- //# sourceMappingURL=parse-url-string.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parse-url-string.d.ts","sourceRoot":"","sources":["../../../src/utils/parse-url-string.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,uBAAuB,QAAsE,CAAA;AAmB1G,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB;AA4FD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAE,SAAS,EAAE,MAAM,GAAG,GAAG,CAuBtD"}