@helia/unixfs 0.0.0 → 1.0.1
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 +10 -4
- package/dist/index.min.js +1 -1
- package/dist/src/commands/cat.d.ts +1 -1
- package/dist/src/commands/cat.d.ts.map +1 -1
- package/dist/src/commands/cat.js +1 -1
- package/dist/src/commands/cat.js.map +1 -1
- package/dist/src/commands/chmod.d.ts.map +1 -1
- package/dist/src/commands/chmod.js +14 -12
- package/dist/src/commands/chmod.js.map +1 -1
- package/dist/src/commands/cp.d.ts +1 -1
- package/dist/src/commands/cp.d.ts.map +1 -1
- package/dist/src/commands/cp.js +5 -2
- package/dist/src/commands/cp.js.map +1 -1
- package/dist/src/commands/ls.d.ts +2 -1
- package/dist/src/commands/ls.d.ts.map +1 -1
- package/dist/src/commands/ls.js +1 -1
- package/dist/src/commands/ls.js.map +1 -1
- package/dist/src/commands/mkdir.d.ts.map +1 -1
- package/dist/src/commands/mkdir.js +4 -2
- package/dist/src/commands/mkdir.js.map +1 -1
- package/dist/src/commands/rm.d.ts +1 -1
- package/dist/src/commands/rm.d.ts.map +1 -1
- package/dist/src/commands/rm.js +9 -3
- package/dist/src/commands/rm.js.map +1 -1
- package/dist/src/commands/stat.d.ts +1 -1
- package/dist/src/commands/stat.d.ts.map +1 -1
- package/dist/src/commands/stat.js +19 -15
- package/dist/src/commands/stat.js.map +1 -1
- package/dist/src/commands/touch.d.ts.map +1 -1
- package/dist/src/commands/touch.js +15 -13
- package/dist/src/commands/touch.js.map +1 -1
- package/dist/src/commands/utils/add-link.d.ts +4 -2
- package/dist/src/commands/utils/add-link.d.ts.map +1 -1
- package/dist/src/commands/utils/add-link.js +24 -24
- package/dist/src/commands/utils/add-link.js.map +1 -1
- package/dist/src/commands/utils/cid-to-directory.d.ts +3 -3
- package/dist/src/commands/utils/cid-to-directory.d.ts.map +1 -1
- package/dist/src/commands/utils/cid-to-directory.js +1 -1
- package/dist/src/commands/utils/cid-to-directory.js.map +1 -1
- package/dist/src/commands/utils/cid-to-pblink.d.ts +3 -3
- package/dist/src/commands/utils/cid-to-pblink.d.ts.map +1 -1
- package/dist/src/commands/utils/cid-to-pblink.js.map +1 -1
- package/dist/src/commands/utils/constants.d.ts +2 -0
- package/dist/src/commands/utils/constants.d.ts.map +1 -0
- package/dist/src/commands/utils/constants.js +2 -0
- package/dist/src/commands/utils/constants.js.map +1 -0
- package/dist/src/commands/utils/dir-sharded.d.ts +47 -41
- package/dist/src/commands/utils/dir-sharded.d.ts.map +1 -1
- package/dist/src/commands/utils/dir-sharded.js +99 -15
- package/dist/src/commands/utils/dir-sharded.js.map +1 -1
- package/dist/src/commands/utils/errors.d.ts +22 -6
- package/dist/src/commands/utils/errors.d.ts.map +1 -1
- package/dist/src/commands/utils/errors.js +34 -8
- package/dist/src/commands/utils/errors.js.map +1 -1
- package/dist/src/commands/utils/hamt-constants.d.ts +1 -1
- package/dist/src/commands/utils/hamt-constants.d.ts.map +1 -1
- package/dist/src/commands/utils/hamt-constants.js +1 -1
- package/dist/src/commands/utils/hamt-constants.js.map +1 -1
- package/dist/src/commands/utils/hamt-utils.d.ts +15 -12
- package/dist/src/commands/utils/hamt-utils.d.ts.map +1 -1
- package/dist/src/commands/utils/hamt-utils.js +40 -39
- package/dist/src/commands/utils/hamt-utils.js.map +1 -1
- package/dist/src/commands/utils/is-over-shard-threshold.d.ts +10 -0
- package/dist/src/commands/utils/is-over-shard-threshold.d.ts.map +1 -0
- package/dist/src/commands/utils/is-over-shard-threshold.js +62 -0
- package/dist/src/commands/utils/is-over-shard-threshold.js.map +1 -0
- package/dist/src/commands/utils/persist.d.ts +7 -6
- package/dist/src/commands/utils/persist.d.ts.map +1 -1
- package/dist/src/commands/utils/persist.js +6 -3
- package/dist/src/commands/utils/persist.js.map +1 -1
- package/dist/src/commands/utils/remove-link.d.ts +6 -2
- package/dist/src/commands/utils/remove-link.d.ts.map +1 -1
- package/dist/src/commands/utils/remove-link.js +143 -55
- package/dist/src/commands/utils/remove-link.js.map +1 -1
- package/dist/src/commands/utils/resolve.d.ts +6 -3
- package/dist/src/commands/utils/resolve.d.ts.map +1 -1
- package/dist/src/commands/utils/resolve.js +4 -4
- package/dist/src/commands/utils/resolve.js.map +1 -1
- package/dist/src/index.d.ts +14 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -7
- package/dist/src/index.js.map +1 -1
- package/dist/typedoc-urls.json +14 -0
- package/package.json +8 -7
- package/src/commands/cat.ts +3 -2
- package/src/commands/chmod.ts +13 -10
- package/src/commands/cp.ts +7 -4
- package/src/commands/ls.ts +4 -3
- package/src/commands/mkdir.ts +5 -3
- package/src/commands/rm.ts +9 -5
- package/src/commands/stat.ts +22 -17
- package/src/commands/touch.ts +15 -12
- package/src/commands/utils/add-link.ts +32 -31
- package/src/commands/utils/cid-to-directory.ts +4 -4
- package/src/commands/utils/cid-to-pblink.ts +3 -3
- package/src/commands/utils/constants.ts +2 -0
- package/src/commands/utils/dir-sharded.ts +162 -63
- package/src/commands/utils/errors.ts +42 -8
- package/src/commands/utils/hamt-constants.ts +1 -1
- package/src/commands/utils/hamt-utils.ts +59 -50
- package/src/commands/utils/is-over-shard-threshold.ts +78 -0
- package/src/commands/utils/persist.ts +13 -8
- package/src/commands/utils/remove-link.ts +178 -77
- package/src/commands/utils/resolve.ts +12 -7
- package/src/index.ts +15 -17
- package/dist/src/commands/add.d.ts +0 -6
- package/dist/src/commands/add.d.ts.map +0 -1
- package/dist/src/commands/add.js +0 -38
- package/dist/src/commands/add.js.map +0 -1
- package/src/commands/add.ts +0 -46
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { PBNode } from '@ipld/dag-pb'
|
|
2
|
+
import type { Blockstore } from 'interface-blockstore'
|
|
3
|
+
import { UnixFS } from 'ipfs-unixfs'
|
|
4
|
+
import * as dagPb from '@ipld/dag-pb'
|
|
5
|
+
import { CID_V0, CID_V1 } from './dir-sharded.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Estimate node size only based on DAGLink name and CID byte lengths
|
|
9
|
+
* https://github.com/ipfs/go-unixfsnode/blob/37b47f1f917f1b2f54c207682f38886e49896ef9/data/builder/directory.go#L81-L96
|
|
10
|
+
*
|
|
11
|
+
* If the node is a hamt sharded directory the calculation is based on if it was a regular directory.
|
|
12
|
+
*/
|
|
13
|
+
export async function isOverShardThreshold (node: PBNode, blockstore: Blockstore, threshold: number): Promise<boolean> {
|
|
14
|
+
if (node.Data == null) {
|
|
15
|
+
throw new Error('DagPB node had no data')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const unixfs = UnixFS.unmarshal(node.Data)
|
|
19
|
+
let size: number
|
|
20
|
+
|
|
21
|
+
if (unixfs.type === 'directory') {
|
|
22
|
+
size = estimateNodeSize(node)
|
|
23
|
+
} else if (unixfs.type === 'hamt-sharded-directory') {
|
|
24
|
+
size = await estimateShardSize(node, 0, threshold, blockstore)
|
|
25
|
+
} else {
|
|
26
|
+
throw new Error('Can only estimate the size of directories or shards')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return size > threshold
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function estimateNodeSize (node: PBNode): number {
|
|
33
|
+
let size = 0
|
|
34
|
+
|
|
35
|
+
// estimate size only based on DAGLink name and CID byte lengths
|
|
36
|
+
// https://github.com/ipfs/go-unixfsnode/blob/37b47f1f917f1b2f54c207682f38886e49896ef9/data/builder/directory.go#L81-L96
|
|
37
|
+
for (const link of node.Links) {
|
|
38
|
+
size += (link.Name ?? '').length
|
|
39
|
+
size += link.Hash.version === 1 ? CID_V1.bytes.byteLength : CID_V0.bytes.byteLength
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return size
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function estimateShardSize (node: PBNode, current: number, max: number, blockstore: Blockstore): Promise<number> {
|
|
46
|
+
if (current > max) {
|
|
47
|
+
return max
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (node.Data == null) {
|
|
51
|
+
return current
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const unixfs = UnixFS.unmarshal(node.Data)
|
|
55
|
+
|
|
56
|
+
if (!unixfs.isDirectory()) {
|
|
57
|
+
return current
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const link of node.Links) {
|
|
61
|
+
let name = link.Name ?? ''
|
|
62
|
+
|
|
63
|
+
// remove hamt hash prefix from name
|
|
64
|
+
name = name.substring(2)
|
|
65
|
+
|
|
66
|
+
current += name.length
|
|
67
|
+
current += link.Hash.bytes.byteLength
|
|
68
|
+
|
|
69
|
+
if (link.Hash.code === dagPb.code) {
|
|
70
|
+
const block = await blockstore.get(link.Hash)
|
|
71
|
+
const node = dagPb.decode(block)
|
|
72
|
+
|
|
73
|
+
current += await estimateShardSize(node, current, max, blockstore)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return current
|
|
78
|
+
}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import { CID
|
|
2
|
-
import * as
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import * as dagPb from '@ipld/dag-pb'
|
|
3
3
|
import { sha256 } from 'multiformats/hashes/sha2'
|
|
4
|
-
import type { BlockCodec } from 'multiformats/codecs/interface'
|
|
5
|
-
import type { AbortOptions } from '@libp2p/interfaces'
|
|
6
4
|
import type { Blockstore } from 'interface-blockstore'
|
|
5
|
+
import type { BlockCodec } from 'multiformats/codecs/interface'
|
|
6
|
+
import type { Version as CIDVersion } from 'multiformats/cid'
|
|
7
7
|
|
|
8
|
-
export interface PersistOptions
|
|
8
|
+
export interface PersistOptions {
|
|
9
9
|
codec?: BlockCodec<any, any>
|
|
10
|
-
cidVersion
|
|
10
|
+
cidVersion: CIDVersion
|
|
11
|
+
signal?: AbortSignal
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
export const persist = async (buffer: Uint8Array, blockstore: Blockstore, options: PersistOptions
|
|
14
|
+
export const persist = async (buffer: Uint8Array, blockstore: Blockstore, options: PersistOptions): Promise<CID> => {
|
|
15
|
+
if (options.codec == null) {
|
|
16
|
+
options.codec = dagPb
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
const multihash = await sha256.digest(buffer)
|
|
15
|
-
const cid = CID.create(options.cidVersion
|
|
20
|
+
const cid = CID.create(options.cidVersion, options.codec.code, multihash)
|
|
16
21
|
|
|
17
22
|
await blockstore.put(cid, buffer, {
|
|
18
23
|
signal: options.signal
|
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
|
|
2
2
|
import * as dagPB from '@ipld/dag-pb'
|
|
3
|
-
import { CID } from 'multiformats/cid'
|
|
3
|
+
import type { CID, Version } from 'multiformats/cid'
|
|
4
4
|
import { logger } from '@libp2p/logger'
|
|
5
5
|
import { UnixFS } from 'ipfs-unixfs'
|
|
6
6
|
import {
|
|
7
7
|
generatePath,
|
|
8
|
+
HamtPathSegment,
|
|
8
9
|
updateHamtDirectory,
|
|
9
|
-
|
|
10
|
+
UpdateHamtDirectoryOptions
|
|
10
11
|
} from './hamt-utils.js'
|
|
11
|
-
import type { PBNode
|
|
12
|
+
import type { PBNode } from '@ipld/dag-pb'
|
|
12
13
|
import type { Blockstore } from 'interface-blockstore'
|
|
13
|
-
import { sha256 } from 'multiformats/hashes/sha2'
|
|
14
|
-
import type { Bucket } from 'hamt-sharding'
|
|
15
14
|
import type { Directory } from './cid-to-directory.js'
|
|
16
15
|
import type { AbortOptions } from '@libp2p/interfaces'
|
|
17
|
-
import { InvalidPBNodeError } from './errors.js'
|
|
18
|
-
import {
|
|
16
|
+
import { InvalidParametersError, InvalidPBNodeError } from './errors.js'
|
|
17
|
+
import { exporter } from 'ipfs-unixfs-exporter'
|
|
18
|
+
import { persist } from './persist.js'
|
|
19
|
+
import { isOverShardThreshold } from './is-over-shard-threshold.js'
|
|
19
20
|
|
|
20
21
|
const log = logger('helia:unixfs:utils:remove-link')
|
|
21
22
|
|
|
23
|
+
export interface RmLinkOptions extends AbortOptions {
|
|
24
|
+
shardSplitThresholdBytes: number
|
|
25
|
+
cidVersion: Version
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
export interface RemoveLinkResult {
|
|
23
29
|
node: PBNode
|
|
24
30
|
cid: CID
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
export async function removeLink (parent: Directory, name: string, blockstore: Blockstore, options:
|
|
33
|
+
export async function removeLink (parent: Directory, name: string, blockstore: Blockstore, options: RmLinkOptions): Promise<RemoveLinkResult> {
|
|
28
34
|
if (parent.node.Data == null) {
|
|
29
35
|
throw new InvalidPBNodeError('Parent node had no data')
|
|
30
36
|
}
|
|
@@ -32,12 +38,20 @@ export async function removeLink (parent: Directory, name: string, blockstore: B
|
|
|
32
38
|
const meta = UnixFS.unmarshal(parent.node.Data)
|
|
33
39
|
|
|
34
40
|
if (meta.type === 'hamt-sharded-directory') {
|
|
35
|
-
log(`
|
|
41
|
+
log(`removing ${name} from sharded directory`)
|
|
42
|
+
|
|
43
|
+
const result = await removeFromShardedDirectory(parent, name, blockstore, options)
|
|
44
|
+
|
|
45
|
+
if (!(await isOverShardThreshold(result.node, blockstore, options.shardSplitThresholdBytes))) {
|
|
46
|
+
log('converting shard to flat directory %c', parent.cid)
|
|
36
47
|
|
|
37
|
-
|
|
48
|
+
return await convertToFlatDirectory(result, blockstore, options)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return result
|
|
38
52
|
}
|
|
39
53
|
|
|
40
|
-
log(`
|
|
54
|
+
log(`removing link ${name} regular directory`)
|
|
41
55
|
|
|
42
56
|
return await removeFromDirectory(parent, name, blockstore, options)
|
|
43
57
|
}
|
|
@@ -49,10 +63,10 @@ const removeFromDirectory = async (parent: Directory, name: string, blockstore:
|
|
|
49
63
|
})
|
|
50
64
|
|
|
51
65
|
const parentBlock = dagPB.encode(parent.node)
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
const parentCid = await persist(parentBlock, blockstore, {
|
|
67
|
+
...options,
|
|
68
|
+
cidVersion: parent.cid.version
|
|
69
|
+
})
|
|
56
70
|
|
|
57
71
|
log(`Updated regular directory ${parentCid}`)
|
|
58
72
|
|
|
@@ -62,90 +76,177 @@ const removeFromDirectory = async (parent: Directory, name: string, blockstore:
|
|
|
62
76
|
}
|
|
63
77
|
}
|
|
64
78
|
|
|
65
|
-
const removeFromShardedDirectory = async (parent: Directory, name: string, blockstore: Blockstore, options:
|
|
66
|
-
const
|
|
67
|
-
rootBucket, path
|
|
68
|
-
} = await generatePath(parent, name, blockstore, options)
|
|
79
|
+
const removeFromShardedDirectory = async (parent: Directory, name: string, blockstore: Blockstore, options: UpdateHamtDirectoryOptions): Promise<{ cid: CID, node: PBNode }> => {
|
|
80
|
+
const path = await generatePath(parent, name, blockstore, options)
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
// remove file from root bucket
|
|
83
|
+
const rootBucket = path[path.length - 1].bucket
|
|
71
84
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
85
|
+
if (rootBucket == null) {
|
|
86
|
+
throw new Error('Could not generate HAMT path')
|
|
87
|
+
}
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
}
|
|
89
|
+
await rootBucket.del(name)
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
// update all nodes in the shard path
|
|
92
|
+
return await updateShard(path, name, blockstore, options)
|
|
93
|
+
}
|
|
81
94
|
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
/**
|
|
96
|
+
* The `path` param is a list of HAMT path segments starting with th
|
|
97
|
+
*/
|
|
98
|
+
const updateShard = async (path: HamtPathSegment[], name: string, blockstore: Blockstore, options: UpdateHamtDirectoryOptions): Promise<{ node: PBNode, cid: CID }> => {
|
|
99
|
+
const fileName = `${path[0].prefix}${name}`
|
|
100
|
+
|
|
101
|
+
// skip first path segment as it is the file to remove
|
|
102
|
+
for (let i = 1; i < path.length; i++) {
|
|
103
|
+
const lastPrefix = path[i - 1].prefix
|
|
104
|
+
const segment = path[i]
|
|
105
|
+
|
|
106
|
+
if (segment.node == null) {
|
|
107
|
+
throw new InvalidParametersError('Path segment had no associated PBNode')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const link = segment.node.Links
|
|
111
|
+
.find(link => (link.Name ?? '').substring(0, 2) === lastPrefix)
|
|
112
|
+
|
|
113
|
+
if (link == null) {
|
|
114
|
+
throw new InvalidParametersError(`No link found with prefix ${lastPrefix} for file ${name}`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (link.Name == null) {
|
|
118
|
+
throw new InvalidParametersError(`${lastPrefix} link had no name`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (link.Name === fileName) {
|
|
122
|
+
log(`removing existing link ${link.Name}`)
|
|
123
|
+
|
|
124
|
+
const links = segment.node.Links.filter((nodeLink) => {
|
|
125
|
+
return nodeLink.Name !== link.Name
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
if (segment.bucket == null) {
|
|
129
|
+
throw new Error('Segment bucket was missing')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
await segment.bucket.del(name)
|
|
133
|
+
|
|
134
|
+
const result = await updateHamtDirectory({
|
|
135
|
+
Data: segment.node.Data,
|
|
136
|
+
Links: links
|
|
137
|
+
}, blockstore, segment.bucket, options)
|
|
138
|
+
|
|
139
|
+
segment.node = result.node
|
|
140
|
+
segment.cid = result.cid
|
|
141
|
+
segment.size = result.size
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (link.Name === lastPrefix) {
|
|
145
|
+
log(`updating subshard with prefix ${lastPrefix}`)
|
|
146
|
+
|
|
147
|
+
const lastSegment = path[i - 1]
|
|
148
|
+
|
|
149
|
+
if (lastSegment.node?.Links.length === 1) {
|
|
150
|
+
log(`removing subshard for ${lastPrefix}`)
|
|
151
|
+
|
|
152
|
+
// convert subshard back to normal file entry
|
|
153
|
+
const link = lastSegment.node.Links[0]
|
|
154
|
+
link.Name = `${lastPrefix}${(link.Name ?? '').substring(2)}`
|
|
155
|
+
|
|
156
|
+
// remove existing prefix
|
|
157
|
+
segment.node.Links = segment.node.Links.filter((link) => {
|
|
158
|
+
return link.Name !== lastPrefix
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// add new child
|
|
162
|
+
segment.node.Links.push(link)
|
|
163
|
+
} else {
|
|
164
|
+
// replace subshard entry
|
|
165
|
+
log(`replacing subshard for ${lastPrefix}`)
|
|
166
|
+
|
|
167
|
+
// remove existing prefix
|
|
168
|
+
segment.node.Links = segment.node.Links.filter((link) => {
|
|
169
|
+
return link.Name !== lastPrefix
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
if (lastSegment.cid == null) {
|
|
173
|
+
throw new Error('Did not persist previous segment')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// add new child
|
|
177
|
+
segment.node.Links.push({
|
|
178
|
+
Name: lastPrefix,
|
|
179
|
+
Hash: lastSegment.cid,
|
|
180
|
+
Tsize: lastSegment.size
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (segment.bucket == null) {
|
|
185
|
+
throw new Error('Segment bucket was missing')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const result = await updateHamtDirectory(segment.node, blockstore, segment.bucket, options)
|
|
189
|
+
segment.node = result.node
|
|
190
|
+
segment.cid = result.cid
|
|
191
|
+
segment.size = result.size
|
|
192
|
+
}
|
|
84
193
|
}
|
|
85
194
|
|
|
86
|
-
const
|
|
87
|
-
bucket,
|
|
88
|
-
prefix,
|
|
89
|
-
node
|
|
90
|
-
} = last
|
|
195
|
+
const rootSegment = path[path.length - 1]
|
|
91
196
|
|
|
92
|
-
if (node == null) {
|
|
93
|
-
throw new InvalidParametersError('
|
|
197
|
+
if (rootSegment == null || rootSegment.cid == null || rootSegment.node == null) {
|
|
198
|
+
throw new InvalidParametersError('Failed to update shard')
|
|
94
199
|
}
|
|
95
200
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (link == null) {
|
|
100
|
-
throw new InvalidParametersError(`No link found with prefix ${prefix} for file ${name}`)
|
|
201
|
+
return {
|
|
202
|
+
cid: rootSegment.cid,
|
|
203
|
+
node: rootSegment.node
|
|
101
204
|
}
|
|
205
|
+
}
|
|
102
206
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const links = node.Links.filter((nodeLink) => {
|
|
107
|
-
return nodeLink.Name !== link.Name
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
await bucket.del(name)
|
|
111
|
-
|
|
112
|
-
parent.node = node
|
|
113
|
-
|
|
114
|
-
return await updateHamtDirectory(parent, blockstore, links, bucket, options)
|
|
207
|
+
const convertToFlatDirectory = async (parent: Directory, blockstore: Blockstore, options: RmLinkOptions): Promise<RemoveLinkResult> => {
|
|
208
|
+
if (parent.node.Data == null) {
|
|
209
|
+
throw new InvalidParametersError('Invalid parent passed to convertToFlatDirectory')
|
|
115
210
|
}
|
|
116
211
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
212
|
+
const rootNode: PBNode = {
|
|
213
|
+
Links: []
|
|
214
|
+
}
|
|
215
|
+
const dir = await exporter(parent.cid, blockstore)
|
|
120
216
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
Tsize: result.size,
|
|
124
|
-
Name: prefix
|
|
217
|
+
if (dir.type !== 'directory') {
|
|
218
|
+
throw new Error('Unexpected node type')
|
|
125
219
|
}
|
|
126
220
|
|
|
127
|
-
|
|
128
|
-
|
|
221
|
+
for await (const entry of dir.content()) {
|
|
222
|
+
let tsize = 0
|
|
129
223
|
|
|
130
|
-
|
|
131
|
-
|
|
224
|
+
if (entry.node instanceof Uint8Array) {
|
|
225
|
+
tsize = entry.node.byteLength
|
|
226
|
+
} else {
|
|
227
|
+
tsize = dagPB.encode(entry.node).length
|
|
228
|
+
}
|
|
132
229
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
230
|
+
rootNode.Links.push({
|
|
231
|
+
Hash: entry.cid,
|
|
232
|
+
Name: entry.name,
|
|
233
|
+
Tsize: tsize
|
|
234
|
+
})
|
|
136
235
|
}
|
|
137
236
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
237
|
+
// copy mode/mtime over if set
|
|
238
|
+
const oldUnixfs = UnixFS.unmarshal(parent.node.Data)
|
|
239
|
+
rootNode.Data = new UnixFS({ type: 'directory', mode: oldUnixfs.mode, mtime: oldUnixfs.mtime }).marshal()
|
|
240
|
+
const block = dagPB.encode(dagPB.prepare(rootNode))
|
|
142
241
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
242
|
+
const cid = await persist(block, blockstore, {
|
|
243
|
+
codec: dagPB,
|
|
244
|
+
cidVersion: parent.cid.version,
|
|
245
|
+
signal: options.signal
|
|
147
246
|
})
|
|
148
|
-
parentLinks.push(child)
|
|
149
247
|
|
|
150
|
-
return
|
|
248
|
+
return {
|
|
249
|
+
cid,
|
|
250
|
+
node: rootNode
|
|
251
|
+
}
|
|
151
252
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { CID } from 'multiformats/cid'
|
|
2
|
-
import {
|
|
2
|
+
import { exporter } from 'ipfs-unixfs-exporter'
|
|
3
3
|
import type { AbortOptions } from '@libp2p/interfaces'
|
|
4
|
-
import { InvalidParametersError } from '@helia/interface/errors'
|
|
5
4
|
import { logger } from '@libp2p/logger'
|
|
6
|
-
import { DoesNotExistError } from './errors.js'
|
|
5
|
+
import { DoesNotExistError, InvalidParametersError } from './errors.js'
|
|
7
6
|
import { addLink } from './add-link.js'
|
|
8
7
|
import { cidToDirectory } from './cid-to-directory.js'
|
|
9
8
|
import { cidToPBLink } from './cid-to-pblink.js'
|
|
9
|
+
import type { Blockstore } from 'interface-blockstore'
|
|
10
10
|
|
|
11
11
|
const log = logger('helia:unixfs:components:utils:add-link')
|
|
12
12
|
|
|
13
13
|
export interface Segment {
|
|
14
14
|
name: string
|
|
15
15
|
cid: CID
|
|
16
|
-
size:
|
|
16
|
+
size: bigint
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface ResolveResult {
|
|
@@ -43,7 +43,7 @@ export async function resolve (cid: CID, path: string | undefined, blockstore: B
|
|
|
43
43
|
const segments: Segment[] = [{
|
|
44
44
|
name: '',
|
|
45
45
|
cid,
|
|
46
|
-
size:
|
|
46
|
+
size: 0n
|
|
47
47
|
}]
|
|
48
48
|
|
|
49
49
|
for (let i = 0; i < parts.length; i++) {
|
|
@@ -88,11 +88,15 @@ export async function resolve (cid: CID, path: string | undefined, blockstore: B
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
export interface UpdatePathCidsOptions extends AbortOptions {
|
|
92
|
+
shardSplitThresholdBytes: number
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
/**
|
|
92
96
|
* Where we have descended into a DAG to update a child node, ascend up the DAG creating
|
|
93
97
|
* new hashes and blocks for the changed content
|
|
94
98
|
*/
|
|
95
|
-
export async function updatePathCids (cid: CID, result: ResolveResult, blockstore: Blockstore, options:
|
|
99
|
+
export async function updatePathCids (cid: CID, result: ResolveResult, blockstore: Blockstore, options: UpdatePathCidsOptions): Promise<CID> {
|
|
96
100
|
if (result.segments == null || result.segments.length === 0) {
|
|
97
101
|
return cid
|
|
98
102
|
}
|
|
@@ -118,7 +122,8 @@ export async function updatePathCids (cid: CID, result: ResolveResult, blockstor
|
|
|
118
122
|
|
|
119
123
|
const result = await addLink(directory, pblink, blockstore, {
|
|
120
124
|
...options,
|
|
121
|
-
allowOverwriting: true
|
|
125
|
+
allowOverwriting: true,
|
|
126
|
+
cidVersion: cid.version
|
|
122
127
|
})
|
|
123
128
|
|
|
124
129
|
cid = result.cid
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { CID, Version } from 'multiformats/cid'
|
|
2
2
|
import type { Blockstore } from 'interface-blockstore'
|
|
3
3
|
import type { AbortOptions } from '@libp2p/interfaces'
|
|
4
|
-
import type { ImportCandidate, ImportResult, UserImporterOptions } from 'ipfs-unixfs-importer'
|
|
5
|
-
import { add, addStream } from './commands/add.js'
|
|
6
4
|
import { cat } from './commands/cat.js'
|
|
7
5
|
import { mkdir } from './commands/mkdir.js'
|
|
8
6
|
import type { Mtime } from 'ipfs-unixfs'
|
|
@@ -27,10 +25,12 @@ export interface CatOptions extends AbortOptions {
|
|
|
27
25
|
export interface ChmodOptions extends AbortOptions {
|
|
28
26
|
recursive: boolean
|
|
29
27
|
path?: string
|
|
28
|
+
shardSplitThresholdBytes: number
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
export interface CpOptions extends AbortOptions {
|
|
33
32
|
force: boolean
|
|
33
|
+
shardSplitThresholdBytes: number
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface LsOptions extends AbortOptions {
|
|
@@ -44,10 +44,11 @@ export interface MkdirOptions extends AbortOptions {
|
|
|
44
44
|
force: boolean
|
|
45
45
|
mode?: number
|
|
46
46
|
mtime?: Mtime
|
|
47
|
+
shardSplitThresholdBytes: number
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export interface RmOptions extends AbortOptions {
|
|
50
|
-
|
|
51
|
+
shardSplitThresholdBytes: number
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export interface StatOptions extends AbortOptions {
|
|
@@ -73,22 +74,22 @@ export interface UnixFSStats {
|
|
|
73
74
|
/**
|
|
74
75
|
* The size of the file in bytes
|
|
75
76
|
*/
|
|
76
|
-
fileSize:
|
|
77
|
+
fileSize: bigint
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* The size of the DAG that holds the file in bytes
|
|
80
81
|
*/
|
|
81
|
-
dagSize:
|
|
82
|
+
dagSize: bigint
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
85
|
* How much of the file is in the local block store
|
|
85
86
|
*/
|
|
86
|
-
localFileSize:
|
|
87
|
+
localFileSize: bigint
|
|
87
88
|
|
|
88
89
|
/**
|
|
89
90
|
* How much of the DAG that holds the file is in the local blockstore
|
|
90
91
|
*/
|
|
91
|
-
localDagSize:
|
|
92
|
+
localDagSize: bigint
|
|
92
93
|
|
|
93
94
|
/**
|
|
94
95
|
* How many blocks make up the DAG - nb. this will only be accurate
|
|
@@ -100,17 +101,22 @@ export interface UnixFSStats {
|
|
|
100
101
|
* The type of file
|
|
101
102
|
*/
|
|
102
103
|
type: 'file' | 'directory' | 'raw'
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* UnixFS metadata about this file or directory. Will not be present
|
|
107
|
+
* if the node is a `raw` type.
|
|
108
|
+
*/
|
|
109
|
+
unixfs?: import('ipfs-unixfs').UnixFS
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
export interface TouchOptions extends AbortOptions {
|
|
106
113
|
mtime?: Mtime
|
|
107
114
|
path?: string
|
|
108
115
|
recursive: boolean
|
|
116
|
+
shardSplitThresholdBytes: number
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
export interface UnixFS {
|
|
112
|
-
add: (source: Uint8Array | Iterator<Uint8Array> | AsyncIterator<Uint8Array> | ImportCandidate, options?: Partial<UserImporterOptions>) => Promise<CID>
|
|
113
|
-
addStream: (source: Iterable<ImportCandidate> | AsyncIterable<ImportCandidate>, options?: Partial<UserImporterOptions>) => AsyncGenerator<ImportResult>
|
|
114
120
|
cat: (cid: CID, options?: Partial<CatOptions>) => AsyncIterable<Uint8Array>
|
|
115
121
|
chmod: (source: CID, mode: number, options?: Partial<ChmodOptions>) => Promise<CID>
|
|
116
122
|
cp: (source: CID, target: CID, name: string, options?: Partial<CpOptions>) => Promise<CID>
|
|
@@ -128,14 +134,6 @@ class DefaultUnixFS implements UnixFS {
|
|
|
128
134
|
this.components = components
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
async add (source: Uint8Array | Iterator<Uint8Array> | AsyncIterator<Uint8Array> | ImportCandidate, options: Partial<UserImporterOptions> = {}): Promise<CID> {
|
|
132
|
-
return await add(source, this.components.blockstore, options)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async * addStream (source: Iterable<ImportCandidate> | AsyncIterable<ImportCandidate>, options: Partial<UserImporterOptions> = {}): AsyncGenerator<ImportResult> {
|
|
136
|
-
yield * addStream(source, this.components.blockstore, options)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
137
|
async * cat (cid: CID, options: Partial<CatOptions> = {}): AsyncIterable<Uint8Array> {
|
|
140
138
|
yield * cat(cid, this.components.blockstore, options)
|
|
141
139
|
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { Blockstore } from 'interface-blockstore';
|
|
2
|
-
import { ImportCandidate, ImportResult, UserImporterOptions } from 'ipfs-unixfs-importer';
|
|
3
|
-
import type { CID } from 'multiformats/cid';
|
|
4
|
-
export declare function add(source: Uint8Array | Iterator<Uint8Array> | AsyncIterator<Uint8Array> | ImportCandidate, blockstore: Blockstore, options?: UserImporterOptions): Promise<CID>;
|
|
5
|
-
export declare function addStream(source: Iterable<ImportCandidate> | AsyncIterable<ImportCandidate>, blockstore: Blockstore, options?: UserImporterOptions): AsyncGenerator<ImportResult>;
|
|
6
|
-
//# sourceMappingURL=add.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAY,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAEnG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAW3C,wBAAsB,GAAG,CAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,GAAG,CAAC,CAuB3L;AAED,wBAAwB,SAAS,CAAE,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC,eAAe,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,mBAAwB,GAAG,cAAc,CAAC,YAAY,CAAC,CAM9L"}
|
package/dist/src/commands/add.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { importer } from 'ipfs-unixfs-importer';
|
|
2
|
-
import last from 'it-last';
|
|
3
|
-
import { UnknownError } from './utils/errors.js';
|
|
4
|
-
function isIterable(obj) {
|
|
5
|
-
return obj[Symbol.iterator] != null;
|
|
6
|
-
}
|
|
7
|
-
function isAsyncIterable(obj) {
|
|
8
|
-
return obj[Symbol.asyncIterator] != null;
|
|
9
|
-
}
|
|
10
|
-
export async function add(source, blockstore, options = {}) {
|
|
11
|
-
let importCandidate;
|
|
12
|
-
if (source instanceof Uint8Array || isIterable(source) || isAsyncIterable(source)) {
|
|
13
|
-
importCandidate = {
|
|
14
|
-
// @ts-expect-error FIXME: work out types
|
|
15
|
-
content: source
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
importCandidate = source;
|
|
20
|
-
}
|
|
21
|
-
const result = await last(importer(importCandidate, blockstore, {
|
|
22
|
-
cidVersion: 1,
|
|
23
|
-
rawLeaves: true,
|
|
24
|
-
...options
|
|
25
|
-
}));
|
|
26
|
-
if (result == null) {
|
|
27
|
-
throw new UnknownError('Could not import');
|
|
28
|
-
}
|
|
29
|
-
return result.cid;
|
|
30
|
-
}
|
|
31
|
-
export async function* addStream(source, blockstore, options = {}) {
|
|
32
|
-
yield* importer(source, blockstore, {
|
|
33
|
-
cidVersion: 1,
|
|
34
|
-
rawLeaves: true,
|
|
35
|
-
...options
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=add.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/commands/add.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,QAAQ,EAAqC,MAAM,sBAAsB,CAAA;AACnG,OAAO,IAAI,MAAM,SAAS,CAAA;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,SAAS,UAAU,CAAE,GAAQ;IAC3B,OAAO,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAA;AACrC,CAAC;AAED,SAAS,eAAe,CAAE,GAAQ;IAChC,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,IAAI,CAAA;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAE,MAAuF,EAAE,UAAsB,EAAE,UAA+B,EAAE;IAC3K,IAAI,eAAgC,CAAA;IAEpC,IAAI,MAAM,YAAY,UAAU,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE;QACjF,eAAe,GAAG;YAChB,yCAAyC;YACzC,OAAO,EAAE,MAAM;SAChB,CAAA;KACF;SAAM;QACL,eAAe,GAAG,MAAM,CAAA;KACzB;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,UAAU,EAAE;QAC9D,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,IAAI;QACf,GAAG,OAAO;KACX,CAAC,CAAC,CAAA;IAEH,IAAI,MAAM,IAAI,IAAI,EAAE;QAClB,MAAM,IAAI,YAAY,CAAC,kBAAkB,CAAC,CAAA;KAC3C;IAED,OAAO,MAAM,CAAC,GAAG,CAAA;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,SAAU,CAAC,CAAC,SAAS,CAAE,MAAkE,EAAE,UAAsB,EAAE,UAA+B,EAAE;IAC9J,KAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE;QACnC,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,IAAI;QACf,GAAG,OAAO;KACX,CAAC,CAAA;AACJ,CAAC"}
|