@helia/verified-fetch 4.1.1 → 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 +7 -40
  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 +302 -238
  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 +199 -68
  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 +353 -270
  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 -69
@@ -0,0 +1,180 @@
1
+ import { code as dagPbCode } from '@ipld/dag-pb';
2
+ import { isPromise } from '@libp2p/utils';
3
+ import { exporter } from 'ipfs-unixfs-exporter';
4
+ import first from 'it-first';
5
+ import itToBrowserReadableStream from 'it-to-browser-readablestream';
6
+ import toBuffer from 'it-to-buffer';
7
+ import * as raw from 'multiformats/codecs/raw';
8
+ import { MEDIA_TYPE_OCTET_STREAM, MEDIA_TYPE_DAG_PB } from "../utils/content-types.js";
9
+ import { getContentDispositionFilename } from "../utils/get-content-disposition-filename.js";
10
+ import { badGatewayResponse, movedPermanentlyResponse, partialContentResponse, okResponse } from '../utils/responses.js';
11
+ import { BasePlugin } from './plugin-base.js';
12
+ /**
13
+ * @see https://specs.ipfs.tech/http-gateways/path-gateway/#use-in-directory-url-normalization
14
+ */
15
+ function getRedirectUrl(resource, url, terminalElement) {
16
+ let uri;
17
+ try {
18
+ // try the requested resource
19
+ uri = new URL(resource);
20
+ }
21
+ catch {
22
+ // fall back to the canonical URL
23
+ uri = url;
24
+ }
25
+ // directories must be requested with a trailing slash
26
+ if (terminalElement?.type === 'directory' && !uri.pathname.endsWith('/')) {
27
+ // make sure we append slash to end of the path
28
+ uri.pathname += '/';
29
+ return uri.toString();
30
+ }
31
+ }
32
+ /**
33
+ * Handles UnixFS content
34
+ */
35
+ export class UnixFSPlugin extends BasePlugin {
36
+ id = 'unixfs-plugin';
37
+ codes = [dagPbCode, raw.code];
38
+ canHandle({ terminalElement, accept }) {
39
+ const supportsCid = this.codes.includes(terminalElement.cid.code);
40
+ const supportsAccept = accept.length === 0 || accept.some(header => header.contentType.mediaType === MEDIA_TYPE_OCTET_STREAM ||
41
+ header.contentType.mediaType === MEDIA_TYPE_DAG_PB);
42
+ return supportsCid && supportsAccept;
43
+ }
44
+ async handle(context) {
45
+ let { url, resource, terminalElement, ipfsRoots } = context;
46
+ let filename = url.searchParams.get('filename') ?? terminalElement.name;
47
+ let redirected;
48
+ if (terminalElement.type === 'directory') {
49
+ const redirectUrl = getRedirectUrl(resource, url, terminalElement);
50
+ if (redirectUrl != null) {
51
+ this.log.trace('directory url normalization spec requires redirect');
52
+ if (context.options?.redirect === 'error') {
53
+ this.log('could not redirect to %s as redirect option was set to "error"', redirectUrl);
54
+ throw new TypeError('Failed to fetch');
55
+ }
56
+ else if (context.options?.redirect === 'manual') {
57
+ this.log('returning 301 permanent redirect to %s', redirectUrl);
58
+ return movedPermanentlyResponse(context.resource, redirectUrl);
59
+ }
60
+ this.log('following redirect to %s', redirectUrl);
61
+ // fall-through simulates following the redirect?
62
+ resource = redirectUrl;
63
+ redirected = true;
64
+ }
65
+ const dirCid = terminalElement.cid;
66
+ // if not disabled, search the directory for an index.html file
67
+ if (context.options?.supportDirectoryIndexes !== false) {
68
+ const rootFilePath = 'index.html';
69
+ try {
70
+ this.log.trace('found directory at %c/%s, looking for index.html', dirCid, url.pathname);
71
+ const entry = await context.serverTiming.time('exporter-dir', '', exporter(`/ipfs/${dirCid}/${rootFilePath}`, context.blockstore, context.options));
72
+ if (entry.type === 'directory' || entry.type === 'object') {
73
+ return badGatewayResponse(resource, 'Unable to stream content');
74
+ }
75
+ // use `index.html` as the file name to help with content types
76
+ filename = rootFilePath;
77
+ this.log.trace('found directory index at %c/%s with cid %c', dirCid, rootFilePath, entry.cid);
78
+ return await this.streamFile(resource, entry, filename, redirected, context.range, context.options);
79
+ }
80
+ catch (err) {
81
+ if (err.name !== 'NotFoundError') {
82
+ this.log.error('error loading path %c/%s - %e', dirCid, rootFilePath, err);
83
+ throw err;
84
+ }
85
+ }
86
+ }
87
+ // no index file found, return the directory listing
88
+ const block = await toBuffer(context.blockstore.get(dirCid, context.options));
89
+ return okResponse(resource, block, {
90
+ headers: {
91
+ 'content-type': MEDIA_TYPE_DAG_PB,
92
+ 'content-length': `${block.byteLength}`,
93
+ 'content-disposition': `${url.searchParams.get('download') === 'true' ? 'attachment' : 'inline'}; ${getContentDispositionFilename(`${dirCid}.dir`)}`,
94
+ 'x-ipfs-roots': ipfsRoots.map(cid => cid.toV1()).join(','),
95
+ 'accept-ranges': 'bytes'
96
+ },
97
+ redirected
98
+ });
99
+ }
100
+ else if (terminalElement.type === 'file' || terminalElement.type === 'raw' || terminalElement.type === 'identity') {
101
+ this.log('streaming file');
102
+ return this.streamFile(resource, terminalElement, filename, redirected, context.range, context.options);
103
+ }
104
+ else {
105
+ this.log.error('cannot stream terminal element type %s', terminalElement.type);
106
+ return badGatewayResponse(resource, 'Unable to stream content');
107
+ }
108
+ }
109
+ async streamFile(resource, entry, filename, redirected, rangeHeader, options) {
110
+ let contentType = MEDIA_TYPE_OCTET_STREAM;
111
+ // only detect content type for non-range requests to avoid loading blocks
112
+ // we aren't going to stream to the user
113
+ if (rangeHeader == null) {
114
+ contentType = await this.detectContentType(entry, filename, options);
115
+ }
116
+ if (rangeHeader != null) {
117
+ return partialContentResponse(resource, (offset, length) => {
118
+ return entry.content({
119
+ ...(options ?? {}),
120
+ offset,
121
+ length
122
+ });
123
+ }, rangeHeader, entry.size, {
124
+ headers: {
125
+ 'content-type': contentType,
126
+ 'content-disposition': `inline; ${getContentDispositionFilename(filename)}`,
127
+ 'x-ipfs-roots': entry.cid.toString(),
128
+ 'accept-ranges': 'bytes'
129
+ },
130
+ redirected
131
+ });
132
+ }
133
+ // nb. if streaming the output fails (network error, unresolvable block,
134
+ // etc), a "TypeError: Failed to fetch" error will occur
135
+ return okResponse(resource, itToBrowserReadableStream(entry.content(options)), {
136
+ headers: {
137
+ 'content-type': contentType,
138
+ 'content-length': `${entry.size}`,
139
+ 'content-disposition': `inline; ${getContentDispositionFilename(filename)}`,
140
+ 'x-ipfs-roots': entry.cid.toString(),
141
+ 'accept-ranges': 'bytes'
142
+ },
143
+ redirected
144
+ });
145
+ }
146
+ async detectContentType(entry, filename, options) {
147
+ let buf;
148
+ if (entry.type === 'raw' || entry.type === 'identity') {
149
+ buf = entry.node;
150
+ }
151
+ else {
152
+ // read the first block of the file
153
+ buf = await first(entry.content(options));
154
+ }
155
+ if (buf == null) {
156
+ throw new Error('stream ended before first block was read');
157
+ }
158
+ let contentType;
159
+ if (this.pluginOptions.contentTypeParser != null) {
160
+ try {
161
+ const parsed = this.pluginOptions.contentTypeParser(buf, filename);
162
+ if (isPromise(parsed)) {
163
+ const result = await parsed;
164
+ if (result != null) {
165
+ contentType = result;
166
+ }
167
+ }
168
+ else if (parsed != null) {
169
+ contentType = parsed;
170
+ }
171
+ this.log.trace('contentTypeParser returned %s for file with name %s', contentType, filename);
172
+ }
173
+ catch (err) {
174
+ this.log.error('error parsing content type - %e', err);
175
+ }
176
+ }
177
+ return contentType ?? MEDIA_TYPE_OCTET_STREAM;
178
+ }
179
+ }
180
+ //# sourceMappingURL=plugin-handle-unixfs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-handle-unixfs.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-unixfs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,KAAK,MAAM,UAAU,CAAA;AAC5B,OAAO,yBAAyB,MAAM,8BAA8B,CAAA;AACpE,OAAO,QAAQ,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AACtF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAA;AAC5F,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACxH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAM7C;;GAEG;AACH,SAAS,cAAc,CAAE,QAAgB,EAAE,GAAQ,EAAE,eAA4B;IAC/E,IAAI,GAAQ,CAAA;IAEZ,IAAI,CAAC;QACH,6BAA6B;QAC7B,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;QACjC,GAAG,GAAG,GAAG,CAAA;IACX,CAAC;IAED,sDAAsD;IACtD,IAAI,eAAe,EAAE,IAAI,KAAK,WAAW,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACzE,+CAA+C;QAC/C,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAA;QAEnB,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;IACvB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,UAAU;IACjC,EAAE,GAAG,eAAe,CAAA;IACpB,KAAK,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;IAEtC,SAAS,CAAE,EAAE,eAAe,EAAE,MAAM,EAAiB;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACjE,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,KAAK,uBAAuB;YAC1H,MAAM,CAAC,WAAW,CAAC,SAAS,KAAK,iBAAiB,CACnD,CAAA;QAED,OAAO,WAAW,IAAI,cAAc,CAAA;IACtC,CAAC;IAED,KAAK,CAAC,MAAM,CAAE,OAAsB;QAClC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;QAC3D,IAAI,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,eAAe,CAAC,IAAI,CAAA;QACvE,IAAI,UAA4B,CAAA;QAEhC,IAAI,eAAe,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACzC,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,eAAe,CAAC,CAAA;YAElE,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;gBAEpE,IAAI,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,EAAE,CAAC;oBAC1C,IAAI,CAAC,GAAG,CAAC,gEAAgE,EAAE,WAAW,CAAC,CAAA;oBACvF,MAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAA;gBACxC,CAAC;qBAAM,IAAI,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;oBAClD,IAAI,CAAC,GAAG,CAAC,wCAAwC,EAAE,WAAW,CAAC,CAAA;oBAC/D,OAAO,wBAAwB,CAAC,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA;gBAChE,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,0BAA0B,EAAE,WAAW,CAAC,CAAA;gBAEjD,iDAAiD;gBACjD,QAAQ,GAAG,WAAW,CAAA;gBACtB,UAAU,GAAG,IAAI,CAAA;YACnB,CAAC;YAED,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAA;YAElC,+DAA+D;YAC/D,IAAI,OAAO,CAAC,OAAO,EAAE,uBAAuB,KAAK,KAAK,EAAE,CAAC;gBACvD,MAAM,YAAY,GAAG,YAAY,CAAA;gBAEjC,IAAI,CAAC;oBACH,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,kDAAkD,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;oBAExF,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,EAAE,QAAQ,CAAC,SAAS,MAAM,IAAI,YAAY,EAAE,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;oBAEnJ,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;wBAC1D,OAAO,kBAAkB,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CAAA;oBACjE,CAAC;oBAED,+DAA+D;oBAC/D,QAAQ,GAAG,YAAY,CAAA;oBAEvB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,4CAA4C,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;oBAE7F,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;gBACrG,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,IAAI,GAAG,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;wBACjC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,+BAA+B,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAA;wBAC1E,MAAM,GAAG,CAAA;oBACX,CAAC;gBACH,CAAC;YACH,CAAC;YAED,oDAAoD;YACpD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;YAE7E,OAAO,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE;gBACjC,OAAO,EAAE;oBACP,cAAc,EAAE,iBAAiB;oBACjC,gBAAgB,EAAE,GAAG,KAAK,CAAC,UAAU,EAAE;oBACvC,qBAAqB,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,KAC7F,6BAA6B,CAAC,GAAG,MAAM,MAAM,CAC/C,EAAE;oBACF,cAAc,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;oBAC1D,eAAe,EAAE,OAAO;iBACzB;gBACD,UAAU;aACX,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,eAAe,CAAC,IAAI,KAAK,MAAM,IAAI,eAAe,CAAC,IAAI,KAAK,KAAK,IAAI,eAAe,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACpH,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;YAC1B,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;QACzG,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,wCAAwC,EAAE,eAAe,CAAC,IAAI,CAAC,CAAA;YAC9E,OAAO,kBAAkB,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CAAA;QACjE,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CAAE,QAAgB,EAAE,KAA0C,EAAE,QAAgB,EAAE,UAAoB,EAAE,WAAyB,EAAE,OAAsB;QAC/K,IAAI,WAAW,GAAG,uBAAuB,CAAA;QAEzC,0EAA0E;QAC1E,wCAAwC;QACxC,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,WAAW,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QACtE,CAAC;QAED,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,OAAO,sBAAsB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;gBACzD,OAAO,KAAK,CAAC,OAAO,CAAC;oBACnB,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;oBAClB,MAAM;oBACN,MAAM;iBACP,CAAC,CAAA;YACJ,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE;gBAC1B,OAAO,EAAE;oBACP,cAAc,EAAE,WAAW;oBAC3B,qBAAqB,EAAE,WACrB,6BAA6B,CAAC,QAAQ,CACxC,EAAE;oBACF,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;oBACpC,eAAe,EAAE,OAAO;iBACzB;gBACD,UAAU;aACX,CAAC,CAAA;QACJ,CAAC;QAED,wEAAwE;QACxE,wDAAwD;QACxD,OAAO,UAAU,CAAC,QAAQ,EAAE,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE;YAC7E,OAAO,EAAE;gBACP,cAAc,EAAE,WAAW;gBAC3B,gBAAgB,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE;gBACjC,qBAAqB,EAAE,WACrB,6BAA6B,CAAC,QAAQ,CACxC,EAAE;gBACF,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;gBACpC,eAAe,EAAE,OAAO;aACzB;YACD,UAAU;SACX,CAAC,CAAA;IACJ,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAE,KAA0C,EAAE,QAAiB,EAAE,OAAsB;QACpH,IAAI,GAA2B,CAAA;QAE/B,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACtD,GAAG,GAAG,KAAK,CAAC,IAAI,CAAA;QAClB,CAAC;aAAM,CAAC;YACN,mCAAmC;YACnC,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;QAC3C,CAAC;QAED,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QAC7D,CAAC;QAED,IAAI,WAA+B,CAAA;QAEnC,IAAI,IAAI,CAAC,aAAa,CAAC,iBAAiB,IAAI,IAAI,EAAE,CAAC;YACjD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;gBAElE,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBACtB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAA;oBAE3B,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;wBACnB,WAAW,GAAG,MAAM,CAAA;oBACtB,CAAC;gBACH,CAAC;qBAAM,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;oBAC1B,WAAW,GAAG,MAAM,CAAA;gBACtB,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,qDAAqD,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAA;YAC9F,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;QAED,OAAO,WAAW,IAAI,uBAAuB,CAAA;IAC/C,CAAC;CACF"}
@@ -1,78 +1,2 @@
1
- import type { ResolveURLResult, VerifiedFetchInit, ContentTypeParser, RequestFormatShorthand } from '../index.js';
2
- import type { ByteRangeContext } from '../utils/byte-range-context.js';
3
- import type { AcceptHeader } from '../utils/select-output-type.ts';
4
- import type { ServerTiming } from '../utils/server-timing.ts';
5
- import type { PathWalkerResponse } from '../utils/walk-path.js';
6
- import type { IPNSResolver } from '@helia/ipns';
7
- import type { AbortOptions, Logger } from '@libp2p/interface';
8
- import type { Helia } from 'helia';
9
- import type { Blockstore } from 'interface-blockstore';
10
- import type { UnixFSEntry } from 'ipfs-unixfs-exporter';
11
- import type { CID } from 'multiformats/cid';
12
- import type { CustomProgressEvent } from 'progress-events';
13
- /**
14
- * Contains common components and functions required by plugins to handle a request.
15
- * - Read-Only: Plugins can read but shouldn't rewrite them.
16
- * - Persistent: Relevant even after the request completes (e.g., logging or metrics).
17
- */
18
- export interface PluginOptions {
19
- logger: Logger;
20
- getBlockstore(cid: CID, resource: string | CID, useSession?: boolean, options?: AbortOptions): Blockstore;
21
- contentTypeParser?: ContentTypeParser;
22
- helia: Helia;
23
- ipnsResolver: IPNSResolver;
24
- }
25
- /**
26
- * Represents the ephemeral, modifiable state used by the pipeline.
27
- * - Mutable: Evolves as you walk the plugin chain.
28
- * - Shared Data: Allows plugins to communicate partial results, discovered data, or interim errors.
29
- * - Ephemeral: Typically discarded once fetch(...) completes.
30
- */
31
- export interface PluginContext extends ResolveURLResult {
32
- readonly resource: string;
33
- readonly accept?: AcceptHeader;
34
- /**
35
- * An array of plugin IDs that are all enabled. You can use this to check if a plugin is enabled and respond accordingly.
36
- */
37
- plugins: string[];
38
- /**
39
- * The last time the context is modified, so we know whether a plugin has modified it.
40
- * A plugin should increment this value if it modifies the context.
41
- */
42
- modified: number;
43
- onProgress?(evt: CustomProgressEvent<any>): void;
44
- options?: Omit<VerifiedFetchInit, 'signal'> & AbortOptions;
45
- isDirectory?: boolean;
46
- directoryEntries?: UnixFSEntry[];
47
- reqFormat?: RequestFormatShorthand;
48
- pathDetails?: PathWalkerResponse;
49
- /**
50
- * ByteRangeContext contains information about the size of the content and range requests.
51
- * This can be used to set the Content-Length header without loading the entire body.
52
- *
53
- * This is set by the ByteRangeContextPlugin
54
- */
55
- byteRangeContext?: ByteRangeContext;
56
- serverTiming: ServerTiming;
57
- ipfsPath: string;
58
- /**
59
- * Allow arbitrary keys/values
60
- */
61
- [key: string]: unknown;
62
- }
63
- export interface VerifiedFetchPlugin {
64
- readonly id: string;
65
- readonly codes: number[];
66
- readonly log: Logger;
67
- canHandle(context: PluginContext): boolean;
68
- handle(context: PluginContext): Promise<Response | null>;
69
- }
70
- export interface VerifiedFetchPluginFactory {
71
- (options: PluginOptions): VerifiedFetchPlugin;
72
- }
73
- export interface PluginErrorOptions {
74
- fatal?: boolean;
75
- details?: Record<string, any>;
76
- response?: Response;
77
- }
1
+ export {};
78
2
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/types.ts"],"names":[],"mappings":""}
@@ -1,22 +1,40 @@
1
1
  import { CID } from 'multiformats/cid';
