@helia/unixfs 1.2.4 → 1.4.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 (35) hide show
  1. package/dist/index.min.js +3 -1
  2. package/dist/src/commands/chmod.js +1 -1
  3. package/dist/src/commands/chmod.js.map +1 -1
  4. package/dist/src/commands/touch.js +1 -1
  5. package/dist/src/commands/touch.js.map +1 -1
  6. package/dist/src/commands/utils/add-link.js +2 -2
  7. package/dist/src/commands/utils/add-link.js.map +1 -1
  8. package/dist/src/commands/utils/is-over-shard-threshold.d.ts +2 -1
  9. package/dist/src/commands/utils/is-over-shard-threshold.d.ts.map +1 -1
  10. package/dist/src/commands/utils/is-over-shard-threshold.js +5 -5
  11. package/dist/src/commands/utils/is-over-shard-threshold.js.map +1 -1
  12. package/dist/src/commands/utils/remove-link.js +1 -1
  13. package/dist/src/commands/utils/remove-link.js.map +1 -1
  14. package/dist/src/index.d.ts +54 -0
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +14 -0
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/utils/glob-source.d.ts +39 -0
  19. package/dist/src/utils/glob-source.d.ts.map +1 -0
  20. package/dist/src/utils/glob-source.js +42 -0
  21. package/dist/src/utils/glob-source.js.map +1 -0
  22. package/dist/src/utils/url-source.d.ts +3 -0
  23. package/dist/src/utils/url-source.d.ts.map +1 -0
  24. package/dist/src/utils/url-source.js +29 -0
  25. package/dist/src/utils/url-source.js.map +1 -0
  26. package/dist/typedoc-urls.json +4 -1
  27. package/package.json +12 -2
  28. package/src/commands/chmod.ts +1 -1
  29. package/src/commands/touch.ts +1 -1
  30. package/src/commands/utils/add-link.ts +2 -2
  31. package/src/commands/utils/is-over-shard-threshold.ts +6 -5
  32. package/src/commands/utils/remove-link.ts +1 -1
  33. package/src/index.ts +63 -0
  34. package/src/utils/glob-source.ts +92 -0
  35. package/src/utils/url-source.ts +35 -0
@@ -108,7 +108,7 @@ export async function touch (cid: CID, blockstore: Blocks, options: Partial<Touc
108
108
  return updatePathCids(root.cid, resolved, blockstore, opts)
109
109
  }
110
110
 
111
- const block = await blockstore.get(resolved.cid)
111
+ const block = await blockstore.get(resolved.cid, options)
112
112
  let metadata: UnixFS
113
113
  let links: PBLink[] = []
114
114
 
