@helia/verified-fetch 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -40
- package/dist/index.min.js +73 -534
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +162 -68
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -43
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/index.d.ts +0 -5
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/index.js +0 -4
- package/dist/src/plugins/index.js.map +1 -1
- package/dist/src/plugins/plugin-base.d.ts +8 -9
- package/dist/src/plugins/plugin-base.d.ts.map +1 -1
- package/dist/src/plugins/plugin-base.js +5 -6
- package/dist/src/plugins/plugin-base.js.map +1 -1
- package/dist/src/plugins/plugin-handle-car.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-car.js +38 -39
- package/dist/src/plugins/plugin-handle-car.js.map +1 -1
- package/dist/src/plugins/plugin-handle-ipld.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-ipld.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-ipld.js +83 -0
- package/dist/src/plugins/plugin-handle-ipld.js.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-ipns-record.js +25 -34
- package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.js +20 -22
- package/dist/src/plugins/plugin-handle-tar.js.map +1 -1
- package/dist/src/plugins/plugin-handle-unixfs.d.ts +14 -0
- package/dist/src/plugins/plugin-handle-unixfs.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-unixfs.js +180 -0
- package/dist/src/plugins/plugin-handle-unixfs.js.map +1 -0
- package/dist/src/plugins/types.d.ts +1 -77
- package/dist/src/plugins/types.d.ts.map +1 -1
- package/dist/src/url-resolver.d.ts +29 -11
- package/dist/src/url-resolver.d.ts.map +1 -1
- package/dist/src/url-resolver.js +152 -74
- package/dist/src/url-resolver.js.map +1 -1
- package/dist/src/utils/content-type-parser.d.ts.map +1 -1
- package/dist/src/utils/content-type-parser.js +4 -3
- package/dist/src/utils/content-type-parser.js.map +1 -1
- package/dist/src/utils/content-types.d.ts +26 -0
- package/dist/src/utils/content-types.d.ts.map +1 -0
- package/dist/src/utils/content-types.js +137 -0
- package/dist/src/utils/content-types.js.map +1 -0
- package/dist/src/utils/convert-output.d.ts +17 -0
- package/dist/src/utils/convert-output.d.ts.map +1 -0
- package/dist/src/utils/convert-output.js +176 -0
- package/dist/src/utils/convert-output.js.map +1 -0
- package/dist/src/utils/error-to-response.d.ts +3 -0
- package/dist/src/utils/error-to-response.d.ts.map +1 -0
- package/dist/src/utils/error-to-response.js +40 -0
- package/dist/src/utils/error-to-response.js.map +1 -0
- package/dist/src/utils/get-content-disposition-filename.d.ts +1 -1
- package/dist/src/utils/get-content-disposition-filename.d.ts.map +1 -1
- package/dist/src/utils/get-content-disposition-filename.js +4 -0
- package/dist/src/utils/get-content-disposition-filename.js.map +1 -1
- package/dist/src/utils/get-e-tag.d.ts +20 -15
- package/dist/src/utils/get-e-tag.d.ts.map +1 -1
- package/dist/src/utils/get-e-tag.js +8 -22
- package/dist/src/utils/get-e-tag.js.map +1 -1
- package/dist/src/utils/get-offset-and-length.d.ts +12 -2
- package/dist/src/utils/get-offset-and-length.d.ts.map +1 -1
- package/dist/src/utils/get-offset-and-length.js +63 -21
- package/dist/src/utils/get-offset-and-length.js.map +1 -1
- package/dist/src/utils/get-range-header.d.ts +22 -0
- package/dist/src/utils/get-range-header.d.ts.map +1 -0
- package/dist/src/utils/get-range-header.js +69 -0
- package/dist/src/utils/get-range-header.js.map +1 -0
- package/dist/src/utils/parse-url-string.d.ts +2 -1
- package/dist/src/utils/parse-url-string.d.ts.map +1 -1
- package/dist/src/utils/parse-url-string.js +46 -71
- package/dist/src/utils/parse-url-string.js.map +1 -1
- package/dist/src/utils/resource-to-cache-key.d.ts +3 -3
- package/dist/src/utils/resource-to-cache-key.js +5 -5
- package/dist/src/utils/resource-to-cache-key.js.map +1 -1
- package/dist/src/utils/response-headers.d.ts +4 -14
- package/dist/src/utils/response-headers.d.ts.map +1 -1
- package/dist/src/utils/response-headers.js +36 -36
- package/dist/src/utils/response-headers.js.map +1 -1
- package/dist/src/utils/responses.d.ts +30 -11
- package/dist/src/utils/responses.d.ts.map +1 -1
- package/dist/src/utils/responses.js +146 -39
- package/dist/src/utils/responses.js.map +1 -1
- package/dist/src/verified-fetch.d.ts +16 -15
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +307 -236
- package/dist/src/verified-fetch.js.map +1 -1
- package/dist/typedoc-urls.json +64 -45
- package/package.json +4 -3
- package/src/constants.ts +3 -0
- package/src/index.ts +203 -71
- package/src/plugins/index.ts +0 -6
- package/src/plugins/plugin-base.ts +8 -10
- package/src/plugins/plugin-handle-car.ts +48 -46
- package/src/plugins/plugin-handle-ipld.ts +93 -0
- package/src/plugins/plugin-handle-ipns-record.ts +31 -41
- package/src/plugins/plugin-handle-tar.ts +25 -29
- package/src/plugins/plugin-handle-unixfs.ts +217 -0
- package/src/plugins/types.ts +0 -86
- package/src/url-resolver.ts +197 -83
- package/src/utils/content-type-parser.ts +4 -3
- package/src/utils/content-types.ts +159 -0
- package/src/utils/convert-output.ts +187 -0
- package/src/utils/error-to-response.ts +49 -0
- package/src/utils/get-content-disposition-filename.ts +7 -1
- package/src/utils/get-e-tag.ts +26 -35
- package/src/utils/get-offset-and-length.ts +75 -21
- package/src/utils/get-range-header.ts +107 -0
- package/src/utils/parse-url-string.ts +51 -80
- package/src/utils/resource-to-cache-key.ts +5 -5
- package/src/utils/response-headers.ts +40 -41
- package/src/utils/responses.ts +186 -45
- package/src/verified-fetch.ts +359 -267
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts +0 -14
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-byte-range-context.js +0 -25
- package/dist/src/plugins/plugin-handle-byte-range-context.js.map +0 -1
- package/dist/src/plugins/plugin-handle-cbor.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-cbor.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-cbor.js +0 -94
- package/dist/src/plugins/plugin-handle-cbor.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts +0 -27
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js +0 -279
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.js +0 -66
- package/dist/src/plugins/plugin-handle-dag-cbor.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-pb.js +0 -209
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts +0 -21
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-walk.js +0 -95
- package/dist/src/plugins/plugin-handle-dag-walk.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +0 -10
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.js +0 -59
- package/dist/src/plugins/plugin-handle-dir-index-html.js.map +0 -1
- package/dist/src/plugins/plugin-handle-json.d.ts +0 -12
- package/dist/src/plugins/plugin-handle-json.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-json.js +0 -73
- package/dist/src/plugins/plugin-handle-json.js.map +0 -1
- package/dist/src/plugins/plugin-handle-raw.d.ts +0 -9
- package/dist/src/plugins/plugin-handle-raw.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-raw.js +0 -92
- package/dist/src/plugins/plugin-handle-raw.js.map +0 -1
- package/dist/src/plugins/plugins.d.ts +0 -6
- package/dist/src/plugins/plugins.d.ts.map +0 -1
- package/dist/src/plugins/plugins.js +0 -6
- package/dist/src/plugins/plugins.js.map +0 -1
- package/dist/src/utils/byte-range-context.d.ts +0 -103
- package/dist/src/utils/byte-range-context.d.ts.map +0 -1
- package/dist/src/utils/byte-range-context.js +0 -504
- package/dist/src/utils/byte-range-context.js.map +0 -1
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts +0 -15
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +0 -1
- package/dist/src/utils/dag-cbor-to-safe-json.js +0 -54
- package/dist/src/utils/dag-cbor-to-safe-json.js.map +0 -1
- package/dist/src/utils/dir-index-html.d.ts +0 -19
- package/dist/src/utils/dir-index-html.d.ts.map +0 -1
- package/dist/src/utils/dir-index-html.js +0 -438
- package/dist/src/utils/dir-index-html.js.map +0 -1
- package/dist/src/utils/get-peer-id-from-string.d.ts +0 -3
- package/dist/src/utils/get-peer-id-from-string.d.ts.map +0 -1
- package/dist/src/utils/get-peer-id-from-string.js +0 -10
- package/dist/src/utils/get-peer-id-from-string.js.map +0 -1
- package/dist/src/utils/get-resolved-accept-header.d.ts +0 -9
- package/dist/src/utils/get-resolved-accept-header.d.ts.map +0 -1
- package/dist/src/utils/get-resolved-accept-header.js +0 -27
- package/dist/src/utils/get-resolved-accept-header.js.map +0 -1
- package/dist/src/utils/get-stream-from-async-iterable.d.ts +0 -9
- package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +0 -1
- package/dist/src/utils/get-stream-from-async-iterable.js +0 -43
- package/dist/src/utils/get-stream-from-async-iterable.js.map +0 -1
- package/dist/src/utils/handle-redirects.d.ts +0 -16
- package/dist/src/utils/handle-redirects.d.ts.map +0 -1
- package/dist/src/utils/handle-redirects.js +0 -84
- package/dist/src/utils/handle-redirects.js.map +0 -1
- package/dist/src/utils/is-accept-explicit.d.ts +0 -15
- package/dist/src/utils/is-accept-explicit.d.ts.map +0 -1
- package/dist/src/utils/is-accept-explicit.js +0 -26
- package/dist/src/utils/is-accept-explicit.js.map +0 -1
- package/dist/src/utils/request-headers.d.ts +0 -13
- package/dist/src/utils/request-headers.d.ts.map +0 -1
- package/dist/src/utils/request-headers.js +0 -63
- package/dist/src/utils/request-headers.js.map +0 -1
- package/dist/src/utils/select-output-type.d.ts +0 -17
- package/dist/src/utils/select-output-type.d.ts.map +0 -1
- package/dist/src/utils/select-output-type.js +0 -153
- package/dist/src/utils/select-output-type.js.map +0 -1
- package/dist/src/utils/tlru.d.ts +0 -15
- package/dist/src/utils/tlru.d.ts.map +0 -1
- package/dist/src/utils/tlru.js +0 -34
- package/dist/src/utils/tlru.js.map +0 -1
- package/dist/src/utils/walk-path.d.ts +0 -27
- package/dist/src/utils/walk-path.d.ts.map +0 -1
- package/dist/src/utils/walk-path.js +0 -45
- package/dist/src/utils/walk-path.js.map +0 -1
- package/src/plugins/plugin-handle-byte-range-context.ts +0 -30
- package/src/plugins/plugin-handle-cbor.ts +0 -107
- package/src/plugins/plugin-handle-dag-cbor-html-preview.ts +0 -295
- package/src/plugins/plugin-handle-dag-cbor.ts +0 -83
- package/src/plugins/plugin-handle-dag-pb.ts +0 -248
- package/src/plugins/plugin-handle-dag-walk.ts +0 -110
- package/src/plugins/plugin-handle-dir-index-html.ts +0 -72
- package/src/plugins/plugin-handle-json.ts +0 -80
- package/src/plugins/plugin-handle-raw.ts +0 -110
- package/src/plugins/plugins.ts +0 -5
- package/src/utils/byte-range-context.ts +0 -597
- package/src/utils/dag-cbor-to-safe-json.ts +0 -63
- package/src/utils/dir-index-html.ts +0 -505
- package/src/utils/get-peer-id-from-string.ts +0 -12
- package/src/utils/get-resolved-accept-header.ts +0 -42
- package/src/utils/get-stream-from-async-iterable.ts +0 -49
- package/src/utils/handle-redirects.ts +0 -109
- package/src/utils/is-accept-explicit.ts +0 -38
- package/src/utils/request-headers.ts +0 -65
- package/src/utils/select-output-type.ts +0 -175
- package/src/utils/tlru.ts +0 -42
- package/src/utils/walk-path.ts +0 -68
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"walk-path.js","sourceRoot":"","sources":["../../../src/utils/walk-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAmBrE,KAAK,UAAU,QAAQ,CAAE,UAA2B,EAAE,IAAY,EAAE,OAA2B;IAC7F,MAAM,SAAS,GAAU,EAAE,CAAA;IAC3B,IAAI,eAAwC,CAAA;IAE5C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC;QAClE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACzB,eAAe,GAAG,KAAK,CAAA;IACzB,CAAC;IAED,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,IAAI,iBAAiB,CAAC,2BAA2B,CAAC,CAAA;IAC1D,CAAC;IAED,OAAO;QACL,SAAS;QACT,eAAe;KAChB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAE,IAAiB;IAC7C,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAA;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAA2D;IACjJ,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACxE,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/C,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACnF,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAA;QACnC,CAAC;QAED,GAAG,CAAC,KAAK,CAAC,8BAA8B,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;QACpD,OAAO,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { ByteRangeContext } from '../utils/byte-range-context.js'
|
|
2
|
-
import { badRangeResponse } from '../utils/responses.js'
|
|
3
|
-
import { BasePlugin } from './plugin-base.js'
|
|
4
|
-
import type { PluginContext } from './types.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* This plugin simply adds the ByteRangeContext to the PluginContext.
|
|
8
|
-
*/
|
|
9
|
-
export class ByteRangeContextPlugin extends BasePlugin {
|
|
10
|
-
readonly id = 'byte-range-context-plugin'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Return false if the ByteRangeContext has already been set, otherwise return true.
|
|
14
|
-
*/
|
|
15
|
-
canHandle (context: PluginContext): boolean {
|
|
16
|
-
return context.byteRangeContext == null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async handle (context: PluginContext): Promise<Response | null> {
|
|
20
|
-
context.byteRangeContext = new ByteRangeContext(this.pluginOptions.logger, context.options?.headers)
|
|
21
|
-
context.modified++
|
|
22
|
-
|
|
23
|
-
if (context.byteRangeContext.isRangeRequest && !context.byteRangeContext.isValidRangeRequest) {
|
|
24
|
-
// invalid range request.. fail
|
|
25
|
-
return badRangeResponse(context.resource)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return null
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import * as ipldDagCbor from '@ipld/dag-cbor'
|
|
2
|
-
import { CID } from 'multiformats/cid'
|
|
3
|
-
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
4
|
-
import { CODEC_CBOR } from '../constants.ts'
|
|
5
|
-
import { cborToObject, dagCborToSafeJSON } from '../utils/dag-cbor-to-safe-json.js'
|
|
6
|
-
import { setIpfsRoots } from '../utils/response-headers.js'
|
|
7
|
-
import { notAcceptableResponse, okRangeResponse } from '../utils/responses.js'
|
|
8
|
-
import { isObjectNode } from '../utils/walk-path.js'
|
|
9
|
-
import { BasePlugin } from './plugin-base.js'
|
|
10
|
-
import type { PluginContext } from './types.js'
|
|
11
|
-
import type { ObjectNode } from 'ipfs-unixfs-exporter'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Handles `cbor` content
|
|
15
|
-
*/
|
|
16
|
-
export class CborPlugin extends BasePlugin {
|
|
17
|
-
readonly id = 'cbor-plugin'
|
|
18
|
-
readonly codes = [CODEC_CBOR]
|
|
19
|
-
|
|
20
|
-
canHandle ({ cid, pathDetails, byteRangeContext }: PluginContext): boolean {
|
|
21
|
-
if (pathDetails == null) {
|
|
22
|
-
return false
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!isObjectNode(pathDetails.terminalElement)) {
|
|
26
|
-
return false
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (cid.code !== CODEC_CBOR) {
|
|
30
|
-
return false
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (byteRangeContext == null) {
|
|
34
|
-
return false
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return true
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext' | 'pathDetails'>> & { pathDetails: { terminalElement: ObjectNode } }): Promise<Response> {
|
|
41
|
-
const { cid, path, resource, accept, pathDetails: { terminalElement, ipfsRoots } } = context
|
|
42
|
-
|
|
43
|
-
this.log.trace('fetching %c/%s with accept header %s', cid, path.join('/'), accept?.mimeType)
|
|
44
|
-
|
|
45
|
-
const block = terminalElement.node
|
|
46
|
-
|
|
47
|
-
let responseContentType = 'application/cbor'
|
|
48
|
-
let body: string | Uint8Array = block
|
|
49
|
-
|
|
50
|
-
if (accept?.mimeType === 'application/json' || accept?.mimeType === 'application/vnd.ipld.dag-json') {
|
|
51
|
-
try {
|
|
52
|
-
body = dagCborToSafeJSON(block)
|
|
53
|
-
responseContentType = accept.mimeType
|
|
54
|
-
} catch (err) {
|
|
55
|
-
this.log.error('could not decode CBOR as JSON-safe - %e', err)
|
|
56
|
-
return notAcceptableResponse(resource)
|
|
57
|
-
}
|
|
58
|
-
} else if (accept?.mimeType === 'application/vnd.ipld.dag-cbor') {
|
|
59
|
-
try {
|
|
60
|
-
const obj = cborToObject(block)
|
|
61
|
-
convert(obj)
|
|
62
|
-
body = ipldDagCbor.encode(obj)
|
|
63
|
-
responseContentType = accept.mimeType
|
|
64
|
-
} catch (err) {
|
|
65
|
-
this.log.error('could not translate CBOR to DAG-CBOR - %e', err)
|
|
66
|
-
|
|
67
|
-
return notAcceptableResponse(resource)
|
|
68
|
-
}
|
|
69
|
-
} else if (accept?.mimeType === 'application/octet-stream' || accept?.mimeType === 'application/vnd.ipld.raw') {
|
|
70
|
-
responseContentType = accept.mimeType
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
context.byteRangeContext.setBody(body)
|
|
74
|
-
|
|
75
|
-
const response = okRangeResponse(resource, context.byteRangeContext.getBody(responseContentType), { byteRangeContext: context.byteRangeContext, log: this.log })
|
|
76
|
-
|
|
77
|
-
response.headers.set('content-type', context.byteRangeContext.getContentType() ?? responseContentType)
|
|
78
|
-
|
|
79
|
-
if (responseContentType !== 'application/json') {
|
|
80
|
-
context.query.download = true
|
|
81
|
-
context.query.filename ??= `${cid}.cbor`
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this.log.trace('setting content type to "%s"', context.byteRangeContext.getContentType() ?? responseContentType)
|
|
85
|
-
setIpfsRoots(response, ipfsRoots)
|
|
86
|
-
|
|
87
|
-
return response
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Turns `{ "/": string }` properties into `CID` instances and
|
|
93
|
-
* `{ "/": { "bytes": base64 } }` into `Uint8Array`s
|
|
94
|
-
*/
|
|
95
|
-
function convert (obj: any): void {
|
|
96
|
-
try {
|
|
97
|
-
for (const key of Object.getOwnPropertyNames(obj)) {
|
|
98
|
-
if (typeof obj[key]?.['/'] === 'string') {
|
|
99
|
-
obj[key] = CID.parse(obj[key]['/'])
|
|
100
|
-
} else if (typeof obj[key]?.['/']?.['bytes'] === 'string') {
|
|
101
|
-
obj[key] = uint8ArrayFromString(obj[key]['/']['bytes'], 'base64')
|
|
102
|
-
} else {
|
|
103
|
-
convert(obj[key])
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
} catch {}
|
|
107
|
-
}
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import * as ipldDagCbor from '@ipld/dag-cbor'
|
|
2
|
-
import { base32 } from 'multiformats/bases/base32'
|
|
3
|
-
import { CID } from 'multiformats/cid'
|
|
4
|
-
import { sha256 } from 'multiformats/hashes/sha2'
|
|
5
|
-
import { isLink } from 'multiformats/link'
|
|
6
|
-
import { getETag } from '../utils/get-e-tag.js'
|
|
7
|
-
import { getIpfsRoots } from '../utils/response-headers.js'
|
|
8
|
-
import { isObjectNode } from '../utils/walk-path.js'
|
|
9
|
-
import { BasePlugin } from './plugin-base.js'
|
|
10
|
-
import type { PluginContext, VerifiedFetchPluginFactory } from './types.js'
|
|
11
|
-
import type { ObjectNode } from 'ipfs-unixfs-exporter'
|
|
12
|
-
|
|
13
|
-
function isPrimitive (value: unknown): boolean {
|
|
14
|
-
return value === null ||
|
|
15
|
-
value === undefined ||
|
|
16
|
-
typeof value === 'string' ||
|
|
17
|
-
typeof value === 'number' ||
|
|
18
|
-
typeof value === 'boolean' ||
|
|
19
|
-
typeof value === 'bigint' ||
|
|
20
|
-
typeof value === 'symbol'
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Converts a cborObject and it's children into a small hash that can be used in the etag header.
|
|
25
|
-
*
|
|
26
|
-
* @see https://github.com/ipfs/boxo/blob/dc60fe747c375c631a92fcfd6c7456f44a760d24/gateway/assets/assets.go#L84
|
|
27
|
-
* @see https://github.com/ipfs/boxo/blob/dc60fe747c375c631a92fcfd6c7456f44a760d24/gateway/handler_unixfs_dir.go#L233-L235
|
|
28
|
-
*/
|
|
29
|
-
async function getAssetHash (cborObject: Record<string, any>): Promise<string> {
|
|
30
|
-
/**
|
|
31
|
-
* Plugin Version represents a "version of gateway implementation" that allows for cache busting if necessary.
|
|
32
|
-
*
|
|
33
|
-
* @see https://specs.ipfs.tech/http-gateways/path-gateway/#etag-response-header
|
|
34
|
-
*/
|
|
35
|
-
const pluginVersion = '0.0.1'
|
|
36
|
-
const entryDetails = Object.entries(cborObject).reduce((acc, [key, value]) => {
|
|
37
|
-
if (isPrimitive(value)) {
|
|
38
|
-
return `${acc}${key}${value}`
|
|
39
|
-
}
|
|
40
|
-
return `${acc}${key}${getAssetHash(value)}`
|
|
41
|
-
}, '')
|
|
42
|
-
const hashBytes = await sha256.encode(new TextEncoder().encode(pluginVersion + entryDetails))
|
|
43
|
-
return base32.encode(hashBytes)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Handles `dag-cbor` content where the Accept: `text/html` header is present.
|
|
48
|
-
*/
|
|
49
|
-
export class DagCborHtmlPreviewPlugin extends BasePlugin {
|
|
50
|
-
readonly id = 'dag-cbor-plugin-html-preview'
|
|
51
|
-
readonly codes = [ipldDagCbor.code]
|
|
52
|
-
|
|
53
|
-
canHandle ({ cid, accept, pathDetails }: PluginContext): boolean {
|
|
54
|
-
if (pathDetails == null) {
|
|
55
|
-
return false
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!isObjectNode(pathDetails.terminalElement)) {
|
|
59
|
-
return false
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (cid.code !== ipldDagCbor.code) {
|
|
63
|
-
return false
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (accept == null || !accept?.mimeType.includes('text/html')) {
|
|
67
|
-
return false
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return isObjectNode(pathDetails.terminalElement)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext' | 'pathDetails'>> & { pathDetails: { terminalElement: ObjectNode } }): Promise<Response> {
|
|
74
|
-
const { cid, path, pathDetails: { terminalElement, ipfsRoots } } = context
|
|
75
|
-
this.log.trace('generating html preview for %c/%s', cid, path)
|
|
76
|
-
|
|
77
|
-
const block = terminalElement.node
|
|
78
|
-
let obj: Record<string, any>
|
|
79
|
-
try {
|
|
80
|
-
obj = ipldDagCbor.decode(block)
|
|
81
|
-
} catch (err) {
|
|
82
|
-
return new Response(`<pre>Failed to decode DAG-CBOR: ${String(err)}</pre>`, {
|
|
83
|
-
status: 500,
|
|
84
|
-
headers: { 'Content-Type': 'text/html' }
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const html = this.getHtml({ path, obj, cid })
|
|
89
|
-
|
|
90
|
-
return new Response(html, {
|
|
91
|
-
status: 200,
|
|
92
|
-
headers: {
|
|
93
|
-
'Content-Type': 'text/html',
|
|
94
|
-
'X-Ipfs-Roots': getIpfsRoots(ipfsRoots),
|
|
95
|
-
'Cache-Control': 'public, max-age=604800, stale-while-revalidate=2678400',
|
|
96
|
-
Etag: getETag({ cid, reqFormat: context.reqFormat, contentPrefix: `DagIndex-${await getAssetHash(obj)}_CID-` })
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getHtml ({ path, obj, cid }: { path?: string[], obj: Record<string, any>, cid: CID }): string {
|
|
102
|
-
const style = `
|
|
103
|
-
:root {
|
|
104
|
-
--sans-serif: "Plex", system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
|
|
105
|
-
--monospace: Consolas, monaco, monospace;
|
|
106
|
-
--navy: #073a53;
|
|
107
|
-
--teal: #6bc4ce;
|
|
108
|
-
--turquoise: #47AFB4;
|
|
109
|
-
--steel-gray: #3f5667;
|
|
110
|
-
--dark-white: #d9dbe2;
|
|
111
|
-
--light-white: #edf0f4;
|
|
112
|
-
--near-white: #f7f8fa;
|
|
113
|
-
--radius: 4px;
|
|
114
|
-
}
|
|
115
|
-
body {
|
|
116
|
-
color: #34373f;
|
|
117
|
-
font-family: var(--sans-serif);
|
|
118
|
-
line-height: 1.43;
|
|
119
|
-
margin: 0;
|
|
120
|
-
word-break: break-all;
|
|
121
|
-
-webkit-text-size-adjust: 100%;
|
|
122
|
-
-ms-text-size-adjust: 100%;
|
|
123
|
-
-webkit-tap-highlight-color: transparent;
|
|
124
|
-
}
|
|
125
|
-
main {
|
|
126
|
-
border: 1px solid var(--dark-white);
|
|
127
|
-
border-radius: var(--radius);
|
|
128
|
-
overflow: hidden;
|
|
129
|
-
margin: 1em;
|
|
130
|
-
font-size: .875em;
|
|
131
|
-
}
|
|
132
|
-
main section header {
|
|
133
|
-
background-color: var(--near-white);
|
|
134
|
-
}
|
|
135
|
-
main header, main section:not(:last-child) {
|
|
136
|
-
border-bottom: 1px solid var(--dark-white);
|
|
137
|
-
}
|
|
138
|
-
main header {
|
|
139
|
-
padding-top: .7em;
|
|
140
|
-
padding-bottom: .7em;
|
|
141
|
-
background-color: var(--light-white);
|
|
142
|
-
}
|
|
143
|
-
main header, main .container {
|
|
144
|
-
padding-left: 1em;
|
|
145
|
-
padding-right: 1em;
|
|
146
|
-
}
|
|
147
|
-
section {
|
|
148
|
-
display: block;
|
|
149
|
-
}
|
|
150
|
-
.grid.dag {
|
|
151
|
-
grid-template-columns: max-content 1fr;
|
|
152
|
-
}
|
|
153
|
-
.grid {
|
|
154
|
-
display: grid;
|
|
155
|
-
overflow-x: auto;
|
|
156
|
-
}
|
|
157
|
-
.grid.dag > div {
|
|
158
|
-
background: white;
|
|
159
|
-
}
|
|
160
|
-
.grid.dag > div:nth-of-type(2n+1) {
|
|
161
|
-
padding-left: 1em;
|
|
162
|
-
}
|
|
163
|
-
.grid.dag > div:nth-child(4n), .grid.dag > div:nth-child(4n+3) {
|
|
164
|
-
background-color: var(--near-white);
|
|
165
|
-
}
|
|
166
|
-
.grid.dag .grid {
|
|
167
|
-
padding: 0;
|
|
168
|
-
}
|
|
169
|
-
/* change coloring of nested grid
|
|
170
|
-
.grid.dag .grid.dag > div:nth-child(4n), .grid.dag .grid.dag > div:nth-child(4n+3) {
|
|
171
|
-
background-color: white;
|
|
172
|
-
}
|
|
173
|
-
.grid.dag .grid.dag div:nth-child(4n+1), .grid.dag .grid.dag div:nth-child(4n+2) {
|
|
174
|
-
background-color: var(--near-white);
|
|
175
|
-
} */
|
|
176
|
-
.grid.dag > div:nth-last-child(-n+2) {
|
|
177
|
-
border-bottom: 0;
|
|
178
|
-
}
|
|
179
|
-
.grid .grid {
|
|
180
|
-
overflow-x: visible;
|
|
181
|
-
}
|
|
182
|
-
.grid > div {
|
|
183
|
-
padding: .7em;
|
|
184
|
-
border-bottom: 1px solid var(--dark-white);
|
|
185
|
-
}
|
|
186
|
-
.nowrap {
|
|
187
|
-
white-space: nowrap;
|
|
188
|
-
}
|
|
189
|
-
pre, code {
|
|
190
|
-
font-family: var(--monospace);
|
|
191
|
-
}
|
|
192
|
-
strong {
|
|
193
|
-
font-weight: bolder;
|
|
194
|
-
}
|
|
195
|
-
a:active, a:visited {
|
|
196
|
-
color: #00b0e9;
|
|
197
|
-
}
|
|
198
|
-
.ipfs-hash {
|
|
199
|
-
color: #7f8491;
|
|
200
|
-
font-family: var(--monospace);
|
|
201
|
-
}
|
|
202
|
-
a {
|
|
203
|
-
color: #117eb3;
|
|
204
|
-
text-decoration: none;
|
|
205
|
-
}
|
|
206
|
-
header { margin-bottom: 2em; }
|
|
207
|
-
.ipfs-logo { width: 32px; height: 32px; background: url('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==') no-repeat center/contain; display: inline-block; }
|
|
208
|
-
`
|
|
209
|
-
|
|
210
|
-
return `<!DOCTYPE html>
|
|
211
|
-
<html lang="en">
|
|
212
|
-
<head>
|
|
213
|
-
<meta charset="utf-8">
|
|
214
|
-
<meta name="description" content="Content-addressed dag-cbor document hosted on IPFS.">
|
|
215
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
216
|
-
<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==">
|
|
217
|
-
<title>${cid.toString()} DAG-CBOR Preview</title>
|
|
218
|
-
<style>${style}</style>
|
|
219
|
-
</head>
|
|
220
|
-
<body>
|
|
221
|
-
<main>
|
|
222
|
-
<header>
|
|
223
|
-
<div><strong>CID: </strong> <code class="nowrap">${cid}</code></div>
|
|
224
|
-
<div><strong>Codec: </strong> ${this.valueHTML('dag-cbor (0x71)', [])}</div>
|
|
225
|
-
</header>
|
|
226
|
-
<section class="container">
|
|
227
|
-
<p>You can download this block as:</p>
|
|
228
|
-
<ul>
|
|
229
|
-
<li><a href="?format=raw&download=true&filename=${cid.toString()}.bin" rel="nofollow" download="${cid.toString()}.bin">Raw Block</a> (no conversion)</li>
|
|
230
|
-
<li><a href="?format=dag-json&download=true&filename=${cid.toString()}.json" rel="nofollow" download="${cid.toString()}">Valid DAG-JSON</a> (specs at <a href="https://ipld.io/specs/codecs/dag-json/spec/" target="_blank" rel="noopener noreferrer">IPLD</a> and <a href="https://www.iana.org/assignments/media-types/application/vnd.ipld.dag-json" target="_blank" rel="noopener noreferrer">IANA</a>)</li>
|
|
231
|
-
<li><a href="?format=dag-cbor&download=true&filename=${cid.toString()}.dag-cbor" rel="nofollow" download="${cid.toString()}.dag-cbor">Valid DAG-CBOR</a> (specs at <a href="https://ipld.io/specs/codecs/dag-cbor/spec/" target="_blank" rel="noopener noreferrer">IPLD</a> and <a href="https://www.iana.org/assignments/media-types/application/vnd.ipld.dag-cbor" target="_blank" rel="noopener noreferrer">IANA</a>)</li>
|
|
232
|
-
</ul>
|
|
233
|
-
</section>
|
|
234
|
-
<section>
|
|
235
|
-
<header><strong>DAG-CBOR Preview</strong></header>
|
|
236
|
-
<div class="grid dag">
|
|
237
|
-
${this.renderRows(obj, path)}
|
|
238
|
-
</div>
|
|
239
|
-
</section>
|
|
240
|
-
</main>
|
|
241
|
-
</body>
|
|
242
|
-
</html>`
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
valueHTML (value: any, link: string[]): string {
|
|
246
|
-
let valueString: string
|
|
247
|
-
const isALinkObject = isLink(value)
|
|
248
|
-
if (!isALinkObject && typeof value !== 'string') {
|
|
249
|
-
valueString = JSON.stringify(value)
|
|
250
|
-
} else {
|
|
251
|
-
// it can be a string or a link object.. call .toString() on it
|
|
252
|
-
valueString = value.toString()
|
|
253
|
-
}
|
|
254
|
-
const valueCodeBlock = `<code class="nowrap">${valueString}</code>`
|
|
255
|
-
if (isALinkObject && link != null) {
|
|
256
|
-
return `<a class="ipfs-hash" href="/${link}">${valueCodeBlock}</a>`
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return valueCodeBlock
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
private renderValue (key: string, value: any, currentPath: string[]): string {
|
|
263
|
-
let rows = ''
|
|
264
|
-
value.forEach((item: any, idx: number) => {
|
|
265
|
-
const itemPath = [...currentPath, key, idx.toString()]
|
|
266
|
-
rows += `<div>${this.valueHTML(idx, [])}</div>`
|
|
267
|
-
if (isPrimitive(item)) {
|
|
268
|
-
rows += `<div>${this.valueHTML(item, itemPath)}</div>`
|
|
269
|
-
} else {
|
|
270
|
-
rows += '<div class="grid dag">'
|
|
271
|
-
rows += this.renderRows(item, itemPath)
|
|
272
|
-
rows += '</div>'
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
return rows
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
renderRows (obj: Record<string, any>, currentPath: string[] = []): string {
|
|
279
|
-
let rows = ''
|
|
280
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
281
|
-
if (Array.isArray(value)) {
|
|
282
|
-
rows += `<div>${key}</div>`
|
|
283
|
-
rows += '<div class="grid dag">'
|
|
284
|
-
rows += this.renderValue(key, value, currentPath)
|
|
285
|
-
rows += '</div>'
|
|
286
|
-
} else {
|
|
287
|
-
const valuePath = [...currentPath, key]
|
|
288
|
-
rows += `<div>${key}</div><div>${this.valueHTML(value, valuePath)}</div>`
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return rows
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export const dagCborHtmlPreviewPluginFactory: VerifiedFetchPluginFactory = (opts) => new DagCborHtmlPreviewPlugin(opts)
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import * as ipldDagCbor from '@ipld/dag-cbor'
|
|
2
|
-
import { dagCborToSafeJSON } from '../utils/dag-cbor-to-safe-json.ts'
|
|
3
|
-
import { setIpfsRoots } from '../utils/response-headers.js'
|
|
4
|
-
import { notAcceptableResponse, okRangeResponse } from '../utils/responses.js'
|
|
5
|
-
import { isObjectNode } from '../utils/walk-path.js'
|
|
6
|
-
import { BasePlugin } from './plugin-base.js'
|
|
7
|
-
import type { PluginContext } from './types.js'
|
|
8
|
-
import type { ObjectNode } from 'ipfs-unixfs-exporter'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Handles `dag-cbor` content, including requests with Accept: `application/vnd.ipld.dag-json` and `application/json`.
|
|
12
|
-
*/
|
|
13
|
-
export class DagCborPlugin extends BasePlugin {
|
|
14
|
-
readonly id = 'dag-cbor-plugin'
|
|
15
|
-
readonly codes = [ipldDagCbor.code]
|
|
16
|
-
|
|
17
|
-
canHandle ({ cid, accept, pathDetails, byteRangeContext, plugins }: PluginContext): boolean {
|
|
18
|
-
if (pathDetails == null) {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!isObjectNode(pathDetails.terminalElement)) {
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (cid.code !== ipldDagCbor.code) {
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (byteRangeContext == null) {
|
|
31
|
-
return false
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (accept != null && accept.mimeType === 'text/html' && plugins.includes('dag-cbor-plugin-html-preview')) {
|
|
35
|
-
// let the dag-cbor-html-preview plugin handle it
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return isObjectNode(pathDetails.terminalElement)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext' | 'pathDetails'>> & { pathDetails: { terminalElement: ObjectNode } }): Promise<Response> {
|
|
43
|
-
const { cid, path, resource, accept, pathDetails: { terminalElement, ipfsRoots } } = context
|
|
44
|
-
|
|
45
|
-
this.log.trace('fetching %c/%s with accept header %s', cid, path.join('/'), accept?.mimeType)
|
|
46
|
-
|
|
47
|
-
const block = terminalElement.node
|
|
48
|
-
|
|
49
|
-
let responseContentType = 'application/cbor'
|
|
50
|
-
let body: string | Uint8Array = block
|
|
51
|
-
|
|
52
|
-
if (accept?.mimeType === 'application/json' || accept?.mimeType === 'application/vnd.ipld.dag-json') {
|
|
53
|
-
try {
|
|
54
|
-
// if vnd.ipld.dag-json has been specified, convert to the format - note
|
|
55
|
-
// that this supports more data types than regular JSON
|
|
56
|
-
body = dagCborToSafeJSON(body)
|
|
57
|
-
responseContentType = accept.mimeType
|
|
58
|
-
} catch (err) {
|
|
59
|
-
this.log.error('could not decode CBOR as JSON-safe - %e', err)
|
|
60
|
-
return notAcceptableResponse(resource)
|
|
61
|
-
}
|
|
62
|
-
} else if (accept?.mimeType === 'application/octet-stream' || accept?.mimeType === 'application/vnd.ipld.raw' || accept?.mimeType === 'application/vnd.ipld.dag-cbor') {
|
|
63
|
-
responseContentType = accept.mimeType
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
context.byteRangeContext.setBody(body)
|
|
67
|
-
|
|
68
|
-
// const responseContentType = accept?.mimeType ?? (body instanceof Uint8Array ? 'application/octet-stream' : 'application/json')
|
|
69
|
-
const response = okRangeResponse(resource, context.byteRangeContext.getBody(responseContentType), { byteRangeContext: context.byteRangeContext, log: this.log })
|
|
70
|
-
|
|
71
|
-
response.headers.set('content-type', context.byteRangeContext.getContentType() ?? responseContentType)
|
|
72
|
-
|
|
73
|
-
if (responseContentType !== 'application/json') {
|
|
74
|
-
context.query.download = true
|
|
75
|
-
context.query.filename ??= `${cid}.cbor`
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.log.trace('setting content type to "%s"', context.byteRangeContext.getContentType() ?? responseContentType)
|
|
79
|
-
setIpfsRoots(response, ipfsRoots)
|
|
80
|
-
|
|
81
|
-
return response
|
|
82
|
-
}
|
|
83
|
-
}
|