@helia/unixfs 0.0.0 → 1.0.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.
Files changed (110) hide show
  1. package/README.md +10 -4
  2. package/dist/index.min.js +1 -1
  3. package/dist/src/commands/cat.d.ts +1 -1
  4. package/dist/src/commands/cat.d.ts.map +1 -1
  5. package/dist/src/commands/cat.js +1 -1
  6. package/dist/src/commands/cat.js.map +1 -1
  7. package/dist/src/commands/chmod.d.ts.map +1 -1
  8. package/dist/src/commands/chmod.js +12 -7
  9. package/dist/src/commands/chmod.js.map +1 -1
  10. package/dist/src/commands/cp.d.ts +1 -1
  11. package/dist/src/commands/cp.d.ts.map +1 -1
  12. package/dist/src/commands/cp.js +5 -2
  13. package/dist/src/commands/cp.js.map +1 -1
  14. package/dist/src/commands/ls.d.ts +2 -1
  15. package/dist/src/commands/ls.d.ts.map +1 -1
  16. package/dist/src/commands/ls.js +1 -1
  17. package/dist/src/commands/ls.js.map +1 -1
  18. package/dist/src/commands/mkdir.d.ts.map +1 -1
  19. package/dist/src/commands/mkdir.js +4 -2
  20. package/dist/src/commands/mkdir.js.map +1 -1
  21. package/dist/src/commands/rm.d.ts +1 -1
  22. package/dist/src/commands/rm.d.ts.map +1 -1
  23. package/dist/src/commands/rm.js +9 -3
  24. package/dist/src/commands/rm.js.map +1 -1
  25. package/dist/src/commands/stat.d.ts +1 -1
  26. package/dist/src/commands/stat.d.ts.map +1 -1
  27. package/dist/src/commands/stat.js +19 -15
  28. package/dist/src/commands/stat.js.map +1 -1
  29. package/dist/src/commands/touch.d.ts.map +1 -1
  30. package/dist/src/commands/touch.js +13 -8
  31. package/dist/src/commands/touch.js.map +1 -1
  32. package/dist/src/commands/utils/add-link.d.ts +4 -2
  33. package/dist/src/commands/utils/add-link.d.ts.map +1 -1
  34. package/dist/src/commands/utils/add-link.js +24 -24
  35. package/dist/src/commands/utils/add-link.js.map +1 -1
  36. package/dist/src/commands/utils/cid-to-directory.d.ts +3 -3
  37. package/dist/src/commands/utils/cid-to-directory.d.ts.map +1 -1
  38. package/dist/src/commands/utils/cid-to-directory.js +1 -1
  39. package/dist/src/commands/utils/cid-to-directory.js.map +1 -1
  40. package/dist/src/commands/utils/cid-to-pblink.d.ts +3 -3
  41. package/dist/src/commands/utils/cid-to-pblink.d.ts.map +1 -1
  42. package/dist/src/commands/utils/cid-to-pblink.js.map +1 -1
  43. package/dist/src/commands/utils/constants.d.ts +2 -0
  44. package/dist/src/commands/utils/constants.d.ts.map +1 -0
  45. package/dist/src/commands/utils/constants.js +2 -0
  46. package/dist/src/commands/utils/constants.js.map +1 -0
  47. package/dist/src/commands/utils/dir-sharded.d.ts +47 -41
  48. package/dist/src/commands/utils/dir-sharded.d.ts.map +1 -1
  49. package/dist/src/commands/utils/dir-sharded.js +99 -15
  50. package/dist/src/commands/utils/dir-sharded.js.map +1 -1
  51. package/dist/src/commands/utils/errors.d.ts +22 -6
  52. package/dist/src/commands/utils/errors.d.ts.map +1 -1
  53. package/dist/src/commands/utils/errors.js +34 -8
  54. package/dist/src/commands/utils/errors.js.map +1 -1
  55. package/dist/src/commands/utils/hamt-constants.d.ts +1 -1
  56. package/dist/src/commands/utils/hamt-constants.d.ts.map +1 -1
  57. package/dist/src/commands/utils/hamt-constants.js +1 -1
  58. package/dist/src/commands/utils/hamt-constants.js.map +1 -1
  59. package/dist/src/commands/utils/hamt-utils.d.ts +15 -12
  60. package/dist/src/commands/utils/hamt-utils.d.ts.map +1 -1
  61. package/dist/src/commands/utils/hamt-utils.js +40 -39
  62. package/dist/src/commands/utils/hamt-utils.js.map +1 -1
  63. package/dist/src/commands/utils/is-over-shard-threshold.d.ts +10 -0
  64. package/dist/src/commands/utils/is-over-shard-threshold.d.ts.map +1 -0
  65. package/dist/src/commands/utils/is-over-shard-threshold.js +62 -0
  66. package/dist/src/commands/utils/is-over-shard-threshold.js.map +1 -0
  67. package/dist/src/commands/utils/persist.d.ts +7 -6
  68. package/dist/src/commands/utils/persist.d.ts.map +1 -1
  69. package/dist/src/commands/utils/persist.js +6 -3
  70. package/dist/src/commands/utils/persist.js.map +1 -1
  71. package/dist/src/commands/utils/remove-link.d.ts +6 -2
  72. package/dist/src/commands/utils/remove-link.d.ts.map +1 -1
  73. package/dist/src/commands/utils/remove-link.js +143 -55
  74. package/dist/src/commands/utils/remove-link.js.map +1 -1
  75. package/dist/src/commands/utils/resolve.d.ts +6 -3
  76. package/dist/src/commands/utils/resolve.d.ts.map +1 -1
  77. package/dist/src/commands/utils/resolve.js +4 -4
  78. package/dist/src/commands/utils/resolve.js.map +1 -1
  79. package/dist/src/index.d.ts +14 -7
  80. package/dist/src/index.d.ts.map +1 -1
  81. package/dist/src/index.js +0 -7
  82. package/dist/src/index.js.map +1 -1
  83. package/dist/typedoc-urls.json +14 -0
  84. package/package.json +8 -7
  85. package/src/commands/cat.ts +3 -2
  86. package/src/commands/chmod.ts +12 -7
  87. package/src/commands/cp.ts +7 -4
  88. package/src/commands/ls.ts +4 -3
  89. package/src/commands/mkdir.ts +5 -3
  90. package/src/commands/rm.ts +9 -5
  91. package/src/commands/stat.ts +22 -17
  92. package/src/commands/touch.ts +14 -9
  93. package/src/commands/utils/add-link.ts +32 -31
  94. package/src/commands/utils/cid-to-directory.ts +4 -4
  95. package/src/commands/utils/cid-to-pblink.ts +3 -3
  96. package/src/commands/utils/constants.ts +2 -0
  97. package/src/commands/utils/dir-sharded.ts +162 -63
  98. package/src/commands/utils/errors.ts +42 -8
  99. package/src/commands/utils/hamt-constants.ts +1 -1
  100. package/src/commands/utils/hamt-utils.ts +59 -50
  101. package/src/commands/utils/is-over-shard-threshold.ts +78 -0
  102. package/src/commands/utils/persist.ts +13 -8
  103. package/src/commands/utils/remove-link.ts +178 -77
  104. package/src/commands/utils/resolve.ts +12 -7
  105. package/src/index.ts +15 -17
  106. package/dist/src/commands/add.d.ts +0 -6
  107. package/dist/src/commands/add.d.ts.map +0 -1
  108. package/dist/src/commands/add.js +0 -38
  109. package/dist/src/commands/add.js.map +0 -1
  110. 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, Version } from 'multiformats/cid'
