@helia/car 5.0.1 → 5.1.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 +54 -4
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/car.d.ts +1 -2
- package/dist/src/car.d.ts.map +1 -1
- package/dist/src/car.js +46 -124
- package/dist/src/car.js.map +1 -1
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +8 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/export-strategies/block-exporter.d.ts +5 -2
- package/dist/src/export-strategies/block-exporter.d.ts.map +1 -1
- package/dist/src/export-strategies/block-exporter.js +9 -3
- package/dist/src/export-strategies/block-exporter.js.map +1 -1
- package/dist/src/export-strategies/subgraph-exporter.d.ts +5 -2
- package/dist/src/export-strategies/subgraph-exporter.d.ts.map +1 -1
- package/dist/src/export-strategies/subgraph-exporter.js +8 -3
- package/dist/src/export-strategies/subgraph-exporter.js.map +1 -1
- package/dist/src/export-strategies/unixfs-exporter.d.ts +10 -4
- package/dist/src/export-strategies/unixfs-exporter.d.ts.map +1 -1
- package/dist/src/export-strategies/unixfs-exporter.js +15 -8
- package/dist/src/export-strategies/unixfs-exporter.js.map +1 -1
- package/dist/src/index.d.ts +64 -12
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +56 -6
- package/dist/src/index.js.map +1 -1
- package/dist/src/traversal-strategies/cid-path.d.ts +9 -6
- package/dist/src/traversal-strategies/cid-path.d.ts.map +1 -1
- package/dist/src/traversal-strategies/cid-path.js +35 -12
- package/dist/src/traversal-strategies/cid-path.js.map +1 -1
- package/dist/src/traversal-strategies/graph-search.d.ts +15 -7
- package/dist/src/traversal-strategies/graph-search.d.ts.map +1 -1
- package/dist/src/traversal-strategies/graph-search.js +57 -11
- package/dist/src/traversal-strategies/graph-search.js.map +1 -1
- package/dist/src/traversal-strategies/unixfs-path.d.ts +53 -3
- package/dist/src/traversal-strategies/unixfs-path.d.ts.map +1 -1
- package/dist/src/traversal-strategies/unixfs-path.js +70 -30
- package/dist/src/traversal-strategies/unixfs-path.js.map +1 -1
- package/dist/typedoc-urls.json +1 -0
- package/package.json +4 -2
- package/src/car.ts +54 -163
- package/src/errors.ts +10 -0
- package/src/export-strategies/block-exporter.ts +13 -4
- package/src/export-strategies/subgraph-exporter.ts +13 -4
- package/src/export-strategies/unixfs-exporter.ts +20 -9
- package/src/index.ts +66 -15
- package/src/traversal-strategies/cid-path.ts +43 -13
- package/src/traversal-strategies/graph-search.ts +70 -12
- package/src/traversal-strategies/unixfs-path.ts +77 -41
package/dist/src/index.js
CHANGED
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
* const c = car(helia)
|
|
24
24
|
* const out = nodeFs.createWriteStream('example.car')
|
|
25
25
|
*
|
|
26
|
-
* for await (const buf of c.export(cid
|
|
26
|
+
* for await (const buf of c.export(cid, {
|
|
27
|
+
* signal: AbortSignal.timeout(5_000)
|
|
28
|
+
* })) {
|
|
27
29
|
* out.write(buf)
|
|
28
30
|
* }
|
|
29
31
|
*
|
|
@@ -32,6 +34,14 @@
|
|
|
32
34
|
*
|
|
33
35
|
* @example Exporting a part of a UnixFS DAG as a CAR file
|
|
34
36
|
*
|
|
37
|
+
* Here the graph traversal will start at `root` and include the blocks for
|
|
38
|
+
* `root`, `/foo`, `/bar`, and all the blocks that make up `baz.txt`.
|
|
39
|
+
*
|
|
40
|
+
* If there are other files/directories in the UnixFS DAG under `root`, they
|
|
41
|
+
* will not be included.
|
|
42
|
+
*
|
|
43
|
+
* `root` will be the only entry in the CAR file roots.
|
|
44
|
+
*
|
|
35
45
|
* ```typescript
|
|
36
46
|
* import { createHelia } from 'helia'
|
|
37
47
|
* import { car, UnixFSPath } from '@helia/car'
|
|
@@ -39,12 +49,13 @@
|
|
|
39
49
|
* import nodeFs from 'node:fs'
|
|
40
50
|
*
|
|
41
51
|
* const helia = await createHelia()
|
|
42
|
-
* const
|
|
52
|
+
* const root = CID.parse('QmFoo...')
|
|
43
53
|
*
|
|
44
54
|
* const c = car(helia)
|
|
45
55
|
* const out = nodeFs.createWriteStream('example.car')
|
|
46
56
|
*
|
|
47
|
-
* for await (const buf of c.export(
|
|
57
|
+
* for await (const buf of c.export(root, {
|
|
58
|
+
* signal: AbortSignal.timeout(5_000),
|
|
48
59
|
* traversal: new UnixFSPath('/foo/bar/baz.txt')
|
|
49
60
|
* })) {
|
|
50
61
|
* out.write(buf)
|
|
@@ -53,6 +64,43 @@
|
|
|
53
64
|
* out.end()
|
|
54
65
|
* ```
|
|
55
66
|
*
|
|
67
|
+
* @example Including traversal path above the root in a CAR
|
|
68
|
+
*
|
|
69
|
+
* The `includeTraversalBlocks` option will include the traversal blocks in the
|
|
70
|
+
* CAR when they would otherwise be excluded (for example when the traversal
|
|
71
|
+
* starts in a parent of the export root).
|
|
72
|
+
*
|
|
73
|
+
* Here `baz` is the CID for `baz.txt`.
|
|
74
|
+
*
|
|
75
|
+
* The CAR file will include the blocks for `parent`, `/foo`, `/bar`, and
|
|
76
|
+
* `/baz.txt`.
|
|
77
|
+
*
|
|
78
|
+
* `baz` will be the only entry in the CAR file roots.
|
|
79
|
+
*
|
|
80
|
+
* ```typescript
|
|
81
|
+
* import { createHelia } from 'helia'
|
|
82
|
+
* import { car, UnixFSPath } from '@helia/car'
|
|
83
|
+
* import { CID } from 'multiformats/cid'
|
|
84
|
+
* import nodeFs from 'node:fs'
|
|
85
|
+
*
|
|
86
|
+
* const helia = await createHelia()
|
|
87
|
+
* const parent = CID.parse('QmFoo...')
|
|
88
|
+
* const baz = CID.parse('QmBar...')
|
|
89
|
+
*
|
|
90
|
+
* const c = car(helia)
|
|
91
|
+
* const out = nodeFs.createWriteStream('example.car')
|
|
92
|
+
*
|
|
93
|
+
* for await (const buf of c.export(baz, {
|
|
94
|
+
* signal: AbortSignal.timeout(5_000),
|
|
95
|
+
* traversal: new UnixFSPath(parent, '/foo/bar/baz.txt'),
|
|
96
|
+
* includeTraversalBlocks: true
|
|
97
|
+
* })) {
|
|
98
|
+
* out.write(buf)
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* out.end()
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
56
104
|
* @example Importing all blocks from a CAR file
|
|
57
105
|
*
|
|
58
106
|
* ```typescript
|
|
@@ -72,7 +120,9 @@
|
|
|
72
120
|
* const reader = await CarReader.fromIterable(inStream)
|
|
73
121
|
*
|
|
74
122
|
* const c = car(helia)
|
|
75
|
-
* await c.import(reader
|
|
123
|
+
* await c.import(reader, {
|
|
124
|
+
* signal: AbortSignal.timeout(5_000)
|
|
125
|
+
* })
|
|
76
126
|
* ```
|
|
77
127
|
*/
|
|
78
128
|
import { Car as CarClass } from './car.js';
|
|
@@ -81,7 +131,7 @@ export * from './traversal-strategies/index.js';
|
|
|
81
131
|
/**
|
|
82
132
|
* Create a {@link Car} instance for use with {@link https://github.com/ipfs/helia Helia}
|
|
83
133
|
*/
|
|
84
|
-
export function car(helia
|
|
85
|
-
return new CarClass(helia
|
|
134
|
+
export function car(helia) {
|
|
135
|
+
return new CarClass(helia);
|
|
86
136
|
}
|
|
87
137
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8HG;AAEH,OAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,UAAU,CAAA;AA0C1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,iCAAiC,CAAA;AAuF/C;;GAEG;AACH,MAAM,UAAU,GAAG,CAAE,KAAoB;IACvC,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC5B,CAAC"}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { TraversalStrategy } from '../index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CodecLoader } from '@helia/interface';
|
|
3
|
+
import type { AbortOptions } from '@libp2p/interface';
|
|
4
|
+
import type { Blockstore } from 'interface-blockstore';
|
|
5
|
+
import type { BlockView } from 'multiformats';
|
|
3
6
|
import type { CID } from 'multiformats/cid';
|
|
4
7
|
/**
|
|
5
8
|
* Simple strategy that traverses a known path to a target CID.
|
|
6
9
|
*
|
|
7
10
|
* All this strategy does is yield the next CID in the known path.
|
|
11
|
+
*
|
|
12
|
+
* The path should end with the CID to be exported
|
|
8
13
|
*/
|
|
9
14
|
export declare class CIDPath implements TraversalStrategy {
|
|
10
|
-
private readonly
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
isTarget(cid: CID): boolean;
|
|
14
|
-
traverse<T extends BlockView<any, any, any, 0 | 1>>(cid: CID, _block?: T): AsyncGenerator<CID, void, undefined>;
|
|
15
|
+
private readonly path;
|
|
16
|
+
constructor(path: CID[]);
|
|
17
|
+
traverse(root: CID, blockstore: Blockstore, getCodec: CodecLoader, options?: AbortOptions): AsyncGenerator<BlockView<unknown, number, number, 0 | 1>, void, undefined>;
|
|
15
18
|
}
|
|
16
19
|
//# sourceMappingURL=cid-path.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cid-path.d.ts","sourceRoot":"","sources":["../../../src/traversal-strategies/cid-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cid-path.d.ts","sourceRoot":"","sources":["../../../src/traversal-strategies/cid-path.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAE3C;;;;;;GAMG;AACH,qBAAa,OAAQ,YAAW,iBAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;gBAEf,IAAI,EAAE,GAAG,EAAE;IAIhB,QAAQ,CAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC;CAkChL"}
|
|
@@ -1,22 +1,45 @@
|
|
|
1
|
+
import toBuffer from 'it-to-buffer';
|
|
2
|
+
import { createUnsafe } from 'multiformats/block';
|
|
3
|
+
import { InvalidTraversalError, NotDescendantError } from '../errors.js';
|
|
1
4
|
/**
|
|
2
5
|
* Simple strategy that traverses a known path to a target CID.
|
|
3
6
|
*
|
|
4
7
|
* All this strategy does is yield the next CID in the known path.
|
|
8
|
+
*
|
|
9
|
+
* The path should end with the CID to be exported
|
|
5
10
|
*/
|
|
6
11
|
export class CIDPath {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.pathToTarget = pathToTarget;
|
|
11
|
-
this.target = pathToTarget[pathToTarget.length - 1];
|
|
12
|
-
}
|
|
13
|
-
isTarget(cid) {
|
|
14
|
-
return this.target.equals(cid);
|
|
12
|
+
path;
|
|
13
|
+
constructor(path) {
|
|
14
|
+
this.path = path;
|
|
15
15
|
}
|
|
16
|
-
async *traverse(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
async *traverse(root, blockstore, getCodec, options) {
|
|
17
|
+
if (!this.path.some(c => c.equals(root))) {
|
|
18
|
+
throw new InvalidTraversalError(`CIDPath traversal must include ${root}`);
|
|
19
|
+
}
|
|
20
|
+
let parentBlock;
|
|
21
|
+
for (const cid of this.path) {
|
|
22
|
+
if (parentBlock != null) {
|
|
23
|
+
let isChild = false;
|
|
24
|
+
for (const [, child] of parentBlock.links()) {
|
|
25
|
+
if (child.equals(cid)) {
|
|
26
|
+
isChild = true;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!isChild) {
|
|
31
|
+
throw new NotDescendantError(`${cid} is not a child of ${parentBlock.cid}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const bytes = await toBuffer(blockstore.get(cid, options));
|
|
35
|
+
const block = createUnsafe({
|
|
36
|
+
cid,
|
|
37
|
+
bytes,
|
|
38
|
+
codec: await getCodec(cid.code)
|
|
39
|
+
});
|
|
40
|
+
parentBlock = block;
|
|
41
|
+
yield block;
|
|
42
|
+
}
|
|
20
43
|
}
|
|
21
44
|
}
|
|
22
45
|
//# sourceMappingURL=cid-path.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cid-path.js","sourceRoot":"","sources":["../../../src/traversal-strategies/cid-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cid-path.js","sourceRoot":"","sources":["../../../src/traversal-strategies/cid-path.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAQxE;;;;;;GAMG;AACH,MAAM,OAAO,OAAO;IACD,IAAI,CAAO;IAE5B,YAAa,IAAW;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,CAAE,QAAQ,CAAE,IAAS,EAAE,UAAsB,EAAE,QAAqB,EAAE,OAAsB;QAChG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,qBAAqB,CAAC,kCAAkC,IAAI,EAAE,CAAC,CAAA;QAC3E,CAAC;QAED,IAAI,WAAkE,CAAA;QAEtE,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,OAAO,GAAG,KAAK,CAAA;gBAEnB,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;oBAC5C,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;wBACtB,OAAO,GAAG,IAAI,CAAA;wBACd,MAAK;oBACP,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,kBAAkB,CAAC,GAAG,GAAG,sBAAsB,WAAW,CAAC,GAAG,EAAE,CAAC,CAAA;gBAC7E,CAAC;YACH,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;YAC1D,MAAM,KAAK,GAAG,YAAY,CAAC;gBACzB,GAAG;gBACH,KAAK;gBACL,KAAK,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;aAChC,CAAC,CAAA;YAEF,WAAW,GAAG,KAAK,CAAA;YACnB,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import type { TraversalStrategy } from '../index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CodecLoader } from '@helia/interface';
|
|
3
|
+
import type { AbortOptions } from '@libp2p/interface';
|
|
4
|
+
import type { Blockstore } from 'interface-blockstore';
|
|
5
|
+
import type { BlockView } from 'multiformats';
|
|
3
6
|
import type { CID } from 'multiformats/cid';
|
|
7
|
+
export interface GraphSearchOptions {
|
|
8
|
+
strategy?: 'depth-first' | 'breadth-first';
|
|
9
|
+
}
|
|
4
10
|
/**
|
|
5
|
-
* A traversal strategy that performs a
|
|
6
|
-
*
|
|
11
|
+
* A traversal strategy that performs a depth-first search looking for a target
|
|
12
|
+
* CID.
|
|
7
13
|
*/
|
|
8
14
|
export declare class GraphSearch implements TraversalStrategy {
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
private haystack?;
|
|
16
|
+
private readonly needle;
|
|
17
|
+
private readonly options?;
|
|
18
|
+
constructor(needle: CID, options?: GraphSearchOptions);
|
|
19
|
+
constructor(haystack: CID, needle: CID, options?: GraphSearchOptions);
|
|
20
|
+
traverse(root: CID, blockstore: Blockstore, getCodec: CodecLoader, options?: AbortOptions): AsyncGenerator<BlockView<unknown, number, number, 0 | 1>, void, undefined>;
|
|
13
21
|
}
|
|
14
22
|
//# sourceMappingURL=graph-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-search.d.ts","sourceRoot":"","sources":["../../../src/traversal-strategies/graph-search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph-search.d.ts","sourceRoot":"","sources":["../../../src/traversal-strategies/graph-search.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAE3C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,aAAa,GAAG,eAAe,CAAA;CAC3C;AAMD;;;GAGG;AACH,qBAAa,WAAY,YAAW,iBAAiB;IACnD,OAAO,CAAC,QAAQ,CAAC,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAoB;gBAEhC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,kBAAkB;gBACzC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAgB7D,QAAQ,CAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC;CAmChL"}
|
|
@@ -1,19 +1,65 @@
|
|
|
1
|
+
import { breadthFirstWalker, depthFirstWalker } from '@helia/utils';
|
|
2
|
+
import { InvalidParametersError } from '@libp2p/interface';
|
|
3
|
+
import toBuffer from 'it-to-buffer';
|
|
4
|
+
import { createUnsafe } from 'multiformats/block';
|
|
5
|
+
import { InvalidTraversalError } from "../errors.js";
|
|
6
|
+
function isCID(obj) {
|
|
7
|
+
return obj != null && obj?.asCID === obj;
|
|
8
|
+
}
|
|
1
9
|
/**
|
|
2
|
-
* A traversal strategy that performs a
|
|
3
|
-
*
|
|
10
|
+
* A traversal strategy that performs a depth-first search looking for a target
|
|
11
|
+
* CID.
|
|
4
12
|
*/
|
|
5
13
|
export class GraphSearch {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
haystack;
|
|
15
|
+
needle;
|
|
16
|
+
options;
|
|
17
|
+
constructor(...args) {
|
|
18
|
+
if (isCID(args[0])) {
|
|
19
|
+
if (isCID(args[1])) {
|
|
20
|
+
this.haystack = args[0];
|
|
21
|
+
this.needle = args[1];
|
|
22
|
+
this.options = args[2];
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.needle = args[0];
|
|
26
|
+
this.options = args[1];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
throw new InvalidParametersError('needle must be specified');
|
|
31
|
+
}
|
|
12
32
|
}
|
|
13
|
-
async *traverse(
|
|
14
|
-
|
|
15
|
-
|
|
33
|
+
async *traverse(root, blockstore, getCodec, options) {
|
|
34
|
+
const start = this.haystack ?? root;
|
|
35
|
+
let walker;
|
|
36
|
+
if (this.options?.strategy === 'breadth-first') {
|
|
37
|
+
walker = breadthFirstWalker({
|
|
38
|
+
blockstore,
|
|
39
|
+
getCodec
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
walker = depthFirstWalker({
|
|
44
|
+
blockstore,
|
|
45
|
+
getCodec
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
for await (const node of walker.walk(start, options)) {
|
|
49
|
+
if (node.block.cid.equals(this.needle)) {
|
|
50
|
+
for (const cid of node.path) {
|
|
51
|
+
const bytes = await toBuffer(blockstore.get(cid, options));
|
|
52
|
+
const block = createUnsafe({
|
|
53
|
+
cid,
|
|
54
|
+
bytes,
|
|
55
|
+
codec: await getCodec(cid.code)
|
|
56
|
+
});
|
|
57
|
+
yield block;
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
16
61
|
}
|
|
62
|
+
throw new InvalidTraversalError(`${this.needle} was not a child of ${start}`);
|
|
17
63
|
}
|
|
18
64
|
}
|
|
19
65
|
//# sourceMappingURL=graph-search.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-search.js","sourceRoot":"","sources":["../../../src/traversal-strategies/graph-search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph-search.js","sourceRoot":"","sources":["../../../src/traversal-strategies/graph-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,QAAQ,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAapD,SAAS,KAAK,CAAE,GAAS;IACvB,OAAO,GAAG,IAAI,IAAI,IAAI,GAAG,EAAE,KAAK,KAAK,GAAG,CAAA;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,WAAW;IACd,QAAQ,CAAM;IACL,MAAM,CAAK;IACX,OAAO,CAAqB;IAI7C,YAAa,GAAG,IAAW;QACzB,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACnB,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;gBACvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;gBACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACxB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;gBACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,sBAAsB,CAAC,0BAA0B,CAAC,CAAA;QAC9D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAE,QAAQ,CAAE,IAAS,EAAE,UAAsB,EAAE,QAAqB,EAAE,OAAsB;QAChG,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAA;QACnC,IAAI,MAAmB,CAAA;QAEvB,IAAI,IAAI,CAAC,OAAO,EAAE,QAAQ,KAAK,eAAe,EAAE,CAAC;YAC/C,MAAM,GAAG,kBAAkB,CAAC;gBAC1B,UAAU;gBACV,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,gBAAgB,CAAC;gBACxB,UAAU;gBACV,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;YACrD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC5B,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;oBAC1D,MAAM,KAAK,GAAG,YAAY,CAAC;wBACzB,GAAG;wBACH,KAAK;wBACL,KAAK,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;qBAChC,CAAC,CAAA;oBAEF,MAAM,KAAK,CAAA;gBACb,CAAC;gBAED,OAAM;YACR,CAAC;QACH,CAAC;QAED,MAAM,IAAI,qBAAqB,CAAC,GAAG,IAAI,CAAC,MAAM,uBAAuB,KAAK,EAAE,CAAC,CAAA;IAC/E,CAAC;CACF"}
|
|
@@ -1,13 +1,63 @@
|
|
|
1
1
|
import type { TraversalStrategy } from '../index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CodecLoader } from '@helia/interface';
|
|
3
|
+
import type { AbortOptions } from '@libp2p/interface';
|
|
4
|
+
import type { Blockstore } from 'interface-blockstore';
|
|
5
|
+
import type { BlockView } from 'multiformats';
|
|
3
6
|
import type { CID } from 'multiformats/cid';
|
|
4
7
|
/**
|
|
5
8
|
* Traverses a DAG containing UnixFS directories
|
|
9
|
+
*
|
|
10
|
+
* A root CID may be specified to begin traversal from, otherwise the root
|
|
11
|
+
* currently being exported will be used.
|
|
12
|
+
*
|
|
13
|
+
* @example Begin traversal from the root being exported
|
|
14
|
+
*
|
|
15
|
+
* In this example, the UnixFS path `/foo/bar/baz.txt` path should be resolvable
|
|
16
|
+
* beneath the `root` CID.
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { createHelia } from 'helia'
|
|
20
|
+
* import { car, UnixFSPath } from '@helia/car'
|
|
21
|
+
* import { CID } from 'multiformats/cid'
|
|
22
|
+
*
|
|
23
|
+
* const helia = await createHelia()
|
|
24
|
+
* const c = car(helia)
|
|
25
|
+
* const root = CID.parse('QmRoot')
|
|
26
|
+
*
|
|
27
|
+
* for await (const buf of c.export(root, {
|
|
28
|
+
* traversal: new UnixFSPath('/foo/bar/baz.txt')
|
|
29
|
+
* })) {
|
|
30
|
+
* // do something with `buf`
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example Begin traversal from a parent node
|
|
35
|
+
*
|
|
36
|
+
* In this example, the `root` CID should be resolvable at the UnixFS path
|
|
37
|
+
* beneath `parentCID`.
|
|
38
|
+
*
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { createHelia } from 'helia'
|
|
41
|
+
* import { car, UnixFSPath } from '@helia/car'
|
|
42
|
+
* import { CID } from 'multiformats/cid'
|
|
43
|
+
*
|
|
44
|
+
* const helia = await createHelia()
|
|
45
|
+
* const c = car(helia)
|
|
46
|
+
* const root = CID.parse('QmRoot')
|
|
47
|
+
* const parentCID = CID.parse('QmParent')
|
|
48
|
+
*
|
|
49
|
+
* for await (const buf of c.export(root, {
|
|
50
|
+
* traversal: new UnixFSPath(parentCID, '/foo/bar/baz.txt')
|
|
51
|
+
* })) {
|
|
52
|
+
* // do something with `buf`
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
6
55
|
*/
|
|
7
56
|
export declare class UnixFSPath implements TraversalStrategy {
|
|
57
|
+
private readonly root?;
|
|
8
58
|
private readonly path;
|
|
9
59
|
constructor(path: string);
|
|
10
|
-
|
|
11
|
-
traverse
|
|
60
|
+
constructor(root: CID, path: string);
|
|
61
|
+
traverse(root: CID, blockstore: Blockstore, getCodec: CodecLoader, options?: AbortOptions): AsyncGenerator<BlockView<unknown, number, number, 0 | 1>, void, undefined>;
|
|
12
62
|
}
|
|
13
63
|
//# sourceMappingURL=unixfs-path.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unixfs-path.d.ts","sourceRoot":"","sources":["../../../src/traversal-strategies/unixfs-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"unixfs-path.d.ts","sourceRoot":"","sources":["../../../src/traversal-strategies/unixfs-path.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBAAa,UAAW,YAAW,iBAAiB;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;gBAEhB,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM;IAoB5B,QAAQ,CAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC;CAShL"}
|
|
@@ -1,42 +1,82 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { NotUnixFSError } from '../errors.js';
|
|
1
|
+
import * as dagPb from '@ipld/dag-pb';
|
|
2
|
+
import { walkPath } from 'ipfs-unixfs-exporter';
|
|
3
|
+
import { createUnsafe } from 'multiformats/block';
|
|
5
4
|
/**
|
|
6
5
|
* Traverses a DAG containing UnixFS directories
|
|
6
|
+
*
|
|
7
|
+
* A root CID may be specified to begin traversal from, otherwise the root
|
|
8
|
+
* currently being exported will be used.
|
|
9
|
+
*
|
|
10
|
+
* @example Begin traversal from the root being exported
|
|
11
|
+
*
|
|
12
|
+
* In this example, the UnixFS path `/foo/bar/baz.txt` path should be resolvable
|
|
13
|
+
* beneath the `root` CID.
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { createHelia } from 'helia'
|
|
17
|
+
* import { car, UnixFSPath } from '@helia/car'
|
|
18
|
+
* import { CID } from 'multiformats/cid'
|
|
19
|
+
*
|
|
20
|
+
* const helia = await createHelia()
|
|
21
|
+
* const c = car(helia)
|
|
22
|
+
* const root = CID.parse('QmRoot')
|
|
23
|
+
*
|
|
24
|
+
* for await (const buf of c.export(root, {
|
|
25
|
+
* traversal: new UnixFSPath('/foo/bar/baz.txt')
|
|
26
|
+
* })) {
|
|
27
|
+
* // do something with `buf`
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Begin traversal from a parent node
|
|
32
|
+
*
|
|
33
|
+
* In this example, the `root` CID should be resolvable at the UnixFS path
|
|
34
|
+
* beneath `parentCID`.
|
|
35
|
+
*
|
|
36
|
+
* ```typescript
|
|
37
|
+
* import { createHelia } from 'helia'
|
|
38
|
+
* import { car, UnixFSPath } from '@helia/car'
|
|
39
|
+
* import { CID } from 'multiformats/cid'
|
|
40
|
+
*
|
|
41
|
+
* const helia = await createHelia()
|
|
42
|
+
* const c = car(helia)
|
|
43
|
+
* const root = CID.parse('QmRoot')
|
|
44
|
+
* const parentCID = CID.parse('QmParent')
|
|
45
|
+
*
|
|
46
|
+
* for await (const buf of c.export(root, {
|
|
47
|
+
* traversal: new UnixFSPath(parentCID, '/foo/bar/baz.txt')
|
|
48
|
+
* })) {
|
|
49
|
+
* // do something with `buf`
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
7
52
|
*/
|
|
8
53
|
export class UnixFSPath {
|
|
54
|
+
root;
|
|
9
55
|
path;
|
|
10
|
-
constructor(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
async *traverse(cid, block) {
|
|
18
|
-
if (cid.code !== DAG_PB_CODEC_CODE) {
|
|
19
|
-
throw new NotUnixFSError('Target CID is not UnixFS');
|
|
56
|
+
constructor(...args) {
|
|
57
|
+
let root = args[0];
|
|
58
|
+
let path = args[1];
|
|
59
|
+
if (typeof root === 'string') {
|
|
60
|
+
path = root;
|
|
61
|
+
root = undefined;
|
|
20
62
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return;
|
|
63
|
+
else if (args.length < 2) {
|
|
64
|
+
throw new Error('path or CID and path must be specified');
|
|
24
65
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
throw new NotUnixFSError('Target CID has no UnixFS data in decoded bytes');
|
|
66
|
+
if (!path.startsWith('/')) {
|
|
67
|
+
path = `/${path}`;
|
|
28
68
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
this.root = root;
|
|
70
|
+
this.path = path;
|
|
71
|
+
}
|
|
72
|
+
async *traverse(root, blockstore, getCodec, options) {
|
|
73
|
+
for await (const entry of walkPath(`${this.root ?? root}${this.path}`, blockstore, options)) {
|
|
74
|
+
yield createUnsafe({
|
|
75
|
+
cid: entry.cid,
|
|
76
|
+
bytes: entry.node instanceof Uint8Array ? entry.node : dagPb.encode(entry.node),
|
|
77
|
+
codec: await getCodec(entry.cid.code)
|
|
78
|
+
});
|
|
37
79
|
}
|
|
38
|
-
// TODO: HAMT support
|
|
39
|
-
throw new NotUnixFSError('Target CID is not a UnixFS directory');
|
|
40
80
|
}
|
|
41
81
|
}
|
|
42
82
|
//# sourceMappingURL=unixfs-path.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unixfs-path.js","sourceRoot":"","sources":["../../../src/traversal-strategies/unixfs-path.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"unixfs-path.js","sourceRoot":"","sources":["../../../src/traversal-strategies/unixfs-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAQjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,OAAO,UAAU;IACJ,IAAI,CAAM;IACV,IAAI,CAAQ;IAI7B,YAAa,GAAG,IAAW;QACzB,IAAI,IAAI,GAA6B,IAAI,CAAC,CAAC,CAAC,CAAA;QAC5C,IAAI,IAAI,GAAW,IAAI,CAAC,CAAC,CAAC,CAAA;QAE1B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,GAAG,IAAI,CAAA;YACX,IAAI,GAAG,SAAS,CAAA;QAClB,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAC3D,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,GAAG,IAAI,IAAI,EAAE,CAAA;QACnB,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,CAAE,QAAQ,CAAE,IAAS,EAAE,UAAsB,EAAE,QAAqB,EAAE,OAAsB;QAChG,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC;YAC5F,MAAM,YAAY,CAAC;gBACjB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,KAAK,EAAE,KAAK,CAAC,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC/E,KAAK,EAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;aACtC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;CACF"}
|
package/dist/typedoc-urls.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
".:ExportCarOptions": "https://ipfs.github.io/helia/interfaces/_helia_car.ExportCarOptions.html",
|
|
14
14
|
"ExportStrategy": "https://ipfs.github.io/helia/interfaces/_helia_car.ExportStrategy.html",
|
|
15
15
|
".:ExportStrategy": "https://ipfs.github.io/helia/interfaces/_helia_car.ExportStrategy.html",
|
|
16
|
+
"GraphSearchOptions": "https://ipfs.github.io/helia/interfaces/_helia_car.GraphSearchOptions.html",
|
|
16
17
|
"TraversalStrategy": "https://ipfs.github.io/helia/interfaces/_helia_car.TraversalStrategy.html",
|
|
17
18
|
".:TraversalStrategy": "https://ipfs.github.io/helia/interfaces/_helia_car.TraversalStrategy.html",
|
|
18
19
|
"car": "https://ipfs.github.io/helia/functions/_helia_car.car.html",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helia/car",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Import/export car files from Helia",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/ipfs/helia/tree/main/packages/car#readme",
|
|
@@ -48,12 +48,13 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@helia/interface": "^6.0.0",
|
|
51
|
+
"@helia/utils": "^2.1.0",
|
|
51
52
|
"@ipld/car": "^5.4.2",
|
|
52
53
|
"@ipld/dag-pb": "^4.1.5",
|
|
53
54
|
"@libp2p/interface": "^3.0.2",
|
|
54
55
|
"@libp2p/utils": "^7.0.5",
|
|
55
56
|
"interface-blockstore": "^6.0.1",
|
|
56
|
-
"ipfs-unixfs": "^
|
|
57
|
+
"ipfs-unixfs-exporter": "^14.0.1",
|
|
57
58
|
"it-drain": "^3.0.10",
|
|
58
59
|
"it-map": "^3.1.4",
|
|
59
60
|
"it-to-buffer": "^4.0.10",
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"blockstore-core": "^6.1.1",
|
|
71
72
|
"datastore-core": "^11.0.2",
|
|
72
73
|
"ipfs-unixfs-importer": "^16.0.1",
|
|
74
|
+
"it-all": "^3.0.9",
|
|
73
75
|
"it-foreach": "^2.1.4",
|
|
74
76
|
"it-length": "^3.0.9",
|
|
75
77
|
"sinon": "^21.0.0"
|