@helia/verified-fetch 4.1.0 → 5.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 (231) hide show
  1. package/README.md +6 -40
  2. package/dist/index.min.js +73 -534
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/constants.d.ts +2 -0
  5. package/dist/src/constants.d.ts.map +1 -1
  6. package/dist/src/constants.js +2 -0
  7. package/dist/src/constants.js.map +1 -1
  8. package/dist/src/index.d.ts +162 -68
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/index.js +11 -43
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/plugins/index.d.ts +0 -5
  13. package/dist/src/plugins/index.d.ts.map +1 -1
  14. package/dist/src/plugins/index.js +0 -4
  15. package/dist/src/plugins/index.js.map +1 -1
  16. package/dist/src/plugins/plugin-base.d.ts +8 -9
  17. package/dist/src/plugins/plugin-base.d.ts.map +1 -1
  18. package/dist/src/plugins/plugin-base.js +5 -6
  19. package/dist/src/plugins/plugin-base.js.map +1 -1
  20. package/dist/src/plugins/plugin-handle-car.d.ts +3 -3
  21. package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -1
  22. package/dist/src/plugins/plugin-handle-car.js +38 -39
  23. package/dist/src/plugins/plugin-handle-car.js.map +1 -1
  24. package/dist/src/plugins/plugin-handle-ipld.d.ts +12 -0
  25. package/dist/src/plugins/plugin-handle-ipld.d.ts.map +1 -0
  26. package/dist/src/plugins/plugin-handle-ipld.js +83 -0
  27. package/dist/src/plugins/plugin-handle-ipld.js.map +1 -0
  28. package/dist/src/plugins/plugin-handle-ipns-record.d.ts +3 -3
  29. package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -1
  30. package/dist/src/plugins/plugin-handle-ipns-record.js +25 -34
  31. package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -1
  32. package/dist/src/plugins/plugin-handle-tar.d.ts +3 -3
  33. package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -1
  34. package/dist/src/plugins/plugin-handle-tar.js +20 -22
  35. package/dist/src/plugins/plugin-handle-tar.js.map +1 -1
  36. package/dist/src/plugins/plugin-handle-unixfs.d.ts +14 -0
  37. package/dist/src/plugins/plugin-handle-unixfs.d.ts.map +1 -0
  38. package/dist/src/plugins/plugin-handle-unixfs.js +180 -0
  39. package/dist/src/plugins/plugin-handle-unixfs.js.map +1 -0
  40. package/dist/src/plugins/types.d.ts +1 -77
  41. package/dist/src/plugins/types.d.ts.map +1 -1
  42. package/dist/src/url-resolver.d.ts +29 -11
  43. package/dist/src/url-resolver.d.ts.map +1 -1
  44. package/dist/src/url-resolver.js +152 -74
  45. package/dist/src/url-resolver.js.map +1 -1
  46. package/dist/src/utils/content-type-parser.d.ts.map +1 -1
  47. package/dist/src/utils/content-type-parser.js +4 -3
  48. package/dist/src/utils/content-type-parser.js.map +1 -1
  49. package/dist/src/utils/content-types.d.ts +26 -0
  50. package/dist/src/utils/content-types.d.ts.map +1 -0
  51. package/dist/src/utils/content-types.js +137 -0
  52. package/dist/src/utils/content-types.js.map +1 -0
  53. package/dist/src/utils/convert-output.d.ts +17 -0
  54. package/dist/src/utils/convert-output.d.ts.map +1 -0
  55. package/dist/src/utils/convert-output.js +176 -0
  56. package/dist/src/utils/convert-output.js.map +1 -0
  57. package/dist/src/utils/error-to-response.d.ts +3 -0
  58. package/dist/src/utils/error-to-response.d.ts.map +1 -0
  59. package/dist/src/utils/error-to-response.js +40 -0
  60. package/dist/src/utils/error-to-response.js.map +1 -0
  61. package/dist/src/utils/get-content-disposition-filename.d.ts +1 -1
  62. package/dist/src/utils/get-content-disposition-filename.d.ts.map +1 -1
  63. package/dist/src/utils/get-content-disposition-filename.js +4 -0
  64. package/dist/src/utils/get-content-disposition-filename.js.map +1 -1
  65. package/dist/src/utils/get-e-tag.d.ts +20 -15
  66. package/dist/src/utils/get-e-tag.d.ts.map +1 -1
  67. package/dist/src/utils/get-e-tag.js +8 -22
  68. package/dist/src/utils/get-e-tag.js.map +1 -1
  69. package/dist/src/utils/get-offset-and-length.d.ts +12 -2
  70. package/dist/src/utils/get-offset-and-length.d.ts.map +1 -1
  71. package/dist/src/utils/get-offset-and-length.js +63 -21
  72. package/dist/src/utils/get-offset-and-length.js.map +1 -1
  73. package/dist/src/utils/get-range-header.d.ts +22 -0
  74. package/dist/src/utils/get-range-header.d.ts.map +1 -0
  75. package/dist/src/utils/get-range-header.js +69 -0
  76. package/dist/src/utils/get-range-header.js.map +1 -0
  77. package/dist/src/utils/parse-url-string.d.ts +2 -1
  78. package/dist/src/utils/parse-url-string.d.ts.map +1 -1
  79. package/dist/src/utils/parse-url-string.js +46 -71
  80. package/dist/src/utils/parse-url-string.js.map +1 -1
  81. package/dist/src/utils/resource-to-cache-key.d.ts +3 -3
  82. package/dist/src/utils/resource-to-cache-key.js +5 -5
  83. package/dist/src/utils/resource-to-cache-key.js.map +1 -1
  84. package/dist/src/utils/response-headers.d.ts +4 -14
  85. package/dist/src/utils/response-headers.d.ts.map +1 -1
  86. package/dist/src/utils/response-headers.js +36 -36
  87. package/dist/src/utils/response-headers.js.map +1 -1
  88. package/dist/src/utils/responses.d.ts +30 -11
  89. package/dist/src/utils/responses.d.ts.map +1 -1
  90. package/dist/src/utils/responses.js +146 -39
  91. package/dist/src/utils/responses.js.map +1 -1
  92. package/dist/src/verified-fetch.d.ts +16 -15
  93. package/dist/src/verified-fetch.d.ts.map +1 -1
  94. package/dist/src/verified-fetch.js +307 -236
  95. package/dist/src/verified-fetch.js.map +1 -1
  96. package/dist/typedoc-urls.json +64 -45
  97. package/package.json +4 -3
  98. package/src/constants.ts +3 -0
  99. package/src/index.ts +203 -71
  100. package/src/plugins/index.ts +0 -6
  101. package/src/plugins/plugin-base.ts +8 -10
  102. package/src/plugins/plugin-handle-car.ts +48 -46
  103. package/src/plugins/plugin-handle-ipld.ts +93 -0
  104. package/src/plugins/plugin-handle-ipns-record.ts +31 -41
  105. package/src/plugins/plugin-handle-tar.ts +25 -29
  106. package/src/plugins/plugin-handle-unixfs.ts +217 -0
  107. package/src/plugins/types.ts +0 -86
  108. package/src/url-resolver.ts +197 -83
  109. package/src/utils/content-type-parser.ts +4 -3
  110. package/src/utils/content-types.ts +159 -0
  111. package/src/utils/convert-output.ts +187 -0
  112. package/src/utils/error-to-response.ts +49 -0
  113. package/src/utils/get-content-disposition-filename.ts +7 -1
  114. package/src/utils/get-e-tag.ts +26 -35
  115. package/src/utils/get-offset-and-length.ts +75 -21
  116. package/src/utils/get-range-header.ts +107 -0
  117. package/src/utils/parse-url-string.ts +51 -80
  118. package/src/utils/resource-to-cache-key.ts +5 -5
  119. package/src/utils/response-headers.ts +40 -41
  120. package/src/utils/responses.ts +186 -45
  121. package/src/verified-fetch.ts +359 -267
  122. package/dist/src/plugins/plugin-handle-byte-range-context.d.ts +0 -14
  123. package/dist/src/plugins/plugin-handle-byte-range-context.d.ts.map +0 -1
  124. package/dist/src/plugins/plugin-handle-byte-range-context.js +0 -25
  125. package/dist/src/plugins/plugin-handle-byte-range-context.js.map +0 -1
  126. package/dist/src/plugins/plugin-handle-cbor.d.ts +0 -17
  127. package/dist/src/plugins/plugin-handle-cbor.d.ts.map +0 -1
  128. package/dist/src/plugins/plugin-handle-cbor.js +0 -94
  129. package/dist/src/plugins/plugin-handle-cbor.js.map +0 -1
  130. package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts +0 -27
  131. package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts.map +0 -1
  132. package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js +0 -279
  133. package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js.map +0 -1
  134. package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +0 -17
  135. package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +0 -1
  136. package/dist/src/plugins/plugin-handle-dag-cbor.js +0 -66
  137. package/dist/src/plugins/plugin-handle-dag-cbor.js.map +0 -1
  138. package/dist/src/plugins/plugin-handle-dag-pb.d.ts +0 -17
  139. package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +0 -1
  140. package/dist/src/plugins/plugin-handle-dag-pb.js +0 -209
  141. package/dist/src/plugins/plugin-handle-dag-pb.js.map +0 -1
  142. package/dist/src/plugins/plugin-handle-dag-walk.d.ts +0 -21
  143. package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +0 -1
  144. package/dist/src/plugins/plugin-handle-dag-walk.js +0 -95
  145. package/dist/src/plugins/plugin-handle-dag-walk.js.map +0 -1
  146. package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +0 -10
  147. package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +0 -1
  148. package/dist/src/plugins/plugin-handle-dir-index-html.js +0 -59
  149. package/dist/src/plugins/plugin-handle-dir-index-html.js.map +0 -1
  150. package/dist/src/plugins/plugin-handle-json.d.ts +0 -12
  151. package/dist/src/plugins/plugin-handle-json.d.ts.map +0 -1
  152. package/dist/src/plugins/plugin-handle-json.js +0 -73
  153. package/dist/src/plugins/plugin-handle-json.js.map +0 -1
  154. package/dist/src/plugins/plugin-handle-raw.d.ts +0 -9
  155. package/dist/src/plugins/plugin-handle-raw.d.ts.map +0 -1
  156. package/dist/src/plugins/plugin-handle-raw.js +0 -92
  157. package/dist/src/plugins/plugin-handle-raw.js.map +0 -1
  158. package/dist/src/plugins/plugins.d.ts +0 -6
  159. package/dist/src/plugins/plugins.d.ts.map +0 -1
  160. package/dist/src/plugins/plugins.js +0 -6
  161. package/dist/src/plugins/plugins.js.map +0 -1
  162. package/dist/src/utils/byte-range-context.d.ts +0 -103
  163. package/dist/src/utils/byte-range-context.d.ts.map +0 -1
  164. package/dist/src/utils/byte-range-context.js +0 -504
  165. package/dist/src/utils/byte-range-context.js.map +0 -1
  166. package/dist/src/utils/dag-cbor-to-safe-json.d.ts +0 -15
  167. package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +0 -1
  168. package/dist/src/utils/dag-cbor-to-safe-json.js +0 -54
  169. package/dist/src/utils/dag-cbor-to-safe-json.js.map +0 -1
  170. package/dist/src/utils/dir-index-html.d.ts +0 -19
  171. package/dist/src/utils/dir-index-html.d.ts.map +0 -1
  172. package/dist/src/utils/dir-index-html.js +0 -438
  173. package/dist/src/utils/dir-index-html.js.map +0 -1
  174. package/dist/src/utils/get-peer-id-from-string.d.ts +0 -3
  175. package/dist/src/utils/get-peer-id-from-string.d.ts.map +0 -1
  176. package/dist/src/utils/get-peer-id-from-string.js +0 -10
  177. package/dist/src/utils/get-peer-id-from-string.js.map +0 -1
  178. package/dist/src/utils/get-resolved-accept-header.d.ts +0 -9
  179. package/dist/src/utils/get-resolved-accept-header.d.ts.map +0 -1
  180. package/dist/src/utils/get-resolved-accept-header.js +0 -27
  181. package/dist/src/utils/get-resolved-accept-header.js.map +0 -1
  182. package/dist/src/utils/get-stream-from-async-iterable.d.ts +0 -9
  183. package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +0 -1
  184. package/dist/src/utils/get-stream-from-async-iterable.js +0 -43
  185. package/dist/src/utils/get-stream-from-async-iterable.js.map +0 -1
  186. package/dist/src/utils/handle-redirects.d.ts +0 -16
  187. package/dist/src/utils/handle-redirects.d.ts.map +0 -1
  188. package/dist/src/utils/handle-redirects.js +0 -84
  189. package/dist/src/utils/handle-redirects.js.map +0 -1
  190. package/dist/src/utils/is-accept-explicit.d.ts +0 -15
  191. package/dist/src/utils/is-accept-explicit.d.ts.map +0 -1
  192. package/dist/src/utils/is-accept-explicit.js +0 -26
  193. package/dist/src/utils/is-accept-explicit.js.map +0 -1
  194. package/dist/src/utils/request-headers.d.ts +0 -13
  195. package/dist/src/utils/request-headers.d.ts.map +0 -1
  196. package/dist/src/utils/request-headers.js +0 -63
  197. package/dist/src/utils/request-headers.js.map +0 -1
  198. package/dist/src/utils/select-output-type.d.ts +0 -17
  199. package/dist/src/utils/select-output-type.d.ts.map +0 -1
  200. package/dist/src/utils/select-output-type.js +0 -153
  201. package/dist/src/utils/select-output-type.js.map +0 -1
  202. package/dist/src/utils/tlru.d.ts +0 -15
  203. package/dist/src/utils/tlru.d.ts.map +0 -1
  204. package/dist/src/utils/tlru.js +0 -34
  205. package/dist/src/utils/tlru.js.map +0 -1
  206. package/dist/src/utils/walk-path.d.ts +0 -27
  207. package/dist/src/utils/walk-path.d.ts.map +0 -1
  208. package/dist/src/utils/walk-path.js +0 -45
  209. package/dist/src/utils/walk-path.js.map +0 -1
  210. package/src/plugins/plugin-handle-byte-range-context.ts +0 -30
  211. package/src/plugins/plugin-handle-cbor.ts +0 -107
  212. package/src/plugins/plugin-handle-dag-cbor-html-preview.ts +0 -295
  213. package/src/plugins/plugin-handle-dag-cbor.ts +0 -83
  214. package/src/plugins/plugin-handle-dag-pb.ts +0 -248
  215. package/src/plugins/plugin-handle-dag-walk.ts +0 -110
  216. package/src/plugins/plugin-handle-dir-index-html.ts +0 -72
  217. package/src/plugins/plugin-handle-json.ts +0 -80
  218. package/src/plugins/plugin-handle-raw.ts +0 -110
  219. package/src/plugins/plugins.ts +0 -5
  220. package/src/utils/byte-range-context.ts +0 -597
  221. package/src/utils/dag-cbor-to-safe-json.ts +0 -63
  222. package/src/utils/dir-index-html.ts +0 -505
  223. package/src/utils/get-peer-id-from-string.ts +0 -12
  224. package/src/utils/get-resolved-accept-header.ts +0 -42
  225. package/src/utils/get-stream-from-async-iterable.ts +0 -49
  226. package/src/utils/handle-redirects.ts +0 -109
  227. package/src/utils/is-accept-explicit.ts +0 -38
  228. package/src/utils/request-headers.ts +0 -65
  229. package/src/utils/select-output-type.ts +0 -175
  230. package/src/utils/tlru.ts +0 -42
  231. package/src/utils/walk-path.ts +0 -68
