@sjcrh/proteinpaint-shared 2.180.0 → 2.180.1
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.
- package/package.json +2 -2
- package/src/bulk.cnv.js +30 -30
- package/src/bulk.del.js +48 -48
- package/src/bulk.itd.js +48 -48
- package/src/bulk.js +31 -31
- package/src/bulk.snv.js +109 -72
- package/src/bulk.sv.js +78 -78
- package/src/bulk.svjson.js +33 -31
- package/src/bulk.trunc.js +53 -47
- package/src/clustering.js +27 -27
- package/src/common.js +665 -558
- package/src/compute.percentile.js +3 -1
- package/src/fetch-helpers.js +67 -42
- package/src/fileSize.js +4 -4
- package/src/filter.js +207 -179
- package/src/hash.js +8 -5
- package/src/helpers.js +17 -9
- package/src/index.js +24 -24
- package/src/mds3tk.js +14 -12
- package/src/roundValue.js +5 -4
- package/src/termdb.bins.js +151 -84
- package/src/termdb.initbinconfig.js +46 -18
- package/src/termdb.usecase.js +125 -116
- package/src/terms.js +281 -266
- package/src/tree.js +4 -4
- package/src/vcf.ann.js +9 -9
- package/src/vcf.csq.js +8 -8
- package/src/vcf.info.js +3 -3
- package/src/vcf.js +99 -74
- package/src/vcf.type.js +8 -2
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
export default function computePercentile(values, percentile, sorted = false) {
|
|
5
5
|
if (!sorted) values.sort((a, b) => a - b)
|
|
6
6
|
const index = Math.abs((percentile / 100) * values.length - 1)
|
|
7
|
-
const value = Number.isInteger(index)
|
|
7
|
+
const value = Number.isInteger(index)
|
|
8
|
+
? (values[index] + values[index + 1]) / 2
|
|
9
|
+
: values[Math.ceil(index)]
|
|
8
10
|
return value
|
|
9
11
|
}
|
package/src/fetch-helpers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { hash } from
|
|
2
|
-
import { encode } from
|
|
3
|
-
import { deepFreeze } from
|
|
1
|
+
import { hash } from "./hash.js"
|
|
2
|
+
import { encode } from "./urljson.js"
|
|
3
|
+
import { deepFreeze } from "./helpers.js"
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
6
|
ezFetch()
|
|
@@ -20,12 +20,12 @@ import { deepFreeze } from './helpers.js'
|
|
|
20
20
|
*/
|
|
21
21
|
export async function ezFetch(_url, init = {}, opts = {}) {
|
|
22
22
|
const url = opts.autoMethod ? mayAdjustRequest(_url, init) : _url
|
|
23
|
-
if (typeof init.body ===
|
|
23
|
+
if (typeof init.body === "object") init.body = JSON.stringify(init.body)
|
|
24
24
|
|
|
25
|
-
return fetch(url, init).then(async r => {
|
|
25
|
+
return fetch(url, init).then(async (r) => {
|
|
26
26
|
const response = await processResponse(r)
|
|
27
27
|
if (!r.ok) {
|
|
28
|
-
console.log(
|
|
28
|
+
console.log("ezFetch error " + r.status)
|
|
29
29
|
console.log(response)
|
|
30
30
|
throw response
|
|
31
31
|
}
|
|
@@ -34,14 +34,15 @@ export async function ezFetch(_url, init = {}, opts = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function mayAdjustRequest(url, init) {
|
|
37
|
-
const method = init.method?.toUpperCase() ||
|
|
38
|
-
if (method ==
|
|
37
|
+
const method = init.method?.toUpperCase() || "GET"
|
|
38
|
+
if (method == "POST") {
|
|
39
39
|
if (!init.headers) init.headers = {}
|
|
40
40
|
if (init.body) {
|
|
41
|
-
if (!init.headers[
|
|
42
|
-
|
|
41
|
+
if (!init.headers["content-type"])
|
|
42
|
+
init.headers["content-type"] = "application/json"
|
|
43
|
+
if (init.headers["content-type"].toLowerCase() == "application/json") {
|
|
43
44
|
// if consumer code has pre-encoded the body, parse to verify correctness
|
|
44
|
-
if (typeof init.body ==
|
|
45
|
+
if (typeof init.body == "string") init.body = JSON.parse(init.body)
|
|
45
46
|
init.body = JSON.stringify(init.body)
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -49,9 +50,9 @@ function mayAdjustRequest(url, init) {
|
|
|
49
50
|
}
|
|
50
51
|
// default to GET method per native fetch
|
|
51
52
|
if (init.body) {
|
|
52
|
-
if (typeof init.body !=
|
|
53
|
+
if (typeof init.body != "object") throw `init.body should be an object`
|
|
53
54
|
// init.body should be an object, to be converted to GET URL search parameter strings
|
|
54
|
-
if (!url.includes(
|
|
55
|
+
if (!url.includes("?")) url += "?"
|
|
55
56
|
return `${url}${encode(init.body)}`
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -68,9 +69,9 @@ export async function processResponse(r) {
|
|
|
68
69
|
// if (!r.ok) {
|
|
69
70
|
// throw new Error(`HTTP error! status: ${r.status}`)
|
|
70
71
|
// }
|
|
71
|
-
const ct = r.headers.get(
|
|
72
|
+
const ct = r.headers.get("content-type") // content type is always present
|
|
72
73
|
if (!ct) throw `missing response.header['content-type']`
|
|
73
|
-
if (ct.includes(
|
|
74
|
+
if (ct.includes("/json")) {
|
|
74
75
|
const payload = await r.json()
|
|
75
76
|
// server should use a standard HTTP response status 400+, 500+
|
|
76
77
|
// so that !r.ok will already be caught when wrapping fetch with try-catch
|
|
@@ -78,14 +79,14 @@ export async function processResponse(r) {
|
|
|
78
79
|
// if (payload.status === 'error') throw payload.message || payload
|
|
79
80
|
return payload
|
|
80
81
|
}
|
|
81
|
-
if (ct.includes(
|
|
82
|
+
if (ct.includes("/text") || ct.includes("text/")) {
|
|
82
83
|
return r.text()
|
|
83
84
|
}
|
|
84
|
-
if (ct.includes(
|
|
85
|
-
if (ct.startsWith(
|
|
85
|
+
if (ct.includes("multipart")) {
|
|
86
|
+
if (ct.startsWith("multipart/form-data")) return processFormData(r)
|
|
86
87
|
else throw `cannot handle response content-type: '${ct}'`
|
|
87
88
|
}
|
|
88
|
-
if (ct ==
|
|
89
|
+
if (ct == "application/x-ndjson-nestedkey") {
|
|
89
90
|
return processNDJSON_nestedKey(r)
|
|
90
91
|
}
|
|
91
92
|
// call blob() as catch-all
|
|
@@ -116,12 +117,12 @@ export async function processFormData(res) {
|
|
|
116
117
|
for (const [key, value] of form.entries()) {
|
|
117
118
|
if (value.type) {
|
|
118
119
|
// value is a Blob
|
|
119
|
-
data[key] = { headers: {
|
|
120
|
+
data[key] = { headers: { "content-type": value.type }, body: value }
|
|
120
121
|
} else {
|
|
121
122
|
// value is a string, assume to be application/x-jsonlines (one json encoded value per line)
|
|
122
123
|
// and convert into an array of json-decoded values
|
|
123
|
-
const body = !value ? [] : value.trim().split(
|
|
124
|
-
data[key] = { headers: {
|
|
124
|
+
const body = !value ? [] : value.trim().split("\n").map(JSON.parse)
|
|
125
|
+
data[key] = { headers: { "content-type": "application/json" }, body }
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
return data
|
|
@@ -136,7 +137,7 @@ async function processNDJSON_nestedKey(r) {
|
|
|
136
137
|
const reader = stream.getReader()
|
|
137
138
|
let rootObj = {}
|
|
138
139
|
|
|
139
|
-
let buffer =
|
|
140
|
+
let buffer = ""
|
|
140
141
|
|
|
141
142
|
while (true) {
|
|
142
143
|
const { value, done } = await reader.read()
|
|
@@ -146,7 +147,7 @@ async function processNDJSON_nestedKey(r) {
|
|
|
146
147
|
buffer += value
|
|
147
148
|
|
|
148
149
|
// 3. Split by newline
|
|
149
|
-
let parts = buffer.split(
|
|
150
|
+
let parts = buffer.split("\n")
|
|
150
151
|
|
|
151
152
|
// 4. Keep the last partial line in the buffer
|
|
152
153
|
buffer = parts.pop()
|
|
@@ -199,7 +200,7 @@ const cacheLifetime = 1000 * 60 * 5
|
|
|
199
200
|
- for server side usage, client may be `xfetch()`, `ky` or other libraries
|
|
200
201
|
*/
|
|
201
202
|
export async function memFetch(url, init, opts = {}) {
|
|
202
|
-
if (typeof init.body ===
|
|
203
|
+
if (typeof init.body === "object") init.body = JSON.stringify(init.body)
|
|
203
204
|
const dataKey = opts.q || (await getDataName(url, init))
|
|
204
205
|
const { response, exp } = dataCache.get(dataKey) || {}
|
|
205
206
|
const now = Date.now()
|
|
@@ -217,35 +218,48 @@ export async function memFetch(url, init, opts = {}) {
|
|
|
217
218
|
// IMPORTANT: do not await so that this same promise may be reused
|
|
218
219
|
// by subsequent requests with the same dataKey
|
|
219
220
|
result = opts.client
|
|
220
|
-
? opts
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
221
|
+
? opts
|
|
222
|
+
.client(url, init, Object.assign(opts, { client: undefined }))
|
|
223
|
+
.then((response) => {
|
|
224
|
+
// replace the cached promise result with the actual data,
|
|
225
|
+
// since persisting a cached promise for a long time is likely not best practice
|
|
226
|
+
dataCache.set(dataKey, {
|
|
227
|
+
response,
|
|
228
|
+
exp: Date.now() + cacheLifetime,
|
|
229
|
+
})
|
|
230
|
+
return response
|
|
231
|
+
})
|
|
226
232
|
: fetch(url, init)
|
|
227
|
-
.then(async r => {
|
|
233
|
+
.then(async (r) => {
|
|
228
234
|
const response = await processResponse(r)
|
|
229
235
|
if (!r.ok) {
|
|
230
236
|
console.trace(response)
|
|
231
237
|
throw (
|
|
232
|
-
|
|
238
|
+
"memFetch error " +
|
|
233
239
|
r.status +
|
|
234
|
-
|
|
235
|
-
(typeof response ==
|
|
240
|
+
": " +
|
|
241
|
+
(typeof response == "object"
|
|
242
|
+
? response.message || response.error
|
|
243
|
+
: response)
|
|
236
244
|
)
|
|
237
245
|
}
|
|
238
246
|
// replace the cached promise result with the actual data,
|
|
239
247
|
// since persisting a cached promise for a long time is likely not best practice
|
|
240
|
-
dataCache.set(dataKey, {
|
|
248
|
+
dataCache.set(dataKey, {
|
|
249
|
+
response: deepFreeze(response),
|
|
250
|
+
exp: Date.now() + cacheLifetime,
|
|
251
|
+
})
|
|
241
252
|
return response
|
|
242
253
|
})
|
|
243
|
-
.catch(e => {
|
|
254
|
+
.catch((e) => {
|
|
244
255
|
if (dataCache.get(dataKey)) dataCache.delete(dataKey)
|
|
245
256
|
throw e
|
|
246
257
|
})
|
|
247
258
|
|
|
248
|
-
dataCache.set(dataKey, {
|
|
259
|
+
dataCache.set(dataKey, {
|
|
260
|
+
response: result,
|
|
261
|
+
exp: Date.now() + cacheLifetime,
|
|
262
|
+
})
|
|
249
263
|
manageCacheSize(now)
|
|
250
264
|
return result
|
|
251
265
|
} catch (e) {
|
|
@@ -269,7 +283,9 @@ export function manageCacheSize(_now) {
|
|
|
269
283
|
else keyExp.push({ key, exp: result.exp })
|
|
270
284
|
}
|
|
271
285
|
if (dataCache.size > maxNumOfDataKeys) {
|
|
272
|
-
const oldestEntries = keyExp
|
|
286
|
+
const oldestEntries = keyExp
|
|
287
|
+
.sort((a, b) => a.exp - b.exp)
|
|
288
|
+
.slice(maxNumOfDataKeys)
|
|
273
289
|
for (const entry of oldestEntries) dataCache.delete(entry.key)
|
|
274
290
|
}
|
|
275
291
|
}
|
|
@@ -280,7 +296,14 @@ export function manageCacheSize(_now) {
|
|
|
280
296
|
*/
|
|
281
297
|
export async function getDataName(url, init) {
|
|
282
298
|
// IMPORTANT: must ensure dataName is unique to either public or logged-in user
|
|
283
|
-
const dataName =
|
|
299
|
+
const dataName =
|
|
300
|
+
url +
|
|
301
|
+
" | " +
|
|
302
|
+
init.method +
|
|
303
|
+
" | " +
|
|
304
|
+
init.body +
|
|
305
|
+
" | " +
|
|
306
|
+
JSON.stringify(init.headers)
|
|
284
307
|
return await hash(dataName)
|
|
285
308
|
}
|
|
286
309
|
|
|
@@ -290,9 +313,11 @@ export function clearMemFetchDataCache(opts = {}) {
|
|
|
290
313
|
dataCache.clear()
|
|
291
314
|
return
|
|
292
315
|
}
|
|
293
|
-
if (typeof opts.serverData !=
|
|
316
|
+
if (typeof opts.serverData != "object")
|
|
317
|
+
throw `opts.serverData is not an object`
|
|
294
318
|
for (const k of Object.keys(opts.serverData)) {
|
|
295
319
|
delete opts.serverData[k]
|
|
296
320
|
}
|
|
297
|
-
if (optsServerDataNames.has(opts.serverData))
|
|
321
|
+
if (optsServerDataNames.has(opts.serverData))
|
|
322
|
+
optsServerDataNames.delete(opts.serverData)
|
|
298
323
|
}
|
package/src/fileSize.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function fileSize(v) {
|
|
2
|
-
if (v > 1e9) return (v / 1e9).toFixed(2) +
|
|
3
|
-
if (v > 1e6) return (v / 1e6).toFixed(2) +
|
|
4
|
-
if (v > 1e3) return (v / 1e3).toFixed(2) +
|
|
5
|
-
return v +
|
|
2
|
+
if (v > 1e9) return (v / 1e9).toFixed(2) + " GB"
|
|
3
|
+
if (v > 1e6) return (v / 1e6).toFixed(2) + " MB"
|
|
4
|
+
if (v > 1e3) return (v / 1e3).toFixed(2) + " KB"
|
|
5
|
+
return v + " Bytes"
|
|
6
6
|
}
|