@helia/verified-fetch 2.4.0 → 2.5.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 (104) hide show
  1. package/README.md +192 -0
  2. package/dist/index.min.js +357 -32
  3. package/dist/src/index.d.ts +198 -0
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +192 -0
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/plugins/errors.d.ts +25 -0
  8. package/dist/src/plugins/errors.d.ts.map +1 -0
  9. package/dist/src/plugins/errors.js +33 -0
  10. package/dist/src/plugins/errors.js.map +1 -0
  11. package/dist/src/plugins/index.d.ts +8 -0
  12. package/dist/src/plugins/index.d.ts.map +1 -0
  13. package/dist/src/plugins/index.js +7 -0
  14. package/dist/src/plugins/index.js.map +1 -0
  15. package/dist/src/plugins/plugin-base.d.ts +19 -0
  16. package/dist/src/plugins/plugin-base.d.ts.map +1 -0
  17. package/dist/src/plugins/plugin-base.js +26 -0
  18. package/dist/src/plugins/plugin-base.js.map +1 -0
  19. package/dist/src/plugins/plugin-handle-car.d.ts +11 -0
  20. package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -0
  21. package/dist/src/plugins/plugin-handle-car.js +28 -0
  22. package/dist/src/plugins/plugin-handle-car.js.map +1 -0
  23. package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +11 -0
  24. package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +1 -0
  25. package/dist/src/plugins/plugin-handle-dag-cbor.js +73 -0
  26. package/dist/src/plugins/plugin-handle-dag-cbor.js.map +1 -0
  27. package/dist/src/plugins/plugin-handle-dag-pb.d.ts +15 -0
  28. package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +1 -0
  29. package/dist/src/plugins/plugin-handle-dag-pb.js +152 -0
  30. package/dist/src/plugins/plugin-handle-dag-pb.js.map +1 -0
  31. package/dist/src/plugins/plugin-handle-dag-walk.d.ts +16 -0
  32. package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +1 -0
  33. package/dist/src/plugins/plugin-handle-dag-walk.js +45 -0
  34. package/dist/src/plugins/plugin-handle-dag-walk.js.map +1 -0
  35. package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +9 -0
  36. package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +1 -0
  37. package/dist/src/plugins/plugin-handle-dir-index-html.js +37 -0
  38. package/dist/src/plugins/plugin-handle-dir-index-html.js.map +1 -0
  39. package/dist/src/plugins/plugin-handle-ipns-record.d.ts +12 -0
  40. package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -0
  41. package/dist/src/plugins/plugin-handle-ipns-record.js +62 -0
  42. package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -0
  43. package/dist/src/plugins/plugin-handle-json.d.ts +11 -0
  44. package/dist/src/plugins/plugin-handle-json.d.ts.map +1 -0
  45. package/dist/src/plugins/plugin-handle-json.js +51 -0
  46. package/dist/src/plugins/plugin-handle-json.js.map +1 -0
  47. package/dist/src/plugins/plugin-handle-raw.d.ts +8 -0
  48. package/dist/src/plugins/plugin-handle-raw.d.ts.map +1 -0
  49. package/dist/src/plugins/plugin-handle-raw.js +80 -0
  50. package/dist/src/plugins/plugin-handle-raw.js.map +1 -0
  51. package/dist/src/plugins/plugin-handle-tar.d.ts +12 -0
  52. package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -0
  53. package/dist/src/plugins/plugin-handle-tar.js +36 -0
  54. package/dist/src/plugins/plugin-handle-tar.js.map +1 -0
  55. package/dist/src/plugins/plugins.d.ts +5 -0
  56. package/dist/src/plugins/plugins.d.ts.map +1 -0
  57. package/dist/src/plugins/plugins.js +5 -0
  58. package/dist/src/plugins/plugins.js.map +1 -0
  59. package/dist/src/plugins/types.d.ts +68 -0
  60. package/dist/src/plugins/types.d.ts.map +1 -0
  61. package/dist/src/plugins/types.js +2 -0
  62. package/dist/src/plugins/types.js.map +1 -0
  63. package/dist/src/types.d.ts +0 -27
  64. package/dist/src/types.d.ts.map +1 -1
  65. package/dist/src/types.js +1 -2
  66. package/dist/src/types.js.map +1 -1
  67. package/dist/src/utils/dir-index-html.d.ts +16 -0
  68. package/dist/src/utils/dir-index-html.d.ts.map +1 -0
  69. package/dist/src/utils/dir-index-html.js +387 -0
  70. package/dist/src/utils/dir-index-html.js.map +1 -0
  71. package/dist/src/utils/get-e-tag.d.ts +1 -1
  72. package/dist/src/utils/get-e-tag.d.ts.map +1 -1
  73. package/dist/src/utils/get-e-tag.js +18 -3
  74. package/dist/src/utils/get-e-tag.js.map +1 -1
  75. package/dist/src/utils/walk-path.d.ts +3 -2
  76. package/dist/src/utils/walk-path.d.ts.map +1 -1
  77. package/dist/src/utils/walk-path.js +1 -1
  78. package/dist/src/utils/walk-path.js.map +1 -1
  79. package/dist/src/verified-fetch.d.ts +6 -24
  80. package/dist/src/verified-fetch.d.ts.map +1 -1
  81. package/dist/src/verified-fetch.js +164 -387
  82. package/dist/src/verified-fetch.js.map +1 -1
  83. package/dist/typedoc-urls.json +32 -24
  84. package/package.json +6 -2
  85. package/src/index.ts +199 -0
  86. package/src/plugins/errors.ts +37 -0
  87. package/src/plugins/index.ts +8 -0
  88. package/src/plugins/plugin-base.ts +30 -0
  89. package/src/plugins/plugin-handle-car.ts +32 -0
  90. package/src/plugins/plugin-handle-dag-cbor.ts +84 -0
  91. package/src/plugins/plugin-handle-dag-pb.ts +168 -0
  92. package/src/plugins/plugin-handle-dag-walk.ts +53 -0
  93. package/src/plugins/plugin-handle-dir-index-html.ts +44 -0
  94. package/src/plugins/plugin-handle-ipns-record.ts +69 -0
  95. package/src/plugins/plugin-handle-json.ts +57 -0
  96. package/src/plugins/plugin-handle-raw.ts +92 -0
  97. package/src/plugins/plugin-handle-tar.ts +44 -0
  98. package/src/plugins/plugins.ts +4 -0
  99. package/src/plugins/types.ts +73 -0
  100. package/src/types.ts +0 -34
  101. package/src/utils/dir-index-html.ts +445 -0
  102. package/src/utils/get-e-tag.ts +20 -3
  103. package/src/utils/walk-path.ts +3 -3
  104. package/src/verified-fetch.ts +187 -430