2
- import * as dagPB from '@ipld/dag-pb'
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 extends AbortOptions {
8
+ export interface PersistOptions {
9
9
  codec?: BlockCodec<any, any>
10
- cidVersion?: Version
10
+ cidVersion: CIDVersion
11
+ signal?: AbortSignal
11
12
  }
12
13
 
13
- export const persist = async (buffer: Uint8Array, blockstore: Blockstore, options: PersistOptions = {}): Promise<CID> => {
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 ?? 1, dagPB.code, multihash)
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
- UpdateHamtResult
10
+ UpdateHamtDirectoryOptions
10
11
  } from './hamt-utils.js'
11
- import type { PBNode, PBLink } from '@ipld/dag-pb'
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 { InvalidParametersError } from '@helia/interface/errors'
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: AbortOptions): Promise<RemoveLinkResult> {
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(`Removing ${name} from sharded directory`)
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
- return await removeFromShardedDirectory(parent, name, blockstore, options)
48
+ return await convertToFlatDirectory(result, blockstore, options)
49
+ }
50
+
51
+ return result
38
52
  }
39
53
 
40
- log(`Removing link ${name} regular directory`)
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 hash = await sha256.digest(parentBlock)
53
- const parentCid = CID.create(parent.cid.version, dagPB.code, hash)
54
-
55
- await blockstore.put(parentCid, parentBlock, options)
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: AbortOptions): Promise<UpdateHamtResult> => {
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
- await rootBucket.del(name)
82
+ // remove file from root bucket
83
+ const rootBucket = path[path.length - 1].bucket
71
84
 
72
- const {
73
- node
74
- } = await updateShard(parent, blockstore, path, name, options)
85
+ if (rootBucket == null) {
86
+ throw new Error('Could not generate HAMT path')
87
+ }
75
88
 
76
- return await updateHamtDirectory(parent, blockstore, node.Links, rootBucket, options)
77
- }
89
+ await rootBucket.del(name)
78
90
 