@@ -0,0 +1,159 @@
1
+ import { code as dagCborCode } from '@ipld/dag-cbor'
2
+ import { code as dagJsonCode } from '@ipld/dag-json'
3
+ import { code as dagPbCode } from '@ipld/dag-pb'
4
+ import { code as jsonCode } from 'multiformats/codecs/json'
5
+ import { code as rawCode } from 'multiformats/codecs/raw'
6
+ import { CODEC_CBOR } from '../constants.ts'
7
+ import type { ContentType } from '../index.ts'
8
+ import type { CID } from 'multiformats/cid'
9
+
10
+ export const MEDIA_TYPE_DAG_CBOR = 'application/vnd.ipld.dag-cbor'
11
+ export const MEDIA_TYPE_CBOR = 'application/cbor'
12
+ export const MEDIA_TYPE_DAG_JSON = 'application/vnd.ipld.dag-json'
13
+ export const MEDIA_TYPE_JSON = 'application/json'
14
+ export const MEDIA_TYPE_RAW = 'application/vnd.ipld.raw'
15
+ export const MEDIA_TYPE_OCTET_STREAM = 'application/octet-stream'
16
+ export const MEDIA_TYPE_IPNS_RECORD = 'application/vnd.ipfs.ipns-record'
17
+ export const MEDIA_TYPE_CAR = 'application/vnd.ipld.car'
18
+ export const MEDIA_TYPE_TAR = 'application/x-tar'
19
+ export const MEDIA_TYPE_DAG_PB = 'application/vnd.ipld.dag-pb'
20
+
21
+ export const CONTENT_TYPE_OCTET_STREAM: ContentType = {
22
+ mediaType: MEDIA_TYPE_OCTET_STREAM,
23
+ codecs: [rawCode, jsonCode, dagJsonCode, CODEC_CBOR, dagCborCode, dagPbCode],
24
+ immutable: true,
25
+ extension: '.bin',
26
+ etag: '',
27
+ disposition: 'attachment'
28
+ }
29
+
30
+ export const CONTENT_TYPE_DAG_CBOR: ContentType = {
31
+ mediaType: MEDIA_TYPE_DAG_CBOR,
32
+ codecs: [dagCborCode, CODEC_CBOR, dagJsonCode, jsonCode, dagPbCode, rawCode],
33
+ immutable: true,
34
+ extension: '.cbor',
35
+ etag: '.dag-cbor',
36
+ disposition: 'attachment'
37
+ }
38
+
39
+ export const CONTENT_TYPE_CBOR: ContentType = {
40
+ mediaType: MEDIA_TYPE_CBOR,
41
+ codecs: [CODEC_CBOR, dagCborCode, dagJsonCode, jsonCode, dagPbCode, rawCode],
42
+ immutable: true,
43
+ extension: '.cbor',
44
+ etag: '.cbor',
45
+ disposition: 'attachment'
46
+ }
47
+
48
+ export const CONTENT_TYPE_DAG_JSON: ContentType = {
49
+ mediaType: MEDIA_TYPE_DAG_JSON,
50
+ codecs: [dagJsonCode, jsonCode, dagCborCode, CODEC_CBOR, dagPbCode, rawCode],
51
+ immutable: true,
52
+ extension: '.json',
53
+ etag: '.dag-json',
54
+ disposition: 'inline'
55
+ }
56
+
57
+ export const CONTENT_TYPE_JSON: ContentType = {
58
+ mediaType: MEDIA_TYPE_JSON,
59
+ codecs: [jsonCode, dagJsonCode, dagCborCode, CODEC_CBOR, dagPbCode, rawCode],
60
+ immutable: true,
61
+ extension: '.json',
62
+ etag: '.json',
63
+ disposition: 'inline'
64
+ }
65
+
66
+ export const CONTENT_TYPE_RAW: ContentType = {
67
+ mediaType: MEDIA_TYPE_RAW,
68
+ codecs: [rawCode, jsonCode, dagJsonCode, CODEC_CBOR, dagCborCode, dagPbCode],
69
+ immutable: true,
70
+ extension: '.raw',
71
+ etag: '.raw',
72
+ disposition: 'attachment'
73
+ }
74
+
75
+ export const CONTENT_TYPE_IPNS: ContentType = {
76
+ mediaType: MEDIA_TYPE_IPNS_RECORD,
77
+ codecs: [],
78
+ immutable: false,
79
+ extension: '.bin',
80
+ etag: '.bin',
81
+ disposition: 'attachment'
82
+ }
83
+
84
+ export const CONTENT_TYPE_CAR: ContentType = {
85
+ mediaType: MEDIA_TYPE_CAR,
86
+ codecs: [rawCode, jsonCode, dagJsonCode, CODEC_CBOR, dagCborCode, dagPbCode],
87
+ immutable: false,
88
+ extension: '.car',
89
+ etag: '.car',
90
+ disposition: 'attachment'
91
+ }
92
+
93
+ export const CONTENT_TYPE_TAR: ContentType = {
94
+ mediaType: MEDIA_TYPE_TAR,
95
+ codecs: [rawCode, dagPbCode],
96
+ immutable: false,
97
+ extension: '.x-tar',
98
+ etag: '.x-tar',
99
+ disposition: 'attachment'
100
+ }
101
+
102
+ export const CONTENT_TYPES: ContentType[] = [
103
+ CONTENT_TYPE_DAG_CBOR,
104
+ CONTENT_TYPE_CBOR,
105
+ CONTENT_TYPE_DAG_JSON,
106
+ CONTENT_TYPE_JSON,
107
+ CONTENT_TYPE_RAW,
108
+ CONTENT_TYPE_IPNS,
109
+ CONTENT_TYPE_CAR,
110
+ CONTENT_TYPE_TAR,
111
+ CONTENT_TYPE_OCTET_STREAM
112
+ ]
113
+
114
+ export function getContentType (mediaType?: string | null): ContentType | undefined {
115
+ return CONTENT_TYPES
116
+ .find(m => m.mediaType === mediaType)
117
+ }
118
+
119
+ export function getContentTypesForCid (cid?: CID): ContentType[] {
120
+ if (cid == null) {
121
+ return []
122
+ }
123
+
124
+ return CONTENT_TYPES
125
+ .filter(m => m.codecs.includes(cid.code))
126
+ .sort((a, b) => {
127
+ // prefer content types where the CID code is earlier in the list of
128
+ // supported codecs
129
+ const posA = a.codecs.indexOf(cid.code)
130
+ const posB = b.codecs.indexOf(cid.code)
131
+
132
+ if (posA < posB) {
133
+ return -1
134
+ }
135
+
136
+ if (posB < posA) {
137
+ return 1
138
+ }
139
+
140
+ return 0
141
+ })
142
+ }
143
+
144
+ export function getSupportedContentTypes (protocol: string = 'ipfs:', cid?: CID): ContentType[] {
145
+ let contentTypes = getContentTypesForCid(cid)
146
+
147
+ if (protocol === 'ipfs:' && contentTypes.length === 0) {
148
+ contentTypes = [
149
+ CONTENT_TYPE_RAW,
150
+ CONTENT_TYPE_OCTET_STREAM
151
+ ]
152
+ }
153
+
154
+ if (protocol === 'ipns:') {
155
+ contentTypes.push(CONTENT_TYPE_IPNS)
156
+ }
157
+
158
+ return contentTypes
159
+ }
@@ -0,0 +1,187 @@
1
+ import * as dagCbor from '@ipld/dag-cbor'
2
+ import * as dagJson from '@ipld/dag-json'
3
+ import * as dagPb from '@ipld/dag-pb'
4
+ import * as json from 'multiformats/codecs/json'
5
+ import * as raw from 'multiformats/codecs/raw'
6
+ import { identity } from 'multiformats/hashes/identity'
7
+ import { CODEC_CBOR } from '../constants.ts'
8
+ import { getContentTypesForCid, MEDIA_TYPE_CBOR, MEDIA_TYPE_DAG_CBOR, MEDIA_TYPE_DAG_JSON, MEDIA_TYPE_JSON, MEDIA_TYPE_OCTET_STREAM, MEDIA_TYPE_RAW } from './content-types.ts'
9
+ import type { AcceptHeader, ContentType } from '../index.ts'
10
+ import type { CID } from 'multiformats/cid'
11
+
12
+ const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array) => Uint8Array>> = {
13
+ [dagCbor.code]: {
14
+ [MEDIA_TYPE_JSON]: (buf) => {
15
+ return dagJson.encode(dagCbor.decode(buf))
16
+ },
17
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
18
+ return dagJson.encode(dagCbor.decode(buf))
19
+ },
20
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
21
+ return buf
22
+ },
23
+ [MEDIA_TYPE_CBOR]: (buf) => {
24
+ return buf
25
+ },
26
+ [MEDIA_TYPE_RAW]: (buf) => {
27
+ return buf
28
+ },
29
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
30
+ return buf
31
+ }
32
+ },
33
+ [CODEC_CBOR]: {
34
+ [MEDIA_TYPE_JSON]: (buf) => {
35
+ return dagJson.encode(dagCbor.decode(buf))
36
+ },
37
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
38
+ return dagJson.encode(dagCbor.decode(buf))
39
+ },
40
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
41
+ return buf
42
+ },
43
+ [MEDIA_TYPE_CBOR]: (buf) => {
44
+ return buf
45
+ },
46
+ [MEDIA_TYPE_RAW]: (buf) => {
47
+ return buf
48
+ },
49
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
50
+ return buf
51
+ }
52
+ },
53
+ [dagJson.code]: {
54
+ [MEDIA_TYPE_CBOR]: (buf) => {
55
+ return dagCbor.encode(dagJson.decode(buf))
56
+ },
57
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
58
+ return dagCbor.encode(dagJson.decode(buf))
59
+ },
60
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
61
+ return buf
62
+ },
63
+ [MEDIA_TYPE_JSON]: (buf) => {
64
+ return buf
65
+ },
66
+ [MEDIA_TYPE_RAW]: (buf) => {
67
+ return buf
68
+ },
69
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
70
+ return buf
71
+ }
72
+ },
73
+ [json.code]: {
74
+ [MEDIA_TYPE_CBOR]: (buf) => {
75
+ return dagCbor.encode(json.decode(buf))
76
+ },
77
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
78
+ return dagCbor.encode(json.decode(buf))
79
+ },
80
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
81
+ return buf
82
+ },
83
+ [MEDIA_TYPE_JSON]: (buf) => {
84
+ return buf
85
+ },
86
+ [MEDIA_TYPE_RAW]: (buf) => {
87
+ return buf
88
+ },
89
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
90
+ return buf
91
+ }
92
+ },
93
+ [dagPb.code]: {
94
+ [MEDIA_TYPE_CBOR]: (buf) => {
95
+ return dagCbor.encode(dagPb.decode(buf))
96
+ },
97
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
98
+ return dagCbor.encode(dagPb.decode(buf))
99
+ },
100
+ [MEDIA_TYPE_JSON]: (buf) => {
101
+ return dagJson.encode(dagPb.decode(buf))
102
+ },
103
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
104
+ return dagJson.encode(dagPb.decode(buf))
105
+ },
106
+ [MEDIA_TYPE_RAW]: (buf) => {
107
+ return buf
108
+ },
109
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
110
+ return buf
111
+ }
112
+ },
113
+ [raw.code]: {
114
+ [MEDIA_TYPE_CBOR]: (buf) => {
115
+ return dagCbor.encode(buf)
116
+ },
117
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
118
+ return dagCbor.encode(buf)
119
+ },
120
+ [MEDIA_TYPE_JSON]: (buf) => {
121
+ return buf
122
+ },
123
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
124
+ return dagJson.encode(buf)
125
+ },
126
+ [MEDIA_TYPE_RAW]: (buf) => {
127
+ return buf
128
+ },
129
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
130
+ return buf
131
+ }
132
+ },
133
+ [identity.code]: {
134
+ [MEDIA_TYPE_CBOR]: (buf) => {
135
+ return dagCbor.encode(buf)
136
+ },
137
+ [MEDIA_TYPE_DAG_CBOR]: (buf) => {
138
+ return dagCbor.encode(buf)
139
+ },
140
+ [MEDIA_TYPE_JSON]: (buf) => {
141
+ return dagJson.encode(buf)
142
+ },
143
+ [MEDIA_TYPE_DAG_JSON]: (buf) => {
144
+ return dagJson.encode(buf)
145
+ },
146
+ [MEDIA_TYPE_RAW]: (buf) => {
147
+ return buf
148
+ },
149
+ [MEDIA_TYPE_OCTET_STREAM]: (buf) => {
150
+ return buf
151
+ }
152
+ }
153
+ }
154
+
155
+ export interface ConvertedOutput {
156
+ block: Uint8Array
157
+ contentType: string
158
+ }
159
+
160
+ /**
161
+ * Where supported, deserialize the passed block using the codec appropriate for
162
+ * the CID, then loop over the acceptable content types and attempt to convert
163
+ * the deserialized value to that format and serialize it back to a block -
164
+ * return the first successful result.
165
+ */
166
+ export function convertOutput (cid: CID, block: Uint8Array, accept: AcceptHeader[]): { contentType: ContentType, output: Uint8Array } {
167
+ if (accept.length === 0) {
168
+ // return current format
169
+ return {
170
+ contentType: getContentTypesForCid(cid)[0],
171
+ output: block
172
+ }
173
+ }
174
+
175
+ for (const format of accept) {
176
+ const converter = CONVERSIONS[cid.code]?.[format.contentType.mediaType]
177
+
178
+ if (converter != null) {
179
+ return {
180
+ contentType: format.contentType,
181
+ output: converter(block)
182
+ }
183
+ }
184
+ }
185
+
186
+ throw new Error(`Could not convert ${cid} to any of ${accept.map(a => a.contentType.mediaType).join(', ')}`)
187
+ }
@@ -0,0 +1,49 @@
1
+ import { CONTENT_TYPE_OCTET_STREAM, CONTENT_TYPE_RAW } from './content-types.ts'
2
+ import { badGatewayResponse, gatewayTimeoutResponse, internalServerErrorResponse, notAcceptableResponse, notFoundResponse, preconditionFailedResponse } from './responses.js'
3
+ import type { Resource } from '../index.js'
4
+
5
+ export function errorToResponse (resource: Resource | string, err: any): Response {
6
+ // if a signal abort caused the error, throw the error
7
+ if (err.name === 'AbortError') {
8
+ throw err
9
+ }
10
+
11
+ // could not reach an upstream server, bad connection or offline
12
+ if (err.code === 'ECONNREFUSED' || err.code === 'ECANCELLED' || err.name === 'DNSQueryFailedError') {
13
+ return badGatewayResponse(resource.toString(), err)
14
+ }
15
+
16
+ // data was not parseable, user may be able to request raw block
17
+ if (['NotUnixFSError'].includes(err.name)) {
18
+ return notAcceptableResponse(resource.toString(), [
19
+ CONTENT_TYPE_RAW,
20
+ CONTENT_TYPE_OCTET_STREAM
21
+ ])
22
+ }
23
+
24
+ // an upstream server didn't respond in time but inside the signal timeout
25
+ if (err.code === 'ETIMEOUT' || err.name === 'TimeoutError') {
26
+ return gatewayTimeoutResponse(resource.toString(), err)
27
+ }
28
+
29
+ // path was not under DAG root
30
+ if (['ERR_NO_PROP', 'ERR_NO_TERMINAL_ELEMENT', 'ERR_NOT_FOUND'].includes(err.code)) {
31
+ return notFoundResponse(resource.toString())
32
+ }
33
+
34
+ // path was not under DAG root
35
+ if (['DoesNotExistError'].includes(err.name)) {
36
+ return notFoundResponse(resource.toString())
37
+ }
38
+
39
+ if (['BlockNotFoundWhileOfflineError'].includes(err.name)) {
40
+ return preconditionFailedResponse(resource.toString())
41
+ }
42
+
43
+ if (['RecordNotFoundError'].includes('err.name')) {
44
+ return badGatewayResponse(resource.toString(), err)
45
+ }
46
+
47
+ // can't tell what went wrong, return a generic error
48
+ return internalServerErrorResponse(resource.toString(), err)
49
+ }
@@ -1,8 +1,14 @@
1
+ import { InvalidParametersError } from '@libp2p/interface'
2
+
1
3
  /**
2
4
  * Takes a filename URL param and returns a string for use in a
3
5
  * `Content-Disposition` header
4
6
  */