@@ -0,0 +1,445 @@
1
+ import type { Logger } from '@libp2p/interface'
2
+ import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
3
+
4
+ /**
5
+ * Types taken from:
6
+ *
7
+ * * https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/assets/assets.go#L92C1-L96C2
8
+ * * https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/assets/assets.go#L114C1-L135C2
9
+ */
10
+
11
+ interface GlobalData {
12
+ // Menu []MenuItem
13
+ gatewayURL: string
14
+ dnsLink: boolean
15
+ // root: UnixFSEntry
16
+ }
17
+
18
+ interface DirectoryTemplateData {
19
+ globalData: GlobalData
20
+ listing: DirectoryItem[]
21
+ size: string
22
+ path: string
23
+ breadcrumbs: Breadcrumb[]
24
+ backLink: string
25
+ hash: string
26
+ name: string
27
+ }
28
+
29
+ interface DirectoryItem {
30
+ size: string
31
+ name: string
32
+ path: string
33
+ hash: string
34
+ shortHash: string
35
+ }
36
+
37
+ interface Breadcrumb {
38
+ name: string
39
+ path: string
40
+ }
41
+
42
+ export interface DirIndexHtmlOptions {
43
+ gatewayURL: string
44
+ dnsLink?: boolean
45
+ log: Logger
46
+ }
47
+
48
+ // see https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/assets/templates.go#L19C1-L25C2
49
+ function iconFromExt (name: string): string {
50
+ // not implemented yet
51
+ // TODO: optimize icons: https://github.com/ipfs-shipyard/ipfs-css/issues/71
52
+ return 'ipfs-_blank'
53
+ }
54
+
55
+ function itemShortHashCell (item: DirectoryItem, dirData: DirectoryTemplateData): string {
56
+ const href = dirData.globalData.dnsLink ? `https://inbrowser.dev/ipfs/${item.hash}` : `${dirData.globalData.gatewayURL}/ipfs/${item.hash}?filename=${item.name}`
57
+
58
+ return `<a class="ipfs-hash" translate="no" href="${href}">${item.shortHash}</a>`
59
+ }
60
+
61
+ function dirListingTitle (dirData: DirectoryTemplateData): string {
62
+ if (dirData.path != null) {
63
+ const href = `${dirData.globalData.gatewayURL}/${dirData.path}`
64
+ return `Index of <a href="${href}">${dirData.name}</a>`
65
+ }
66
+ return `Index of ${dirData.name} ${dirData.path}`
67
+ }
68
+
69
+ function getAllDirListingRows (dirData: DirectoryTemplateData): string {
70
+ return dirData.listing.map((item) => `<div class="type-icon">
71
+ <div class="${iconFromExt(item.name)}">&nbsp;</div>
72
+ </div>
73
+ <div>
74
+ <a href="${item.path}">${item.name}</a>
75
+ </div>
76
+ <div class="nowrap">
77
+ ${itemShortHashCell(item, dirData)}
78
+ </div>
79
+ <div class="nowrap" title="Cumulative size of IPFS DAG (data + metadata)">${item.size}</div>`).join(' ')
80
+ }
81
+
82
+ function getItemPath (item: UnixFSEntry): string {
83
+ const itemPathParts = item.path.split('/')
84
+
85
+ return itemPathParts.pop() ?? item.path
86
+ }
87
+
88
+ /**
89
+ * todo: https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/handler_unixfs_dir.go#L200-L208
90
+ *
91
+ * @see https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/assets/directory.html
92
+ * @see https://github.com/ipfs/boxo/pull/298
93
+ * @see https://github.com/ipfs/kubo/pull/8555
94
+ */
95
+ export const dirIndexHtml = (dir: UnixFSEntry, items: UnixFSEntry[], { gatewayURL, dnsLink, log }: DirIndexHtmlOptions): string => {
96
+ log('loading directory html for %s', dir.path)
97
+
98
+ const dirData: DirectoryTemplateData = {
99
+ globalData: {
100
+ gatewayURL,
101
+ dnsLink: dnsLink ?? false
102
+ },
103
+ listing: items.map((item) => {
104
+ return {
105
+ size: item.size.toString(),
106
+ name: item.name,
107
+ path: getItemPath(item),
108
+ hash: item.cid.toString(),
109
+ shortHash: item.cid.toString().slice(0, 8)
110
+ } satisfies DirectoryItem
111
+ }),
112
+ name: dir.name,
113
+ size: dir.size.toString(),
114
+ path: dir.path,
115
+ breadcrumbs: [],
116
+ backLink: '',
117
+ hash: dir.cid.toString()
118
+ }
119
+
120
+ return `
121
+ <!DOCTYPE html>
122
+ <!--{{ $root := . }}-->
123
+ <html lang="en">
124
+ <head>
125
+ <meta charset="utf-8" />
126
+ <meta name="description" content="A directory of content-addressed files hosted on IPFS.">
127
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
128
+ <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlo89/56ZQ/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUjDu1lo89/6mhTP+zrVP/nplD/5+aRK8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHNiIS6Wjz3/ubFY/761W/+vp1D/urRZ/8vDZf/GvmH/nplD/1BNIm8AAAAAAAAAAAAAAAAAAAAAAAAAAJaPPf+knEj/vrVb/761W/++tVv/r6dQ/7q0Wf/Lw2X/y8Nl/8vDZf+tpk7/nplD/wAAAAAAAAAAAAAAAJaPPf+2rVX/vrVb/761W/++tVv/vrVb/6+nUP+6tFn/y8Nl/8vDZf/Lw2X/y8Nl/8G6Xv+emUP/AAAAAAAAAACWjz3/vrVb/761W/++tVv/vrVb/761W/+vp1D/urRZ/8vDZf/Lw2X/y8Nl/8vDZf/Lw2X/nplD/wAAAAAAAAAAlo89/761W/++tVv/vrVb/761W/++tVv/r6dQ/7q0Wf/Lw2X/y8Nl/8vDZf/Lw2X/y8Nl/56ZQ/8AAAAAAAAAAJaPPf++tVv/vrVb/761W/++tVv/vbRa/5aPPf+emUP/y8Nl/8vDZf/Lw2X/y8Nl/8vDZf+emUP/AAAAAAAAAACWjz3/vrVb/761W/++tVv/vrVb/5qTQP+inkb/op5G/6KdRv/Lw2X/y8Nl/8vDZf/Lw2X/nplD/wAAAAAAAAAAlo89/761W/++tVv/sqlS/56ZQ//LxWb/0Mlp/9DJaf/Kw2X/oJtE/7+3XP/Lw2X/y8Nl/56ZQ/8AAAAAAAAAAJaPPf+9tFr/mJE+/7GsUv/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav+xrFL/nplD/8vDZf+emUP/AAAAAAAAAACWjz3/op5G/9HKav/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav+inkb/nplD/wAAAAAAAAAAAAAAAKKeRv+3slb/0cpq/9HKav/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav+1sFX/op5G/wAAAAAAAAAAAAAAAAAAAAAAAAAAop5GUKKeRv/Nxmf/0cpq/9HKav/Rymr/0cpq/83GZ/+inkb/op5GSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAop5G16KeRv/LxWb/y8Vm/6KeRv+inkaPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAop5G/6KeRtcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/n8AAPgfAADwDwAAwAMAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAwAMAAPAPAAD4HwAA/n8AAA==" />
129
+ <title>${dirData.path}</title>
130
+ <style>${style}</style>
131
+ </head>
132
+ <body>
133
+ <!--
134
+ # Some JSON content for debugging:
135
+
136
+ ## dirData
137
+ ${JSON.stringify(dirData, null, 2)}
138
+ -->
139
+ <header id="header">
140
+ <div class="ipfs-logo">&nbsp;</div>
141
+ <!--
142
+ <nav>
143
+ <a href="https://ipfs.tech" target="_blank" rel="noopener noreferrer">About<span class="dn-mobile"> IPFS</span></a>
144
+ <a href="https://docs.ipfs.tech/install/" target="_blank" rel="noopener noreferrer">Install<span class="dn-mobile"> IPFS</span></a>
145
+ </nav>
146
+ -->
147
+ </header>
148
+ <main id="main">
149
+ <header class="flex flex-wrap">
150
+ <div>
151
+ <strong>${dirListingTitle(dirData)}</strong>
152
+ ${dirData.hash == null
153
+ ? ''
154
+ : `<div class="ipfs-hash" translate="no">
155
+ ${dirData.hash}
156
+ </div>`
157
+ }
158
+ </div>
159
+ ${dirData.size == null
160
+ ? ''
161
+ : `<div class="nowrap flex-shrink ml-auto">
162
+ <strong title="Cumulative size of IPFS DAG (data + metadata)">&nbsp;${dirData.size}</strong>
163
+ </div>`
164
+ }
165
+ </header>
166
+ <section>
167
+ <div class="grid dir">
168
+ <!--{{ if .BackLink }}
169
+ <div class="type-icon">
170
+ <div class="ipfs-_blank">&nbsp;</div>
171
+ </div>
172
+ <div>
173
+ <a href="{{.BackLink | urlEscape}}">..</a>
174
+ </div>
175
+ <div></div>
176
+ <div></div>
177
+ </tr>
178
+ {{ end }}-->
179
+ ${getAllDirListingRows(dirData)}
180
+ </div>
181
+ </section>
182
+ </main>
183
+ </body>
184
+ </html>
185
+ `
186
+ }
187
+
188
+ const style = `
189
+
190
+ .ipfs-_blank {
191
+ background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='36' y1='1' x2='36' y2='99' gradientTransform='matrix(1 0 0 -1 0 100)'%3E%3Cstop offset='0' stop-color='%23c8d4db'/%3E%3Cstop offset='.139' stop-color='%23d8e1e6'/%3E%3Cstop offset='.359' stop-color='%23ebf0f3'/%3E%3Cstop offset='.617' stop-color='%23f9fafb'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3Cpath d='M45 1l27 26.7V99H0V1h45z' fill='url(%23a)'/%3E%3Cpath d='M45 1l27 26.7V99H0V1h45z' fill-opacity='0' stroke='%237191a1' stroke-width='2'/%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='45.068' y1='72.204' x2='58.568' y2='85.705' gradientTransform='matrix(1 0 0 -1 0 100)'%3E%3Cstop offset='0' stop-color='%23fff'/%3E%3Cstop offset='.35' stop-color='%23fafbfb'/%3E%3Cstop offset='.532' stop-color='%23edf1f4'/%3E%3Cstop offset='.675' stop-color='%23dde5e9'/%3E%3Cstop offset='.799' stop-color='%23c7d3da'/%3E%3Cstop offset='.908' stop-color='%23adbdc7'/%3E%3Cstop offset='1' stop-color='%2392a5b0'/%3E%3C/linearGradient%3E%3Cpath d='M45 1l27 26.7H45V1z' fill='url(%23b)'/%3E%3Cpath d='M45 1l27 26.7H45V1z' fill-opacity='0' stroke='%237191a1' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E");
192
+ background-repeat: no-repeat;
193
+ background-size: contain
194
+ }
195
+
196
+ :root {
197
+ --sans-serif: "Plex",system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;
198
+ --monospace: Consolas, monaco, monospace;
199
+ --navy: #073a53;
200
+ --teal: #6bc4ce;
201
+ --turquoise: #47AFB4;
202
+ --steel-gray: #3f5667;
203
+ --dark-white: #d9dbe2;
204
+ --light-white: #edf0f4;
205
+ --near-white: #f7f8fa;
206
+ --radius: 4px;
207
+ }
208
+
209
+ body {
210
+ color: #34373f;
211
+ font-family: var(--sans-serif);
212
+ line-height: 1.43;
213
+ margin: 0;
214
+ word-break: break-all;
215
+ -webkit-text-size-adjust: 100%;
216
+ -ms-text-size-adjust: 100%;
217
+ -webkit-tap-highlight-color: transparent;
218
+ }
219
+
220
+ pre, code {
221
+ font-family: var(--monospace);
222
+ }
223
+
224
+ a {
225
+ color: #117eb3;
226
+ text-decoration: none;
227
+ }
228
+
229
+ a:hover {
230
+ color: #00b0e9;
231
+ text-decoration: underline;
232
+ }
233
+
234
+ a:active,a:visited {
235
+ color: #00b0e9;
236
+ }
237
+
238
+ .flex {
239
+ display: flex;
240
+ }
241
+
242
+ .flex-wrap {
243
+ flex-flow: wrap;
244
+ }
245
+
246
+ .flex-shrink {
247
+ flex-shrink: 1;
248
+ }
249
+
250
+ .ml-auto {
251
+ margin-left: auto;
252
+ }
253
+
254
+ .nowrap {
255
+ white-space: nowrap
256
+ }
257
+
258
+ .ipfs-hash {
259
+ color: #7f8491;
260
+ font-family: var(--monospace);
261
+ }
262
+
263
+ #header {
264
+ align-items: center;
265
+ background: var(--navy);
266
+ border-bottom: 4px solid var(--teal);
267
+ color: #fff;
268
+ display: flex;
269
+ font-weight: 500;
270
+ justify-content: space-between;
271
+ padding: 0 1em;
272
+ }
273
+
274
+ #header a {
275
+ color: var(--teal);
276
+ }
277
+
278
+ #header a:active {
279
+ color: #9ad4db;
280
+ }
281
+
282
+ #header a:hover {
283
+ color: #fff;
284
+ }
285
+
286
+ #header .ipfs-logo {
287
+ height: 2.25em;
288
+ margin: .7em .7em .7em 0;
289
+ width: 7.15em
290
+ }
291
+
292
+ #header nav {
293
+ align-items: center;
294
+ display: flex;
295
+ margin: .65em 0;
296
+ }
297
+
298
+ #header nav a {
299
+ margin: 0 .6em;
300
+ }
301
+
302
+ #header nav a:last-child {
303
+ margin: 0 0 0 .6em;
304
+ }
305
+
306
+ #header nav svg {
307
+ fill: var(--teal);
308
+ height: 1.8em;
309
+ margin-top: .125em;
310
+ }
311
+
312
+ #header nav svg:hover {
313
+ fill: #fff;
314
+ }
315
+
316
+ main {
317
+ border: 1px solid var(--dark-white);
318
+ border-radius: var(--radius);
319
+ overflow: hidden;
320
+ margin: 1em;
321
+ font-size: .875em;
322
+ }
323
+
324
+ main header,main .container {
325
+ padding-left: 1em;
326
+ padding-right: 1em;
327
+ }
328
+
329
+ main header {
330
+ padding-top: .7em;
331
+ padding-bottom: .7em;
332
+ background-color: var(--light-white);
333
+ }
334
+
335
+ main header,main section:not(:last-child) {
336
+ border-bottom: 1px solid var(--dark-white);
337
+ }
338
+
339
+ main section header {
340
+ background-color: var(--near-white);
341
+ }
342
+
343
+ .grid {
344
+ display: grid;
345
+ overflow-x: auto;
346
+ }
347
+
348
+ .grid .grid {
349
+ overflow-x: visible;
350
+ }
351
+
352
+ .grid > div {
353
+ padding: .7em;
354
+ border-bottom: 1px solid var(--dark-white);
355
+ }
356
+
357
+ .grid.dir {
358
+ grid-template-columns: min-content 1fr min-content min-content;
359
+ }
360
+
361
+ .grid.dir > div:nth-of-type(4n+1) {
362
+ padding-left: 1em;
363
+ }
364
+
365
+ .grid.dir > div:nth-of-type(4n+4) {
366
+ padding-right: 1em;
367
+ }
368
+
369
+ .grid.dir > div:nth-last-child(-n+4) {
370
+ border-bottom: 0;
371
+ }
372
+
373
+ .grid.dir > div:nth-of-type(8n+5),.grid.dir > div:nth-of-type(8n+6),.grid.dir > div:nth-of-type(8n+7),.grid.dir > div:nth-of-type(8n+8) {
374
+ background-color: var(--near-white);
375
+ }
376
+
377
+ .grid.dag {
378
+ grid-template-columns: max-content 1fr;
379
+ }
380
+
381
+ .grid.dag pre {
382
+ margin: 0;
383
+ }
384
+
385
+ .grid.dag .grid {
386
+ padding: 0;
387
+ }
388
+
389
+ .grid.dag > div:nth-last-child(-n+2) {
390
+ border-bottom: 0;
391
+ }
392
+
393
+ .grid.dag > div {
394
+ background: white
395
+ }
396
+
397
+ .grid.dag > div:nth-child(4n),.grid.dag > div:nth-child(4n+3) {
398
+ background-color: var(--near-white);
399
+ }
400
+
401
+ section > .grid.dag > div:nth-of-type(2n+1) {
402
+ padding-left: 1em;
403
+ }
404
+
405
+ .type-icon,.type-icon > * {
406
+ width: 1.15em
407
+ }
408
+
409
+ .terminal {
410
+ background: var(--steel-gray);
411
+ color: white;
412
+ padding: .7em;
413
+ border-radius: var(--radius);
414
+ word-wrap: break-word;
415
+ white-space: break-spaces;
416
+ }
417
+
418
+ @media print {
419
+ #header {
420
+ display: none;
421
+ }
422
+
423
+ #main header,.ipfs-hash,body {
424
+ color: #000;
425
+ }
426
+
427
+ #main,#main header {
428
+ border-color: #000;
429
+ }
430
+
431
+ a,a:visited {
432
+ color: #000;
433
+ text-decoration: underline;
434
+ }
435
+
436
+ a[href]:after {
437
+ content: " (" attr(href) ")"
438
+ }
439
+ }
440
+
441
+ @media only screen and (max-width: 500px) {
442
+ .dn-mobile {
443
+ display: none;
444
+ }
445
+ }`
@@ -15,19 +15,36 @@ interface GetETagArg {
15
15
  */
16
16
  weak?: boolean
17
17
  }