2
- import type { ResolveURLOptions, ResolveURLResult, Resource, URLResolver as URLResolverInterface } from './index.ts';
3
- import type { ParsedURL } from './utils/parse-url-string.ts';
4
- import type { ServerTiming } from './utils/server-timing.ts';
2
+ import { ServerTiming } from './utils/server-timing.ts';
3
+ import type { ResolveURLResult, URLResolver as URLResolverInterface } from './index.ts';
5
4
  import type { DNSLink } from '@helia/dnslink';
6
5
  import type { IPNSResolver } from '@helia/ipns';
7
- import type { AbortOptions, PeerId } from '@libp2p/interface';
6
+ import type { AbortOptions } from '@libp2p/interface';
7
+ import type { Helia } from 'helia';
8
+ import type { Blockstore } from 'interface-blockstore';
9
+ import type { UnixFSEntry } from 'ipfs-unixfs-exporter';
10
+ export interface WalkPathResult {
11
+ ipfsRoots: CID[];
12
+ terminalElement: UnixFSEntry;
13
+ blockstore: Blockstore;
14
+ }
8
15
  export interface URLResolverComponents {
16
+ helia: Helia;
9
17
  ipnsResolver: IPNSResolver;
10
18
  dnsLink: DNSLink;
11
- timing: ServerTiming;
19
+ }
20
+ export interface URLResolverInit {
21
+ sessionCacheSize?: number;
22
+ sessionTTLms?: number;
23
+ }
24
+ export interface ResolveURLOptions extends AbortOptions {
25
+ session?: boolean;
26
+ isRawBlockRequest?: boolean;
27
+ onlyIfCached?: boolean;
12
28
  }