79
- const updateShard = async (parent: Directory, blockstore: Blockstore, positions: Array<{ bucket: Bucket<any>, prefix: string, node?: PBNode }>, name: string, options: AbortOptions): Promise<{ node: PBNode, cid: CID, size: number }> => {
80
- const last = positions.pop()
91
+ // update all nodes in the shard path
92
+ return await updateShard(path, name, blockstore, options)
93
+ }
81
94
 
82
- if (last == null) {
83
- throw new InvalidParametersError('Could not find parent')
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('Could not find parent')
197
+ if (rootSegment == null || rootSegment.cid == null || rootSegment.node == null) {
198
+ throw new InvalidParametersError('Failed to update shard')
94
199
  }
95
200
 
96
- const link = node.Links
97
- .find(link => (link.Name ?? '').substring(0, 2) === prefix)
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
- if (link.Name === `${prefix}${name}`) {
104
- log(`Removing existing link ${link.Name}`)
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
- log(`Descending into sub-shard ${link.Name} for ${prefix}${name}`)
118
-
119
- const result = await updateShard(parent, blockstore, positions, name, options)
212
+ const rootNode: PBNode = {
213
+ Links: []
214
+ }
215
+ const dir = await exporter(parent.cid, blockstore)
120
216
 
121
- const child: Required<PBLink> = {
122
- Hash: result.cid,
123
- Tsize: result.size,
124
- Name: prefix
217
+ if (dir.type !== 'directory') {
218
+ throw new Error('Unexpected node type')
125
219
  }
126
220
 
127
- if (result.node.Links.length === 1) {
128
- log(`Removing subshard for ${prefix}`)
221
+ for await (const entry of dir.content()) {
222
+ let tsize = 0
129
223
 
130
- // convert shard back to normal dir
131
- const link = result.node.Links[0]
224
+ if (entry.node instanceof Uint8Array) {
225
+ tsize = entry.node.byteLength
226
+ } else {
227
+ tsize = dagPB.encode(entry.node).length
228
+ }
132
229
 
133
- child.Name = `${prefix}${(link.Name ?? '').substring(2)}`
134
- child.Hash = link.Hash
135
- child.Tsize = link.Tsize ?? 0
230
+ rootNode.Links.push({
231
+ Hash: entry.cid,
232
+ Name: entry.name,
233
+ Tsize: tsize
234
+ })
136
235
  }
137
236
 
138
- log(`Updating shard ${prefix} with name ${child.Name}`)
139
-
140
- return await updateShardParent(parent, child, prefix, blockstore, bucket, options)
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 updateShardParent = async (parent: Directory, child: Required<PBLink>, oldName: string, blockstore: Blockstore, bucket: Bucket<any>, options: AbortOptions): Promise<UpdateHamtResult> => {
144
- // Remove existing link if it exists
145
- const parentLinks = parent.node.Links.filter((link) => {
146
- return link.Name !== oldName
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 await updateHamtDirectory(parent, blockstore, parentLinks, bucket, options)
248
+ return {
249
+ cid,
250
+ node: rootNode
251
+ }
151
252
  }
@@ -1,19 +1,19 @@
1
1
  import type { CID } from 'multiformats/cid'
2
- import { Blockstore, exporter } from 'ipfs-unixfs-exporter'
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: number
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: 0
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: AbortOptions): Promise<CID> {
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: number
77
+ fileSize: bigint
77
78
 
78
79
  /**
79
80
  * The size of the DAG that holds the file in bytes
80
81
  */
81
- dagSize: number
82
+ dagSize: bigint
82
83
 
83
84
  /**
84
85
  * How much of the file is in the local block store
85
86
  */
86
- localFileSize: number
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: number
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"}
@@ -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"}