@helia/verified-fetch 2.4.0 → 2.5.1
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 +192 -0
- package/dist/index.min.js +354 -32
- package/dist/src/index.d.ts +198 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +192 -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 +42 -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 -27
- 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 +384 -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/response-headers.d.ts.map +1 -1
- package/dist/src/utils/response-headers.js +4 -0
- package/dist/src/utils/response-headers.js.map +1 -1
- 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 +6 -24
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +164 -387
- 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 +199 -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 +50 -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 -34
- package/src/utils/dir-index-html.ts +442 -0
- package/src/utils/get-e-tag.ts +20 -3
- package/src/utils/response-headers.ts +4 -0
- package/src/utils/walk-path.ts +3 -3
- package/src/verified-fetch.ts +187 -430
|
@@ -0,0 +1,442 @@
|
|
|
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 `<!DOCTYPE html>
|
|
121
|
+
<html lang="en">
|
|
122
|
+
<head>
|
|
123
|
+
<meta charset="utf-8">
|
|
124
|
+
<meta name="description" content="A directory of content-addressed files hosted on IPFS.">
|
|
125
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
126
|
+
<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==">
|
|
127
|
+
<title>${dirData.path}</title>
|
|
128
|
+
<style>${style}</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<!--
|
|
132
|
+
# Some JSON content for debugging:
|
|
133
|
+
|
|
134
|
+
## dirData
|
|
135
|
+
${JSON.stringify(dirData, null, 2)}
|
|
136
|
+
-->
|
|
137
|
+
<header id="header">
|
|
138
|
+
<div class="ipfs-logo"> </div>
|
|
139
|
+
<!--
|
|
140
|
+
<nav>
|
|
141
|
+
<a href="https://ipfs.tech" target="_blank" rel="noopener noreferrer">About<span class="dn-mobile"> IPFS</span></a>
|
|
142
|
+
<a href="https://docs.ipfs.tech/install/" target="_blank" rel="noopener noreferrer">Install<span class="dn-mobile"> IPFS</span></a>
|
|
143
|
+
</nav>
|
|
144
|
+
-->
|
|
145
|
+
</header>
|
|
146
|
+
<main id="main">
|
|
147
|
+
<header class="flex flex-wrap">
|
|
148
|
+
<div>
|
|
149
|
+
<strong>${dirListingTitle(dirData)}</strong>
|
|
150
|
+
${dirData.hash == null
|
|
151
|
+
? ''
|
|
152
|
+
: `<div class="ipfs-hash" translate="no">
|
|
153
|
+
${dirData.hash}
|
|
154
|
+
</div>`
|
|
155
|
+
}
|
|
156
|
+
</div>
|
|
157
|
+
${dirData.size == null
|
|
158
|
+
? ''
|
|
159
|
+
: `<div class="nowrap flex-shrink ml-auto">
|
|
160
|
+
<strong title="Cumulative size of IPFS DAG (data + metadata)"> ${dirData.size}</strong>
|
|
161
|
+
</div>`
|
|
162
|
+
}
|
|
163
|
+
</header>
|
|
164
|
+
<div>
|
|
165
|
+
<div class="grid dir">
|
|
166
|
+
<!--{{ if .BackLink }}
|
|
167
|
+
<div class="type-icon">
|
|
168
|
+
<div class="ipfs-_blank"> </div>
|
|
169
|
+
</div>
|
|
170
|
+
<div>
|
|
171
|
+
<a href="{{.BackLink | urlEscape}}">..</a>
|
|
172
|
+
</div>
|
|
173
|
+
<div></div>
|
|
174
|
+
<div></div>
|
|
175
|
+
</tr>
|
|
176
|
+
{{ end }}-->
|
|
177
|
+
${getAllDirListingRows(dirData)}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</main>
|
|
181
|
+
</body>
|
|
182
|
+
</html>`
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const style = `
|
|
186
|
+
|
|
187
|
+
.ipfs-_blank {
|
|
188
|
+
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");
|
|
189
|
+
background-repeat: no-repeat;
|
|
190
|
+
background-size: contain
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
:root {
|
|
194
|
+
--sans-serif: "Plex",system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;
|
|
195
|
+
--monospace: Consolas, monaco, monospace;
|
|
196
|
+
--navy: #073a53;
|
|
197
|
+
--teal: #6bc4ce;
|
|
198
|
+
--turquoise: #47AFB4;
|
|
199
|
+
--steel-gray: #3f5667;
|
|
200
|
+
--dark-white: #d9dbe2;
|
|
201
|
+
--light-white: #edf0f4;
|
|
202
|
+
--near-white: #f7f8fa;
|
|
203
|
+
--radius: 4px;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
body {
|
|
207
|
+
color: #34373f;
|
|
208
|
+
font-family: var(--sans-serif);
|
|
209
|
+
line-height: 1.43;
|
|
210
|
+
margin: 0;
|
|
211
|
+
word-break: break-all;
|
|
212
|
+
-webkit-text-size-adjust: 100%;
|
|
213
|
+
-ms-text-size-adjust: 100%;
|
|
214
|
+
-webkit-tap-highlight-color: transparent;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pre, code {
|
|
218
|
+
font-family: var(--monospace);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
a {
|
|
222
|
+
color: #117eb3;
|
|
223
|
+
text-decoration: none;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
a:hover {
|
|
227
|
+
color: #00b0e9;
|
|
228
|
+
text-decoration: underline;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
a:active,a:visited {
|
|
232
|
+
color: #00b0e9;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.flex {
|
|
236
|
+
display: flex;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.flex-wrap {
|
|
240
|
+
flex-flow: wrap;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.flex-shrink {
|
|
244
|
+
flex-shrink: 1;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.ml-auto {
|
|
248
|
+
margin-left: auto;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.nowrap {
|
|
252
|
+
white-space: nowrap
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.ipfs-hash {
|
|
256
|
+
color: #7f8491;
|
|
257
|
+
font-family: var(--monospace);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
#header {
|
|
261
|
+
align-items: center;
|
|
262
|
+
background: var(--navy);
|
|
263
|
+
border-bottom: 4px solid var(--teal);
|
|
264
|
+
color: #fff;
|
|
265
|
+
display: flex;
|
|
266
|
+
font-weight: 500;
|
|
267
|
+
justify-content: space-between;
|
|
268
|
+
padding: 0 1em;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
#header a {
|
|
272
|
+
color: var(--teal);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#header a:active {
|
|
276
|
+
color: #9ad4db;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#header a:hover {
|
|
280
|
+
color: #fff;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
#header .ipfs-logo {
|
|
284
|
+
height: 2.25em;
|
|
285
|
+
margin: .7em .7em .7em 0;
|
|
286
|
+
width: 7.15em
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#header nav {
|
|
290
|
+
align-items: center;
|
|
291
|
+
display: flex;
|
|
292
|
+
margin: .65em 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#header nav a {
|
|
296
|
+
margin: 0 .6em;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
#header nav a:last-child {
|
|
300
|
+
margin: 0 0 0 .6em;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
#header nav svg {
|
|
304
|
+
fill: var(--teal);
|
|
305
|
+
height: 1.8em;
|
|
306
|
+
margin-top: .125em;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#header nav svg:hover {
|
|
310
|
+
fill: #fff;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
main {
|
|
314
|
+
border: 1px solid var(--dark-white);
|
|
315
|
+
border-radius: var(--radius);
|
|
316
|
+
overflow: hidden;
|
|
317
|
+
margin: 1em;
|
|
318
|
+
font-size: .875em;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
main header,main .container {
|
|
322
|
+
padding-left: 1em;
|
|
323
|
+
padding-right: 1em;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
main header {
|
|
327
|
+
padding-top: .7em;
|
|
328
|
+
padding-bottom: .7em;
|
|
329
|
+
background-color: var(--light-white);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
main header,main section:not(:last-child) {
|
|
333
|
+
border-bottom: 1px solid var(--dark-white);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
main section header {
|
|
337
|
+
background-color: var(--near-white);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.grid {
|
|
341
|
+
display: grid;
|
|
342
|
+
overflow-x: auto;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.grid .grid {
|
|
346
|
+
overflow-x: visible;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.grid > div {
|
|
350
|
+
padding: .7em;
|
|
351
|
+
border-bottom: 1px solid var(--dark-white);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.grid.dir {
|
|
355
|
+
grid-template-columns: min-content 1fr min-content min-content;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.grid.dir > div:nth-of-type(4n+1) {
|
|
359
|
+
padding-left: 1em;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.grid.dir > div:nth-of-type(4n+4) {
|
|
363
|
+
padding-right: 1em;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.grid.dir > div:nth-last-child(-n+4) {
|
|
367
|
+
border-bottom: 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.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) {
|
|
371
|
+
background-color: var(--near-white);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.grid.dag {
|
|
375
|
+
grid-template-columns: max-content 1fr;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.grid.dag pre {
|
|
379
|
+
margin: 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.grid.dag .grid {
|
|
383
|
+
padding: 0;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.grid.dag > div:nth-last-child(-n+2) {
|
|
387
|
+
border-bottom: 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.grid.dag > div {
|
|
391
|
+
background: white
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.grid.dag > div:nth-child(4n),.grid.dag > div:nth-child(4n+3) {
|
|
395
|
+
background-color: var(--near-white);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
section > .grid.dag > div:nth-of-type(2n+1) {
|
|
399
|
+
padding-left: 1em;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.type-icon,.type-icon > * {
|
|
403
|
+
width: 1.15em
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.terminal {
|
|
407
|
+
background: var(--steel-gray);
|
|
408
|
+
color: white;
|
|
409
|
+
padding: .7em;
|
|
410
|
+
border-radius: var(--radius);
|
|
411
|
+
word-wrap: break-word;
|
|
412
|
+
white-space: break-spaces;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
@media print {
|
|
416
|
+
#header {
|
|
417
|
+
display: none;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#main header,.ipfs-hash,body {
|
|
421
|
+
color: #000;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
#main,#main header {
|
|
425
|
+
border-color: #000;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
a,a:visited {
|
|
429
|
+
color: #000;
|
|
430
|
+
text-decoration: underline;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
a[href]:after {
|
|
434
|
+
content: " (" attr(href) ")"
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
@media only screen and (max-width: 500px) {
|
|
439
|
+
.dn-mobile {
|
|
440
|
+
display: none;
|
|
441
|
+
}
|
|
442
|
+
}`
|
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
|
}
|
|
@@ -20,6 +20,10 @@ interface CacheControlHeaderOptions {
|
|
|
20
20
|
* @see https://specs.ipfs.tech/http-gateways/path-gateway/#cache-control-response-header
|
|
21
21
|
*/
|
|
22
22
|
export function setCacheControlHeader ({ ttl, protocol, response }: CacheControlHeaderOptions): void {
|
|
23
|
+
if (response.headers.has('cache-control')) {
|
|
24
|
+
// don't set the header if it's already set by a plugin
|
|
25
|
+
return
|
|
26
|
+
}
|
|
23
27
|
let headerValue: string
|
|
24
28
|
if (protocol === 'ipfs') {
|
|
25
29
|
headerValue = 'public, max-age=29030400, immutable'
|
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) {
|