18
+ const getPrefix = ({ weak, reqFormat }: Partial<GetETagArg>): string => {
19
+ if (reqFormat === 'tar' || reqFormat === 'car' || reqFormat === 'ipns-record' || weak === true) {
20
+ return 'W/'
21
+ }
22
+ return ''
23
+ }
24
+
25
+ const getFormatSuffix = ({ reqFormat }: Partial<GetETagArg>): string => {
26
+ if (reqFormat == null) {
27
+ return ''
28
+ }
29
+ if (reqFormat === 'tar') {
30
+ return '.x-tar'
31
+ }
32
+
33
+ return `.${reqFormat}`
34
+ }
18
35
 
19
36
  /**
20
37
  * etag
21
38
  * you need to wrap cid with ""
22
- * we use strong Etags for immutable responses and weak one (prefixed with W/ ) for mutable/generated ones (ipns and generated HTML).
39
+ * we use strong Etags for immutable responses and weak one (prefixed with W/ ) for mutable/generated ones (ipns, car, tar, and generated HTML).
23
40
  * block and car responses should have different etag than deserialized one, so you can add some prefix like we do in existing gateway
24
41
  *
25
42
  * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
26
43
  * @see https://specs.ipfs.tech/http-gateways/path-gateway/#etag-response-header
27
44
  */