13
29
  export declare class URLResolver implements URLResolverInterface {
14
30
  private readonly components;
15
- constructor(components: URLResolverComponents);
16
- resolve(resource: Resource, options?: ResolveURLOptions): Promise<ResolveURLResult>;
17
- parseUrlString(urlString: string, options?: ResolveURLOptions): Promise<ResolveURLResult>;
18
- resolveCIDResource(cid: CID, parsed: Partial<ParsedURL> & Pick<ParsedURL, 'url'>, options?: ResolveURLOptions): Promise<ResolveURLResult>;
19
- resolveDNSLink(domain: string, parsed: ParsedURL, options?: ResolveURLOptions): Promise<ResolveURLResult>;
20
- resolveIPNSName(resource: string, key: PeerId, parsed: Partial<ParsedURL> & Pick<ParsedURL, 'url'>, options?: AbortOptions): Promise<ResolveURLResult>;
31
+ private readonly blockstoreSessions;
32
+ constructor(components: URLResolverComponents, init?: URLResolverInit);
33
+ resolve(url: URL, serverTiming?: ServerTiming, options?: ResolveURLOptions): Promise<ResolveURLResult>;
34
+ private getBlockstore;
35
+ private resolveDNSLink;
36
+ private resolveIPNSName;
37
+ private resolveIPFSPath;
38
+ private walkPath;
21
39
  }
