-
Notifications
You must be signed in to change notification settings - Fork 45
/
unixfs.js
110 lines (98 loc) · 3.26 KB
/
unixfs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* eslint-env browser */
import * as UnixFS from '@ipld/unixfs'
import * as raw from 'multiformats/codecs/raw'
const SHARD_THRESHOLD = 1000 // shard directory after > 1,000 items
const queuingStrategy = UnixFS.withCapacity()
const defaultSettings = UnixFS.configure({
fileChunkEncoder: raw,
smallFileEncoder: raw
})
/**
* @param {import('./types').BlobLike} blob
* @param {UnixFS.EncoderSettings} [settings]
* @returns {ReadableStream<import('@ipld/unixfs').Block>}
*/
export function createFileEncoderStream (blob, settings = defaultSettings) {
/** @type {TransformStream<import('@ipld/unixfs').Block, import('@ipld/unixfs').Block>} */
const { readable, writable } = new TransformStream({}, queuingStrategy)
const unixfsWriter = UnixFS.createWriter({ writable, settings })
const fileBuilder = new UnixFsFileBuilder(blob)
;(async () => {
await fileBuilder.finalize(unixfsWriter)
await unixfsWriter.close()
})()
return readable
}
class UnixFsFileBuilder {
#file
/** @param {{ stream: () => ReadableStream }} file */
constructor (file) {
this.#file = file
}
/** @param {import('@ipld/unixfs').View} writer */
async finalize (writer) {
const unixfsFileWriter = UnixFS.createFileWriter(writer)
await this.#file.stream().pipeTo(
new WritableStream({
async write (chunk) {
await unixfsFileWriter.write(chunk)
}
})
)
return unixfsFileWriter.close()
}
}
class UnixFSDirectoryBuilder {
/** @type {Map<string, UnixFsFileBuilder | UnixFSDirectoryBuilder>} */
entries = new Map()
/** @param {import('@ipld/unixfs').View} writer */
async finalize (writer) {
const dirWriter = this.entries.size <= SHARD_THRESHOLD
? UnixFS.createDirectoryWriter(writer)
/* c8 ignore next */
: UnixFS.createShardedDirectoryWriter(writer)
for (const [name, entry] of this.entries) {
const link = await entry.finalize(writer)
dirWriter.set(name, link)
}
return dirWriter.close()
}
}
/**
* @param {Iterable<import('./types').FileLike>} files
* @param {UnixFS.EncoderSettings} [settings]
* @returns {ReadableStream<import('@ipld/unixfs').Block>}
*/
export function createDirectoryEncoderStream (files, settings = defaultSettings) {
const rootDir = new UnixFSDirectoryBuilder()
for (const file of files) {
const path = file.name.split('/')
if (path[0] === '' || path[0] === '.') {
path.shift()
}
let dir = rootDir
for (const [i, name] of path.entries()) {
if (i === path.length - 1) {
dir.entries.set(name, new UnixFsFileBuilder(file))
break
}
let dirBuilder = dir.entries.get(name)
if (dirBuilder == null) {
dirBuilder = new UnixFSDirectoryBuilder()
dir.entries.set(name, dirBuilder)
}
if (!(dirBuilder instanceof UnixFSDirectoryBuilder)) {
throw new Error(`"${name}" cannot be a file and a directory`)
}
dir = dirBuilder
}
}
/** @type {TransformStream<import('@ipld/unixfs').Block, import('@ipld/unixfs').Block>} */
const { readable, writable } = new TransformStream({}, queuingStrategy)
const unixfsWriter = UnixFS.createWriter({ writable, settings })
;(async () => {
await rootDir.finalize(unixfsWriter)
await unixfsWriter.close()
})()
return readable
}