@helia/verified-fetch 2.3.1 → 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.
- package/README.md +200 -0
- package/dist/index.min.js +357 -35
- package/dist/src/index.d.ts +220 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +200 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/errors.d.ts +25 -0
- package/dist/src/plugins/errors.d.ts.map +1 -0
- package/dist/src/plugins/errors.js +33 -0
- package/dist/src/plugins/errors.js.map +1 -0
- package/dist/src/plugins/index.d.ts +8 -0
- package/dist/src/plugins/index.d.ts.map +1 -0
- package/dist/src/plugins/index.js +7 -0
- package/dist/src/plugins/index.js.map +1 -0
- package/dist/src/plugins/plugin-base.d.ts +19 -0
- package/dist/src/plugins/plugin-base.d.ts.map +1 -0
- package/dist/src/plugins/plugin-base.js +26 -0
- package/dist/src/plugins/plugin-base.js.map +1 -0
- package/dist/src/plugins/plugin-handle-car.d.ts +11 -0
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-car.js +28 -0
- package/dist/src/plugins/plugin-handle-car.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +11 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.js +73 -0
- package/dist/src/plugins/plugin-handle-dag-cbor.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts +15 -0
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-pb.js +152 -0
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts +16 -0
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dag-walk.js +45 -0
- package/dist/src/plugins/plugin-handle-dag-walk.js.map +1 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +9 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.js +37 -0
- package/dist/src/plugins/plugin-handle-dir-index-html.js.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.js +62 -0
- package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -0
- package/dist/src/plugins/plugin-handle-json.d.ts +11 -0
- package/dist/src/plugins/plugin-handle-json.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-json.js +51 -0
- package/dist/src/plugins/plugin-handle-json.js.map +1 -0
- package/dist/src/plugins/plugin-handle-raw.d.ts +8 -0
- package/dist/src/plugins/plugin-handle-raw.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-raw.js +80 -0
- package/dist/src/plugins/plugin-handle-raw.js.map +1 -0
- package/dist/src/plugins/plugin-handle-tar.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-tar.js +36 -0
- package/dist/src/plugins/plugin-handle-tar.js.map +1 -0
- package/dist/src/plugins/plugins.d.ts +5 -0
- package/dist/src/plugins/plugins.d.ts.map +1 -0
- package/dist/src/plugins/plugins.js +5 -0
- package/dist/src/plugins/plugins.js.map +1 -0
- package/dist/src/plugins/types.d.ts +68 -0
- package/dist/src/plugins/types.d.ts.map +1 -0
- package/dist/src/plugins/types.js +2 -0
- package/dist/src/plugins/types.js.map +1 -0
- package/dist/src/types.d.ts +0 -23
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +1 -2
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/dir-index-html.d.ts +16 -0
- package/dist/src/utils/dir-index-html.d.ts.map +1 -0
- package/dist/src/utils/dir-index-html.js +387 -0
- package/dist/src/utils/dir-index-html.js.map +1 -0
- package/dist/src/utils/get-e-tag.d.ts +1 -1
- package/dist/src/utils/get-e-tag.d.ts.map +1 -1
- package/dist/src/utils/get-e-tag.js +18 -3
- package/dist/src/utils/get-e-tag.js.map +1 -1
- package/dist/src/utils/parse-resource.d.ts +2 -1
- package/dist/src/utils/parse-resource.d.ts.map +1 -1
- package/dist/src/utils/parse-resource.js +4 -3
- package/dist/src/utils/parse-resource.js.map +1 -1
- package/dist/src/utils/parse-url-string.d.ts +8 -3
- package/dist/src/utils/parse-url-string.d.ts.map +1 -1
- package/dist/src/utils/parse-url-string.js +30 -4
- package/dist/src/utils/parse-url-string.js.map +1 -1
- package/dist/src/utils/server-timing.d.ts +13 -0
- package/dist/src/utils/server-timing.d.ts.map +1 -0
- package/dist/src/utils/server-timing.js +19 -0
- package/dist/src/utils/server-timing.js.map +1 -0
- package/dist/src/utils/walk-path.d.ts +3 -2
- package/dist/src/utils/walk-path.d.ts.map +1 -1
- package/dist/src/utils/walk-path.js +1 -1
- package/dist/src/utils/walk-path.js.map +1 -1
- package/dist/src/verified-fetch.d.ts +11 -20
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +174 -367
- package/dist/src/verified-fetch.js.map +1 -1
- package/dist/typedoc-urls.json +32 -24
- package/package.json +6 -2
- package/src/index.ts +223 -0
- package/src/plugins/errors.ts +37 -0
- package/src/plugins/index.ts +8 -0
- package/src/plugins/plugin-base.ts +30 -0
- package/src/plugins/plugin-handle-car.ts +32 -0
- package/src/plugins/plugin-handle-dag-cbor.ts +84 -0
- package/src/plugins/plugin-handle-dag-pb.ts +168 -0
- package/src/plugins/plugin-handle-dag-walk.ts +53 -0
- package/src/plugins/plugin-handle-dir-index-html.ts +44 -0
- package/src/plugins/plugin-handle-ipns-record.ts +69 -0
- package/src/plugins/plugin-handle-json.ts +57 -0
- package/src/plugins/plugin-handle-raw.ts +92 -0
- package/src/plugins/plugin-handle-tar.ts +44 -0
- package/src/plugins/plugins.ts +4 -0
- package/src/plugins/types.ts +73 -0
- package/src/types.ts +0 -29
- package/src/utils/dir-index-html.ts +445 -0
- package/src/utils/get-e-tag.ts +20 -3
- package/src/utils/parse-resource.ts +5 -4
- package/src/utils/parse-url-string.ts +38 -7
- package/src/utils/server-timing.ts +37 -0
- package/src/utils/walk-path.ts +3 -3
- package/src/verified-fetch.ts +198 -403
|
@@ -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)}"> </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"> </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)"> ${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"> </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
|
+
}`
|
package/src/utils/get-e-tag.ts
CHANGED
|
@@ -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
|
|
30
|
-
let suffix = 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
|
}
|
|
@@ -11,16 +11,16 @@ export interface ParseResourceComponents {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface ParseResourceOptions extends ParseUrlStringOptions {
|
|
14
|
-
|
|
14
|
+
withServerTiming?: boolean
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Handles the different use cases for the `resource` argument.
|
|
18
18
|
* The resource can represent an IPFS path, IPNS path, or CID.
|
|
19
19
|
* If the resource represents an IPNS path, we need to resolve it to a CID.
|
|
20
20
|
*/
|
|
21
|
-
export async function parseResource (resource: Resource, { ipns, logger }: ParseResourceComponents, options
|
|
21
|
+
export async function parseResource (resource: Resource, { ipns, logger }: ParseResourceComponents, { withServerTiming = false, ...options }: ParseResourceOptions = { withServerTiming: false }): Promise<ParsedUrlStringResults> {
|
|
22
22
|
if (typeof resource === 'string') {
|
|
23
|
-
return parseUrlString({ urlString: resource, ipns, logger }, options)
|
|
23
|
+
return parseUrlString({ urlString: resource, ipns, logger, withServerTiming }, options)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
const cid = CID.asCID(resource)
|
|
@@ -33,7 +33,8 @@ export async function parseResource (resource: Resource, { ipns, logger }: Parse
|
|
|
33
33
|
path: '',
|
|
34
34
|
query: {},
|
|
35
35
|
ipfsPath: `/ipfs/${cid.toString()}`,
|
|
36
|
-
ttl: 29030400 // 1 year for ipfs content
|
|
36
|
+
ttl: 29030400, // 1 year for ipfs content
|
|
37
|
+
serverTimings: []
|
|
37
38
|
} satisfies ParsedUrlStringResults
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid'
|
|
2
2
|
import { getPeerIdFromString } from './get-peer-id-from-string.js'
|
|
3
|
+
import { serverTiming, type ServerTimingResult } from './server-timing.js'
|
|
3
4
|
import { TLRU } from './tlru.js'
|
|
4
5
|
import type { RequestFormatShorthand } from '../types.js'
|
|
5
6
|
import type { DNSLinkResolveResult, IPNS, IPNSResolveResult, IPNSRoutingEvents, ResolveDNSLinkProgressEvents, ResolveProgressEvents, ResolveResult } from '@helia/ipns'
|
|
@@ -12,6 +13,7 @@ export interface ParseUrlStringInput {
|
|
|
12
13
|
urlString: string
|
|
13
14
|
ipns: IPNS
|
|
14
15
|
logger: ComponentLogger
|
|
16
|
+
withServerTiming?: boolean
|
|
15
17
|
}
|
|
16
18
|
export interface ParseUrlStringOptions extends ProgressOptions<ResolveProgressEvents | IPNSRoutingEvents | ResolveDNSLinkProgressEvents>, AbortOptions {
|
|
17
19
|
|
|
@@ -23,7 +25,7 @@ export interface ParsedUrlQuery extends Record<string, string | unknown> {
|
|
|
23
25
|
filename?: string
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
interface
|
|
28
|
+
export interface ParsedUrlStringResults extends ResolveResult {
|
|
27
29
|
protocol: 'ipfs' | 'ipns'
|
|
28
30
|
query: ParsedUrlQuery
|
|
29
31
|
|
|
@@ -41,9 +43,12 @@ interface ParsedUrlStringResultsBase extends ResolveResult {
|
|
|
41
43
|
* seconds as a number
|
|
42
44
|
*/
|
|
43
45
|
ttl?: number
|
|
44
|
-
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
/**
|
|
48
|
+
* serverTiming items
|
|
49
|
+
*/
|
|
50
|
+
serverTimings: Array<ServerTimingResult<any>>
|
|
51
|
+
}
|
|
47
52
|
|
|
48
53
|
const URL_REGEX = /^(?<protocol>ip[fn]s):\/\/(?<cidOrPeerIdOrDnsLink>[^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
|
|
49
54
|
const PATH_REGEX = /^\/(?<protocol>ip[fn]s)\/(?<cidOrPeerIdOrDnsLink>[^/?]+)\/?(?<path>[^?]*)\??(?<queryString>.*)$/
|
|
@@ -145,7 +150,7 @@ function dnsLinkLabelDecoder (linkLabel: string): string {
|
|
|
145
150
|
* @todo we need to break out each step of this function (cid parsing, ipns resolving, dnslink resolving) into separate functions and then remove the eslint-disable comment
|
|
146
151
|
*/
|
|
147
152
|
// eslint-disable-next-line complexity
|
|
148
|
-
export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStringInput, options?: ParseUrlStringOptions): Promise<ParsedUrlStringResults> {
|
|
153
|
+
export async function parseUrlString ({ urlString, ipns, logger, withServerTiming = false }: ParseUrlStringInput, options?: ParseUrlStringOptions): Promise<ParsedUrlStringResults> {
|
|
149
154
|
const log = logger.forComponent('helia:verified-fetch:parse-url-string')
|
|
150
155
|
const { protocol, cidOrPeerIdOrDnsLink, path: urlPath, queryString } = matchURLString(urlString)
|
|
151
156
|
|
|
@@ -153,6 +158,7 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
|
|
|
153
158
|
let resolvedPath: string | undefined
|
|
154
159
|
const errors: Error[] = []
|
|
155
160
|
let resolveResult: IPNSResolveResult | DNSLinkResolveResult | undefined
|
|
161
|
+
const serverTimings: Array<ServerTimingResult<any>> = []
|
|
156
162
|
|
|
157
163
|
if (protocol === 'ipfs') {
|
|
158
164
|
try {
|
|
@@ -182,7 +188,19 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
|
|
|
182
188
|
if (peerId.publicKey == null) {
|
|
183
189
|
throw new TypeError('cidOrPeerIdOrDnsLink contains no public key')
|
|
184
190
|
}
|
|
185
|
-
|
|
191
|
+
|
|
192
|
+
if (withServerTiming) {
|
|
193
|
+
const resolveResultWithServerTiming = await serverTiming('ipns.resolve', `Resolve IPNS name ${cidOrPeerIdOrDnsLink}`, ipns.resolve.bind(null, peerId.publicKey, options))
|
|
194
|
+
serverTimings.push(resolveResultWithServerTiming)
|
|
195
|
+
|
|
196
|
+
// eslint-disable-next-line max-depth
|
|
197
|
+
if (resolveResultWithServerTiming.error != null) {
|
|
198
|
+
throw resolveResultWithServerTiming.error
|
|
199
|
+
}
|
|
200
|
+
resolveResult = resolveResultWithServerTiming.result
|
|
201
|
+
} else {
|
|
202
|
+
resolveResult = await ipns.resolve(peerId.publicKey, options)
|
|
203
|
+
}
|
|
186
204
|
cid = resolveResult?.cid
|
|
187
205
|
resolvedPath = resolveResult?.path
|
|
188
206
|
log.trace('resolved %s to %c', cidOrPeerIdOrDnsLink, cid)
|
|
@@ -207,7 +225,19 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
|
|
|
207
225
|
log.trace('Attempting to resolve DNSLink for %s', decodedDnsLinkLabel)
|
|
208
226
|
|
|
209
227
|
try {
|
|
210
|
-
|
|
228
|
+
// eslint-disable-next-line max-depth
|
|
229
|
+
if (withServerTiming) {
|
|
230
|
+
const resolveResultWithServerTiming = await serverTiming('ipns.resolveDNSLink', `Resolve DNSLink ${decodedDnsLinkLabel}`, ipns.resolveDNSLink.bind(ipns, decodedDnsLinkLabel, options))
|
|
231
|
+
serverTimings.push(resolveResultWithServerTiming)
|
|
232
|
+
// eslint-disable-next-line max-depth
|
|
233
|
+
if (resolveResultWithServerTiming.error != null) {
|
|
234
|
+
throw resolveResultWithServerTiming.error
|
|
235
|
+
}
|
|
236
|
+
resolveResult = resolveResultWithServerTiming.result
|
|
237
|
+
} else {
|
|
238
|
+
resolveResult = await ipns.resolveDNSLink(decodedDnsLinkLabel, options)
|
|
239
|
+
}
|
|
240
|
+
|
|
211
241
|
cid = resolveResult?.cid
|
|
212
242
|
resolvedPath = resolveResult?.path
|
|
213
243
|
log.trace('resolved %s to %c', decodedDnsLinkLabel, cid)
|
|
@@ -263,7 +293,8 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
|
|
|
263
293
|
path: joinPaths(resolvedPath, urlPath ?? ''),
|
|
264
294
|
query,
|
|
265
295
|
ttl,
|
|
266
|
-
ipfsPath: `/${protocol}/${cidOrPeerIdOrDnsLink}${urlPath != null && urlPath !== '' ? `/${urlPath}` : ''}
|
|
296
|
+
ipfsPath: `/${protocol}/${cidOrPeerIdOrDnsLink}${urlPath != null && urlPath !== '' ? `/${urlPath}` : ''}`,
|
|
297
|
+
serverTimings
|
|
267
298
|
} satisfies ParsedUrlStringResults
|
|
268
299
|
}
|
|
269
300
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ServerTimingSuccess<T> {
|
|
2
|
+
error: null
|
|
3
|
+
result: T
|
|
4
|
+
header: string
|
|
5
|
+
}
|
|
6
|
+
export interface ServerTimingError {
|
|
7
|
+
result: null
|
|
8
|
+
error: Error
|
|
9
|
+
header: string
|
|
10
|
+
}
|
|
11
|
+
export type ServerTimingResult<T> = ServerTimingSuccess<T> | ServerTimingError
|
|
12
|
+
|
|
13
|
+
export async function serverTiming<T> (
|
|
14
|
+
name: string,
|
|
15
|
+
description: string,
|
|
16
|
+
fn: () => Promise<T>
|
|
17
|
+
): Promise<ServerTimingResult<T>> {
|
|
18
|
+
const startTime = performance.now()
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const result = await fn() // Execute the function
|
|
22
|
+
const endTime = performance.now()
|
|
23
|
+
|
|
24
|
+
const duration = (endTime - startTime).toFixed(1) // Duration in milliseconds
|
|
25
|
+
|
|
26
|
+
// Create the Server-Timing header string
|
|
27
|
+
const header = `${name};dur=${duration};desc="${description}"`
|
|
28
|
+
return { result, header, error: null }
|
|
29
|
+
} catch (error: any) {
|
|
30
|
+
const endTime = performance.now()
|
|
31
|
+
const duration = (endTime - startTime).toFixed(1)
|
|
32
|
+
|
|
33
|
+
// Still return a timing header even on error
|
|
34
|
+
const header = `${name};dur=${duration};desc="${description}"`
|
|
35
|
+
return { result: null, error, header } // Pass error with timing info
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/utils/walk-path.ts
CHANGED
|
@@ -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 }:
|
|
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) {
|