@helia/verified-fetch 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -0
- package/dist/index.min.js +357 -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 +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 -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 +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/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 +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 -34
- package/src/utils/dir-index-html.ts +445 -0
- package/src/utils/get-e-tag.ts +20 -3
- package/src/utils/walk-path.ts +3 -3
- package/src/verified-fetch.ts +187 -430
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { car } from '@helia/car';
|
|
2
|
+
import toBrowserReadableStream from 'it-to-browser-readablestream';
|
|
3
|
+
import { okResponse } from '../utils/responses.js';
|
|
4
|
+
import { BasePlugin } from './plugin-base.js';
|
|
5
|
+
/**
|
|
6
|
+
* Accepts a `CID` and returns a `Response` with a body stream that is a CAR
|
|
7
|
+
* of the `DAG` referenced by the `CID`.
|
|
8
|
+
*/
|
|
9
|
+
export class CarPlugin extends BasePlugin {
|
|
10
|
+
canHandle(context) {
|
|
11
|
+
this.log('checking if we can handle %c with accept %s', context.cid, context.accept);
|
|
12
|
+
return context.accept?.startsWith('application/vnd.ipld.car') === true || context.query.format === 'car'; // application/vnd.ipld.car
|
|
13
|
+
}
|
|
14
|
+
async handle(context) {
|
|
15
|
+
const { options } = context;
|
|
16
|
+
const { getBlockstore, helia } = this.pluginOptions;
|
|
17
|
+
context.reqFormat = 'car';
|
|
18
|
+
context.query.download = true;
|
|
19
|
+
context.query.filename = context.query.filename ?? `${context.cid.toString()}.car`;
|
|
20
|
+
const blockstore = getBlockstore(context.cid, context.resource, options?.session ?? true, options);
|
|
21
|
+
const c = car({ blockstore, getCodec: helia.getCodec });
|
|
22
|
+
const stream = toBrowserReadableStream(c.stream(context.cid, options));
|
|
23
|
+
const response = okResponse(context.resource, stream);
|
|
24
|
+
response.headers.set('content-type', 'application/vnd.ipld.car; version=1');
|
|
25
|
+
return response;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=plugin-handle-car.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-car.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-car.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;AAChC,OAAO,uBAAuB,MAAM,8BAA8B,CAAA;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C;;;GAGG;AACH,MAAM,OAAO,SAAU,SAAQ,UAAU;IACvC,SAAS,CAAE,OAAsB;QAC/B,IAAI,CAAC,GAAG,CAAC,6CAA6C,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QACpF,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,0BAA0B,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,KAAK,CAAA,CAAC,2BAA2B;IACtI,CAAC;IAED,KAAK,CAAC,MAAM,CAAE,OAAsB;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;QAC3B,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,aAAa,CAAA;QACnD,OAAO,CAAC,SAAS,GAAG,KAAK,CAAA;QACzB,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAA;QAC7B,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAA;QAClF,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,CAAC,CAAA;QAClG,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;QAEtE,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACrD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,qCAAqC,CAAC,CAAA;QAE3E,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles `dag-cbor` content, including requests with Accept: `application/vnd.ipld.dag-json` and `application/json`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class DagCborPlugin extends BasePlugin {
|
|
7
|
+
readonly codes: 113[];
|
|
8
|
+
canHandle({ cid, accept, pathDetails }: PluginContext): boolean;
|
|
9
|
+
handle(context: PluginContext): Promise<Response>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=plugin-handle-dag-cbor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dag-cbor.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dag-cbor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAG/C;;GAEG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,QAAQ,CAAC,KAAK,QAAqB;IAEnC,SAAS,CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,OAAO;IAe1D,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAoDzD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as ipldDagCbor from '@ipld/dag-cbor';
|
|
2
|
+
import * as ipldDagJson from '@ipld/dag-json';
|
|
3
|
+
import { dagCborToSafeJSON } from '../utils/dag-cbor-to-safe-json.js';
|
|
4
|
+
import { setIpfsRoots } from '../utils/response-headers.js';
|
|
5
|
+
import { notAcceptableResponse, okResponse } from '../utils/responses.js';
|
|
6
|
+
import { isObjectNode } from '../utils/walk-path.js';
|
|
7
|
+
import { BasePlugin } from './plugin-base.js';
|
|
8
|
+
/**
|
|
9
|
+
* Handles `dag-cbor` content, including requests with Accept: `application/vnd.ipld.dag-json` and `application/json`.
|
|
10
|
+
*/
|
|
11
|
+
export class DagCborPlugin extends BasePlugin {
|
|
12
|
+
codes = [ipldDagCbor.code];
|
|
13
|
+
canHandle({ cid, accept, pathDetails }) {
|
|
14
|
+
this.log('checking if we can handle %c with accept %s', cid, accept);
|
|
15
|
+
if (pathDetails == null) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (!isObjectNode(pathDetails.terminalElement)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (cid.code !== ipldDagCbor.code) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return isObjectNode(pathDetails.terminalElement);
|
|
25
|
+
}
|
|
26
|
+
async handle(context) {
|
|
27
|
+
const { cid, path, resource, accept, pathDetails } = context;
|
|
28
|
+
this.log.trace('fetching %c/%s', cid, path);
|
|
29
|
+
if (pathDetails == null) {
|
|
30
|
+
throw new Error('pathDetails is null');
|
|
31
|
+
}
|
|
32
|
+
const ipfsRoots = pathDetails.ipfsRoots;
|
|
33
|
+
const terminalElement = pathDetails.terminalElement; // checked in canHandle fn.
|
|
34
|
+
const block = terminalElement.node;
|
|
35
|
+
let body;
|
|
36
|
+
if (accept === 'application/octet-stream' || accept === 'application/vnd.ipld.dag-cbor' || accept === 'application/cbor') {
|
|
37
|
+
// skip decoding
|
|
38
|
+
body = block;
|
|
39
|
+
}
|
|
40
|
+
else if (accept === 'application/vnd.ipld.dag-json') {
|
|
41
|
+
try {
|
|
42
|
+
// if vnd.ipld.dag-json has been specified, convert to the format - note
|
|
43
|
+
// that this supports more data types than regular JSON, the content-type
|
|
44
|
+
// response header is set so the user knows to process it differently
|
|
45
|
+
const obj = ipldDagCbor.decode(block);
|
|
46
|
+
body = ipldDagJson.encode(obj);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
this.log.error('could not transform %c to application/vnd.ipld.dag-json', err);
|
|
50
|
+
return notAcceptableResponse(resource);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
try {
|
|
55
|
+
body = dagCborToSafeJSON(block);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (accept === 'application/json') {
|
|
59
|
+
this.log('could not decode DAG-CBOR as JSON-safe, but the client sent "Accept: application/json"', err);
|
|
60
|
+
return notAcceptableResponse(resource);
|
|
61
|
+
}
|
|
62
|
+
this.log('could not decode DAG-CBOR as JSON-safe, falling back to `application/octet-stream`', err);
|
|
63
|
+
body = block;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const response = okResponse(resource, body);
|
|
67
|
+
const responseContentType = accept ?? (body instanceof Uint8Array ? 'application/octet-stream' : 'application/json');
|
|
68
|
+
response.headers.set('content-type', responseContentType);
|
|
69
|
+
setIpfsRoots(response, ipfsRoots);
|
|
70
|
+
return response;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=plugin-handle-dag-cbor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dag-cbor.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dag-cbor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles UnixFS and dag-pb content.
|
|
5
|
+
*/
|
|
6
|
+
export declare class DagPbPlugin extends BasePlugin {
|
|
7
|
+
readonly codes: 112[];
|
|
8
|
+
canHandle({ cid, accept, pathDetails }: PluginContext): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* @see https://specs.ipfs.tech/http-gateways/path-gateway/#use-in-directory-url-normalization
|
|
11
|
+
*/
|
|
12
|
+
getRedirectUrl(context: PluginContext): string | null;
|
|
13
|
+
handle(context: PluginContext): Promise<Response | null>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=plugin-handle-dag-pb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dag-pb.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dag-pb.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAG/C;;GAEG;AACH,qBAAa,WAAY,SAAQ,UAAU;IACzC,QAAQ,CAAC,KAAK,QAAc;IAC5B,SAAS,CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,OAAO;IAShE;;OAEG;IACH,cAAc,CAAE,OAAO,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI;IAiBhD,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;CAwHhE"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { unixfs } from '@helia/unixfs';
|
|
2
|
+
import { code as dagPbCode } from '@ipld/dag-pb';
|
|
3
|
+
import { exporter } from 'ipfs-unixfs-exporter';
|
|
4
|
+
import { CustomProgressEvent } from 'progress-events';
|
|
5
|
+
import { ByteRangeContext } from '../utils/byte-range-context.js';
|
|
6
|
+
import { getStreamFromAsyncIterable } from '../utils/get-stream-from-async-iterable.js';
|
|
7
|
+
import { setIpfsRoots } from '../utils/response-headers.js';
|
|
8
|
+
import { badGatewayResponse, badRangeResponse, movedPermanentlyResponse, notSupportedResponse, okRangeResponse } from '../utils/responses.js';
|
|
9
|
+
import { setContentType } from '../utils/set-content-type.js';
|
|
10
|
+
import { BasePlugin } from './plugin-base.js';
|
|
11
|
+
/**
|
|
12
|
+
* Handles UnixFS and dag-pb content.
|
|
13
|
+
*/
|
|
14
|
+
export class DagPbPlugin extends BasePlugin {
|
|
15
|
+
codes = [dagPbCode];
|
|
16
|
+
canHandle({ cid, accept, pathDetails }) {
|
|
17
|
+
this.log('checking if we can handle %c with accept %s', cid, accept);
|
|
18
|
+
if (pathDetails == null) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return cid.code === dagPbCode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @see https://specs.ipfs.tech/http-gateways/path-gateway/#use-in-directory-url-normalization
|
|
25
|
+
*/
|
|
26
|
+
getRedirectUrl(context) {
|
|
27
|
+
const { resource, path } = context;
|
|
28
|
+
const redirectCheckNeeded = path === '' ? !resource.toString().endsWith('/') : !path.endsWith('/');
|
|
29
|
+
if (redirectCheckNeeded) {
|
|
30
|
+
try {
|
|
31
|
+
const url = new URL(resource.toString());
|
|
32
|
+
// make sure we append slash to end of the path
|
|
33
|
+
url.pathname = `${url.pathname}/`;
|
|
34
|
+
return url.toString();
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
// resource is likely a CID
|
|
38
|
+
return `${resource.toString()}/`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
async handle(context) {
|
|
44
|
+
const { cid, options, withServerTiming = false, pathDetails } = context;
|
|
45
|
+
const { handleServerTiming, contentTypeParser, helia, getBlockstore } = this.pluginOptions;
|
|
46
|
+
const log = this.log;
|
|
47
|
+
let resource = context.resource;
|
|
48
|
+
let path = context.path;
|
|
49
|
+
let redirected = false;
|
|
50
|
+
const byteRangeContext = new ByteRangeContext(this.pluginOptions.logger, options?.headers);
|
|
51
|
+
if (pathDetails == null) {
|
|
52
|
+
throw new TypeError('Path details are required');
|
|
53
|
+
}
|
|
54
|
+
const ipfsRoots = pathDetails.ipfsRoots;
|
|
55
|
+
const terminalElement = pathDetails.terminalElement;
|
|
56
|
+
let resolvedCID = terminalElement.cid;
|
|
57
|
+
if (terminalElement?.type === 'directory') {
|
|
58
|
+
const dirCid = terminalElement.cid;
|
|
59
|
+
const redirectUrl = this.getRedirectUrl(context);
|
|
60
|
+
if (redirectUrl != null) {
|
|
61
|
+
log.trace('directory url normalization spec requires redirect...');
|
|
62
|
+
if (options?.redirect === 'error') {
|
|
63
|
+
log('could not redirect to %s as redirect option was set to "error"', redirectUrl);
|
|
64
|
+
throw new TypeError('Failed to fetch');
|
|
65
|
+
}
|
|
66
|
+
else if (options?.redirect === 'manual') {
|
|
67
|
+
log('returning 301 permanent redirect to %s', redirectUrl);
|
|
68
|
+
return movedPermanentlyResponse(resource, redirectUrl);
|
|
69
|
+
}
|
|
70
|
+
log('following redirect to %s', redirectUrl);
|
|
71
|
+
// fall-through simulates following the redirect?
|
|
72
|
+
resource = redirectUrl;
|
|
73
|
+
redirected = true;
|
|
74
|
+
}
|
|
75
|
+
const rootFilePath = 'index.html';
|
|
76
|
+
try {
|
|
77
|
+
log.trace('found directory at %c/%s, looking for index.html', cid, path);
|
|
78
|
+
const entry = await handleServerTiming('exporter-dir', '', async () => exporter(`/ipfs/${dirCid}/${rootFilePath}`, helia.blockstore, {
|
|
79
|
+
signal: options?.signal,
|
|
80
|
+
onProgress: options?.onProgress
|
|
81
|
+
}), withServerTiming);
|
|
82
|
+
log.trace('found root file at %c/%s with cid %c', dirCid, rootFilePath, entry.cid);
|
|
83
|
+
path = rootFilePath;
|
|
84
|
+
resolvedCID = entry.cid;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
this.log.error('error loading path %c/%s', dirCid, rootFilePath, err);
|
|
88
|
+
options?.signal?.throwIfAborted();
|
|
89
|
+
context.isDirectory = true;
|
|
90
|
+
context.directoryEntries = [];
|
|
91
|
+
this.log.trace('attempting to get directory entries because index.html was not found');
|
|
92
|
+
const fs = unixfs({ ...helia, blockstore: getBlockstore(context.cid, context.resource, options?.session ?? true, options) });
|
|
93
|
+
try {
|
|
94
|
+
for await (const dirItem of fs.ls(dirCid, { signal: options?.signal, onProgress: options?.onProgress })) {
|
|
95
|
+
context.directoryEntries.push(dirItem);
|
|
96
|
+
}
|
|
97
|
+
// dir-index-html plugin or dir-index-json (future idea?) plugin should handle this
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
log.error('error listing directory %c', dirCid, e);
|
|
102
|
+
return notSupportedResponse('Unable to get directory contents');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
options?.onProgress?.(new CustomProgressEvent('verified-fetch:request:end', { cid: dirCid, path: rootFilePath }));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// we have a validRangeRequest & terminalElement is a file, we know the size and should set it
|
|
110
|
+
if (byteRangeContext.isRangeRequest && byteRangeContext.isValidRangeRequest && terminalElement.type === 'file') {
|
|
111
|
+
byteRangeContext.setFileSize(terminalElement.unixfs.fileSize());
|
|
112
|
+
log.trace('fileSize for rangeRequest %d', byteRangeContext.getFileSize());
|
|
113
|
+
}
|
|
114
|
+
const offset = byteRangeContext.offset;
|
|
115
|
+
const length = byteRangeContext.length;
|
|
116
|
+
log.trace('calling exporter for %c/%s with offset=%o & length=%o', resolvedCID, path, offset, length);
|
|
117
|
+
try {
|
|
118
|
+
const entry = await handleServerTiming('exporter-file', '', async () => exporter(resolvedCID, helia.blockstore, {
|
|
119
|
+
signal: options?.signal,
|
|
120
|
+
onProgress: options?.onProgress
|
|
121
|
+
}), withServerTiming);
|
|
122
|
+
const asyncIter = entry.content({
|
|
123
|
+
signal: options?.signal,
|
|
124
|
+
onProgress: options?.onProgress,
|
|
125
|
+
offset,
|
|
126
|
+
length
|
|
127
|
+
});
|
|
128
|
+
log('got async iterator for %c/%s', cid, path);
|
|
129
|
+
const { stream, firstChunk } = await handleServerTiming('stream-and-chunk', '', async () => getStreamFromAsyncIterable(asyncIter, path ?? '', this.pluginOptions.logger, {
|
|
130
|
+
onProgress: options?.onProgress,
|
|
131
|
+
signal: options?.signal
|
|
132
|
+
}), withServerTiming);
|
|
133
|
+
byteRangeContext.setBody(stream);
|
|
134
|
+
// if not a valid range request, okRangeRequest will call okResponse
|
|
135
|
+
const response = okRangeResponse(resource, byteRangeContext.getBody(), { byteRangeContext, log }, {
|
|
136
|
+
redirected
|
|
137
|
+
});
|
|
138
|
+
await handleServerTiming('set-content-type', '', async () => setContentType({ bytes: firstChunk, path, response, contentTypeParser, log }), withServerTiming);
|
|
139
|
+
setIpfsRoots(response, ipfsRoots);
|
|
140
|
+
return response;
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
options?.signal?.throwIfAborted();
|
|
144
|
+
log.error('error streaming %c/%s', cid, path, err);
|
|
145
|
+
if (byteRangeContext.isRangeRequest && err.code === 'ERR_INVALID_PARAMS') {
|
|
146
|
+
return badRangeResponse(resource);
|
|
147
|
+
}
|
|
148
|
+
return badGatewayResponse(resource.toString(), 'Unable to stream content');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=plugin-handle-dag-pb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dag-pb.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dag-pb.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* This plugin should almost always run first because it's going to handle path walking if needed, and will only say it can handle
|
|
5
|
+
* the request if path walking is possible (path is not empty, terminalCid is unknown, and the path has not been walked yet).
|
|
6
|
+
*
|
|
7
|
+
* Once this plugin has run, the PluginContext will be updated and then this plugin will return false for canHandle, so it won't run again.
|
|
8
|
+
*/
|
|
9
|
+
export declare class DagWalkPlugin extends BasePlugin {
|
|
10
|
+
/**
|
|
11
|
+
* Return false if the path has already been walked, otherwise return true if the CID is encoded with a codec that supports pathing.
|
|
12
|
+
*/
|
|
13
|
+
canHandle(context: PluginContext): boolean;
|
|
14
|
+
handle(context: PluginContext): Promise<Response | null>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=plugin-handle-dag-walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dag-walk.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dag-walk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C;;OAEG;IACH,SAAS,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO;IAWrC,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;CAyBhE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { code as dagCborCode } from '@ipld/dag-cbor';
|
|
2
|
+
import { code as dagPbCode } from '@ipld/dag-pb';
|
|
3
|
+
import { handlePathWalking } from '../utils/walk-path.js';
|
|
4
|
+
import { BasePlugin } from './plugin-base.js';
|
|
5
|
+
/**
|
|
6
|
+
* This plugin should almost always run first because it's going to handle path walking if needed, and will only say it can handle
|
|
7
|
+
* the request if path walking is possible (path is not empty, terminalCid is unknown, and the path has not been walked yet).
|
|
8
|
+
*
|
|
9
|
+
* Once this plugin has run, the PluginContext will be updated and then this plugin will return false for canHandle, so it won't run again.
|
|
10
|
+
*/
|
|
11
|
+
export class DagWalkPlugin extends BasePlugin {
|
|
12
|
+
/**
|
|
13
|
+
* Return false if the path has already been walked, otherwise return true if the CID is encoded with a codec that supports pathing.
|
|
14
|
+
*/
|
|
15
|
+
canHandle(context) {
|
|
16
|
+
this.log('checking if we can handle %c with accept %s', context.cid, context.accept);
|
|
17
|
+
const { pathDetails, cid } = context;
|
|
18
|
+
if (pathDetails != null) {
|
|
19
|
+
// path has already been walked
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return (cid.code === dagPbCode || cid.code === dagCborCode);
|
|
23
|
+
}
|
|
24
|
+
async handle(context) {
|
|
25
|
+
const { cid, resource, options, withServerTiming = false } = context;
|
|
26
|
+
const { getBlockstore, handleServerTiming } = this.pluginOptions;
|
|
27
|
+
const blockstore = getBlockstore(cid, resource, options?.session ?? true, options);
|
|
28
|
+
// TODO: migrate handlePathWalking into this plugin
|
|
29
|
+
const pathDetails = await handleServerTiming('path-walking', '', async () => handlePathWalking({ ...context, blockstore, log: this.log }), withServerTiming);
|
|
30
|
+
context.modified++;
|
|
31
|
+
if (pathDetails instanceof Response) {
|
|
32
|
+
this.log.trace('path walking failed');
|
|
33
|
+
if (pathDetails.status === 404) {
|
|
34
|
+
// invalid or incorrect path.. we walked the path but nothing is there
|
|
35
|
+
// send the 404 response
|
|
36
|
+
return pathDetails;
|
|
37
|
+
}
|
|
38
|
+
// some error walking the path
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
context.pathDetails = pathDetails;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=plugin-handle-dag-walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dag-walk.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dag-walk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAC3C;;OAEG;IACH,SAAS,CAAE,OAAsB;QAC/B,IAAI,CAAC,GAAG,CAAC,6CAA6C,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QACpF,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;QACpC,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,+BAA+B;YAC/B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;IAC7D,CAAC;IAED,KAAK,CAAC,MAAM,CAAE,OAAsB;QAClC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,GAAG,KAAK,EAAE,GAAG,OAAO,CAAA;QACpE,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC,aAAa,CAAA;QAChE,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,CAAC,CAAA;QAClF,mDAAmD;QACnD,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,cAAc,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE,CAAC,iBAAiB,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAA;QAE5J,OAAO,CAAC,QAAQ,EAAE,CAAA;QAClB,IAAI,WAAW,YAAY,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAA;YAErC,IAAI,WAAW,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC/B,sEAAsE;gBACtE,wBAAwB;gBACxB,OAAO,WAAW,CAAA;YACpB,CAAC;YAED,8BAA8B;YAC9B,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,CAAC,WAAW,GAAG,WAAW,CAAA;QAEjC,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext, VerifiedFetchPluginFactory } from './types.js';
|
|
3
|
+
export declare class DirIndexHtmlPlugin extends BasePlugin {
|
|
4
|
+
readonly codes: 112[];
|
|
5
|
+
canHandle(context: PluginContext): boolean;
|
|
6
|
+
handle(context: PluginContext): Promise<Response>;
|
|
7
|
+
}
|
|
8
|
+
export declare const dirIndexHtmlPluginFactory: VerifiedFetchPluginFactory;
|
|
9
|
+
//# sourceMappingURL=plugin-handle-dir-index-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dir-index-html.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dir-index-html.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAA;AAE3E,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,QAAQ,CAAC,KAAK,QAAc;IAC5B,SAAS,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO;IAgBrC,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAiBzD;AAED,eAAO,MAAM,yBAAyB,EAAE,0BAAmE,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { code as dagPbCode } from '@ipld/dag-pb';
|
|
2
|
+
import { dirIndexHtml } from '../utils/dir-index-html.js';
|
|
3
|
+
import { okResponse } from '../utils/responses.js';
|
|
4
|
+
import { BasePlugin } from './plugin-base.js';
|
|
5
|
+
export class DirIndexHtmlPlugin extends BasePlugin {
|
|
6
|
+
codes = [dagPbCode];
|
|
7
|
+
canHandle(context) {
|
|
8
|
+
const { cid, pathDetails, directoryEntries } = context;
|
|
9
|
+
if (pathDetails == null) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (pathDetails.terminalElement?.type !== 'directory') {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (directoryEntries?.length === 0) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return cid.code === dagPbCode;
|
|
19
|
+
}
|
|
20
|
+
async handle(context) {
|
|
21
|
+
const { resource, pathDetails, directoryEntries } = context;
|
|
22
|
+
if (pathDetails?.terminalElement == null) {
|
|
23
|
+
throw new Error('Path details are required');
|
|
24
|
+
}
|
|
25
|
+
if (directoryEntries == null || directoryEntries?.length === 0) {
|
|
26
|
+
throw new Error('Directory entries are required');
|
|
27
|
+
}
|
|
28
|
+
const terminalElement = pathDetails.terminalElement;
|
|
29
|
+
const gatewayURL = resource;
|
|
30
|
+
const htmlResponse = dirIndexHtml(terminalElement, directoryEntries, { gatewayURL, log: this.log });
|
|
31
|
+
const response = okResponse(resource, htmlResponse);
|
|
32
|
+
response.headers.set('content-type', 'text/html');
|
|
33
|
+
return response;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export const dirIndexHtmlPluginFactory = (opts) => new DirIndexHtmlPlugin(opts);
|
|
37
|
+
//# sourceMappingURL=plugin-handle-dir-index-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-dir-index-html.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-dir-index-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IACvC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAA;IAC5B,SAAS,CAAE,OAAsB;QAC/B,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAA;QACtD,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,WAAW,CAAC,eAAe,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;YACtD,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,GAAG,CAAC,IAAI,KAAK,SAAS,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,MAAM,CAAE,OAAsB;QAClC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAA;QAE3D,IAAI,WAAW,EAAE,eAAe,IAAI,IAAI,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;QAC9C,CAAC;QACD,IAAI,gBAAgB,IAAI,IAAI,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QACnD,CAAC;QACD,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,CAAA;QAEnD,MAAM,UAAU,GAAG,QAAQ,CAAA;QAC3B,MAAM,YAAY,GAAG,YAAY,CAAC,eAAe,EAAE,gBAAgB,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QACnG,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;QACnD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;QACjD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,yBAAyB,GAA+B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts an `ipns://...`, `https?://<ipnsname>.ipns.<domain>`, or `https?://<domain>/ipns/...` URL as a string and
|
|
5
|
+
* returns a `Response` containing a raw IPNS record.
|
|
6
|
+
*/
|
|
7
|
+
export declare class IpnsRecordPlugin extends BasePlugin {
|
|
8
|
+
readonly codes: never[];
|
|
9
|
+
canHandle({ cid, accept, query }: PluginContext): boolean;
|
|
10
|
+
handle(context: PluginContext): Promise<Response>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=plugin-handle-ipns-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-ipns-record.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-ipns-record.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAG/C;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,QAAQ,CAAC,KAAK,UAAK;IACnB,SAAS,CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,aAAa,GAAG,OAAO;IAMpD,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CA4CzD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Record as DHTRecord } from '@libp2p/kad-dht';
|
|
2
|
+
import { Key } from 'interface-datastore';
|
|
3
|
+
import { concat as uint8ArrayConcat } from 'uint8arrays/concat';
|
|
4
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
|
|
5
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
|
|
6
|
+
import { getPeerIdFromString } from '../utils/get-peer-id-from-string.js';
|
|
7
|
+
import { badRequestResponse, okResponse } from '../utils/responses.js';
|
|
8
|
+
import { PluginFatalError } from './errors.js';
|
|
9
|
+
import { BasePlugin } from './plugin-base.js';
|
|
10
|
+
/**
|
|
11
|
+
* Accepts an `ipns://...`, `https?://<ipnsname>.ipns.<domain>`, or `https?://<domain>/ipns/...` URL as a string and
|
|
12
|
+
* returns a `Response` containing a raw IPNS record.
|
|
13
|
+
*/
|
|
14
|
+
export class IpnsRecordPlugin extends BasePlugin {
|
|
15
|
+
codes = [];
|
|
16
|
+
canHandle({ cid, accept, query }) {
|
|
17
|
+
this.log('checking if we can handle %c with accept %s', cid, accept);
|
|
18
|
+
return accept === 'application/vnd.ipfs.ipns-record' || query.format === 'ipns-record';
|
|
19
|
+
}
|
|
20
|
+
async handle(context) {
|
|
21
|
+
const { resource, path, options } = context;
|
|
22
|
+
const { helia } = this.pluginOptions;
|
|
23
|
+
context.reqFormat = 'ipns-record';
|
|
24
|
+
if (path !== '' || !(resource.startsWith('ipns://') || resource.includes('.ipns.') || resource.includes('/ipns/'))) {
|
|
25
|
+
this.log.error('invalid request for IPNS name "%s" and path "%s"', resource, path);
|
|
26
|
+
throw new PluginFatalError('ERR_INVALID_IPNS_NAME', 'Invalid IPNS name', { response: badRequestResponse(resource, 'Invalid IPNS name') });
|
|
27
|
+
}
|
|
28
|
+
let peerId;
|
|
29
|
+
try {
|
|
30
|
+
let peerIdString;
|
|
31
|
+
if (resource.startsWith('ipns://')) {
|
|
32
|
+
peerIdString = resource.replace('ipns://', '');
|
|
33
|
+
}
|
|
34
|
+
else if (resource.includes('/ipns/')) {
|
|
35
|
+
peerIdString = resource.split('/ipns/')[1].split('/')[0].split('?')[0];
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
peerIdString = resource.split('.ipns.')[0].split('://')[1];
|
|
39
|
+
}
|
|
40
|
+
this.log.trace('trying to parse peer id from "%s"', peerIdString);
|
|
41
|
+
peerId = getPeerIdFromString(peerIdString);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
this.log.error('could not parse peer id from IPNS url %s', resource, err);
|
|
45
|
+
throw new PluginFatalError('ERR_NO_PEER_ID_FOUND', 'could not parse peer id from url', { response: badRequestResponse(resource, err) });
|
|
46
|
+
}
|
|
47
|
+
// since this call happens after parseResource, we've already resolved the
|
|
48
|
+
// IPNS name so a local copy should be in the helia datastore, so we can
|
|
49
|
+
// just read it out..
|
|
50
|
+
const routingKey = uint8ArrayConcat([
|
|
51
|
+
uint8ArrayFromString('/ipns/'),
|
|
52
|
+
peerId.toMultihash().bytes
|
|
53
|
+
]);
|
|
54
|
+
const datastoreKey = new Key('/dht/record/' + uint8ArrayToString(routingKey, 'base32'), false);
|
|
55
|
+
const buf = await helia.datastore.get(datastoreKey, options);
|
|
56
|
+
const record = DHTRecord.deserialize(buf);
|
|
57
|
+
const response = okResponse(resource, record.value);
|
|
58
|
+
response.headers.set('content-type', 'application/vnd.ipfs.ipns-record');
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=plugin-handle-ipns-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-ipns-record.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-ipns-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles `dag-json` content, including requests with Accept: `application/vnd.ipld.dag-cbor` and `application/cbor`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class JsonPlugin extends BasePlugin {
|
|
7
|
+
readonly codes: number[];
|
|
8
|
+
canHandle({ cid, accept }: PluginContext): boolean;
|
|
9
|
+
handle(context: PluginContext): Promise<Response>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=plugin-handle-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-json.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-json.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;GAEG;AACH,qBAAa,UAAW,SAAQ,UAAU;IACxC,QAAQ,CAAC,KAAK,WAA+B;IAC7C,SAAS,CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,aAAa,GAAG,OAAO;IAY7C,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAgCzD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as ipldDagCbor from '@ipld/dag-cbor';
|
|
2
|
+
import * as ipldDagJson from '@ipld/dag-json';
|
|
3
|
+
import { code as jsonCode } from 'multiformats/codecs/json';
|
|
4
|
+
import { notAcceptableResponse, okResponse } from '../utils/responses.js';
|
|
5
|
+
import { BasePlugin } from './plugin-base.js';
|
|
6
|
+
/**
|
|
7
|
+
* Handles `dag-json` content, including requests with Accept: `application/vnd.ipld.dag-cbor` and `application/cbor`.
|
|
8
|
+
*/
|
|
9
|
+
export class JsonPlugin extends BasePlugin {
|
|
10
|
+
codes = [ipldDagJson.code, jsonCode];
|
|
11
|
+
canHandle({ cid, accept }) {
|
|
12
|
+
this.log('checking if we can handle %c with accept %s', cid, accept);
|
|
13
|
+
if (accept === 'application/vnd.ipld.dag-json' && cid.code !== ipldDagCbor.code) {
|
|
14
|
+
// we can handle application/vnd.ipld.dag-json, but if the CID codec is ipldDagCbor, DagCborPlugin should handle it
|
|
15
|
+
// TODO: remove the need for deny-listing cases in plugins
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return ipldDagJson.code === cid.code || jsonCode === cid.code;
|
|
19
|
+
}
|
|
20
|
+
async handle(context) {
|
|
21
|
+
const { path, resource, cid, accept, options } = context;
|
|
22
|
+
const { getBlockstore } = this.pluginOptions;
|
|
23
|
+
const session = options?.session ?? true;
|
|
24
|
+
this.log.trace('fetching %c/%s', cid, path);
|
|
25
|
+
const terminalCid = context.pathDetails?.terminalElement.cid ?? context.cid;
|
|
26
|
+
const blockstore = getBlockstore(terminalCid, resource, session, options);
|
|
27
|
+
const block = await blockstore.get(terminalCid, options);
|
|
28
|
+
let body;
|
|
29
|
+
if (accept === 'application/vnd.ipld.dag-cbor' || accept === 'application/cbor') {
|
|
30
|
+
try {
|
|
31
|
+
// if vnd.ipld.dag-cbor has been specified, convert to the format - note
|
|
32
|
+
// that this supports more data types than regular JSON, the content-type
|
|
33
|
+
// response header is set so the user knows to process it differently
|
|
34
|
+
const obj = ipldDagJson.decode(block);
|
|
35
|
+
body = ipldDagCbor.encode(obj);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
this.log.error('could not transform %c to application/vnd.ipld.dag-cbor', err);
|
|
39
|
+
return notAcceptableResponse(resource);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// skip decoding
|
|
44
|
+
body = block;
|
|
45
|
+
}
|
|
46
|
+
const response = okResponse(resource, body);
|
|
47
|
+
response.headers.set('content-type', accept ?? 'application/json');
|
|
48
|
+
return response;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=plugin-handle-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-json.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,UAAU;IAC/B,KAAK,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC7C,SAAS,CAAE,EAAE,GAAG,EAAE,MAAM,EAAiB;QACvC,IAAI,CAAC,GAAG,CAAC,6CAA6C,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QAEpE,IAAI,MAAM,KAAK,+BAA+B,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE,CAAC;YAChF,mHAAmH;YACnH,0DAA0D;YAC1D,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,WAAW,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAA;IAC/D,CAAC;IAED,KAAK,CAAC,MAAM,CAAE,OAAsB;QAClC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;QACxD,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,aAAa,CAAA;QAC5C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,CAAA;QAExC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QAE3C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,eAAe,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAA;QAC3E,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QACzE,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QACxD,IAAI,IAAyB,CAAA;QAE7B,IAAI,MAAM,KAAK,+BAA+B,IAAI,MAAM,KAAK,kBAAkB,EAAE,CAAC;YAChF,IAAI,CAAC;gBACH,wEAAwE;gBACxE,yEAAyE;gBACzE,qEAAqE;gBACrE,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBACrC,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAChC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,yDAAyD,EAAE,GAAG,CAAC,CAAA;gBAC9E,OAAO,qBAAqB,CAAC,QAAQ,CAAC,CAAA;YACxC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,gBAAgB;YAChB,IAAI,GAAG,KAAK,CAAA;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAC3C,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,IAAI,kBAAkB,CAAC,CAAA;QAClE,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BasePlugin } from './plugin-base.js';
|
|
2
|
+
import type { PluginContext } from './types.js';
|
|
3
|
+
export declare class RawPlugin extends BasePlugin {
|
|
4
|
+
codes: number[];
|
|
5
|
+
canHandle({ cid, accept, query }: PluginContext): boolean;
|
|
6
|
+
handle(context: PluginContext): Promise<Response>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=plugin-handle-raw.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-raw.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-raw.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAsC/C,qBAAa,SAAU,SAAQ,UAAU;IACvC,KAAK,EAAE,MAAM,EAAE,CAA2B;IAE1C,SAAS,CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,aAAa,GAAG,OAAO;IAKpD,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAsCzD"}
|