5
- export function getContentDispositionFilename (filename: string): string {
7
+ export function getContentDispositionFilename (filename: string | null): string {
8
+ if (filename == null) {
9
+ throw new InvalidParametersError('Cannot get filename for Content-Disposition header - filename argument was missing')
10
+ }
11
+
6
12
  const asciiOnly = replaceNonAsciiCharacters(filename)
7
13
 
8
14
  if (asciiOnly === filename) {
@@ -1,56 +1,47 @@
1
- import type { RequestFormatShorthand } from '../index.js'
1
+ import type { ContentType } from '../index.ts'
2
2
  import type { CID } from 'multiformats/cid'
3
3
 
4
4
  interface GetETagArg {
5
- cid: CID
6
- reqFormat?: RequestFormatShorthand
7
- rangeStart?: number
8
- rangeEnd?: number
9
5
  /**
10
- * Weak Etag is used when we can't guarantee byte-for-byte-determinism (generated, or mutable content).
11
- * Some examples:
12
- * - IPNS requests
13
- * - CAR streamed with blocks in non-deterministic order
14
- * - TAR streamed with files in non-deterministic order
6
+ * The CID that was resolved to the content in the response
15
7
  */
16
- weak?: boolean
8
+ cid: CID
17
9
 
18
10
  /**
19
- * A custom prefix to use for the content of the etag. This is needed for some cases (like dir-index-html) where we need to use a custom prefix for the etag.
11
+ * The content type of the response
20
12
  */
21
- contentPrefix?: string
22
- }
13
+ contentType: ContentType
23
14
 
24
- const getPrefix = ({ weak, reqFormat }: Partial<GetETagArg>): string => {
25
- if (reqFormat === 'tar' || reqFormat === 'car' || reqFormat === 'ipns-record' || weak === true) {
26
- return 'W/'
27
- }
28
- return ''
29
- }
15
+ /**
16
+ * Where the byte range starts, if specified
17
+ */
18
+ rangeStart?: number
30
19
 
31
- const getFormatSuffix = ({ reqFormat }: Partial<GetETagArg>): string => {
32
- if (reqFormat == null) {
33
- return ''
34
- }
35
- if (reqFormat === 'tar') {
36
- return '.x-tar'
37
- }
20
+ /**
21
+ * Where the byte range ends, if specified
22
+ */
23
+ rangeEnd?: number
38
24
 
39
- return `.${reqFormat}`
25
+ /**
26
+ * A custom prefix to use for the content of the etag
27
+ */
28
+ contentPrefix?: string
40
29
  }
41
30
 
42
31
  /**
43
- * etag
44
- * you need to wrap cid with ""
45
- * we use strong Etags for immutable responses and weak one (prefixed with W/ ) for mutable/generated ones (ipns, car, tar, and generated HTML).
46
- * block and car responses should have different etag than deserialized one, so you can add some prefix like we do in existing gateway
32
+ * We use strong Etags for immutable responses and weak ones (prefixed with W/ )
33
+ * for mutable/generated ones (ipns, car, tar, and generated HTML).
34
+ *
35
+ * Block and car responses should have different etags to deserialized ones, so
36
+ * you can add a prefix like we do in the existing gateway.
47
37
  *
48
38
  * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
49
39
  * @see https://specs.ipfs.tech/http-gateways/path-gateway/#etag-response-header
50
40
  */
51
- export function getETag ({ cid, reqFormat, weak, rangeStart, rangeEnd, contentPrefix }: GetETagArg): string {
52
- const prefix = getPrefix({ weak, reqFormat })
53
- let suffix = getFormatSuffix({ reqFormat })
41
+ export function getETag ({ cid, contentType, rangeStart, rangeEnd, contentPrefix }: GetETagArg): string {
42
+ const prefix = contentType.immutable ? '' : 'W/'
43
+ let suffix = contentType.etag
44
+
54
45
  if (rangeStart != null || rangeEnd != null) {
55
46
  suffix += `.${rangeStart ?? '0'}-${rangeEnd ?? 'N'}`
56
47
  }
@@ -1,25 +1,79 @@
1
- import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
1
+ import { InvalidRangeError } from '../errors.ts'
2
2
 
3
- export function getOffsetAndLength (entry: UnixFSEntry, entityBytes?: string): { offset: number, length: number } {
4
- if (entityBytes == null) {
5
- return {
6
- offset: 0,
7
- length: Infinity
3
+ interface OffsetLength<T = number> {
4
+ offset: T
5
+ length: T
6
+ }
7
+
8
+ function downCast ({ offset, length }: OffsetLength<bigint>): OffsetLength {
9
+ return {
10
+ offset: Number(offset),
11
+ length: Number(length)
12
+ }
13
+ }
14
+
15
+ /**
16
+ * N.b. Range start (zero indexed, inclusive) and end (inclusive) - this is
17
+ * different to `Uint8Array.subarray` which is end exclusive.
18
+ */
19
+ export function rangeToOffsetAndLength (size: bigint | number, start?: number | bigint, end?: number | bigint): { offset: number, length: number } {
20
+ size = BigInt(size)
21
+
22
+ if (start != null) {
23
+ start = BigInt(start)
24
+
25
+ if (start < 0n) {
26
+ throw new InvalidRangeError('Range start cannot be negative')
27
+ }
28
+
29
+ if (start >= size) {
30
+ throw new InvalidRangeError('Range start cannot be larger than total bytes')
8
31
  }
9
32
  }
10
33
 
11
- const parts = entityBytes.split(':')
12
- const start = parseInt(parts[0], 10)
13
- const end = parts[1] === '*' ? Infinity : parseInt(parts[1], 10)
34
+ if (end != null) {
35
+ end = BigInt(end)
14
36
 
15
- if (isNaN(start) || isNaN(end)) {
16
- throw new Error('Could not parse entity-bytes')
37
+ const abs = end > 0n ? end : -end
38
+
39
+ if (abs >= size) {
40
+ throw new InvalidRangeError('Range end cannot be larger than total bytes')
41
+ }
42
+
43
+ if (end < 0n) {
44
+ if (start == null) {
45
+ start = size + end
46
+ end = size - 1n
47
+ } else {
48
+ throw new InvalidRangeError('Range end cannot be negative')
49
+ }
50
+ }
51
+
52
+ // make inclusive end exclusive
53
+ end += 1n
17
54
  }
18
55
 
19
- const entrySize = Number(entry.size)
56
+ return downCast(toOffsetAndLength(size, start, end))
57
+ }
58
+
59
+ /**
60
+ * Translate `from:to` zero-indexed inclusive bytes to offset/length
61
+ */
62
+ export function entityBytesToOffsetAndLength (size: number | bigint, entityBytes?: string | null): { offset: number, length: number } {
63
+ size = BigInt(size)
64
+ const parts = (entityBytes ?? '0:*').split(':')
65
+ const start = BigInt(parts[0])
66
+ const end = parts[1] === '*' ? size : BigInt(parts[1])
67
+
68
+ return downCast(toOffsetAndLength(size, start, end))
69
+ }
20
70
 
21
- if (start >= 0) {
22
- if (end >= 0) {
71
+ /**
72
+ * Translate `from:to` zero-indexed inclusive bytes to offset/length
73
+ */
74
+ function toOffsetAndLength (size: bigint, start: bigint = 0n, end: bigint = size): { offset: bigint, length: bigint } {
75
+ if (start >= 0n) {
76
+ if (end >= 0n) {
23
77
  return {
24
78
  offset: start,
25
79
  length: end - start
@@ -27,28 +81,28 @@ export function getOffsetAndLength (entry: UnixFSEntry, entityBytes?: string): {
27
81
  } else {
28
82
  return {
29
83
  offset: start,
30
- length: (entrySize - start) + end
84
+ length: (size - start) + end
31
85
  }
32
86
  }
33
87
  }
34
88
 
35
89
  // start < 0
36
- let offset = entrySize + start
90
+ let offset = size + start
37
91
 
38
- if (Math.abs(start) > entrySize) {
39
- offset = 0
92
+ if (Math.abs(Number(start)) > Number(size)) {
93
+ offset = 0n
40
94
  }
41
95
 
42
- if (end >= 0) {
96
+ if (end >= 0n) {
43
97
  return {
44
98
  offset,
45
- length: (entrySize - offset) + end
99
+ length: end - offset
46
100
  }
47
101
  }
48
102
 
49
103
  // end < 0
50
104
  return {
51
105
  offset,
52
- length: (entrySize - offset) + end
106
+ length: (size - offset) + end
53
107
  }
54
108
  }