Skip to content

Commit 0addfe8

Browse files
committed
feat: unixfs reifier and path selector
1 parent 06267ed commit 0addfe8

File tree

8 files changed

+303
-56
lines changed

8 files changed

+303
-56
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@types/uuid": "^8.3.4",
1717
"aegir": "^36.2.3",
1818
"blockstore-core": "^1.0.5",
19+
"ipfs-unixfs-importer": "^9.0.6",
1920
"prettier": "^2.6.1"
2021
},
2122
"dependencies": {

src/async-loader.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import {LinkLoader, Blockstore, blockFromStore} from "./traversal";
1+
import {
2+
LinkLoader,
3+
Blockstore,
4+
blockFromStore,
5+
KnownReifiers,
6+
NodeReifier,
7+
} from "./traversal";
28
import type {Block} from "multiformats/block";
39
import type {CID} from "multiformats";
410

@@ -22,6 +28,8 @@ export class AsyncLoader implements LinkLoader {
2228

2329
pullQueue: Map<string, Resolvable[]> = new Map();
2430

31+
reifiers: KnownReifiers = {};
32+
2533
constructor(store: Blockstore, tracker?: BlockNotifyFn) {
2634
this.store = store;
2735
this.tracker = tracker;
@@ -79,6 +87,9 @@ export class AsyncLoader implements LinkLoader {
7987
.then(() => this.pending.delete(blk.cid.toString()));
8088
}
8189
}
90+
reifier(name: string): NodeReifier {
91+
return this.reifiers[name];
92+
}
8293
// cleanup any block in memory
8394
close() {
8495
this.pending = new Map();

src/graphsync.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ import {
2121
decodeMessage,
2222
newRequest,
2323
} from "./messages";
24-
import type {SelectorNode, Blockstore} from "./traversal";
25-
import {Node, walkBlocks, parseContext} from "./traversal";
24+
import type {
25+
SelectorNode,
26+
Blockstore,
27+
KnownReifiers,
28+
NodeReifier,
29+
} from "./traversal";
30+
import {Node, walkBlocks, parseContext, unixfsReifier} from "./traversal";
2631

2732
export class GraphSync {
2833
started = false;
@@ -102,6 +107,10 @@ export class Request extends EventEmitter {
102107
dialer: ProtocolDialer;
103108
loader: AsyncLoader;
104109

110+
reifiers: KnownReifiers = {
111+
unixfs: unixfsReifier,
112+
};
113+
105114
constructor(
106115
id: string,
107116
root: CID,
@@ -136,6 +145,10 @@ export class Request extends EventEmitter {
136145
);
137146
}
138147

148+
reifier(name: string): NodeReifier {
149+
return this.reifiers[name];
150+
}
151+
139152
// TODO
140153
close() {}
141154

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export * from "./traversal";
22
export {GraphSync, Request} from "./graphsync";
3-
export {fetch} from "./resolver";
3+
export {fetch, unixfsPathSelector} from "./resolver";

src/resolver.ts

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import {CID} from "multiformats";
22
import {UnixFS} from "ipfs-unixfs";
3-
import * as dagJSON from "multiformats/codecs/json";
4-
import type {PBLink} from "@ipld/dag-pb";
53
import PeerId from "peer-id";
64
import type {Multiaddr} from "multiaddr";
7-
import {allSelector, Node, parseContext, walkBlocks} from "./traversal";
5+
import {
6+
allSelector,
7+
Node,
8+
Kind,
9+
parseContext,
10+
walkBlocks,
11+
SelectorNode,
12+
} from "./traversal";
813
import mime from "mime/lite";
914
import type {GraphSync} from "./graphsync";
1015

@@ -19,11 +24,37 @@ export function parsePath(path: string): {root: CID; segments: string[]} {
1924
const comps = toPathComponents(path);
2025
const root = CID.parse(comps[0]);
2126
return {
22-
segments: comps,
27+
segments: comps.slice(1),
2328
root,
2429
};
2530
}
2631

32+
export function unixfsPathSelector(path: string): {
33+
root: CID;
34+
selector: SelectorNode;
35+
} {
36+
const {root, segments} = parsePath(path);
37+
let selector = allSelector;
38+
if (segments.length === 0) {
39+
return {root, selector};
40+
}
41+
for (let i = segments.length - 1; i >= 0; i--) {
42+
selector = {
43+
"~": {
44+
as: "unixfs",
45+
">": {
46+
f: {
47+
"f>": {
48+
[segments[i]]: selector,
49+
},
50+
},
51+
},
52+
},
53+
};
54+
}
55+
return {root, selector};
56+
}
57+
2758
export function getPeerID(addr: Multiaddr): PeerId {
2859
const addrStr = addr.toString();
2960
const parts = addrStr.split("/");
@@ -39,17 +70,14 @@ export async function* resolve(
3970
provider: Multiaddr,
4071
exchange: GraphSync
4172
): AsyncIterable<any> {
42-
const {segments, root} = parsePath(path);
43-
let cid = root;
44-
let segs = segments.slice(1);
45-
const sel = allSelector;
73+
const {root, selector: sel} = unixfsPathSelector(path);
4674
const pid = getPeerID(provider);
4775
exchange.network.peerStore.addressBook.add(pid, [provider]);
48-
const request = exchange.request(cid, sel);
76+
const request = exchange.request(root, sel);
4977
const id = Date.now();
5078
const voucher = {
5179
ID: id,
52-
PayloadCID: cid,
80+
PayloadCID: root,
5381
Params: {
5482
Selector: sel,
5583
PieceCID: null,
@@ -63,7 +91,7 @@ export async function* resolve(
6391
[EXTENSION]: {
6492
IsRq: true,
6593
Request: {
66-
BCid: cid,
94+
BCid: root,
6795
Type: 0,
6896
Pull: true,
6997
Paus: false,
@@ -78,7 +106,7 @@ export async function* resolve(
78106
},
79107
});
80108
for await (const blk of walkBlocks(
81-
new Node(cid),
109+
new Node(root),
82110
parseContext().parseSelector(sel),
83111
request
84112
)) {
@@ -88,46 +116,22 @@ export async function* resolve(
88116
case 0x71:
89117
break;
90118
default:
91-
console.log("raw bytes");
92119
yield blk.bytes;
93120
continue;
94121
}
95-
try {
96-
const unixfs = UnixFS.unmarshal(blk.value.Data);
97-
if (unixfs.isDirectory()) {
98-
// if it's a directory and we have a segment to resolve, identify the link
99-
if (segs.length > 0) {
100-
for (const link of blk.value.Links) {
101-
if (link.Name === segs[0]) {
102-
cid = link.Hash;
103-
segs = segs.slice(1);
104-
console.log("found link in directory");
105-
continue;
106-
}
122+
if (blk.value.kind === Kind.Map && blk.value.Data) {
123+
try {
124+
const unixfs = UnixFS.unmarshal(blk.value.Data);
125+
if (unixfs.type === "file") {
126+
if (unixfs.data && unixfs.data.length) {
127+
yield unixfs.data;
107128
}
108-
throw new Error("key not found: " + segs[0]);
109-
} else {
110-
// if the block is a directory and we have no key return the entries as JSON
111-
yield dagJSON.encode(
112-
blk.value.Links.map((l: PBLink) => ({
113-
name: l.Name,
114-
hash: l.Hash.toString(),
115-
size: l.Tsize,
116-
}))
117-
);
118-
break;
129+
continue;
119130
}
120-
}
121-
if (unixfs.type === "file") {
122-
if (unixfs.data && unixfs.data.length) {
123-
console.log("unixfs file");
124-
yield unixfs.data;
125-
}
126-
continue;
127-
}
128-
} catch (e) {}
129-
// we're outside of unixfs territory
130-
// ignore
131+
} catch (e) {}
132+
// we're outside of unixfs territory
133+
// ignore
134+
}
131135
}
132136
// tell the loader we're done receiving blocks for this traversal
133137
request.close();

0 commit comments

Comments
 (0)