-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
119 lines (102 loc) · 3.17 KB
/
index.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
111
112
113
114
115
116
117
118
119
const collect = require('collect-stream')
const Readable = require('stream').Readable
const pump = require('pump')
module.exports = {readlink, link, resolve, encode, decode, deepResolve, deepClose}
function readlink (archive, entry, cb) {
collect(archive.createFileReadStream(entry), (err, body) => {
if (err) return cb(err, null)
var link
try {
link = decode(body)
} catch (e) {
return cb(new Error('not a link'), null)
}
if (!link) return cb(new Error('not a link'), null)
cb(null, link)
})
}
function link (archive, entry, destArchiveKey, meta, cb) {
if (typeof meta === 'function') {
cb = meta
meta = undefined
}
var s = new Readable()
s.push(encode(destArchiveKey, meta))
s.push(null)
var w = archive.createFileWriteStream(entry)
pump(s, w, cb)
}
function resolve (archive, path, cb) {
var components = path.split('/')
var partialPath = []
var found = false
archive.list((err, entries) => {
if (err) return cb(err)
for (var i = 0; i < components.length; i++) {
var c = components[i]
partialPath.push(c)
if (exist(entries, partialPath)) {
readlink(archive, partialPath.join('/'), (err, link) => {
if (err && err.message === 'not a link') {
if (i === components.length - 1) {
// found the file
return cb(null, {}, partialPath.join('/'))
}
return cb(new Error(`unresolvable at ${partialPath.join('/')}`))
}
if (err) return cb(err)
return cb(null, link, components.slice(i + 1).join('/'))
})
found = true
break
}
}
if (!found) {
cb(new Error(`unresolvable path ${path}`))
}
})
function exist (entries, partialPath) {
if (entries.find(x => x.name === partialPath.join('/'))) return true
return false
}
}
function deepResolve (drive, swarmer, archive, path, cb) {
_resolve(archive, path, cb)
function _resolve (archive, path, cb) {
var swarm = swarmer(archive)
resolve(archive, path, (err, linkInfo, nextPath) => {
// must return result: swarm need to be closed
var result = {archive, path, swarm}
if (err) {
return cb(err, result)
}
if (nextPath === path) return cb(null, result)
var nextArchive = drive.createArchive(linkInfo.link, {sparse: true})
_resolve(nextArchive, nextPath, (err, resolved) => {
cb(err, {
archive,
swarm,
path: path.replace(new RegExp(`\/${nextPath.replace('/', '\/')}$`), ''), // eslint-disable-line no-useless-escape
next: resolved
})
})
})
}
}
function deepClose (deepResolveResult) {
if (!(deepResolveResult && deepResolveResult.swarm)) return
deepResolveResult.swarm.close()
deepClose(deepResolveResult.next)
}
function encode (destKey, meta) {
if (destKey instanceof Buffer) return encode(destKey.toString('hex'), meta)
var body = {'$$hdln$$': destKey}
if (meta) body.meta = meta
return JSON.stringify(body)
}
function decode (b) {
var data = JSON.parse(b)
var link = JSON.parse(b)['$$hdln$$']
if (!link) throw new Error('not a link')
return {link: link, meta: data.meta}
}