@@ -52,12 +52,12 @@ export async function addLink (parent: Directory, child: Required<PBLink>, block
52
52
 
53
53
  const result = await addToDirectory(parent, child, blockstore, options)
54
54
 
55
- if (await isOverShardThreshold(result.node, blockstore, options.shardSplitThresholdBytes)) {
55
+ if (await isOverShardThreshold(result.node, blockstore, options.shardSplitThresholdBytes, options)) {
56
56
  log('converting directory to sharded directory')
57
57
 
58
58
  const converted = await convertToShardedDirectory(result, blockstore)
59
59
  result.cid = converted.cid
60
- result.node = dagPB.decode(await blockstore.get(converted.cid))
60
+ result.node = dagPB.decode(await blockstore.get(converted.cid, options))
61
61
  }
62
62
 
63
63
  return result
@@ -3,6 +3,7 @@ import { UnixFS } from 'ipfs-unixfs'
3
3
  import { CID_V0, CID_V1 } from './dir-sharded.js'
4
4
  import type { Blocks } from '@helia/interface/blocks'
5
5
  import type { PBNode } from '@ipld/dag-pb'
6
+ import type { AbortOptions } from '@libp2p/interfaces'
6
7
 
7
8
  /**
8
9
  * Estimate node size only based on DAGLink name and CID byte lengths
@@ -10,7 +11,7 @@ import type { PBNode } from '@ipld/dag-pb'
10
11
  *
11
12
  * If the node is a hamt sharded directory the calculation is based on if it was a regular directory.
12
13
  */
13
- export async function isOverShardThreshold (node: PBNode, blockstore: Blocks, threshold: number): Promise<boolean> {
14
+ export async function isOverShardThreshold (node: PBNode, blockstore: Blocks, threshold: number, options: AbortOptions): Promise<boolean> {
14
15
  if (node.Data == null) {
15
16
  throw new Error('DagPB node had no data')
16
17
  }
@@ -21,7 +22,7 @@ export async function isOverShardThreshold (node: PBNode, blockstore: Blocks, th
21
22
  if (unixfs.type === 'directory') {
22
23
  size = estimateNodeSize(node)
23
24
  } else if (unixfs.type === 'hamt-sharded-directory') {
24
- size = await estimateShardSize(node, 0, threshold, blockstore)
25
+ size = await estimateShardSize(node, 0, threshold, blockstore, options)
25
26
  } else {
26
27
  throw new Error('Can only estimate the size of directories or shards')
27
28
  }
@@ -42,7 +43,7 @@ function estimateNodeSize (node: PBNode): number {
42
43
  return size
43
44
  }
44
45
 
45
- async function estimateShardSize (node: PBNode, current: number, max: number, blockstore: Blocks): Promise<number> {
46
+ async function estimateShardSize (node: PBNode, current: number, max: number, blockstore: Blocks, options: AbortOptions): Promise<number> {
46
47
  if (current > max) {
47
48
  return max
48
49
  }
@@ -67,10 +68,10 @@ async function estimateShardSize (node: PBNode, current: number, max: number, bl
67
68
  current += link.Hash.bytes.byteLength
68
69
 
69
70
  if (link.Hash.code === dagPb.code) {
70
- const block = await blockstore.get(link.Hash)
71
+ const block = await blockstore.get(link.Hash, options)
71
72
  const node = dagPb.decode(block)
72
73
 
73
- current += await estimateShardSize(node, current, max, blockstore)
74
+ current += await estimateShardSize(node, current, max, blockstore, options)
74
75
  }
75
76
  }
76
77
 
@@ -41,7 +41,7 @@ export async function removeLink (parent: Directory, name: string, blockstore: B
41
41
 
42
42
  const result = await removeFromShardedDirectory(parent, name, blockstore, options)
43
43
 
44
- if (!(await isOverShardThreshold(result.node, blockstore, options.shardSplitThresholdBytes))) {
44
+ if (!(await isOverShardThreshold(result.node, blockstore, options.shardSplitThresholdBytes, options))) {
45
45
  log('converting shard to flat directory %c', parent.cid)
46
46
 
47
47
  return convertToFlatDirectory(result, blockstore, options)
package/src/index.ts CHANGED
@@ -29,6 +29,18 @@
29
29
  * console.info(entry)
30
30
  * }
31
31
  * ```
32
+ *
33
+ * @example
34
+ *
35
+ * Recursively adding a directory (Node.js-compatibly environments only):
36
+ *
37
+ * ```typescript
38
+ * import { globSource } from '@helia/unixfs'
39
+ *
40
+ * for await (const entry of fs.addAll(globSource('path/to/containing/dir', 'glob-pattern'))) {
41
+ * console.info(entry)
42
+ * }
43
+ * ```
32
44
  */
33
45
 
34
46
  import { addAll, addBytes, addByteStream, addDirectory, addFile } from './commands/add.js'
@@ -80,6 +92,12 @@ export interface CatOptions extends AbortOptions, ProgressOptions<GetEvents> {
80
92
  * An optional path to allow reading files inside directories
81
93
  */
82
94
  path?: string
95
+
96
+ /**
97
+ * If true, do not perform any network operations and throw if blocks are
98
+ * missing from the local store. (default: false)
99
+ */
100
+ offline?: boolean
83
101
  }
84
102
 
85
103
  /**
@@ -102,6 +120,12 @@ export interface ChmodOptions extends AbortOptions, ProgressOptions<GetEvents |
102
120
  * smaller than this value will be regular UnixFS directories.
103
121
  */
104
122
  shardSplitThresholdBytes: number
123
+
124
+ /**
125
+ * If true, do not perform any network operations and throw if blocks are
126
+ * missing from the local store. (default: false)
127
+ */
128
+ offline?: boolean
105
129
  }
106
130
 
107
131
  /**
@@ -118,6 +142,12 @@ export interface CpOptions extends AbortOptions, ProgressOptions<GetEvents | Put
118
142
  * smaller than this value will be regular UnixFS directories.
119
143
  */
120
144
  shardSplitThresholdBytes: number
145
+
146
+ /**
147
+ * If true, do not perform any network operations and throw if blocks are
148
+ * missing from the local store. (default: false)
149
+ */
150
+ offline?: boolean
121
151
  }
122
152
 
123
153
  /**
@@ -139,6 +169,12 @@ export interface LsOptions extends AbortOptions, ProgressOptions<GetEvents> {
139
169
  * Stop reading the directory contents after this many directory entries
140
170
  */
141
171
  length?: number
172
+
173
+ /**
174
+ * If true, do not perform any network operations and throw if blocks are
175
+ * missing from the local store. (default: false)
176
+ */
177
+ offline?: boolean
142
178
  }
143
179
 
144
180
  /**
@@ -171,6 +207,12 @@ export interface MkdirOptions extends AbortOptions, ProgressOptions<GetEvents |
171
207
  * smaller than this value will be regular UnixFS directories.
172
208
  */
173
209
  shardSplitThresholdBytes: number
210
+
211
+ /**
212
+ * If true, do not perform any network operations and throw if blocks are
213
+ * missing from the local store. (default: false)
214
+ */
215
+ offline?: boolean
174
216
  }
175
217
 
176
218
  /**
@@ -182,6 +224,12 @@ export interface RmOptions extends AbortOptions, ProgressOptions<GetEvents | Put
182
224
  * smaller than this value will be regular UnixFS directories.
183
225
  */
184
226
  shardSplitThresholdBytes: number
227
+
228
+ /**
229
+ * If true, do not perform any network operations and throw if blocks are
230
+ * missing from the local store. (default: false)
231
+ */
232
+ offline?: boolean
185
233
  }
186
234
 
187
235
  /**
@@ -192,6 +240,12 @@ export interface StatOptions extends AbortOptions, ProgressOptions<GetEvents> {
192
240
  * An optional path to allow statting paths inside directories
193
241
  */
194
242
  path?: string
243
+
244
+ /**
245
+ * If true, do not perform any network operations and throw if blocks are
246
+ * missing from the local store. (default: false)
247
+ */
248
+ offline?: boolean
195
249
  }
196
250
 
197
251
  /**
@@ -275,6 +329,12 @@ export interface TouchOptions extends AbortOptions, ProgressOptions<GetEvents |
275
329
  * smaller than this value will be regular UnixFS directories.
276
330
  */
277
331
  shardSplitThresholdBytes: number
332
+
333
+ /**
334
+ * If true, do not perform any network operations and throw if blocks are
335
+ * missing from the local store. (default: false)
336
+ */
337
+ offline?: boolean
278
338
  }
279
339
 
280
340
  /**
@@ -559,3 +619,6 @@ class DefaultUnixFS implements UnixFS {
559
619
  export function unixfs (helia: { blockstore: Blocks }): UnixFS {
560
620
  return new DefaultUnixFS(helia)
561
621
  }
622
+
623
+ export { globSource } from './utils/glob-source.js'
624
+ export { urlSource } from './utils/url-source.js'
@@ -0,0 +1,92 @@
1
+ import fs from 'node:fs'
2
+ import fsp from 'node:fs/promises'
3
+ import Path from 'node:path'
4
+ import glob from 'it-glob'
5
+ import { InvalidParametersError } from '../errors.js'
6
+ import type { MtimeLike } from 'ipfs-unixfs'
7
+ import type { ImportCandidateStream } from 'ipfs-unixfs-importer'
8
+
9
+ export interface GlobSourceOptions {
10
+ /**
11
+ * Include .dot files in matched paths
12
+ */
13
+ hidden?: boolean
14
+
15
+ /**
16
+ * follow symlinks
17
+ */
18
+ followSymlinks?: boolean
19
+
20
+ /**
21
+ * Preserve mode
22
+ */
23
+ preserveMode?: boolean
24
+
25
+ /**
26
+ * Preserve mtime
27
+ */
28
+ preserveMtime?: boolean
29
+
30
+ /**
31
+ * mode to use - if preserveMode is true this will be ignored
32
+ */
33
+ mode?: number
34
+
35
+ /**
36
+ * mtime to use - if preserveMtime is true this will be ignored
37
+ */
38
+ mtime?: MtimeLike
39
+ }
40
+
41
+ export interface GlobSourceResult {
42
+ path: string
43
+ content: AsyncIterable<Uint8Array> | undefined
44
+ mode: number | undefined
45
+ mtime: MtimeLike | undefined
46
+ }
47
+
48
+ /**
49
+ * Create an async iterator that yields paths that match requested glob pattern
50
+ */
51
+ export async function * globSource (cwd: string, pattern: string, options: GlobSourceOptions = {}): ImportCandidateStream {
52
+ if (typeof pattern !== 'string') {
53
+ throw new InvalidParametersError('Pattern must be a string')
54
+ }
55
+
56
+ if (!Path.isAbsolute(cwd)) {
57
+ cwd = Path.resolve(process.cwd(), cwd)
58
+ }
59
+
60
+ const globOptions = Object.assign({}, {
61
+ nodir: false,
62
+ realpath: false,
63
+ absolute: true,
64
+ dot: Boolean(options.hidden),
65
+ follow: options.followSymlinks != null ? options.followSymlinks : true
66
+ })
67
+
68
+ for await (const p of glob(cwd, pattern, globOptions)) {
69
+ const stat = await fsp.stat(p)
70
+
71
+ let mode = options.mode
72
+
73
+ if (options.preserveMode === true) {
74
+ mode = stat.mode
75
+ }
76
+
77
+ let mtime = options.mtime
78
+
79
+ if (options.preserveMtime === true) {
80
+ mtime = stat.mtime
81
+ }
82
+
83
+ yield {
84
+ path: toPosix(p.replace(cwd, '')),
85
+ content: stat.isFile() ? fs.createReadStream(p) : undefined,
86
+ mode,
87
+ mtime
88
+ }
89
+ }
90
+ }
91
+
92
+ const toPosix = (path: string): string => path.replace(/\\/g, '/')
@@ -0,0 +1,35 @@
1
+ import { UnknownError } from '../errors.js'
2
+ import type { FileCandidate } from 'ipfs-unixfs-importer'
3
+
4
+ export function urlSource (url: URL, options?: RequestInit): FileCandidate<AsyncGenerator<Uint8Array, void, unknown>> {
5
+ return {
6
+ path: decodeURIComponent(new URL(url).pathname.split('/').pop() ?? ''),
7
+ content: readURLContent(url, options)
8
+ }
9
+ }
10
+
11
+ async function * readURLContent (url: URL, options?: RequestInit): AsyncGenerator<Uint8Array, void, unknown> {
12
+ const response = await globalThis.fetch(url, options)
13
+
14
+ if (response.body == null) {
15
+ throw new UnknownError('HTTP response did not have a body')
16
+ }
17
+
18
+ const reader = response.body.getReader()
19
+
20
+ try {
21
+ while (true) {
22
+ const { done, value } = await reader.read()
23
+
24
+ if (done) {
25
+ return
26
+ }
27
+
28
+ if (value != null) {
29
+ yield value
30
+ }
31
+ }
32
+ } finally {
33
+ reader.releaseLock()
34
+ }
35
+ }