@sjcrh/proteinpaint-shared 2.170.0 → 2.170.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-shared",
3
- "version": "2.170.0",
3
+ "version": "2.170.3",
4
4
  "description": "ProteinPaint code that is shared between server and client-side workspaces",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -15,8 +15,9 @@ import { encode } from './urljson.js'
15
15
  init{headers?, body?}
16
16
  - first two arguments are same as native fetch
17
17
  */
18
- export async function ezFetch(_url, init = {}) {
19
- const url = mayAdjustRequest(_url, init)
18
+ export async function ezFetch(_url, init = {}, opts = {}) {
19
+ const url = opts.autoMethod ? mayAdjustRequest(_url, init) : _url
20
+ if (typeof init.body === 'object') init.body = JSON.stringify(init.body)
20
21
 
21
22
  return fetch(url, init).then(async r => {
22
23
  const response = await processResponse(r)
@@ -61,10 +62,18 @@ also the caller should just call dofetch2() without a serverData{}
61
62
  rather than dofetch3
62
63
  */
63
64
  export async function processResponse(r) {
65
+ // if (!r.ok) {
66
+ // throw new Error(`HTTP error! status: ${r.status}`)
67
+ // }
64
68
  const ct = r.headers.get('content-type') // content type is always present
65
69
  if (!ct) throw `missing response.header['content-type']`
66
70
  if (ct.includes('/json')) {
67
- return r.json()
71
+ const payload = await r.json()
72
+ // server should use a standard HTTP response status 400+, 500+
73
+ // so that !r.ok will already be caught when wrapping fetch with try-catch
74
+ // if (payload.error || payload.status == '') throw payload
75
+ // if (payload.status === 'error') throw payload.message || payload
76
+ return payload
68
77
  }
69
78
  if (ct.includes('/text') || ct.includes('text/')) {
70
79
  return r.text()
@@ -73,6 +82,9 @@ export async function processResponse(r) {
73
82
  if (ct.startsWith('multipart/form-data')) return processFormData(r)
74
83
  else throw `cannot handle response content-type: '${ct}'`
75
84
  }
85
+ if (ct == 'application/x-ndjson-nestedkey') {
86
+ return processNDJSON_nestedKey(r)
87
+ }
76
88
  // call blob() as catch-all
77
89
  // https://developer.mozilla.org/en-US/docs/Web/API/Response
78
90
  return r.blob()
@@ -115,6 +127,44 @@ export async function processFormData(res) {
115
127
  }
116
128
  }
117
129
 
130
+ async function processNDJSON_nestedKey(r) {
131
+ // 1. Pipe through TextDecoder to convert bytes to text
132
+ const stream = r.body.pipeThrough(new TextDecoderStream())
133
+ const reader = stream.getReader()
134
+ let rootObj = {}
135
+
136
+ let buffer = ''
137
+
138
+ while (true) {
139
+ const { value, done } = await reader.read()
140
+ if (done) break
141
+
142
+ // 2. Add new chunk to buffer
143
+ buffer += value
144
+
145
+ // 3. Split by newline
146
+ let parts = buffer.split('\n')
147
+
148
+ // 4. Keep the last partial line in the buffer
149
+ buffer = parts.pop()
150
+
151
+ // 5. Process complete lines
152
+ for (const line of parts) {
153
+ if (line.trim()) {
154
+ const [keys, data] = JSON.parse(line) //; console.log(143, keys, data) // Process JSON data
155
+ if (!keys.length) rootObj = data
156
+ else {
157
+ const lastKey = keys.pop()
158
+ let target = rootObj
159
+ for (const k of keys) target = target[k]
160
+ target[lastKey] = data
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return rootObj
166
+ }
167
+
118
168
  // key: request object reference or conputed string dataName
119
169
  // value: fetch promise or response
120
170
  const dataCache = new Map()
@@ -145,6 +195,7 @@ const maxNumOfDataKeys = 360
145
195
  - if not provided, then a string cache data key will be computed from the request url, body, headers
146
196
  */
147
197
  export async function memFetch(url, init, opts = {}) {
198
+ if (typeof init.body === 'object') init.body = JSON.stringify(init.body)
148
199
  const dataKey = opts.q || (await getDataName(url, init))
149
200
  let result = dataCache.get(dataKey)
150
201
 
@@ -267,6 +267,7 @@ export function get_bin_label(bin, binconfig, valueConversion) {
267
267
  /*
268
268
  Generate a numeric bin label given a bin configuration and an optional term valueConversion object
269
269
  */
270
+ if (!bin) return 'missing bin.label'
270
271
  if (bin.label) return bin.label
271
272
 
272
273
  const bc = binconfig
@@ -6,6 +6,8 @@ Initialize a bin configuration for a numeric dataset
6
6
  {format: 'string'}: output bin config as JSON string
7
7
  */
8
8
  export default function initBinConfig(data, opts = {}) {
9
+ if (!data.length)
10
+ return { mode: 'discrete', type: 'regular-bin', startinclusive: true, bin_size: null, first_bin: { stop: null } }
9
11
  if (data.find(d => !Number.isFinite(d))) throw new Error('non-numeric values found')
10
12
 
11
13
  let binConfig