@helia/verified-fetch 2.6.2 → 2.6.3
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/dist/index.min.js +39 -39
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-car.js +12 -4
- package/dist/src/plugins/plugin-handle-car.js.map +1 -1
- package/dist/src/utils/content-type-parser.d.ts.map +1 -1
- package/dist/src/utils/content-type-parser.js +34 -10
- package/dist/src/utils/content-type-parser.js.map +1 -1
- package/package.json +1 -1
- package/src/plugins/plugin-handle-car.ts +14 -4
- package/src/utils/content-type-parser.ts +32 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-handle-car.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-car.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin-handle-car.d.ts","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-car.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAY/C;;;GAGG;AACH,qBAAa,SAAU,SAAQ,UAAU;IACvC,SAAS,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO;IAKrC,MAAM,CAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAezD"}
|
|
@@ -2,6 +2,14 @@ import { car } from '@helia/car';
|
|
|
2
2
|
import toBrowserReadableStream from 'it-to-browser-readablestream';
|
|
3
3
|
import { okResponse } from '../utils/responses.js';
|
|
4
4
|
import { BasePlugin } from './plugin-base.js';
|
|
5
|
+
function getFilename({ cid, ipfsPath, query }) {
|
|
6
|
+
if (query.filename != null) {
|
|
7
|
+
return query.filename;
|
|
8
|
+
}
|
|
9
|
+
// convert context.ipfsPath to a filename. replace all / with _, replace prefix protocol with empty string
|
|
10
|
+
const filename = ipfsPath.replace(/\/ipfs\//, '').replace(/\/ipns\//, '').replace(/\//g, '_');
|
|
11
|
+
return `${filename}.car`;
|
|
12
|
+
}
|
|
5
13
|
/**
|
|
6
14
|
* Accepts a `CID` and returns a `Response` with a body stream that is a CAR
|
|
7
15
|
* of the `DAG` referenced by the `CID`.
|
|
@@ -12,14 +20,14 @@ export class CarPlugin extends BasePlugin {
|
|
|
12
20
|
return context.accept?.startsWith('application/vnd.ipld.car') === true || context.query.format === 'car'; // application/vnd.ipld.car
|
|
13
21
|
}
|
|
14
22
|
async handle(context) {
|
|
15
|
-
const { options } = context;
|
|
23
|
+
const { options, pathDetails, cid } = context;
|
|
16
24
|
const { getBlockstore, helia } = this.pluginOptions;
|
|
17
25
|
context.reqFormat = 'car';
|
|
18
26
|
context.query.download = true;
|
|
19
|
-
context.query.filename = context
|
|
20
|
-
const blockstore = getBlockstore(
|
|
27
|
+
context.query.filename = getFilename(context);
|
|
28
|
+
const blockstore = getBlockstore(cid, context.resource, options?.session ?? true, options);
|
|
21
29
|
const c = car({ blockstore, getCodec: helia.getCodec });
|
|
22
|
-
const stream = toBrowserReadableStream(c.stream(
|
|
30
|
+
const stream = toBrowserReadableStream(c.stream(pathDetails?.terminalElement.cid ?? cid, options));
|
|
23
31
|
const response = okResponse(context.resource, stream);
|
|
24
32
|
response.headers.set('content-type', 'application/vnd.ipld.car; version=1');
|
|
25
33
|
return response;
|
|
@@ -1 +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;
|
|
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,SAAS,WAAW,CAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAqD;IAC/F,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,QAAQ,CAAA;IACvB,CAAC;IAED,0GAA0G;IAC1G,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAE7F,OAAO,GAAG,QAAQ,MAAM,CAAA;AAC1B,CAAC;AACD;;;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,WAAW,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;QAC7C,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,WAAW,CAAC,OAAO,CAAC,CAAA;QAC7C,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,CAAC,CAAA;QAC1F,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,WAAW,EAAE,eAAe,CAAC,GAAG,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;QAElG,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-type-parser.d.ts","sourceRoot":"","sources":["../../../src/utils/content-type-parser.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"content-type-parser.d.ts","sourceRoot":"","sources":["../../../src/utils/content-type-parser.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,6BAA6B,CAAA;AAgCzD,wBAAsB,iBAAiB,CAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyD9F"}
|
|
@@ -2,14 +2,14 @@ import { logger } from '@libp2p/logger';
|
|
|
2
2
|
import { fileTypeFromBuffer } from 'file-type';
|
|
3
3
|
const log = logger('helia:verified-fetch:content-type-parser');
|
|
4
4
|
export const defaultMimeType = 'application/octet-stream';
|
|
5
|
-
function checkForSvg(
|
|
5
|
+
function checkForSvg(text) {
|
|
6
6
|
log('checking for svg');
|
|
7
|
-
return /^(<\?xml[^>]+>)?[^<^\w]+<svg/ig.test(
|
|
7
|
+
return /^(<\?xml[^>]+>)?[^<^\w]+<svg/ig.test(text);
|
|
8
8
|
}
|
|
9
|
-
async function checkForJson(
|
|
9
|
+
async function checkForJson(text) {
|
|
10
10
|
log('checking for json');
|
|
11
11
|
try {
|
|
12
|
-
JSON.parse(
|
|
12
|
+
JSON.parse(text);
|
|
13
13
|
return true;
|
|
14
14
|
}
|
|
15
15
|
catch (err) {
|
|
@@ -17,6 +17,20 @@ async function checkForJson(bytes) {
|
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
function getText(bytes) {
|
|
21
|
+
log('checking for text');
|
|
22
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
23
|
+
try {
|
|
24
|
+
return decoder.decode(bytes);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function checkForHtml(text) {
|
|
31
|
+
log('checking for html');
|
|
32
|
+
return /^\s*<(?:!doctype\s+html|html|head|body)\b/i.test(text);
|
|
33
|
+
}
|
|
20
34
|
export async function contentTypeParser(bytes, fileName) {
|
|
21
35
|
log('contentTypeParser called for fileName: %s, byte size=%s', fileName, bytes.length);
|
|
22
36
|
const detectedType = (await fileTypeFromBuffer(bytes))?.mime;
|
|
@@ -26,12 +40,22 @@ export async function contentTypeParser(bytes, fileName) {
|
|
|
26
40
|
}
|
|
27
41
|
log('no detectedType');
|
|
28
42
|
if (fileName == null) {
|
|
29
|
-
// no other way to determine file-type.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
// it's likely text... no other way to determine file-type.
|
|
44
|
+
const text = getText(bytes);
|
|
45
|
+
if (text != null) {
|
|
46
|
+
// check for svg, json, html, or it's plain text.
|
|
47
|
+
if (checkForSvg(text)) {
|
|
48
|
+
return 'image/svg+xml';
|
|
49
|
+
}
|
|
50
|
+
else if (await checkForJson(text)) {
|
|
51
|
+
return 'application/json';
|
|
52
|
+
}
|
|
53
|
+
else if (await checkForHtml(text)) {
|
|
54
|
+
return 'text/html; charset=utf-8';
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return 'text/plain; charset=utf-8';
|
|
58
|
+
}
|
|
35
59
|
}
|
|
36
60
|
return defaultMimeType;
|
|
37
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-type-parser.js","sourceRoot":"","sources":["../../../src/utils/content-type-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAE9C,MAAM,GAAG,GAAG,MAAM,CAAC,0CAA0C,CAAC,CAAA;AAE9D,MAAM,CAAC,MAAM,eAAe,GAAG,0BAA0B,CAAA;AACzD,SAAS,WAAW,CAAE,
|
|
1
|
+
{"version":3,"file":"content-type-parser.js","sourceRoot":"","sources":["../../../src/utils/content-type-parser.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helia/verified-fetch",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.3",
|
|
4
4
|
"description": "A fetch-like API for obtaining verified & trustless IPFS content on the web",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/ipfs/helia-verified-fetch/tree/main/packages/verified-fetch#readme",
|
|
@@ -4,6 +4,16 @@ import { okResponse } from '../utils/responses.js'
|
|
|
4
4
|
import { BasePlugin } from './plugin-base.js'
|
|
5
5
|
import type { PluginContext } from './types.js'
|
|
6
6
|
|
|
7
|
+
function getFilename ({ cid, ipfsPath, query }: Pick<PluginContext, 'query' | 'cid' | 'ipfsPath'>): string {
|
|
8
|
+
if (query.filename != null) {
|
|
9
|
+
return query.filename
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// convert context.ipfsPath to a filename. replace all / with _, replace prefix protocol with empty string
|
|
13
|
+
const filename = ipfsPath.replace(/\/ipfs\//, '').replace(/\/ipns\//, '').replace(/\//g, '_')
|
|
14
|
+
|
|
15
|
+
return `${filename}.car`
|
|
16
|
+
}
|
|
7
17
|
/**
|
|
8
18
|
* Accepts a `CID` and returns a `Response` with a body stream that is a CAR
|
|
9
19
|
* of the `DAG` referenced by the `CID`.
|
|
@@ -15,14 +25,14 @@ export class CarPlugin extends BasePlugin {
|
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
async handle (context: PluginContext): Promise<Response> {
|
|
18
|
-
const { options } = context
|
|
28
|
+
const { options, pathDetails, cid } = context
|
|
19
29
|
const { getBlockstore, helia } = this.pluginOptions
|
|
20
30
|
context.reqFormat = 'car'
|
|
21
31
|
context.query.download = true
|
|
22
|
-
context.query.filename = context
|
|
23
|
-
const blockstore = getBlockstore(
|
|
32
|
+
context.query.filename = getFilename(context)
|
|
33
|
+
const blockstore = getBlockstore(cid, context.resource, options?.session ?? true, options)
|
|
24
34
|
const c = car({ blockstore, getCodec: helia.getCodec })
|
|
25
|
-
const stream = toBrowserReadableStream(c.stream(
|
|
35
|
+
const stream = toBrowserReadableStream(c.stream(pathDetails?.terminalElement.cid ?? cid, options))
|
|
26
36
|
|
|
27
37
|
const response = okResponse(context.resource, stream)
|
|
28
38
|
response.headers.set('content-type', 'application/vnd.ipld.car; version=1')
|
|
@@ -4,15 +4,15 @@ import { fileTypeFromBuffer } from 'file-type'
|
|
|
4
4
|
const log = logger('helia:verified-fetch:content-type-parser')
|
|
5
5
|
|
|
6
6
|
export const defaultMimeType = 'application/octet-stream'
|
|
7
|
-
function checkForSvg (
|
|
7
|
+
function checkForSvg (text: string): boolean {
|
|
8
8
|
log('checking for svg')
|
|
9
|
-
return /^(<\?xml[^>]+>)?[^<^\w]+<svg/ig.test(
|
|
9
|
+
return /^(<\?xml[^>]+>)?[^<^\w]+<svg/ig.test(text)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async function checkForJson (
|
|
12
|
+
async function checkForJson (text: string): Promise<boolean> {
|
|
13
13
|
log('checking for json')
|
|
14
14
|
try {
|
|
15
|
-
JSON.parse(
|
|
15
|
+
JSON.parse(text)
|
|
16
16
|
return true
|
|
17
17
|
} catch (err) {
|
|
18
18
|
log('failed to parse as json', err)
|
|
@@ -20,6 +20,21 @@ async function checkForJson (bytes: Uint8Array): Promise<boolean> {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function getText (bytes: Uint8Array): string | null {
|
|
24
|
+
log('checking for text')
|
|
25
|
+
const decoder = new TextDecoder('utf-8', { fatal: true })
|
|
26
|
+
try {
|
|
27
|
+
return decoder.decode(bytes)
|
|
28
|
+
} catch (err) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function checkForHtml (text: string): Promise<boolean> {
|
|
34
|
+
log('checking for html')
|
|
35
|
+
return /^\s*<(?:!doctype\s+html|html|head|body)\b/i.test(text)
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
export async function contentTypeParser (bytes: Uint8Array, fileName?: string): Promise<string> {
|
|
24
39
|
log('contentTypeParser called for fileName: %s, byte size=%s', fileName, bytes.length)
|
|
25
40
|
const detectedType = (await fileTypeFromBuffer(bytes))?.mime
|
|
@@ -30,11 +45,19 @@ export async function contentTypeParser (bytes: Uint8Array, fileName?: string):
|
|
|
30
45
|
log('no detectedType')
|
|
31
46
|
|
|
32
47
|
if (fileName == null) {
|
|
33
|
-
// no other way to determine file-type.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
// it's likely text... no other way to determine file-type.
|
|
49
|
+
const text = getText(bytes)
|
|
50
|
+
if (text != null) {
|
|
51
|
+
// check for svg, json, html, or it's plain text.
|
|
52
|
+
if (checkForSvg(text)) {
|
|
53
|
+
return 'image/svg+xml'
|
|
54
|
+
} else if (await checkForJson(text)) {
|
|
55
|
+
return 'application/json'
|
|
56
|
+
} else if (await checkForHtml(text)) {
|
|
57
|
+
return 'text/html; charset=utf-8'
|
|
58
|
+
} else {
|
|
59
|
+
return 'text/plain; charset=utf-8'
|
|
60
|
+
}
|
|
38
61
|
}
|
|
39
62
|
return defaultMimeType
|
|
40
63
|
}
|