22
40
  //# sourceMappingURL=url-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"url-resolver.d.ts","sourceRoot":"","sources":["../../src/url-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACpH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAI7D,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,YAAY,CAAA;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,YAAY,CAAA;CACrB;AAED,qBAAa,WAAY,YAAW,oBAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;gBAErC,UAAU,EAAE,qBAAqB;IAIxC,OAAO,CAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgBxF,cAAc,CAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2B9F,kBAAkB,CAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkB9I,cAAc,CAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgC1G,eAAe,CAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAe9J"}
1
+ {"version":3,"file":"url-resolver.d.ts","sourceRoot":"","sources":["../../src/url-resolver.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAMtC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAyBvD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,GAAG,EAAE,CAAA;IAChB,eAAe,EAAE,WAAW,CAAA;IAC5B,UAAU,EAAE,UAAU,CAAA;CACvB;AAiBD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,YAAY,CAAA;IAC1B,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,qBAAa,WAAY,YAAW,oBAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqC;gBAE3D,UAAU,EAAE,qBAAqB,EAAE,IAAI,GAAE,eAAoB;IAYpE,OAAO,CAAE,GAAG,EAAE,GAAG,EAAE,YAAY,GAAE,YAAiC,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgBrI,OAAO,CAAC,aAAa;YAgBP,cAAc;YAiCd,eAAe;YAmBf,eAAe;YAWf,QAAQ;CAuDvB"}
@@ -1,106 +1,184 @@
1
- import { peerIdFromCID, peerIdFromString } from '@libp2p/peer-id';
1
+ import { DoesNotExistError } from '@helia/unixfs/errors';
2
+ import * as dagCbor from '@ipld/dag-cbor';
3
+ import * as dagJson from '@ipld/dag-json';
4
+ import * as dagPb from '@ipld/dag-pb';
5
+ import { peerIdFromString } from '@libp2p/peer-id';
6
+ import { InvalidParametersError, walkPath } from 'ipfs-unixfs-exporter';
7
+ import toBuffer from 'it-to-buffer';
2
8
  import { CID } from 'multiformats/cid';
3
- import { parseURLString } from "./utils/parse-url-string.js";
4
- const CODEC_LIBP2P_KEY = 0x72;
9
+ import * as json from 'multiformats/codecs/json';
10
+ import * as raw from 'multiformats/codecs/raw';
11
+ import QuickLRU from 'quick-lru';
12
+ import { SESSION_CACHE_MAX_SIZE, SESSION_CACHE_TTL_MS, CODEC_CBOR, CODEC_IDENTITY } from "./constants.js";
13
+ import { resourceToSessionCacheKey } from "./utils/resource-to-cache-key.js";
14
+ import { ServerTiming } from "./utils/server-timing.js";
15
+ // 1 year in seconds for ipfs content
16
+ const IPFS_CONTENT_TTL = 29030400;
17
+ const ENTITY_CODECS = [
18
+ CODEC_CBOR,
19
+ json.code,
20
+ raw.code
21
+ ];
22
+ /**
23
+ * These are supported by the UnixFS exporter
24
+ */
25
+ const EXPORTABLE_CODECS = [
26
+ dagPb.code,
27
+ dagCbor.code,
28
+ dagJson.code,
29
+ raw.code
30
+ ];
31
+ function basicEntry(type, cid, bytes) {
32
+ return {
33
+ name: cid.toString(),
34
+ path: cid.toString(),
35
+ depth: 0,
36
+ type,
37
+ node: bytes,
38
+ cid,
39
+ size: BigInt(bytes.byteLength),
40
+ content: async function* () {
41
+ yield bytes;
42
+ }
43
+ };
44
+ }
5
45
  export class URLResolver {
6
46
  components;
7
- constructor(components) {
47
+ blockstoreSessions;
48
+ constructor(components, init = {}) {
8
49
  this.components = components;
50
+ this.blockstoreSessions = new QuickLRU({
51
+ maxSize: init.sessionCacheSize ?? SESSION_CACHE_MAX_SIZE,
52
+ maxAge: init.sessionTTLms ?? SESSION_CACHE_TTL_MS,
53
+ onEviction: (key, store) => {
54
+ store.close();
55
+ }
56
+ });
9
57
  }
10
- async resolve(resource, options = {}) {
11
- if (typeof resource === 'string') {
12
- return this.parseUrlString(resource, options);
58
+ async resolve(url, serverTiming = new ServerTiming(), options = {}) {
59
+ if (url.protocol === 'ipfs:') {
60
+ return this.resolveIPFSPath(url, serverTiming, options);
13
61
  }
14
- const cid = CID.asCID(resource);
15
- if (cid != null) {
16
- return this.resolveCIDResource(cid, {
17
- url: new URL(`ipfs://${cid}`)
18
- }, options);
62
+ if (url.protocol === 'ipns:') {
63
+ return this.resolveIPNSName(url, serverTiming, options);
19
64
  }
20
- throw new TypeError(`Invalid resource. Cannot determine CID from resource: ${resource}`);
21
- }
22
- async parseUrlString(urlString, options = {}) {
23
- const result = parseURLString(urlString);
24
- if (result.protocol === 'ipfs') {
25
- const cid = CID.parse(result.cidOrPeerIdOrDnsLink);
26
- return this.resolveCIDResource(cid, result, options);
27
- }
28
- if (result.protocol === 'ipns') {
29
- // try to parse target as peer id
30
- let peerId;
31
- try {
32
- peerId = peerIdFromString(result.cidOrPeerIdOrDnsLink);
33
- }
34
- catch {
35
- // fall back to DNSLink (e.g. /ipns/example.com)
36
- return this.resolveDNSLink(result.cidOrPeerIdOrDnsLink, result, options);
37
- }
38
- // parse multihash from string (e.g. /ipns/QmFoo...)
39
- return this.resolveIPNSName(result.cidOrPeerIdOrDnsLink, peerId, result, options);
65
+ if (url.protocol === 'dnslink:') {
66
+ return this.resolveDNSLink(url, serverTiming, options);
40
67
  }
41
- throw new TypeError(`Invalid resource. Cannot determine CID from resource: ${urlString}`);
68
+ throw new InvalidParametersError(`Invalid resource. Unsupported protocol in URL, must be ipfs:, ipns:, or dnslink: ${url}`);
42
69
  }
43
- async resolveCIDResource(cid, parsed, options = {}) {
44
- if (cid.code === CODEC_LIBP2P_KEY) {
45
- // special case - peer id encoded as a CID
46
- return this.resolveIPNSName(cid.toString(), peerIdFromCID(cid), parsed, options);
70
+ getBlockstore(root, options = {}) {
71
+ if (options.session === false) {
72
+ return this.components.helia.blockstore;
47
73
  }
48
- return {
49
- url: parsed.url,
50
- cid,
51
- protocol: 'ipfs',
52
- query: parsed.query ?? {},
53
- path: parsed.path ?? [],
54
- fragment: parsed.fragment ?? '',
55
- ttl: 29030400, // 1 year for ipfs content
56
- ipfsPath: `/ipfs/${cid}${parsed.url.pathname}`
57
- };
74
+ const key = resourceToSessionCacheKey(root);
75
+ let session = this.blockstoreSessions.get(key);
76
+ if (session == null) {
77
+ session = this.components.helia.blockstore.createSession(root, options);
78
+ this.blockstoreSessions.set(key, session);
79
+ }
80
+ return session;
58
81
  }
59
- async resolveDNSLink(domain, parsed, options) {
60
- const results = await this.components.timing.time('dnsLink.resolve', `Resolve DNSLink ${domain}`, this.components.dnsLink.resolve(domain, options));
82
+ async resolveDNSLink(url, serverTiming, options) {
83
+ const results = await serverTiming.time('dnsLink.resolve', `Resolve DNSLink ${url.hostname}`, this.components.dnsLink.resolve(url.hostname, options));
61
84
  const result = results?.[0];
62
85
  if (result == null) {
63
- throw new TypeError(`Invalid resource. Cannot resolve DNSLink from domain: ${domain}`);
86
+ throw new TypeError(`Invalid resource. Cannot resolve DNSLink from domain: ${url.hostname}`);
64
87
  }
65
88
  // dnslink resolved to IPNS name
66
89
  if (result.namespace === 'ipns') {
67
- return this.resolveIPNSName(domain, result.peerId, parsed, options);
90
+ return this.resolveIPNSName(url, serverTiming, options);
68
91
  }
69
92
  // dnslink resolved to CID
70
93
  if (result.namespace !== 'ipfs') {
71
94
  // @ts-expect-error result namespace should only be ipns or ipfs
72
95
  throw new TypeError(`Invalid resource. Unexpected DNSLink namespace ${result.namespace} from domain: ${domain}`);
73
96
  }
97
+ if (result.path != null && url.pathname !== '') {
98
+ // path conflict?
99
+ }
100
+ const ipfsUrl = new URL(`ipfs://${result.cid}/${url.pathname}`);
101
+ const ipfsResult = await this.resolveIPFSPath(ipfsUrl, serverTiming, options);
74
102
  return {
75
- url: parsed.url,
76
- cid: result.cid,
77
- path: concatPaths(...(result.path ?? '').split('/'), ...(parsed.path ?? [])),
78
- fragment: parsed.fragment,
79
- // dnslink is mutable so return 'ipns' protocol so we do not include immutable in cache-control header
80
- protocol: 'ipns',
81
- ttl: result.answer.TTL,
82
- query: parsed.query,
83
- ipfsPath: `/ipns/${domain}${parsed.url.pathname}`
103
+ ...ipfsResult,
104
+ url,
105
+ ttl: result.answer.TTL
84
106
  };
85
107
  }
86
- async resolveIPNSName(resource, key, parsed, options) {
87
- const result = await this.components.timing.time('ipns.resolve', `Resolve IPNS name ${key}`, this.components.ipnsResolver.resolve(key, options));
108
+ async resolveIPNSName(url, serverTiming, options) {
109
+ const peerId = peerIdFromString(url.hostname);
110
+ const result = await serverTiming.time('ipns.resolve', `Resolve IPNS name ${peerId}`, this.components.ipnsResolver.resolve(peerId, options));
111
+ if (result.path != null && url.pathname !== '') {
112
+ // path conflict?
113
+ }
114
+ const ipfsUrl = new URL(`ipfs://${result.cid}/${url.pathname}`);
115
+ const ipfsResult = await this.resolveIPFSPath(ipfsUrl, serverTiming, options);
88
116
  return {
89
- url: parsed.url,
90
- cid: result.cid,
91
- path: concatPaths(...(result.path ?? '').split('/'), ...(parsed.path ?? [])),
92
- query: parsed.query ?? {},
93
- fragment: parsed.fragment ?? '',
94
- protocol: 'ipns',
117
+ ...ipfsResult,
118
+ url,
95
119
  // IPNS ttl is in nanoseconds, convert to seconds
96
- ttl: Number((result.record.ttl ?? 0n) / BigInt(1e9)),
97
- ipfsPath: `/ipns/${resource}${parsed.url.pathname}`
120
+ ttl: Number((result.record.ttl ?? 0n) / BigInt(1e9))
121
+ };
122
+ }
123
+ async resolveIPFSPath(url, serverTiming, options) {
124
+ const walkPathResult = await serverTiming.time('ipfs.resolve', '', this.walkPath(url, options));
125
+ return {
126
+ ...walkPathResult,
127
+ url,
128
+ ttl: IPFS_CONTENT_TTL,
129
+ blockstore: walkPathResult.blockstore
130
+ };
131
+ }
132
+ async walkPath(url, options = {}) {
133
+ const cid = CID.parse(url.hostname);
134
+ const blockstore = this.getBlockstore(cid, options);
135
+ if (EXPORTABLE_CODECS.includes(cid.code)) {
136
+ const ipfsRoots = [];
137
+ let terminalElement;
138
+ const ipfsPath = toIPFSPath(url);
139
+ // @ts-expect-error offline is a helia option
140
+ for await (const entry of walkPath(ipfsPath, blockstore, {
141
+ ...options,
142
+ offline: options.onlyIfCached === true,
143
+ extended: options.isRawBlockRequest !== true
144
+ })) {
145
+ ipfsRoots.push(entry.cid);
146
+ terminalElement = entry;
147
+ }
148
+ if (terminalElement == null) {
149
+ throw new DoesNotExistError('No terminal element found');
150
+ }
151
+ return {
152
+ ipfsRoots,
153
+ terminalElement,
154
+ blockstore
155
+ };
156
+ }
157
+ let bytes;
158
+ if (cid.multihash.code === CODEC_IDENTITY) {
159
+ bytes = cid.multihash.digest;
160
+ }
161
+ else {
162
+ bytes = await toBuffer(blockstore.get(cid, options));
163
+ }
164
+ // entity codecs contain all the bytes for an entity in one block and no
165
+ // path walking outside of that block is possible
166
+ if (ENTITY_CODECS.includes(cid.code)) {
167
+ return {
168
+ ipfsRoots: [cid],
169
+ terminalElement: basicEntry('object', cid, bytes),
170
+ blockstore
171
+ };
172
+ }
173
+ // may be an unknown codec
174
+ return {
175
+ ipfsRoots: [cid],
176
+ terminalElement: basicEntry('raw', cid, bytes),
177
+ blockstore
98
178
  };
99
179
  }
100
180
  }
101
- function concatPaths(...paths) {
102
- // @ts-expect-error undefined is filtered out
103
- return paths
104
- .filter(p => p != null && p !== '');
181
+ function toIPFSPath(url) {
182
+ return `/ipfs/${url.hostname}${decodeURI(url.pathname)}`;
105
183
  }
106
184
  //# sourceMappingURL=url-resolver.js.map