@helia/unixfs 4.0.3 → 5.0.0-d883eaf
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 +1 -1
- package/dist/src/commands/add.d.ts +7 -6
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +35 -13
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/stat.d.ts +3 -2
- package/dist/src/commands/stat.d.ts.map +1 -1
- package/dist/src/commands/stat.js +125 -63
- package/dist/src/commands/stat.js.map +1 -1
- package/dist/src/index.d.ts +151 -29
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/unixfs.d.ts +4 -3
- package/dist/src/unixfs.d.ts.map +1 -1
- package/dist/src/unixfs.js.map +1 -1
- package/dist/src/utils/glob-source.d.ts +19 -0
- package/dist/src/utils/glob-source.d.ts.map +1 -1
- package/dist/src/utils/glob-source.js +19 -0
- package/dist/src/utils/glob-source.js.map +1 -1
- package/dist/src/utils/url-source.d.ts +48 -2
- package/dist/src/utils/url-source.d.ts.map +1 -1
- package/dist/src/utils/url-source.js +50 -0
- package/dist/src/utils/url-source.js.map +1 -1
- package/package.json +4 -3
- package/src/commands/add.ts +47 -17
- package/src/commands/stat.ts +149 -73
- package/src/index.ts +162 -29
- package/src/unixfs.ts +5 -3
- package/src/utils/glob-source.ts +19 -0
- package/src/utils/url-source.ts +55 -2
- package/dist/typedoc-urls.json +0 -56
package/src/commands/add.ts
CHANGED
|
@@ -1,31 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { importBytes, importByteStream, importer } from 'ipfs-unixfs-importer'
|
|
2
2
|
import { fixedSize } from 'ipfs-unixfs-importer/chunker'
|
|
3
3
|
import { balanced } from 'ipfs-unixfs-importer/layout'
|
|
4
|
+
import first from 'it-first'
|
|
5
|
+
import last from 'it-last'
|
|
6
|
+
import { InvalidParametersError } from '../errors.js'
|
|
7
|
+
import type { FileCandidate, AddOptions, AddFileOptions } from '../index.js'
|
|
4
8
|
import type { PutStore } from '../unixfs.js'
|
|
9
|
+
import type { ByteStream, DirectoryCandidate, ImportCandidateStream, ImportResult } from 'ipfs-unixfs-importer'
|
|
5
10
|
import type { CID } from 'multiformats/cid'
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* Default importer settings match Filecoin
|
|
9
14
|
*/
|
|
10
|
-
const defaultImporterSettings:
|
|
15
|
+
const defaultImporterSettings: AddOptions = {
|
|
11
16
|
cidVersion: 1,
|
|
12
17
|
rawLeaves: true,
|
|
13
18
|
layout: balanced({
|
|
14
19
|
maxChildrenPerNode: 1024
|
|
15
20
|
}),
|
|
16
21
|
chunker: fixedSize({
|
|
17
|
-
chunkSize:
|
|
22
|
+
chunkSize: 1_048_576
|
|
18
23
|
})
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
export async function * addAll (source: ImportCandidateStream, blockstore: PutStore, options: Partial<
|
|
26
|
+
export async function * addAll (source: ImportCandidateStream, blockstore: PutStore, options: Partial<AddOptions> = {}): AsyncGenerator<ImportResult, void, unknown> {
|
|
22
27
|
yield * importer(source, blockstore, {
|
|
23
28
|
...defaultImporterSettings,
|
|
24
29
|
...options
|
|
25
30
|
})
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
export async function addBytes (bytes: Uint8Array, blockstore: PutStore, options: Partial<
|
|
33
|
+
export async function addBytes (bytes: Uint8Array, blockstore: PutStore, options: Partial<AddFileOptions> = {}): Promise<CID> {
|
|
29
34
|
const { cid } = await importBytes(bytes, blockstore, {
|
|
30
35
|
...defaultImporterSettings,
|
|
31
36
|
...options
|
|
@@ -34,7 +39,7 @@ export async function addBytes (bytes: Uint8Array, blockstore: PutStore, options
|
|
|
34
39
|
return cid
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
export async function addByteStream (bytes: ByteStream, blockstore: PutStore, options: Partial<
|
|
42
|
+
export async function addByteStream (bytes: ByteStream, blockstore: PutStore, options: Partial<AddFileOptions> = {}): Promise<CID> {
|
|
38
43
|
const { cid } = await importByteStream(bytes, blockstore, {
|
|
39
44
|
...defaultImporterSettings,
|
|
40
45
|
...options
|
|
@@ -43,23 +48,48 @@ export async function addByteStream (bytes: ByteStream, blockstore: PutStore, op
|
|
|
43
48
|
return cid
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
export async function addFile (file: FileCandidate, blockstore: PutStore, options: Partial<
|
|
47
|
-
|
|
51
|
+
export async function addFile (file: FileCandidate, blockstore: PutStore, options: Partial<AddFileOptions> = {}): Promise<CID> {
|
|
52
|
+
if (file.path == null) {
|
|
53
|
+
throw new InvalidParametersError('path is required')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (file.content == null) {
|
|
57
|
+
throw new InvalidParametersError('content is required')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = await last(addAll([file], blockstore, {
|
|
48
61
|
...defaultImporterSettings,
|
|
49
|
-
...options
|
|
50
|
-
|
|
62
|
+
...options,
|
|
63
|
+
wrapWithDirectory: true
|
|
64
|
+
}))
|
|
51
65
|
|
|
52
|
-
|
|
66
|
+
if (result == null) {
|
|
67
|
+
throw new InvalidParametersError('Nothing imported')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return result.cid
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
export async function addDirectory (dir: Partial<DirectoryCandidate>, blockstore: PutStore, options: Partial<
|
|
56
|
-
|
|
73
|
+
export async function addDirectory (dir: Partial<DirectoryCandidate>, blockstore: PutStore, options: Partial<AddFileOptions> = {}): Promise<CID> {
|
|
74
|
+
// @ts-expect-error field is not in the types
|
|
75
|
+
if (dir.content != null) {
|
|
76
|
+
throw new InvalidParametersError('Directories cannot have content, use addFile instead')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const ord = dir.path == null ? first : last
|
|
80
|
+
|
|
81
|
+
const result = await ord(addAll([{
|
|
57
82
|
...dir,
|
|
58
83
|
path: dir.path ?? '-'
|
|
59
|
-
}, blockstore, {
|
|
84
|
+
}], blockstore, {
|
|
60
85
|
...defaultImporterSettings,
|
|
61
|
-
...options
|
|
62
|
-
|
|
86
|
+
...options,
|
|
87
|
+
wrapWithDirectory: dir.path != null
|
|
88
|
+
}))
|
|
63
89
|
|
|
64
|
-
|
|
90
|
+
if (result == null) {
|
|
91
|
+
throw new InvalidParametersError('Nothing imported')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result.cid
|
|
65
95
|
}
|
package/src/commands/stat.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import * as dagPb from '@ipld/dag-pb'
|
|
2
2
|
import { logger } from '@libp2p/logger'
|
|
3
|
+
import { ScalableCuckooFilter } from '@libp2p/utils/filters'
|
|
3
4
|
import { UnixFS } from 'ipfs-unixfs'
|
|
4
|
-
import { exporter } from 'ipfs-unixfs-exporter'
|
|
5
|
+
import { exporter, type RawNode, type UnixFSDirectory, type UnixFSFile } from 'ipfs-unixfs-exporter'
|
|
5
6
|
import mergeOpts from 'merge-options'
|
|
6
7
|
import * as raw from 'multiformats/codecs/raw'
|
|
7
8
|
import { InvalidPBNodeError, NotUnixFSError, UnknownError } from '../errors.js'
|
|
8
9
|
import { resolve } from './utils/resolve.js'
|
|
9
|
-
import type { StatOptions,
|
|
10
|
+
import type { ExtendedStatOptions, ExtendedDirectoryStats, ExtendedFileStats, StatOptions, DirectoryStats, FileStats, RawStats, ExtendedRawStats } from '../index.js'
|
|
10
11
|
import type { GetStore, HasStore } from '../unixfs.js'
|
|
11
|
-
import type {
|
|
12
|
-
import type { Mtime } from 'ipfs-unixfs'
|
|
12
|
+
import type { Filter } from '@libp2p/utils/filters'
|
|
13
13
|
import type { CID } from 'multiformats/cid'
|
|
14
14
|
|
|
15
|
+
// https://github.com/ipfs/specs/blob/main/UNIXFS.md#metadata
|
|
16
|
+
const DEFAULT_DIR_MODE = 0x755
|
|
17
|
+
const DEFAULT_FILE_MODE = 0x644
|
|
18
|
+
|
|
15
19
|
const mergeOptions = mergeOpts.bind({ ignoreUndefined: true })
|
|
16
20
|
const log = logger('helia:unixfs:stat')
|
|
17
21
|
|
|
@@ -19,7 +23,9 @@ const defaultOptions: StatOptions = {
|
|
|
19
23
|
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
export async function stat (cid: CID, blockstore: GetStore & HasStore, options
|
|
26
|
+
export async function stat (cid: CID, blockstore: GetStore & HasStore, options?: StatOptions): Promise<FileStats | DirectoryStats | RawStats>
|
|
27
|
+
export async function stat (cid: CID, blockstore: GetStore & HasStore, options?: ExtendedStatOptions): Promise<ExtendedFileStats | ExtendedDirectoryStats | ExtendedRawStats>
|
|
28
|
+
export async function stat (cid: CID, blockstore: GetStore & HasStore, options: Partial<ExtendedStatOptions> = {}): Promise<any> {
|
|
23
29
|
const opts: StatOptions = mergeOptions(defaultOptions, options)
|
|
24
30
|
const resolved = await resolve(cid, options.path, blockstore, opts)
|
|
25
31
|
|
|
@@ -27,116 +33,186 @@ export async function stat (cid: CID, blockstore: GetStore & HasStore, options:
|
|
|
27
33
|
|
|
28
34
|
const result = await exporter(resolved.cid, blockstore, opts)
|
|
29
35
|
|
|
30
|
-
if (result.type
|
|
31
|
-
|
|
36
|
+
if (result.type === 'raw') {
|
|
37
|
+
if (options.extended === true) {
|
|
38
|
+
return createExtendedRawStats(result)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return createRawStats(result)
|
|
42
|
+
} else if (result.type === 'file' || result.type === 'directory') {
|
|
43
|
+
if (options.extended === true) {
|
|
44
|
+
return createExtendedStats(result, blockstore, options.filter ?? new ScalableCuckooFilter({ filterSize: 1024 }), options)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return createStats(result)
|
|
32
48
|
}
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
let localFileSize: bigint = 0n
|
|
37
|
-
let localDagSize: bigint = 0n
|
|
38
|
-
let blocks: number = 0
|
|
39
|
-
let mode: number | undefined
|
|
40
|
-
let mtime: Mtime | undefined
|
|
41
|
-
const type = result.type
|
|
42
|
-
let unixfs: UnixFS | undefined
|
|
50
|
+
throw new NotUnixFSError()
|
|
51
|
+
}
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
function createStats (entry: UnixFSFile | UnixFSDirectory): FileStats | DirectoryStats {
|
|
54
|
+
return {
|
|
55
|
+
type: entry.type,
|
|
56
|
+
cid: entry.cid,
|
|
57
|
+
unixfs: entry.unixfs,
|
|
58
|
+
mode: entry.unixfs.mode ?? (entry.unixfs.isDirectory() ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE),
|
|
59
|
+
mtime: entry.unixfs.mtime,
|
|
60
|
+
size: entry.unixfs.fileSize()
|
|
50
61
|
}
|
|
62
|
+
}
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
unixfs
|
|
64
|
+
async function createExtendedStats (entry: UnixFSFile | UnixFSDirectory, blockstore: GetStore & HasStore, filter: Filter, options: StatOptions): Promise<ExtendedFileStats | ExtendedDirectoryStats> {
|
|
65
|
+
const stats = await inspectDag(entry.cid, blockstore, false, filter, options)
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
type: entry.type,
|
|
69
|
+
cid: entry.cid,
|
|
70
|
+
unixfs: entry.unixfs,
|
|
71
|
+
size: entry.unixfs.isDirectory() ? stats.dirSize : entry.unixfs.fileSize(),
|
|
72
|
+
mode: entry.unixfs.mode ?? (entry.unixfs.isDirectory() ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE),
|
|
73
|
+
mtime: entry.unixfs.mtime,
|
|
74
|
+
localSize: stats.localSize,
|
|
75
|
+
dagSize: stats.dagSize,
|
|
76
|
+
deduplicatedDagSize: stats.deduplicatedDagSize,
|
|
77
|
+
blocks: stats.blocks,
|
|
78
|
+
uniqueBlocks: stats.uniqueBlocks
|
|
61
79
|
}
|
|
80
|
+
}
|
|
62
81
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
mode = result.unixfs.mode
|
|
72
|
-
mtime = result.unixfs.mtime
|
|
73
|
-
unixfs = result.unixfs
|
|
82
|
+
function createRawStats (entry: RawNode): RawStats {
|
|
83
|
+
return {
|
|
84
|
+
type: entry.type,
|
|
85
|
+
cid: entry.cid,
|
|
86
|
+
unixfs: undefined,
|
|
87
|
+
mode: DEFAULT_FILE_MODE,
|
|
88
|
+
mtime: undefined,
|
|
89
|
+
size: BigInt(entry.node.byteLength)
|
|
74
90
|
}
|
|
91
|
+
}
|
|
75
92
|
|
|
93
|
+
function createExtendedRawStats (entry: RawNode): ExtendedRawStats {
|
|
76
94
|
return {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
95
|
+
type: entry.type,
|
|
96
|
+
cid: entry.cid,
|
|
97
|
+
unixfs: undefined,
|
|
98
|
+
mode: DEFAULT_FILE_MODE,
|
|
99
|
+
mtime: undefined,
|
|
100
|
+
size: BigInt(entry.node.byteLength),
|
|
101
|
+
localSize: BigInt(entry.node.byteLength),
|
|
102
|
+
dagSize: BigInt(entry.node.byteLength),
|
|
103
|
+
deduplicatedDagSize: BigInt(entry.node.byteLength),
|
|
104
|
+
blocks: 1n,
|
|
105
|
+
uniqueBlocks: 1n
|
|
87
106
|
}
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
interface InspectDagResults {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
dirSize: bigint
|
|
111
|
+
localSize: bigint
|
|
112
|
+
dagSize: bigint
|
|
113
|
+
deduplicatedDagSize: bigint
|
|
114
|
+
blocks: bigint
|
|
115
|
+
uniqueBlocks: bigint
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
async function inspectDag (cid: CID, blockstore: GetStore & HasStore, options:
|
|
97
|
-
const results = {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
118
|
+
async function inspectDag (cid: CID, blockstore: GetStore & HasStore, isFile: boolean, filter: Filter, options: StatOptions): Promise<InspectDagResults> {
|
|
119
|
+
const results: InspectDagResults = {
|
|
120
|
+
dirSize: 0n,
|
|
121
|
+
localSize: 0n,
|
|
122
|
+
dagSize: 0n,
|
|
123
|
+
deduplicatedDagSize: 0n,
|
|
124
|
+
blocks: 0n,
|
|
125
|
+
uniqueBlocks: 0n
|
|
101
126
|
}
|
|
102
127
|
|
|
103
|
-
|
|
128
|
+
try {
|
|
129
|
+
const alreadyTraversed = filter.has(cid.bytes)
|
|
130
|
+
filter.add(cid.bytes)
|
|
131
|
+
|
|
104
132
|
const block = await blockstore.get(cid, options)
|
|
105
133
|
results.blocks++
|
|
106
|
-
results.
|
|
134
|
+
results.dagSize += BigInt(block.byteLength)
|
|
135
|
+
|
|
136
|
+
if (!alreadyTraversed) {
|
|
137
|
+
results.uniqueBlocks++
|
|
138
|
+
results.deduplicatedDagSize += BigInt(block.byteLength)
|
|
139
|
+
}
|
|
107
140
|
|
|
108
141
|
if (cid.code === raw.code) {
|
|
109
|
-
results.
|
|
142
|
+
results.localSize += BigInt(block.byteLength)
|
|
143
|
+
|
|
144
|
+
if (isFile) {
|
|
145
|
+
results.dirSize += BigInt(block.byteLength)
|
|
146
|
+
}
|
|
110
147
|
} else if (cid.code === dagPb.code) {
|
|
111
148
|
const pbNode = dagPb.decode(block)
|
|
112
149
|
|
|
150
|
+
let unixfs: UnixFS | undefined
|
|
151
|
+
|
|
152
|
+
if (pbNode.Data != null) {
|
|
153
|
+
unixfs = UnixFS.unmarshal(pbNode.Data)
|
|
154
|
+
}
|
|
155
|
+
|
|
113
156
|
if (pbNode.Links.length > 0) {
|
|
114
157
|
// intermediate node
|
|
115
158
|
for (const link of pbNode.Links) {
|
|
116
|
-
const linkResult = await inspectDag(link.Hash, blockstore, options)
|
|
159
|
+
const linkResult = await inspectDag(link.Hash, blockstore, linkIsFile(link, unixfs), filter, options)
|
|
117
160
|
|
|
118
|
-
results.
|
|
119
|
-
results.
|
|
161
|
+
results.localSize += linkResult.localSize
|
|
162
|
+
results.dagSize += linkResult.dagSize
|
|
163
|
+
results.deduplicatedDagSize += linkResult.deduplicatedDagSize
|
|
120
164
|
results.blocks += linkResult.blocks
|
|
165
|
+
results.uniqueBlocks += linkResult.uniqueBlocks
|
|
166
|
+
results.dirSize += linkResult.dirSize
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// multi-block file node
|
|
170
|
+
if (isFile && unixfs != null) {
|
|
171
|
+
results.dirSize += unixfs.fileSize()
|
|
121
172
|
}
|
|
122
173
|
} else {
|
|
123
|
-
|
|
124
|
-
if (pbNode.Data == null) {
|
|
174
|
+
if (unixfs == null) {
|
|
125
175
|
throw new InvalidPBNodeError(`PBNode ${cid.toString()} had no data`)
|
|
126
176
|
}
|
|
127
177
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
throw new InvalidPBNodeError(`UnixFS node ${cid.toString()} had no data`)
|
|
178
|
+
// multi-block file leaf node
|
|
179
|
+
if (unixfs.data != null) {
|
|
180
|
+
results.localSize += BigInt(unixfs.data.byteLength ?? 0)
|
|
132
181
|
}
|
|
133
182
|
|
|
134
|
-
|
|
183
|
+
// single-block file node
|
|
184
|
+
if (isFile) {
|
|
185
|
+
results.dirSize += unixfs.fileSize()
|
|
186
|
+
}
|
|
135
187
|
}
|
|
136
188
|
} else {
|
|
137
189
|
throw new UnknownError(`${cid.toString()} was neither DAG_PB nor RAW`)
|
|
138
190
|
}
|
|
191
|
+
} catch (err: any) {
|
|
192
|
+
if (err.name !== 'NotFoundError' || options.offline !== true) {
|
|
193
|
+
throw err
|
|
194
|
+
}
|
|
139
195
|
}
|
|
140
196
|
|
|
141
197
|
return results
|
|
142
198
|
}
|
|
199
|
+
|
|
200
|
+
function linkIsFile (link: dagPb.PBLink, parent?: UnixFS): boolean {
|
|
201
|
+
if (parent == null) {
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const name = link.Name
|
|
206
|
+
|
|
207
|
+
if (name == null) {
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (parent.type === 'directory') {
|
|
212
|
+
return true
|
|
213
|
+
} else if (parent.type === 'hamt-sharded-directory' && name.length > 2) {
|
|
214
|
+
return true
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return false
|
|
218
|
+
}
|