28
45
  export function getETag ({ cid, reqFormat, weak, rangeStart, rangeEnd }: GetETagArg): string {
29
- const prefix = weak === true ? 'W/' : ''
30
- let suffix = reqFormat == null ? '' : `.${reqFormat}`
46
+ const prefix = getPrefix({ weak, reqFormat })
47
+ let suffix = getFormatSuffix({ reqFormat })
31
48
  if (rangeStart != null || rangeEnd != null) {
32
49
  suffix += `.${rangeStart ?? '0'}-${rangeEnd ?? 'N'}`
33
50
  }
@@ -2,8 +2,8 @@ import { DoesNotExistError } from '@helia/unixfs/errors'
2
2
  import { type Logger } from '@libp2p/interface'
3
3
  import { type Blockstore } from 'interface-blockstore'
4
4
  import { walkPath as exporterWalk, type ExporterOptions, type ReadableStorage, type ObjectNode, type UnixFSEntry } from 'ipfs-unixfs-exporter'
5
- import { type FetchHandlerFunctionArg } from '../types.js'
6
5
  import { badGatewayResponse, notFoundResponse } from './responses.js'
6
+ import type { PluginContext } from '../plugins/types.js'
7
7
  import type { CID } from 'multiformats/cid'
8
8
 
9
9
  export interface PathWalkerOptions extends ExporterOptions {
@@ -12,7 +12,6 @@ export interface PathWalkerOptions extends ExporterOptions {
12
12
  export interface PathWalkerResponse {
13
13
  ipfsRoots: CID[]
14
14
  terminalElement: UnixFSEntry
15
-
16
15
  }
17
16
 
18
17
  export interface PathWalkerFn {
@@ -47,8 +46,9 @@ export function isObjectNode (node: UnixFSEntry): node is ObjectNode {
47
46
  * If the signal is aborted, the function will throw an AbortError
48
47
  * If a terminal element is not found, a notFoundResponse is returned
49
48
  * If another unknown error occurs, a badGatewayResponse is returned
49
+ *
50
50
  */
51
- export async function handlePathWalking ({ cid, path, resource, options, blockstore, log }: Omit<FetchHandlerFunctionArg, 'session'> & { blockstore: Blockstore, log: Logger }): Promise<PathWalkerResponse | Response> {
51
+ export async function handlePathWalking ({ cid, path, resource, options, blockstore, log }: PluginContext & { blockstore: Blockstore, log: Logger }): Promise<PathWalkerResponse | Response> {
52
52
  try {
53
53
  return await walkPath(blockstore, `${cid.toString()}/${path}`, options)
54
54
  } catch (err: any) {