git ssb

16+

cel / patchfoo



Tree: d185716fb11842271c37ff499725b2b66ba2a5e3

Files: d185716fb11842271c37ff499725b2b66ba2a5e3 / lib / util.js

8838 bytesRaw
1var pull = require('pull-stream')
2var cat = require('pull-cat')
3var h = require('hyperscript')
4var b64url = require('base64-url')
5var u = exports
6
7u.ssbRefRegex = /((?:@|%|&|ssb:\/\/%)[A-Za-z0-9\/+]{43}=\.[\w\d]+)/g
8u.ssbRefRegexOnly = /^(?:@|%|&|ssb:\/\/%)[A-Za-z0-9\/+]{43}=\.[\w\d]+$/
9u.ssbRefEncRegex = /((?:ssb:\/\/)?(?:[@%&]|%26|%40|%25)(?:[A-Za-z0-9\/+]|%2[fF]|%2[bB]){43}(?:=|%3[dD])\.[\w\d]+)/g
10u.symbolRegex = /([^\u0000-\u00ff]+)/g
11u.channelRegex = /^#[^\s#]+$/
12u.ssbURIRegex = /^(ssb:(message|blob|feed)([:\/])([^:?]*?)([:/])([^?]*)(\\?.*)?)$/
13u.gitSsbRepoIdRegex = /^ssb:\/\/(%[A-Za-z0-9\/+]{43}=\.sha256)(\?.*)?$/
14
15u.isRef = function (str) {
16 if (!str) return false
17 u.ssbRefRegexOnly.lastIndex = 0
18 return u.ssbRefRegexOnly.test(str)
19}
20
21u.isChannel = function (value) {
22 return typeof value === 'string'
23 && value.length < 30
24 && u.channelRegex.test(value)
25}
26
27u.readNext = function (fn) {
28 var next
29 return function (end, cb) {
30 if (next) return next(end, cb)
31 fn(function (err, _next) {
32 if (err) return cb(err)
33 next = _next
34 next(null, cb)
35 })
36 }
37}
38
39u.pullReverse = function () {
40 return function (read) {
41 return u.readNext(function (cb) {
42 pull(read, pull.collect(function (err, items) {
43 cb(err, items && pull.values(items.reverse()))
44 }))
45 })
46 }
47}
48
49u.toHTML = function (el) {
50 if (!el) return ''
51 if (typeof el === 'string' || Array.isArray(el)) {
52 return h('div', el).innerHTML
53 }
54 var html = el.outerHTML || String(el)
55 if (el.nodeName === 'html') html = '<!doctype html>' + html + '\n'
56 return html
57}
58
59u.hyperwrap = function (fn) {
60 var token = '__HYPERWRAP_' + Math.random() + '__'
61 return function (read) {
62 return u.readNext(function (cb) {
63 fn(token, function (err, el) {
64 if (err) return cb(err)
65 var parts = u.toHTML(el).split(token)
66 switch (parts.length) {
67 case 0: return cb(null, pull.empty())
68 case 1: return cb(null, pull.once(parts[0]))
69 case 2: return cb(null,
70 cat([pull.once(parts[0]), read, pull.once(parts[1])]))
71 default: return cb(new Error('duplicate wrap'))
72 }
73 })
74 })
75 }
76}
77
78u.ifString = function (str) {
79 if (typeof str === 'string') return str
80}
81
82u.ifNumber = function (num) {
83 if (!isNaN(num)) return num
84}
85
86u.toLink = function (link) {
87 return typeof link === 'string' ? {link: link} : link || null
88}
89
90u.linkDest = function (link) {
91 return link && (u.ifString(link) || u.ifString(link.link))
92}
93
94u.toArray = function (x) {
95 return x == null ? [] : Array.isArray(x) ? x : [x]
96}
97
98u.fromArray = function (arr) {
99 return Array.isArray(arr) && arr.length === 1 ? arr[0] : arr
100}
101
102u.toLinkArray = function (x) {
103 return u.toArray(x).map(u.toLink).filter(u.linkDest)
104}
105
106u.toString = function (str) {
107 switch (typeof str) {
108 case 'string': return str
109 case 'object':
110 if (str !== null) return JSON.stringify(str)
111 default:
112 return String(str)
113 }
114}
115
116u.renderError = function(err) {
117 return h('div.error',
118 h('h3', err.name),
119 h('pre', err.stack))
120}
121
122u.pullLength = function (cb) {
123 var len = 0
124 return pull.through(function (data) {
125 len += data.length
126 }, function (err) {
127 cb(err, len)
128 })
129}
130
131u.tryDecodeJSON = function (json) {
132 try {
133 return JSON.parse(json)
134 } catch(e) {
135 return null
136 }
137}
138
139u.extractRefs = function (str) {
140 var ids = []
141 String(str).replace(u.ssbRefRegex, function (id) {
142 ids.push(id)
143 })
144 return ids
145}
146
147u.extractFeedIds = function (str) {
148 return u.extractRefs(str).filter(function (ref) {
149 return ref[0] === '@'
150 })
151}
152
153u.extractBlobIds = function (str) {
154 return u.extractRefs(str).filter(function (ref) {
155 return ref[0] === '&'
156 })
157}
158
159u.isMsgReadable = function (msg) {
160 var c = msg && msg.value && msg.value.content
161 return typeof c === 'object' && c !== null
162}
163
164u.isMsgEncrypted = function (msg) {
165 var value = msg && msg.value
166 return value && (value.private || typeof value.content === 'string')
167}
168
169u.pullConcat = function (cb) {
170 return pull.collect(function (err, bufs) {
171 if (err) return cb(err)
172 cb(null, Buffer.concat(bufs))
173 })
174}
175
176u.customError = function (name) {
177 return function (message) {
178 var error = new Error(message)
179 error.name = name
180 error.stack = error.stack.replace(/^ at .*\n/m, '')
181 return error
182 }
183}
184
185u.escapeHTML = function (html) {
186 if (!html) return ''
187 return html.toString('utf8')
188 .replace(/&/g, '&amp;')
189 .replace(/"/g, '&quot;')
190 .replace(/</g, '&lt;')
191 .replace(/>/g, '&gt;')
192 .replace(/\n/g, '&#x0a;')
193}
194
195u.unescapeHTML = function (text) {
196 if (typeof text !== 'string') return text
197 return text.replace(/&amp;/g, '&')
198 .replace(/&quot;/g, '"')
199 .replace(/&#39;/g, '\'')
200 .replace(/&gt;/g, '>')
201 .replace(/&lt;/g, '<')
202}
203
204u.pullSlice = function (start, end) {
205 if (end == null) end = Infinity
206 var offset = 0
207 return function (read) {
208 return function (abort, cb) {
209 if (abort) read(abort, cb)
210 else if (offset >= end) read(true, function (err) {
211 cb(err || true)
212 })
213 else if (offset < start) read(null, function next(err, data) {
214 if (err) return cb(err)
215 offset += data.length
216 if (offset <= start) read(null, next)
217 else if (offset < end) cb(null,
218 data.slice(data.length - (offset - start)))
219 else cb(null, data.slice(data.length - (offset - start),
220 data.length - (offset - end)))
221 })
222 else read(null, function (err, data) {
223 if (err) return cb(err)
224 offset += data.length
225 if (offset <= end) cb(null, data)
226 else cb(null, data.slice(0, data.length - (offset - end)))
227 })
228 }
229 }
230}
231
232u.unboxSize = function (size) {
233 // Transform size of boxed blob into size of cleartext.
234 // Assume that the blob contains box-stream packets that are all full 4096-byte packets except maybe the last one, plus one goodbye packet
235 // boxedlen = origlen + ceil(origlen / 4096)*34 + 34
236 const numBoxHeaders = Math.ceil((size - 34) / 4130) + 1
237 return size - numBoxHeaders * 34
238}
239
240u.mergeOpts = function (a, b) {
241 if (typeof b !== 'object' || b === null) return b
242 if (typeof a !== 'object' || a === null) return a
243 var obj = {}, k
244 for (k in a) obj[k] = k in b ? u.mergeOpts(a[k], b[k]) : a[k]
245 for (k in b) if (!(k in a) && b[k] != null) obj[k] = b[k]
246 return obj
247}
248
249u.escapeId = function (id) {
250 return b64url.escape(id)
251}
252
253u.unescapeId = function (str) {
254 var m = /^(.)(.*)(\..*)$/.exec(str)
255 if (!m) return b64url.unescape(str)
256 return m[1] + b64url.unescape(m[2]) + m[3]
257}
258
259u.rows = function (str) {
260 return String(str).split(/[^\n]{150}|\n/).length
261}
262
263u.token = function () {
264 return '__' + Math.random().toString(36).substr(2) + '__'
265}
266
267u.unwrapP = function(html) {
268 return String(html).replace(/^<p>(.*)<\/p>\s*$/, function ($0, $1) {
269 return $1
270 })
271}
272
273u.estimateMessageSize = function (content) {
274 var draftValue = {
275 previous: '%0000000000000000000000000000000000000000000=.sha256',
276 author: '@0000000000000000000000000000000000000000000=.ed25519',
277 sequence: 10000,
278 timestamp: 1000000000000,
279 hash: 'sha256',
280 content: content,
281 signature: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000==.sig.ed25519'
282 }
283
284 return JSON.stringify(draftValue, null, 2).length
285}
286
287u.truncate = function (str, len) {
288 str = String(str)
289 return str.length > len ? str.substr(0, len) + '...' : str
290}
291
292u.extractHostPort = function (id, addr) {
293 var i = addr.lastIndexOf(':' + id)
294 if (i > -1 && addr.length === i + id.length + 1) {
295 return addr.substr(0, i)
296 }
297 var m = /^(?:net|onion):([^~;]*(?::[0-9]*)?)~shs:([0-9a-zA-Z/=+]{44})$/.exec(addr)
298 if (m && id === '@' + m[2] + '.ed25519') {
299 return m[1]
300 }
301 return addr
302}
303
304u.translateFromGitSsbRepoId = function (url) {
305 var m = u.gitSsbRepoIdRegex.exec(url)
306 if (!m) return
307 var id = m[1]
308 var qs = m[2] || ''
309 return id + qs
310}
311
312u.translateFromURI = function (url) {
313 var m = u.ssbURIRegex.exec(url)
314 if (!m) return u.translateFromGitSsbRepoId(url)
315 var uri = m[1]
316 var prefix = m[2]
317 var separator = m[3]
318 var format = m[4]
319 var separator2 = m[5]
320 var hash = m[6]
321 var qs = m[7] || ''
322 if (separator !== separator2) return
323 hash = b64url.unescape(hash)
324 if (separator === '/') {
325 // ssb-custom-uri
326 try { hash = decodeURIComponent(hash) }
327 catch(e) {}
328 }
329 var sigil
330 switch (prefix) {
331 case 'message': sigil = '%'; break
332 case 'blob': sigil = '&'; break
333 case 'feed': sigil = '@'; break
334 default: return
335 }
336 return sigil + hash + '.' + format + qs
337}
338
339u.isNotFoundError = function (err) {
340 return err && err.name == 'NotFoundError'
341 || (typeof err.message === 'string'
342 && err.message.startsWith('Key not found in database'))
343}
344

Built with git-ssb-web