@sjcrh/proteinpaint-shared 2.189.0 → 2.190.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/README.md CHANGED
@@ -8,26 +8,30 @@ code. Do NOT put utility/helper code here that are specific to only one
8
8
  workspace, those files should be saved in that workspace.
9
9
 
10
10
  IMPORTANT:
11
- - code must work in browser and nodejs: do not import libs, deps, or globals that
12
- are specific to `nodejs` or `browser` environments, like `fs` or `DOM` elements
11
+ - Shared code must work in browser and nodejs: do not import libs, deps, or globals that
12
+ are specific to `nodejs` or `browser` environments, like `fs` or `DOM` elements.
13
+ - Except for contants, do not import from this workspace (aka `@sjcrh/proteinpaint-shared/`
14
+ or `#shared`) into `shared/types` as that will cause cyclical imports that break bundler
15
+ startup or `tsc` compilation.
13
16
 
14
17
  ## Develop
15
18
 
16
- It is much simpler to import *runtime* code directly from `@sjcrh/proteinpaint-shared`,
17
- or static code from `@sjcrh/proteinpaint-shared/devTs`, instead of specifying a code
18
- filename.
19
+ It is much simpler to import directly from `@sjcrh/proteinpaint-shared`. Avoid file-specific
20
+ imports unless tree-shaking performance is a concern. If a specific shared file must be imported,
21
+ prefer an alias such as `#shared/someFile.js`, which shields importers from shared code
22
+ file renames or reorganizations.
19
23
 
20
24
  For server dev, the `tsx` library will accept imports with or without file extension.
21
25
  Server (consumer) code should use `@sjcrh/proteinpaint-shared`, or if for some reason
22
26
  a shared file must be specific, it MUST use the `.js` file extension (e.g.,
23
27
  `#shared/someFile.js`).
24
28
 
25
- For client dev, the esbuild config will bundle the #shared imports correctly, even
26
- when `.js` extension is used to import what is actually a `.ts` file, by using
27
- custom plugins like dirname.
29
+ For client dev, the esbuild config will bundle the `#shared` imports correctly, even
30
+ when `.js` extension is used to import what is actually a `.ts` file.
28
31
 
29
- When importing types that are defined in `shared/utils` to `shared/types` dev,
30
- always use `@sjcrh/proteinpaint-shared/devTs` (or an alias such as `#shared/devTs`).
32
+ Do not import from `#shared/utils` to `#shared/types` - it may cause `tsc` compilation or `esbuild`
33
+ build errors. Only imports in the opposite direction, from `shared/types` to `shared/utils`, is allowed
34
+ to to ensure that there are no cyclical imports that breaks type checks or bundling.
31
35
 
32
36
  ## Build
33
37
 
@@ -4,5 +4,7 @@ export declare function processFormData(res: any): Promise<{}>;
4
4
  export declare function memFetch(url: any, init: Record<string, any>, opts?: Record<string, any>): Promise<any>;
5
5
  export declare function deleteCache(key: any): void;
6
6
  export declare function manageCacheSize(_now: any): void;
7
- export declare function getDataName(url: any, init: any): Promise<string>;
7
+ export declare function getDataName(url: string, init: {
8
+ [optKey: string]: any;
9
+ }): Promise<string>;
8
10
  export declare function clearMemFetchDataCache(opts?: Record<string, any>): void;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/fetch-helpers.ts"],
4
- "sourcesContent": ["import { hash } from './hash.js'\nimport { encode } from './urljson.js'\nimport { deepFreeze } from './helpers.js'\n\n// tracks serverData objects so their associated cache entries can be cleared; uses WeakMap to avoid memory leaks\nconst optsServerDataNames = new WeakMap()\n\n/*\n\tezFetch()\n\tfetch wrapper with automatic response content-type detection and handling\n\n\t- this addresses issues with ky.json() or got.json(), where a HTTP 404 NOT FOUND\n\t\tresponse with text/html can break error handling/logging, making it harder to debug\n\n\t- this also automatically handles multipart responses\n\n - NOTE: for backend, use xfetch() instead, it uses ky() and its built-in retry support\n\n\targuments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n*/\nexport async function ezFetch(_url, init: Record<string, any> = {}, opts: Record<string, any> = {}) {\n\tconst url = opts.autoMethod ? mayAdjustRequest(_url, init) : _url\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\n\treturn fetch(url, init).then(async r => {\n\t\tconst response = await processResponse(r)\n\t\tif (!r.ok) {\n\t\t\tconsole.log('ezFetch error ' + r.status)\n\t\t\tconsole.log(response)\n\t\t\tthrow response\n\t\t}\n\t\treturn response\n\t})\n}\n\nfunction mayAdjustRequest(url, init: Record<string, any>) {\n\tconst method = init.method?.toUpperCase() || 'GET'\n\tif (method == 'POST') {\n\t\tif (!init.headers) init.headers = {}\n\t\tif (init.body) {\n\t\t\tif (!init.headers['content-type']) init.headers['content-type'] = 'application/json'\n\t\t\tif (init.headers['content-type'].toLowerCase() == 'application/json') {\n\t\t\t\t// if consumer code has pre-encoded the body, parse to verify correctness\n\t\t\t\tif (typeof init.body == 'string') init.body = JSON.parse(init.body)\n\t\t\t\tinit.body = JSON.stringify(init.body)\n\t\t\t}\n\t\t}\n\t\treturn url\n\t}\n\t// default to GET method per native fetch\n\tif (init.body) {\n\t\tif (typeof init.body != 'object') throw `init.body should be an object`\n\t\t// init.body should be an object, to be converted to GET URL search parameter strings\n\t\tif (!url.includes('?')) url += '?'\n\t\treturn `${url}${encode(init.body)}`\n\t}\n}\n\n/* \nr: a native fetch response argument\n\npotentially allow \"application/octet-stream\" as response type\nin such case it will not try to parse it as json\nalso the caller should just call dofetch2() without a serverData{}\nrather than dofetch3\n*/\nexport async function processResponse(r) {\n\t// if (!r.ok) {\n\t// throw new Error(`HTTP error! status: ${r.status}`)\n\t// }\n\tconst ct = r.headers.get('content-type') // content type is always present\n\tif (!ct) throw `missing response.header['content-type']`\n\tif (ct.includes('/json')) {\n\t\tconst payload = await r.json()\n\t\t// server should use a standard HTTP response status 400+, 500+\n\t\t// so that !r.ok will already be caught when wrapping fetch with try-catch\n\t\t// if (payload.error || payload.status == '') throw payload\n\t\t// if (payload.status === 'error') throw payload.message || payload\n\t\treturn payload\n\t}\n\tif (ct.includes('/text') || ct.includes('text/')) {\n\t\treturn r.text()\n\t}\n\tif (ct.includes('multipart')) {\n\t\tif (ct.startsWith('multipart/form-data')) return processFormData(r)\n\t\telse throw `cannot handle response content-type: '${ct}'`\n\t}\n\tif (ct == 'application/x-ndjson-nestedkey') {\n\t\treturn processNDJSON_nestedKey(r)\n\t}\n\t// call blob() as catch-all\n\t// https://developer.mozilla.org/en-US/docs/Web/API/Response\n\treturn r.blob()\n}\n\n/*\n\texpected response format\n\t--boundary-text-from-HTTP-headers-content-type\n\theader-key1: header-value1\n\theader-key2: header-value2\n\n\t...json, text, blob, value, etc...\n\t--boundary-text-from-HTTP-headers-content-type\n\t... same format as previous chunk ...\n\n\t--boundary-text-from-HTTP-headers-content-type--\n*/\n\nexport async function processFormData(res) {\n\tconst data = {}\n\tconst form = await res.formData()\n\t// The key of each form entry is a string, and the value is either a string or a Blob.\n\t// see https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries\n\tfor (const [key, value] of form.entries()) {\n\t\tif (value.type) {\n\t\t\t// value is a Blob\n\t\t\tdata[key] = { headers: { 'content-type': value.type }, body: value }\n\t\t} else {\n\t\t\t// value is a string, assume to be application/x-jsonlines (one json encoded value per line)\n\t\t\t// and convert into an array of json-decoded values\n\t\t\tconst body = !value ? [] : value.trim().split('\\n').map(JSON.parse)\n\t\t\tdata[key] = { headers: { 'content-type': 'application/json' }, body }\n\t\t}\n\t}\n\treturn data\n}\n\nasync function processNDJSON_nestedKey(r) {\n\t// 1. Pipe through TextDecoder to convert bytes to text\n\tconst stream = r.body.pipeThrough(new TextDecoderStream())\n\tconst reader = stream.getReader()\n\tlet rootObj = {}\n\n\tlet buffer = ''\n\n\tlet done = false\n\twhile (!done) {\n\t\tconst readResult = await reader.read()\n\t\tdone = readResult.done\n\t\tif (done) break\n\t\tconst value = readResult.value\n\n\t\t// 2. Add new chunk to buffer\n\t\tbuffer += value\n\n\t\t// 3. Split by newline\n\t\tconst parts = buffer.split('\\n')\n\n\t\t// 4. Keep the last partial line in the buffer\n\t\tbuffer = parts.pop() ?? ''\n\n\t\t// 5. Process complete lines\n\t\tfor (const line of parts) {\n\t\t\tif (line.trim()) {\n\t\t\t\tconst [keys, data] = JSON.parse(line) //; console.log(143, keys, data) // Process JSON data\n\t\t\t\tif (!keys.length) rootObj = data\n\t\t\t\telse {\n\t\t\t\t\tconst lastKey = keys.pop()\n\t\t\t\t\tlet target = rootObj\n\t\t\t\t\tfor (const k of keys) target = target[k]\n\t\t\t\t\ttarget[lastKey] = data\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn rootObj\n}\n\n// key: request object reference or computed string dataName\n// value: {\n// response: fetch promise or response,\n// exp: expiration timestamp\n// }\nconst dataCache = new Map()\n// maximum number of cached dataNames, oldest will be deleted if this is exceeded\nconst maxNumOfDataKeys = 10\nconst cacheLifetime = 1000 * 60 * 5\n/*\n\tmemFetch()\n\t- fetch wrapper that saves cached responses into memory and recovers them for matching subsequent requests\n\t- recommended for caching responses in the backend, with the opts.q argument to cache per expressjs request object\n\t- should call deleteCache(request) at the end of request handling, to free unneeded cache\n\t\n\tSee the usage note for getDataName() to avoid non-unique request/response.\n\t\n\tArguments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n - when passing opts.client, may include other applicable options inside the init{} object, such as retry\n\n\topts{client}\n\n client: use this http client instead of native fetch\n - since fetch-helpers is shared between server and frontend workspaces, \n cannot directly import non-native modules at the beginning of this code file \n - for server side usage, client may be `xfetch()`, `ky` or other libraries \n*/\nexport async function memFetch(url, init: Record<string, any>, opts: Record<string, any> = {}) {\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\tconst dataKey = opts.q || (await getDataName(url, init))\n\tconst { response } = dataCache.get(dataKey) || {}\n\tconst now = Date.now()\n\tlet result = response // either a Promise or actual data\n\n\tif (result) {\n\t\t// extend the expiration, since exp is more about managing the cache size\n\t\t// and not the validity of the cached response. A response for the current\n\t\t// dataName req.url + body + headers is technically valid until a new data version\n\t\t// gets published.\n\t\tdataCache.set(dataKey, { response, exp: now + cacheLifetime })\n\t\treturn result\n\t} else {\n\t\ttry {\n\t\t\t// IMPORTANT: do not await so that this same promise may be reused\n\t\t\t// by subsequent requests with the same dataKey\n\t\t\tresult = opts.client\n\t\t\t\t? opts.client(url, init, Object.assign(opts, { client: undefined })).then(response => {\n\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\tdataCache.set(dataKey, { response, exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\treturn response\n\t\t\t\t })\n\t\t\t\t: fetch(url, init)\n\t\t\t\t\t\t.then(async r => {\n\t\t\t\t\t\t\tconst response = await processResponse(r)\n\t\t\t\t\t\t\tif (!r.ok) {\n\t\t\t\t\t\t\t\tconsole.trace(response)\n\t\t\t\t\t\t\t\tthrow (\n\t\t\t\t\t\t\t\t\t'memFetch error ' +\n\t\t\t\t\t\t\t\t\tr.status +\n\t\t\t\t\t\t\t\t\t': ' +\n\t\t\t\t\t\t\t\t\t(typeof response == 'object' ? response.message || response.error : response)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\t\tdataCache.set(dataKey, { response: deepFreeze(response), exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\t\treturn response\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(e => {\n\t\t\t\t\t\t\tif (dataCache.get(dataKey)) dataCache.delete(dataKey)\n\t\t\t\t\t\t\tthrow e\n\t\t\t\t\t\t})\n\n\t\t\tdataCache.set(dataKey, { response: result, exp: Date.now() + cacheLifetime })\n\t\t\tmanageCacheSize(now)\n\t\t\treturn result\n\t\t} catch (e) {\n\t\t\t// delete this cache only if it is a promise;\n\t\t\t// do not delete a valid resolved data cache\n\t\t\tif (dataCache.get(dataKey) instanceof Promise) dataCache.delete(dataKey)\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport function deleteCache(key) {\n\tdataCache.delete(key)\n}\n\nexport function manageCacheSize(_now) {\n\tconst now = _now || Date.now()\n\tconst keyExp: { key: any; exp: number }[] = []\n\tfor (const [key, result] of dataCache.entries()) {\n\t\tif (result.exp < now) dataCache.delete(key)\n\t\telse keyExp.push({ key, exp: result.exp })\n\t}\n\tif (dataCache.size > maxNumOfDataKeys) {\n\t\tconst oldestEntries = keyExp.sort((a, b) => a.exp - b.exp).slice(maxNumOfDataKeys)\n\t\tfor (const entry of oldestEntries) dataCache.delete(entry.key)\n\t}\n}\n\n/*\n\tNOTE: When used in client-side code, an HttpOnly cookie for a logged in user will not be\n\ttracked in init.headers below. \n*/\nexport async function getDataName(url, init) {\n\t// IMPORTANT: must ensure dataName is unique to either public or logged-in user\n\tconst dataName = url + ' | ' + init.method + ' | ' + init.body + ' | ' + JSON.stringify(init.headers)\n\treturn await hash(dataName)\n}\n\n//\nexport function clearMemFetchDataCache(opts: Record<string, any> = {}) {\n\tif (!opts.serverData) {\n\t\tdataCache.clear()\n\t\treturn\n\t}\n\tif (typeof opts.serverData != 'object') throw `opts.serverData is not an object`\n\tfor (const k of Object.keys(opts.serverData)) {\n\t\tdelete opts.serverData[k]\n\t}\n\tif (optsServerDataNames.has(opts.serverData)) optsServerDataNames.delete(opts.serverData)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { hash } from './hash.js'\nimport { encode } from './urljson.js'\nimport { deepFreeze } from './helpers.js'\n\n// tracks serverData objects so their associated cache entries can be cleared; uses WeakMap to avoid memory leaks\nconst optsServerDataNames = new WeakMap()\n\n/*\n\tezFetch()\n\tfetch wrapper with automatic response content-type detection and handling\n\n\t- this addresses issues with ky.json() or got.json(), where a HTTP 404 NOT FOUND\n\t\tresponse with text/html can break error handling/logging, making it harder to debug\n\n\t- this also automatically handles multipart responses\n\n - NOTE: for backend, use xfetch() instead, it uses ky() and its built-in retry support\n\n\targuments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n*/\nexport async function ezFetch(_url, init: Record<string, any> = {}, opts: Record<string, any> = {}) {\n\tconst url = opts.autoMethod ? mayAdjustRequest(_url, init) : _url\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\n\treturn fetch(url, init).then(async r => {\n\t\tconst response = await processResponse(r)\n\t\tif (!r.ok) {\n\t\t\tconsole.log('ezFetch error ' + r.status)\n\t\t\tconsole.log(response)\n\t\t\tthrow response\n\t\t}\n\t\treturn response\n\t})\n}\n\nfunction mayAdjustRequest(url, init: Record<string, any>) {\n\tconst method = init.method?.toUpperCase() || 'GET'\n\tif (method == 'POST') {\n\t\tif (!init.headers) init.headers = {}\n\t\tif (init.body) {\n\t\t\tif (!init.headers['content-type']) init.headers['content-type'] = 'application/json'\n\t\t\tif (init.headers['content-type'].toLowerCase() == 'application/json') {\n\t\t\t\t// if consumer code has pre-encoded the body, parse to verify correctness\n\t\t\t\tif (typeof init.body == 'string') init.body = JSON.parse(init.body)\n\t\t\t\tinit.body = JSON.stringify(init.body)\n\t\t\t}\n\t\t}\n\t\treturn url\n\t}\n\t// default to GET method per native fetch\n\tif (init.body) {\n\t\tif (typeof init.body != 'object') throw `init.body should be an object`\n\t\t// init.body should be an object, to be converted to GET URL search parameter strings\n\t\tif (!url.includes('?')) url += '?'\n\t\treturn `${url}${encode(init.body)}`\n\t}\n}\n\n/* \nr: a native fetch response argument\n\npotentially allow \"application/octet-stream\" as response type\nin such case it will not try to parse it as json\nalso the caller should just call dofetch2() without a serverData{}\nrather than dofetch3\n*/\nexport async function processResponse(r) {\n\t// if (!r.ok) {\n\t// throw new Error(`HTTP error! status: ${r.status}`)\n\t// }\n\tconst ct = r.headers.get('content-type') // content type is always present\n\tif (!ct) throw `missing response.header['content-type']`\n\tif (ct.includes('/json')) {\n\t\tconst payload = await r.json()\n\t\t// server should use a standard HTTP response status 400+, 500+\n\t\t// so that !r.ok will already be caught when wrapping fetch with try-catch\n\t\t// if (payload.error || payload.status == '') throw payload\n\t\t// if (payload.status === 'error') throw payload.message || payload\n\t\treturn payload\n\t}\n\tif (ct.includes('/text') || ct.includes('text/')) {\n\t\treturn r.text()\n\t}\n\tif (ct.includes('multipart')) {\n\t\tif (ct.startsWith('multipart/form-data')) return processFormData(r)\n\t\telse throw `cannot handle response content-type: '${ct}'`\n\t}\n\tif (ct == 'application/x-ndjson-nestedkey') {\n\t\treturn processNDJSON_nestedKey(r)\n\t}\n\t// call blob() as catch-all\n\t// https://developer.mozilla.org/en-US/docs/Web/API/Response\n\treturn r.blob()\n}\n\n/*\n\texpected response format\n\t--boundary-text-from-HTTP-headers-content-type\n\theader-key1: header-value1\n\theader-key2: header-value2\n\n\t...json, text, blob, value, etc...\n\t--boundary-text-from-HTTP-headers-content-type\n\t... same format as previous chunk ...\n\n\t--boundary-text-from-HTTP-headers-content-type--\n*/\n\nexport async function processFormData(res) {\n\tconst data = {}\n\tconst form = await res.formData()\n\t// The key of each form entry is a string, and the value is either a string or a Blob.\n\t// see https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries\n\tfor (const [key, value] of form.entries()) {\n\t\tif (value.type) {\n\t\t\t// value is a Blob\n\t\t\tdata[key] = { headers: { 'content-type': value.type }, body: value }\n\t\t} else {\n\t\t\t// value is a string, assume to be application/x-jsonlines (one json encoded value per line)\n\t\t\t// and convert into an array of json-decoded values\n\t\t\tconst body = !value ? [] : value.trim().split('\\n').map(JSON.parse)\n\t\t\tdata[key] = { headers: { 'content-type': 'application/json' }, body }\n\t\t}\n\t}\n\treturn data\n}\n\nasync function processNDJSON_nestedKey(r) {\n\t// 1. Pipe through TextDecoder to convert bytes to text\n\tconst stream = r.body.pipeThrough(new TextDecoderStream())\n\tconst reader = stream.getReader()\n\tlet rootObj = {}\n\n\tlet buffer = ''\n\n\tlet done = false\n\twhile (!done) {\n\t\tconst readResult = await reader.read()\n\t\tdone = readResult.done\n\t\tif (done) break\n\t\tconst value = readResult.value\n\n\t\t// 2. Add new chunk to buffer\n\t\tbuffer += value\n\n\t\t// 3. Split by newline\n\t\tconst parts = buffer.split('\\n')\n\n\t\t// 4. Keep the last partial line in the buffer\n\t\tbuffer = parts.pop() ?? ''\n\n\t\t// 5. Process complete lines\n\t\tfor (const line of parts) {\n\t\t\tif (line.trim()) {\n\t\t\t\tconst [keys, data] = JSON.parse(line) //; console.log(143, keys, data) // Process JSON data\n\t\t\t\tif (!keys.length) rootObj = data\n\t\t\t\telse {\n\t\t\t\t\tconst lastKey = keys.pop()\n\t\t\t\t\tlet target = rootObj\n\t\t\t\t\tfor (const k of keys) target = target[k]\n\t\t\t\t\ttarget[lastKey] = data\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn rootObj\n}\n\n// key: request object reference or computed string dataName\n// value: {\n// response: fetch promise or response,\n// exp: expiration timestamp\n// }\nconst dataCache = new Map()\n// maximum number of cached dataNames, oldest will be deleted if this is exceeded\nconst maxNumOfDataKeys = 10\nconst cacheLifetime = 1000 * 60 * 5\n/*\n\tmemFetch()\n\t- fetch wrapper that saves cached responses into memory and recovers them for matching subsequent requests\n\t- recommended for caching responses in the backend, with the opts.q argument to cache per expressjs request object\n\t- should call deleteCache(request) at the end of request handling, to free unneeded cache\n\t\n\tSee the usage note for getDataName() to avoid non-unique request/response.\n\t\n\tArguments:\n\turl\n\tinit{headers?, body?}\n\t- first two arguments are same as native fetch\n - when passing opts.client, may include other applicable options inside the init{} object, such as retry\n\n\topts{client}\n\n client: use this http client instead of native fetch\n - since fetch-helpers is shared between server and frontend workspaces, \n cannot directly import non-native modules at the beginning of this code file \n - for server side usage, client may be `xfetch()`, `ky` or other libraries \n*/\nexport async function memFetch(url, init: Record<string, any>, opts: Record<string, any> = {}) {\n\tif (typeof init.body === 'object') init.body = JSON.stringify(init.body)\n\tconst dataKey = opts.q || (await getDataName(url, init))\n\tconst { response } = dataCache.get(dataKey) || {}\n\tconst now = Date.now()\n\tlet result = response // either a Promise or actual data\n\n\tif (result) {\n\t\t// extend the expiration, since exp is more about managing the cache size\n\t\t// and not the validity of the cached response. A response for the current\n\t\t// dataName req.url + body + headers is technically valid until a new data version\n\t\t// gets published.\n\t\tdataCache.set(dataKey, { response, exp: now + cacheLifetime })\n\t\treturn result\n\t} else {\n\t\ttry {\n\t\t\t// IMPORTANT: do not await so that this same promise may be reused\n\t\t\t// by subsequent requests with the same dataKey\n\t\t\tresult = opts.client\n\t\t\t\t? opts.client(url, init, Object.assign(opts, { client: undefined })).then(response => {\n\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\tdataCache.set(dataKey, { response, exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\treturn response\n\t\t\t\t })\n\t\t\t\t: fetch(url, init)\n\t\t\t\t\t\t.then(async r => {\n\t\t\t\t\t\t\tconst response = await processResponse(r)\n\t\t\t\t\t\t\tif (!r.ok) {\n\t\t\t\t\t\t\t\tconsole.trace(response)\n\t\t\t\t\t\t\t\tthrow (\n\t\t\t\t\t\t\t\t\t'memFetch error ' +\n\t\t\t\t\t\t\t\t\tr.status +\n\t\t\t\t\t\t\t\t\t': ' +\n\t\t\t\t\t\t\t\t\t(typeof response == 'object' ? response.message || response.error : response)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// replace the cached promise result with the actual data,\n\t\t\t\t\t\t\t// since persisting a cached promise for a long time is likely not best practice\n\t\t\t\t\t\t\tdataCache.set(dataKey, { response: deepFreeze(response), exp: Date.now() + cacheLifetime })\n\t\t\t\t\t\t\treturn response\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(e => {\n\t\t\t\t\t\t\tif (dataCache.get(dataKey)) dataCache.delete(dataKey)\n\t\t\t\t\t\t\tthrow e\n\t\t\t\t\t\t})\n\n\t\t\tdataCache.set(dataKey, { response: result, exp: Date.now() + cacheLifetime })\n\t\t\tmanageCacheSize(now)\n\t\t\treturn result\n\t\t} catch (e) {\n\t\t\t// delete this cache only if it is a promise;\n\t\t\t// do not delete a valid resolved data cache\n\t\t\tif (dataCache.get(dataKey) instanceof Promise) dataCache.delete(dataKey)\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport function deleteCache(key) {\n\tdataCache.delete(key)\n}\n\nexport function manageCacheSize(_now) {\n\tconst now = _now || Date.now()\n\tconst keyExp: { key: any; exp: number }[] = []\n\tfor (const [key, result] of dataCache.entries()) {\n\t\tif (result.exp < now) dataCache.delete(key)\n\t\telse keyExp.push({ key, exp: result.exp })\n\t}\n\tif (dataCache.size > maxNumOfDataKeys) {\n\t\tconst oldestEntries = keyExp.sort((a, b) => a.exp - b.exp).slice(maxNumOfDataKeys)\n\t\tfor (const entry of oldestEntries) dataCache.delete(entry.key)\n\t}\n}\n\n/*\n\tNOTE: When used in client-side code, an HttpOnly cookie for a logged in user will not be\n\ttracked in init.headers below. \n*/\nexport async function getDataName(url: string, init: { [optKey: string]: any }) {\n\t// IMPORTANT: must ensure dataName is unique to either public or logged-in user\n\tconst dataName = url + ' | ' + init.method + ' | ' + init.body + ' | ' + JSON.stringify(init.headers)\n\treturn await hash(dataName)\n}\n\n//\nexport function clearMemFetchDataCache(opts: Record<string, any> = {}) {\n\tif (!opts.serverData) {\n\t\tdataCache.clear()\n\t\treturn\n\t}\n\tif (typeof opts.serverData != 'object') throw `opts.serverData is not an object`\n\tfor (const k of Object.keys(opts.serverData)) {\n\t\tdelete opts.serverData[k]\n\t}\n\tif (optsServerDataNames.has(opts.serverData)) optsServerDataNames.delete(opts.serverData)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["response"]
7
7
  }
@@ -1,5 +1,3 @@
1
- export * from '../constants/AiHisto.js';
2
- export * from './aiHisto.js';
3
1
  export * from './bulk.js';
4
2
  export * from './clustering.js';
5
3
  export * from './common.js';
@@ -12,6 +10,7 @@ export * from './helpers.js';
12
10
  export * from './joinUrl.js';
13
11
  export * from './mds3tk.js';
14
12
  export * from './roundValue.js';
13
+ export * from './scatter.js';
15
14
  export * from './termdb.bins.js';
16
15
  export * from './termdb.initbinconfig.js';
17
16
  export * from './termdb.usecase.js';
package/dist/src/index.js CHANGED
@@ -1,5 +1,3 @@
1
- export * from "../constants/AiHisto.js";
2
- export * from "./aiHisto.js";
3
1
  export * from "./bulk.js";
4
2
  export * from "./clustering.js";
5
3
  export * from "./common.js";
@@ -12,6 +10,7 @@ export * from "./helpers.js";
12
10
  export * from "./joinUrl.js";
13
11
  export * from "./mds3tk.js";
14
12
  export * from "./roundValue.js";
13
+ export * from "./scatter.js";
15
14
  export * from "./termdb.bins.js";
16
15
  export * from "./termdb.initbinconfig.js";
17
16
  export * from "./termdb.usecase.js";
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["// IMPORTANT: import .js extension, even though the actual code files are .ts\nexport * from '../constants/AiHisto.js'\n\n// please list in alphanumeric order for readability\nexport * from './aiHisto.js'\nexport * from './bulk.js'\nexport * from './clustering.js'\nexport * from './common.js'\nexport * from './compute.percentile.js'\nexport * from './fetch-helpers.js'\nexport * from './fileSize.js'\nexport * from './filter.js'\nexport * from './hash.js'\nexport * from './helpers.js'\nexport * from './joinUrl.js'\nexport * from './mds3tk.js'\nexport * from './roundValue.js'\nexport * from './termdb.bins.js'\nexport * from './termdb.initbinconfig.js'\nexport * from './termdb.usecase.js'\nexport * from './terms.js'\nexport * from './time.js'\nexport * from './tree.js'\nexport * from './urljson.js'\nexport * from './vcf.ann.js'\nexport * from './vcf.csq.js'\nexport * from './vcf.info.js'\nexport * from './vcf.js'\nexport * from './vcf.type.js'\n"],
5
- "mappings": "AACA,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
4
+ "sourcesContent": ["// IMPORTANT: import .js extension, even though the actual code files are .ts\n// please list in alphanumeric order for readability\nexport * from './bulk.js'\nexport * from './clustering.js'\nexport * from './common.js'\nexport * from './compute.percentile.js'\nexport * from './fetch-helpers.js'\nexport * from './fileSize.js'\nexport * from './filter.js'\nexport * from './hash.js'\nexport * from './helpers.js'\nexport * from './joinUrl.js'\nexport * from './mds3tk.js'\nexport * from './roundValue.js'\nexport * from './scatter.js'\nexport * from './termdb.bins.js'\nexport * from './termdb.initbinconfig.js'\nexport * from './termdb.usecase.js'\nexport * from './terms.js'\nexport * from './time.js'\nexport * from './tree.js'\nexport * from './urljson.js'\nexport * from './vcf.ann.js'\nexport * from './vcf.csq.js'\nexport * from './vcf.info.js'\nexport * from './vcf.js'\nexport * from './vcf.type.js'\n"],
5
+ "mappings": "AAEA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,8 @@
1
+ /** The scatter renders both on the client and the server.
2
+ * The consts and functions below are data processing and formatting utils used
3
+ * in both contexts. */
4
+ export declare const xAxisOffSet = 80;
5
+ export declare const yAxisOffSet = 30;
6
+ export declare function getCoordinate(val: number, min: number | null, max: number | null): number;
7
+ /** Extra space prevents clipping data points on the scale (i.e. plot axis) */
8
+ export declare function calculatePadding(minScale: number | null, maxScale: number | null, min: number, max: number): number;
@@ -0,0 +1,17 @@
1
+ const xAxisOffSet = 80;
2
+ const yAxisOffSet = 30;
3
+ function getCoordinate(val, min, max) {
4
+ if (min != null && val < min) return min;
5
+ if (max != null && val > max) return max;
6
+ return val;
7
+ }
8
+ function calculatePadding(minScale, maxScale, min, max) {
9
+ return minScale != null || maxScale != null ? 0 : (max - min) * 0.01;
10
+ }
11
+ export {
12
+ calculatePadding,
13
+ getCoordinate,
14
+ xAxisOffSet,
15
+ yAxisOffSet
16
+ };
17
+ //# sourceMappingURL=scatter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scatter.ts"],
4
+ "sourcesContent": ["/** The scatter renders both on the client and the server.\n * The consts and functions below are data processing and formatting utils used\n * in both contexts. */\n\nexport const xAxisOffSet = 80\nexport const yAxisOffSet = 30\n\nexport function getCoordinate(val: number, min: number | null, max: number | null) {\n\tif (min != null && val < min) return min\n\tif (max != null && val > max) return max\n\treturn val\n}\n\n/** Extra space prevents clipping data points on the scale (i.e. plot axis) */\nexport function calculatePadding(minScale: number | null, maxScale: number | null, min: number, max: number) {\n\treturn minScale != null || maxScale != null ? 0 : (max - min) * 0.01\n}\n"],
5
+ "mappings": "AAIO,MAAM,cAAc;AACpB,MAAM,cAAc;AAEpB,SAAS,cAAc,KAAa,KAAoB,KAAoB;AAClF,MAAI,OAAO,QAAQ,MAAM,IAAK,QAAO;AACrC,MAAI,OAAO,QAAQ,MAAM,IAAK,QAAO;AACrC,SAAO;AACR;AAGO,SAAS,iBAAiB,UAAyB,UAAyB,KAAa,KAAa;AAC5G,SAAO,YAAY,QAAQ,YAAY,OAAO,KAAK,MAAM,OAAO;AACjE;",
6
+ "names": []
7
+ }
@@ -90,10 +90,13 @@ function validate_bins(binconfig) {
90
90
  throw `invalid binconfig.type="${bc.type}"`;
91
91
  }
92
92
  }
93
+ const maxNumBins = 100;
93
94
  function compute_bins(binconfig, summaryfxn, valueConversion) {
94
95
  const bc = binconfig;
95
96
  validate_bins(bc);
96
97
  if (bc.lst) {
98
+ if (!Array.isArray(bc.lst)) throw `bc.lst is not an array`;
99
+ if (bc.lst.length > maxNumBins) throw `bc.lst exceed the maximum of ${maxNumBins} allowed entries`;
97
100
  const k2c2 = getColors(bc.lst.length);
98
101
  for (const bin of bc.lst) bin.color = k2c2(bin.label);
99
102
  }
@@ -134,7 +137,6 @@ function compute_bins(binconfig, summaryfxn, valueConversion) {
134
137
  stopinclusive: bc.stopinclusive
135
138
  };
136
139
  if (!isStrictNumeric(currBin.stop)) throw "the computed first_bin.stop is non-numeric" + currBin.stop;
137
- const maxNumBins = 100;
138
140
  while (numericMax && currBin.stop <= max || currBin.startunbounded && !bins.length || currBin.stopunbounded) {
139
141
  bins.push(currBin);
140
142
  if (currBin.stop >= max) {
@@ -167,7 +169,12 @@ function compute_bins(binconfig, summaryfxn, valueConversion) {
167
169
  else break;
168
170
  }
169
171
  if (bins.length + 1 >= maxNumBins) {
170
- bc.error = "max_num_bins_reached";
172
+ delete currBin.stop;
173
+ currBin.stopunbounded = true;
174
+ currBin.label = get_bin_label(currBin, bc, valueConversion);
175
+ if (!bins.includes(currBin)) bins.push(currBin);
176
+ const hint = "Please increase the bin_size or first bin stop, or have a lower last bin start.";
177
+ bc.error = hint + " (max_num_bins_reached)";
171
178
  break;
172
179
  }
173
180
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/termdb.bins.ts"],
4
- "sourcesContent": ["import { format } from 'd3-format'\nimport { getColors } from './common.js'\nimport { isNumeric, isStrictNumeric, convertUnits } from './helpers.js'\n\nexport default function validate_bins(binconfig) {\n\t// Number.isFinite('1') returns false, which is desired\n\n\tconst bc = binconfig\n\tif (!bc || typeof bc !== 'object') throw 'bin schema must be an object'\n\t// assign default type\n\tif (!('type' in bc)) bc.type = 'regular-bin'\n\n\tif (bc.type == 'custom-bin') {\n\t\tif (!Array.isArray(bc.lst)) throw 'binconfig.lst must be an array'\n\t\tif (!bc.lst.length) throw 'binconfig.lst must have entries'\n\t\tconst first_bin = bc.lst[0]\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\n\t\tfor (const bin of bc.lst) {\n\t\t\tif (!('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\t\t\tthrow 'custom bin.startinclusive and/or bin.stopinclusive must be defined'\n\t\t\t}\n\n\t\t\tif (bin == first_bin) {\n\t\t\t\tif ('startunbounded' in bin && !bin.startunbounded) {\n\t\t\t\t\tthrow `a custom first bin must not set bin.startunbounded to false`\n\t\t\t\t}\n\t\t\t\tbin.startunbounded = true\n\t\t\t\tif ('start' in bin) {\n\t\t\t\t\tthrow 'a custom first bin must not set a bin.start value'\n\t\t\t\t}\n\t\t\t\tif ('start_percentile' in bin) {\n\t\t\t\t\tthrow 'the first bin must not set a bin.start_percentile value'\n\t\t\t\t}\n\t\t\t\tif (!('stop' in bin)) {\n\t\t\t\t\tthrow `a custom first bin must define a bin.stop value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.stop)) {\n\t\t\t\t\tthrow `a custom first bin.stop value should be numeric`\n\t\t\t\t}\n\t\t\t} else if (bin == last_bin) {\n\t\t\t\tif (!('start' in bin)) {\n\t\t\t\t\tthrow `a custom last bin must define a bin.start value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) {\n\t\t\t\t\tthrow `a custom last bin.start must be numeric`\n\t\t\t\t}\n\t\t\t\tif ('stopunbounded' in bin && !bin.stopunbounded) {\n\t\t\t\t\tthrow 'a custom last bin must not set bin.stopunbounded to false'\n\t\t\t\t}\n\t\t\t\tbin.stopunbounded = true\n\t\t\t\tif ('stop' in bin) {\n\t\t\t\t\tthrow 'a custom last bin must not set a bin.stop value'\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t\t\t\tthrow 'bin.startunbounded and bin.stopunbounded must not be set for non-first/non-last bins'\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) throw 'bin.start must be numeric for a non-first bin'\n\t\t\t\tif (!isStrictNumeric(bin.stop)) throw 'bin.stop must be numeric for a non-last bin'\n\t\t\t}\n\t\t}\n\t} else if (bc.type == 'regular-bin') {\n\t\t// required custom_bin parameter\n\t\tif (!Number.isFinite(bc.bin_size)) throw 'non-numeric bin_size'\n\t\tif (bc.bin_size <= 0) throw 'bin_size must be greater than 0'\n\n\t\tif (!bc.startinclusive && !bc.stopinclusive) {\n\t\t\tbc.startinclusive = 1\n\t\t\tbc.stopinclusive = 0\n\t\t}\n\n\t\tif (!bc.first_bin) throw 'first_bin{} missing'\n\t\tif (typeof bc.first_bin != 'object') throw 'first_bin{} is not an object'\n\t\tif (!Object.keys(bc.first_bin).length) throw 'first_bin is an empty object'\n\n\t\t{\n\t\t\tconst b = bc.first_bin\n\t\t\tb.startunbounded = true\n\t\t\t// requires stop_percentile, or stop\n\t\t\tif (b.stop_percentile) {\n\t\t\t\tif (!Number.isInteger(b.stop_percentile)) throw 'first_bin.stop_percentile should be integer'\n\t\t\t\tif (b.stop_percentile <= 0 || b.stop_percentile >= 100) throw 'first_bin.stop_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.stop)) {\n\t\t\t\tthrow 'first_bin.stop not a number when stop_percentile is not set'\n\t\t\t}\n\t\t}\n\n\t\tif (bc.last_bin) {\n\t\t\tconst b = bc.last_bin\n\t\t\t// requires start_percentile or start\n\t\t\tif (b.start_percentile) {\n\t\t\t\tif (!Number.isInteger(b.start_percentile)) throw 'last_bin.start_percentile should be integer'\n\t\t\t\tif (b.start_percentile <= 0 || b.start_percentile >= 100) throw 'last_bin.start_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.start)) {\n\t\t\t\tthrow 'last_bin.start not a number when start_percentile is not set'\n\t\t\t}\n\n\t\t\tb.stopunbounded = true\n\t\t\tif ('stop' in b) {\n\t\t\t\tthrow 'a regular last bin must not set a bin.stop value'\n\t\t\t}\n\t\t}\n\t} else {\n\t\tthrow `invalid binconfig.type=\"${bc.type}\"`\n\t}\n}\n\nexport function compute_bins(binconfig, summaryfxn, valueConversion) {\n\t/*\n\t Bins generator\n\t \n\tbinconfig \n\t configuration of bins per the Numerical Binning Scheme\n\n\tsummaryfxn (percentiles)=> return {min, max, pX, pY, ...}\n\t - required function\n\n\t - must accept an array of desired percentile values\n\t and returns an object of computed properties\n\t {\n\t min: minimum value\n\t max: maximum value\n\t pX: percentile at X value, so p10 will be 10th percentile value\n\t pY: .. corresponding to the desired percentile values \n\t }\n\t*/\n\tconst bc = binconfig\n\n\tvalidate_bins(bc)\n\tif (bc.lst) {\n\t\tconst k2c = getColors(bc.lst.length) //to color bins\n\t\tfor (const bin of bc.lst) bin.color = k2c(bin.label)\n\t}\n\tif (bc.type == 'custom-bin') return JSON.parse(JSON.stringify(bc.lst))\n\tif (typeof summaryfxn != 'function') throw 'summaryfxn required for modules/termdb.bins.js compute_bins()'\n\tconst percentiles = target_percentiles(bc)\n\tconst summary = summaryfxn(percentiles)\n\tif (!summary || typeof summary !== 'object') throw 'invalid returned value by summaryfxn'\n\tbc.results = { summary }\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\n\t// round the min and max values for use as bin start and stop\n\t// in the first and last bins, respectively\n\tconst minFloor = Math.floor(summary.min * 100) / 100\n\tconst maxCeil = Math.ceil(summary.max * 100) / 100\n\tconst min = bc.first_bin.startunbounded\n\t\t? minFloor\n\t\t: bc.first_bin.start_percentile\n\t\t? summary['p' + bc.first_bin.start_percentile]\n\t\t: bc.first_bin.start\n\tlet max = maxCeil, // in order to include the max value in the last bin\n\t\tlast_start,\n\t\tlast_stop\n\n\tif (bc.last_bin) {\n\t\tmax = bc.last_bin.stopunbounded\n\t\t\t? maxCeil // in order to include the max value in the last bin\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isNumeric(bc.last_bin.stop) && bc.last_bin.stop <= summary.max // '0.0088' < 0.0088\n\t\t\t? bc.last_bin.stop\n\t\t\t: maxCeil // in order to include the max value in the last bin\n\t\tlast_start = isStrictNumeric(bc.last_bin.start_percentile)\n\t\t\t? summary['p' + bc.last_bin.start_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.start)\n\t\t\t? bc.last_bin.start\n\t\t\t: undefined\n\t\tlast_stop = bc.last_bin.stopunbounded\n\t\t\t? null\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.stop)\n\t\t\t? bc.last_bin.stop\n\t\t\t: null\n\t} else if (bc.lst) {\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\t\tlast_start = last_bin.start\n\t\tlast_stop = 'stop' in last_bin && !last_bin.stopunbounded ? last_bin.stop : maxCeil\n\t\tmax = last_stop\n\t} else {\n\t\tlast_start = maxCeil\n\t\tlast_stop = maxCeil\n\t}\n\n\tconst numericMax = isStrictNumeric(max)\n\tconst numericLastStart = isStrictNumeric(last_start)\n\tconst numericLastStop = isStrictNumeric(last_stop)\n\n\tif (!numericMax && !numericLastStart) return [] //throw 'unable to compute the last bin start or stop'\n\n\tconst bins: any[] = []\n\tlet currBin: Record<string, any> = {\n\t\tstartunbounded: bc.first_bin.startunbounded,\n\t\tstart: bc.first_bin.startunbounded ? undefined : min,\n\t\tstop: isStrictNumeric(bc.first_bin.stop_percentile)\n\t\t\t? +summary['p' + bc.first_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.first_bin.stop)\n\t\t\t? +bc.first_bin.stop\n\t\t\t: min + bc.bin_size,\n\t\tstartinclusive: bc.startinclusive,\n\t\tstopinclusive: bc.stopinclusive\n\t}\n\n\tif (!isStrictNumeric(currBin.stop)) throw 'the computed first_bin.stop is non-numeric' + currBin.stop\n\tconst maxNumBins = 100 // harcoded limit for now to not stress sqlite\n\n\twhile ((numericMax && currBin.stop <= max) || (currBin.startunbounded && !bins.length) || currBin.stopunbounded) {\n\t\tbins.push(currBin)\n\t\t// force a computed last bin to have stopunbounded true\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stopunbounded = true\n\t\t\tif (bins.length > 1) {\n\t\t\t\tdelete currBin.stop\n\t\t\t}\n\t\t}\n\t\tcurrBin.label = get_bin_label(currBin, bc, valueConversion)\n\t\tif (currBin.stopunbounded) break\n\n\t\tconst upper = currBin.stop + bc.bin_size\n\t\tconst previousStop = currBin.stop\n\t\tcurrBin = {\n\t\t\tstartinclusive: bc.startinclusive,\n\t\t\tstopinclusive: bc.stopinclusive,\n\t\t\tstart: previousStop,\n\t\t\tstop:\n\t\t\t\tnumericLastStop && (previousStop == last_start || upper > last_stop)\n\t\t\t\t\t? last_stop\n\t\t\t\t\t: numericLastStart && upper > last_start && previousStop != last_start\n\t\t\t\t\t? last_start\n\t\t\t\t\t: upper\n\t\t}\n\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stop = max\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t\tif (bc.last_bin && bc.last_bin.stopinclusive) currBin.stopinclusive = 1\n\t\t}\n\t\tif (numericLastStart && currBin.start == last_start) {\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t}\n\t\tif (currBin.start > currBin.stop) {\n\t\t\tif (numericLastStart && currBin.stop == last_start && bc.last_bin && bc.last_bin.stopunbounded)\n\t\t\t\tcurrBin.stopunbounded = true\n\t\t\telse break\n\t\t}\n\t\tif (bins.length + 1 >= maxNumBins) {\n\t\t\tbc.error = 'max_num_bins_reached'\n\t\t\tbreak\n\t\t}\n\t}\n\tdelete bc.binLabelFormatter\n\tif (bins.length > 1) {\n\t\tdelete bins[bins.length - 1].stop\n\t}\n\tconst k2c = getColors(bins.length) //to color bins\n\tfor (const bin of bins) bin.color = k2c(bin.label)\n\treturn bins\n}\n\nfunction getNumDecimalsFormatter(bc) {\n\t//return format('rounding' in bc ? bc.rounding : '')\n\treturn 'rounding' in bc ? format(bc.rounding) : d => d // default to labeling using the start/stop value as-is\n}\n\nexport function get_bin_label(bin, binconfig, valueConversion?: any) {\n\t/*\n\t Generate a numeric bin label given a bin configuration and an optional term valueConversion object\n\t*/\n\tif (!bin) return 'missing bin.label'\n\tif (bin.label) return bin.label\n\n\tconst bc = binconfig\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\tif (!bin.startunbounded && !bin.stopunbounded && !('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\tif (bc.startinclusive) bin.startinclusive = true\n\t\telse if (bc.stopinclusive) bin.stopinclusive = true\n\t}\n\n\tconst start = bc.use_as == 'bins' || bin.start\n\tconst stop = bc.use_as == 'bins' || bin.stop\n\n\tlet label_offset = 0\n\tif ('label_offset' in bc) {\n\t\tbc.label_offset_ignored = 'bin_size' in bc && bc.bin_size < bc.label_offset\n\t\tif (!bc.label_offset_ignored) label_offset = bc.label_offset\n\t} else if (bc.bin_size === 1 && bc.termtype == 'integer') {\n\t\tlabel_offset = 1\n\t}\n\n\t// one side-unbounded bins\n\t// label will be \">v\" or \"<v\"\n\tif (bin.startunbounded) {\n\t\tconst oper = bin.stopinclusive ? '\u2264' : '<' // \\u2264\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(stop) //bin.startinclusive && label_offset ? stop - label_offset : stop)\n\t\treturn oper + v1\n\t}\n\t// a data value may coincide with the last bin's start\n\tif (bin.stopunbounded || start === stop) {\n\t\tconst oper = bin.startinclusive /*|| label_offset*/ ? '\u2265' : '>' // \\u2265\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(start) //bin.startinclusive || start == min ? start : start + label_offset)\n\t\treturn oper + v0\n\t}\n\n\t// two-sided bins\n\tif (label_offset && bin.startinclusive && !bin.stopinclusive) {\n\t\tif (Number.isInteger(bc.bin_size) && Math.abs(start - stop) === label_offset) {\n\t\t\t// make a simpler label when the range simply spans the bin_size\n\t\t\treturn (\n\t\t\t\t'' +\n\t\t\t\t(valueConversion\n\t\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t\t: bc.binLabelFormatter(start))\n\t\t\t)\n\t\t} else {\n\t\t\tconst v0 = valueConversion\n\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t: bc.binLabelFormatter(start)\n\t\t\tconst v1 = valueConversion\n\t\t\t\t? convertUnits(\n\t\t\t\t\t\tstop - label_offset,\n\t\t\t\t\t\tvalueConversion.fromUnit,\n\t\t\t\t\t\tvalueConversion.toUnit,\n\t\t\t\t\t\tvalueConversion.scaleFactor,\n\t\t\t\t\t\ttrue\n\t\t\t\t )\n\t\t\t\t: bc.binLabelFormatter(stop - label_offset)\n\t\t\t// ensure that last two bin labels make sense (the last is stopunbounded)\n\t\t\treturn +v0 >= +v1 ? v0.toString() : v0 + ' to ' + v1\n\t\t}\n\t} else {\n\t\t// stop_inclusive || label_offset == 0\n\t\tconst oper0 = bin.startinclusive ? '' : '>'\n\t\tconst oper1 = bin.stopinclusive ? '' : '<'\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(start)\n\t\t\t? start\n\t\t\t: bc.binLabelFormatter(start)\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(stop)\n\t\t\t? stop\n\t\t\t: bc.binLabelFormatter(stop)\n\t\t// after rounding the bin labels, the bin start may equal the last bin stop as derived from actual data\n\t\tif (+v0 >= +v1) {\n\t\t\tconst oper = bin.startinclusive ? '\u2265' : '>' // \\u2265\n\t\t\treturn oper + v0\n\t\t} else {\n\t\t\treturn oper0 + v0 + ' to ' + oper1 + v1\n\t\t}\n\t}\n}\n\n// get bin range equation from bin label and bin properties\nexport function get_bin_range_equation(bin, binconfig) {\n\tconst x = '<span style=\"font-family:Times;font-style:italic;\">x</span>'\n\tlet range_eq\n\t// should always use computed (not user-customized) bin label to determine bin range text\n\tconst copy = structuredClone(bin)\n\tcopy.label = '' // mutate only the copy, and not the original bin argument\n\tconst bin_label = get_bin_label(copy, binconfig, undefined)\n\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t// first or last bins, e.g. x \u2264 14 and x > 16\n\t\trange_eq = x + ' ' + bin_label\n\t} else if (bin.startinclusive) {\n\t\t// bins with startinclusive, e.g. 14 \u2264 x < 16\n\t\trange_eq = bin_label.replace('to <', '\u2264 ' + x + ' <')\n\t} else if (bin.stopinclusive) {\n\t\t// bins with stopinclusive, e.g. 14 < x \u2264 16\n\t\trange_eq = bin_label.replace('>', '').replace('to', '< ' + x + ' \u2264')\n\t}\n\treturn range_eq\n}\n\nexport function target_percentiles(binconfig) {\n\tconst percentiles: any[] = []\n\tconst f = binconfig.first_bin\n\tif (f && isStrictNumeric(f.start_percentile)) percentiles.push(f.start_percentile)\n\tif (f && isStrictNumeric(f.stop_percentile)) percentiles.push(f.stop_percentile)\n\tconst l = binconfig.last_bin\n\tif (l && isStrictNumeric(l.start_percentile)) percentiles.push(l.start_percentile)\n\tif (l && isStrictNumeric(l.stop_percentile)) percentiles.push(l.stop_percentile)\n\treturn percentiles\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { format } from 'd3-format'\nimport { getColors } from './common.js'\nimport { isNumeric, isStrictNumeric, convertUnits } from './helpers.js'\n\nexport default function validate_bins(binconfig) {\n\t// Number.isFinite('1') returns false, which is desired\n\n\tconst bc = binconfig\n\tif (!bc || typeof bc !== 'object') throw 'bin schema must be an object'\n\t// assign default type\n\tif (!('type' in bc)) bc.type = 'regular-bin'\n\n\tif (bc.type == 'custom-bin') {\n\t\tif (!Array.isArray(bc.lst)) throw 'binconfig.lst must be an array'\n\t\tif (!bc.lst.length) throw 'binconfig.lst must have entries'\n\t\tconst first_bin = bc.lst[0]\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\n\t\tfor (const bin of bc.lst) {\n\t\t\tif (!('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\t\t\tthrow 'custom bin.startinclusive and/or bin.stopinclusive must be defined'\n\t\t\t}\n\n\t\t\tif (bin == first_bin) {\n\t\t\t\tif ('startunbounded' in bin && !bin.startunbounded) {\n\t\t\t\t\tthrow `a custom first bin must not set bin.startunbounded to false`\n\t\t\t\t}\n\t\t\t\tbin.startunbounded = true\n\t\t\t\tif ('start' in bin) {\n\t\t\t\t\tthrow 'a custom first bin must not set a bin.start value'\n\t\t\t\t}\n\t\t\t\tif ('start_percentile' in bin) {\n\t\t\t\t\tthrow 'the first bin must not set a bin.start_percentile value'\n\t\t\t\t}\n\t\t\t\tif (!('stop' in bin)) {\n\t\t\t\t\tthrow `a custom first bin must define a bin.stop value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.stop)) {\n\t\t\t\t\tthrow `a custom first bin.stop value should be numeric`\n\t\t\t\t}\n\t\t\t} else if (bin == last_bin) {\n\t\t\t\tif (!('start' in bin)) {\n\t\t\t\t\tthrow `a custom last bin must define a bin.start value`\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) {\n\t\t\t\t\tthrow `a custom last bin.start must be numeric`\n\t\t\t\t}\n\t\t\t\tif ('stopunbounded' in bin && !bin.stopunbounded) {\n\t\t\t\t\tthrow 'a custom last bin must not set bin.stopunbounded to false'\n\t\t\t\t}\n\t\t\t\tbin.stopunbounded = true\n\t\t\t\tif ('stop' in bin) {\n\t\t\t\t\tthrow 'a custom last bin must not set a bin.stop value'\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t\t\t\tthrow 'bin.startunbounded and bin.stopunbounded must not be set for non-first/non-last bins'\n\t\t\t\t}\n\t\t\t\tif (!isStrictNumeric(bin.start)) throw 'bin.start must be numeric for a non-first bin'\n\t\t\t\tif (!isStrictNumeric(bin.stop)) throw 'bin.stop must be numeric for a non-last bin'\n\t\t\t}\n\t\t}\n\t} else if (bc.type == 'regular-bin') {\n\t\t// required custom_bin parameter\n\t\tif (!Number.isFinite(bc.bin_size)) throw 'non-numeric bin_size'\n\t\tif (bc.bin_size <= 0) throw 'bin_size must be greater than 0'\n\n\t\tif (!bc.startinclusive && !bc.stopinclusive) {\n\t\t\tbc.startinclusive = 1\n\t\t\tbc.stopinclusive = 0\n\t\t}\n\n\t\tif (!bc.first_bin) throw 'first_bin{} missing'\n\t\tif (typeof bc.first_bin != 'object') throw 'first_bin{} is not an object'\n\t\tif (!Object.keys(bc.first_bin).length) throw 'first_bin is an empty object'\n\n\t\t{\n\t\t\tconst b = bc.first_bin\n\t\t\tb.startunbounded = true\n\t\t\t// requires stop_percentile, or stop\n\t\t\tif (b.stop_percentile) {\n\t\t\t\tif (!Number.isInteger(b.stop_percentile)) throw 'first_bin.stop_percentile should be integer'\n\t\t\t\tif (b.stop_percentile <= 0 || b.stop_percentile >= 100) throw 'first_bin.stop_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.stop)) {\n\t\t\t\tthrow 'first_bin.stop not a number when stop_percentile is not set'\n\t\t\t}\n\t\t}\n\n\t\tif (bc.last_bin) {\n\t\t\tconst b = bc.last_bin\n\t\t\t// requires start_percentile or start\n\t\t\tif (b.start_percentile) {\n\t\t\t\tif (!Number.isInteger(b.start_percentile)) throw 'last_bin.start_percentile should be integer'\n\t\t\t\tif (b.start_percentile <= 0 || b.start_percentile >= 100) throw 'last_bin.start_percentile out of bound (0-100)'\n\t\t\t} else if (!Number.isFinite(b.start)) {\n\t\t\t\tthrow 'last_bin.start not a number when start_percentile is not set'\n\t\t\t}\n\n\t\t\tb.stopunbounded = true\n\t\t\tif ('stop' in b) {\n\t\t\t\tthrow 'a regular last bin must not set a bin.stop value'\n\t\t\t}\n\t\t}\n\t} else {\n\t\tthrow `invalid binconfig.type=\"${bc.type}\"`\n\t}\n}\n\nconst maxNumBins = 100 // hardcoded limit for now to not stress sqlite\n\nexport function compute_bins(binconfig, summaryfxn, valueConversion) {\n\t/*\n\t Bins generator\n\t \n\tbinconfig \n\t configuration of bins per the Numerical Binning Scheme\n\n\tsummaryfxn (percentiles)=> return {min, max, pX, pY, ...}\n\t - required function\n\n\t - must accept an array of desired percentile values\n\t and returns an object of computed properties\n\t {\n\t min: minimum value\n\t max: maximum value\n\t pX: percentile at X value, so p10 will be 10th percentile value\n\t pY: .. corresponding to the desired percentile values \n\t }\n\t*/\n\tconst bc = binconfig\n\n\tvalidate_bins(bc)\n\tif (bc.lst) {\n\t\tif (!Array.isArray(bc.lst)) throw `bc.lst is not an array`\n\t\tif (bc.lst.length > maxNumBins) throw `bc.lst exceed the maximum of ${maxNumBins} allowed entries`\n\t\tconst k2c = getColors(bc.lst.length) //to color bins\n\t\tfor (const bin of bc.lst) bin.color = k2c(bin.label)\n\t}\n\tif (bc.type == 'custom-bin') return JSON.parse(JSON.stringify(bc.lst))\n\tif (typeof summaryfxn != 'function') throw 'summaryfxn required for modules/termdb.bins.js compute_bins()'\n\tconst percentiles = target_percentiles(bc)\n\tconst summary = summaryfxn(percentiles)\n\tif (!summary || typeof summary !== 'object') throw 'invalid returned value by summaryfxn'\n\tbc.results = { summary }\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\n\t// round the min and max values for use as bin start and stop\n\t// in the first and last bins, respectively\n\tconst minFloor = Math.floor(summary.min * 100) / 100\n\tconst maxCeil = Math.ceil(summary.max * 100) / 100\n\tconst min = bc.first_bin.startunbounded\n\t\t? minFloor\n\t\t: bc.first_bin.start_percentile\n\t\t? summary['p' + bc.first_bin.start_percentile]\n\t\t: bc.first_bin.start\n\tlet max = maxCeil, // in order to include the max value in the last bin\n\t\tlast_start,\n\t\tlast_stop\n\n\tif (bc.last_bin) {\n\t\tmax = bc.last_bin.stopunbounded\n\t\t\t? maxCeil // in order to include the max value in the last bin\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isNumeric(bc.last_bin.stop) && bc.last_bin.stop <= summary.max // '0.0088' < 0.0088\n\t\t\t? bc.last_bin.stop\n\t\t\t: maxCeil // in order to include the max value in the last bin\n\t\tlast_start = isStrictNumeric(bc.last_bin.start_percentile)\n\t\t\t? summary['p' + bc.last_bin.start_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.start)\n\t\t\t? bc.last_bin.start\n\t\t\t: undefined\n\t\tlast_stop = bc.last_bin.stopunbounded\n\t\t\t? null\n\t\t\t: bc.last_bin.stop_percentile\n\t\t\t? summary['p' + bc.last_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.last_bin.stop)\n\t\t\t? bc.last_bin.stop\n\t\t\t: null\n\t} else if (bc.lst) {\n\t\tconst last_bin = bc.lst[bc.lst.length - 1]\n\t\tlast_start = last_bin.start\n\t\tlast_stop = 'stop' in last_bin && !last_bin.stopunbounded ? last_bin.stop : maxCeil\n\t\tmax = last_stop\n\t} else {\n\t\tlast_start = maxCeil\n\t\tlast_stop = maxCeil\n\t}\n\n\tconst numericMax = isStrictNumeric(max)\n\tconst numericLastStart = isStrictNumeric(last_start)\n\tconst numericLastStop = isStrictNumeric(last_stop)\n\n\tif (!numericMax && !numericLastStart) return [] //throw 'unable to compute the last bin start or stop'\n\n\tconst bins: any[] = []\n\tlet currBin: Record<string, any> = {\n\t\tstartunbounded: bc.first_bin.startunbounded,\n\t\tstart: bc.first_bin.startunbounded ? undefined : min,\n\t\tstop: isStrictNumeric(bc.first_bin.stop_percentile)\n\t\t\t? +summary['p' + bc.first_bin.stop_percentile]\n\t\t\t: isStrictNumeric(bc.first_bin.stop)\n\t\t\t? +bc.first_bin.stop\n\t\t\t: min + bc.bin_size,\n\t\tstartinclusive: bc.startinclusive,\n\t\tstopinclusive: bc.stopinclusive\n\t}\n\n\tif (!isStrictNumeric(currBin.stop)) throw 'the computed first_bin.stop is non-numeric' + currBin.stop\n\n\twhile ((numericMax && currBin.stop <= max) || (currBin.startunbounded && !bins.length) || currBin.stopunbounded) {\n\t\tbins.push(currBin)\n\t\t// force a computed last bin to have stopunbounded true\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stopunbounded = true\n\t\t\tif (bins.length > 1) {\n\t\t\t\tdelete currBin.stop\n\t\t\t}\n\t\t}\n\t\tcurrBin.label = get_bin_label(currBin, bc, valueConversion)\n\t\tif (currBin.stopunbounded) break\n\n\t\tconst upper = currBin.stop + bc.bin_size\n\t\tconst previousStop = currBin.stop\n\t\tcurrBin = {\n\t\t\tstartinclusive: bc.startinclusive,\n\t\t\tstopinclusive: bc.stopinclusive,\n\t\t\tstart: previousStop,\n\t\t\tstop:\n\t\t\t\tnumericLastStop && (previousStop == last_start || upper > last_stop)\n\t\t\t\t\t? last_stop\n\t\t\t\t\t: numericLastStart && upper > last_start && previousStop != last_start\n\t\t\t\t\t? last_start\n\t\t\t\t\t: upper\n\t\t}\n\n\t\tif (currBin.stop >= max) {\n\t\t\tcurrBin.stop = max\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t\tif (bc.last_bin && bc.last_bin.stopinclusive) currBin.stopinclusive = 1\n\t\t}\n\t\tif (numericLastStart && currBin.start == last_start) {\n\t\t\tif (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1\n\t\t}\n\t\tif (currBin.start > currBin.stop) {\n\t\t\tif (numericLastStart && currBin.stop == last_start && bc.last_bin && bc.last_bin.stopunbounded)\n\t\t\t\tcurrBin.stopunbounded = true\n\t\t\telse break\n\t\t}\n\t\tif (bins.length + 1 >= maxNumBins) {\n\t\t\tdelete currBin.stop\n\t\t\tcurrBin.stopunbounded = true\n\t\t\tcurrBin.label = get_bin_label(currBin, bc, valueConversion)\n\t\t\tif (!bins.includes(currBin)) bins.push(currBin)\n\t\t\tconst hint = 'Please increase the bin_size or first bin stop, or have a lower last bin start.'\n\t\t\tbc.error = hint + ' (max_num_bins_reached)'\n\t\t\tbreak\n\t\t}\n\t}\n\tdelete bc.binLabelFormatter\n\tif (bins.length > 1) {\n\t\tdelete bins[bins.length - 1].stop\n\t}\n\tconst k2c = getColors(bins.length) //to color bins\n\tfor (const bin of bins) bin.color = k2c(bin.label)\n\treturn bins\n}\n\nfunction getNumDecimalsFormatter(bc) {\n\t//return format('rounding' in bc ? bc.rounding : '')\n\treturn 'rounding' in bc ? format(bc.rounding) : d => d // default to labeling using the start/stop value as-is\n}\n\nexport function get_bin_label(bin, binconfig, valueConversion?: any) {\n\t/*\n\t Generate a numeric bin label given a bin configuration and an optional term valueConversion object\n\t*/\n\tif (!bin) return 'missing bin.label'\n\tif (bin.label) return bin.label\n\n\tconst bc = binconfig\n\tif (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)\n\tif (!bin.startunbounded && !bin.stopunbounded && !('startinclusive' in bin) && !('stopinclusive' in bin)) {\n\t\tif (bc.startinclusive) bin.startinclusive = true\n\t\telse if (bc.stopinclusive) bin.stopinclusive = true\n\t}\n\n\tconst start = bc.use_as == 'bins' || bin.start\n\tconst stop = bc.use_as == 'bins' || bin.stop\n\n\tlet label_offset = 0\n\tif ('label_offset' in bc) {\n\t\tbc.label_offset_ignored = 'bin_size' in bc && bc.bin_size < bc.label_offset\n\t\tif (!bc.label_offset_ignored) label_offset = bc.label_offset\n\t} else if (bc.bin_size === 1 && bc.termtype == 'integer') {\n\t\tlabel_offset = 1\n\t}\n\n\t// one side-unbounded bins\n\t// label will be \">v\" or \"<v\"\n\tif (bin.startunbounded) {\n\t\tconst oper = bin.stopinclusive ? '\u2264' : '<' // \\u2264\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(stop) //bin.startinclusive && label_offset ? stop - label_offset : stop)\n\t\treturn oper + v1\n\t}\n\t// a data value may coincide with the last bin's start\n\tif (bin.stopunbounded || start === stop) {\n\t\tconst oper = bin.startinclusive /*|| label_offset*/ ? '\u2265' : '>' // \\u2265\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: bc.binLabelFormatter(start) //bin.startinclusive || start == min ? start : start + label_offset)\n\t\treturn oper + v0\n\t}\n\n\t// two-sided bins\n\tif (label_offset && bin.startinclusive && !bin.stopinclusive) {\n\t\tif (Number.isInteger(bc.bin_size) && Math.abs(start - stop) === label_offset) {\n\t\t\t// make a simpler label when the range simply spans the bin_size\n\t\t\treturn (\n\t\t\t\t'' +\n\t\t\t\t(valueConversion\n\t\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t\t: bc.binLabelFormatter(start))\n\t\t\t)\n\t\t} else {\n\t\t\tconst v0 = valueConversion\n\t\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t\t: bc.binLabelFormatter(start)\n\t\t\tconst v1 = valueConversion\n\t\t\t\t? convertUnits(\n\t\t\t\t\t\tstop - label_offset,\n\t\t\t\t\t\tvalueConversion.fromUnit,\n\t\t\t\t\t\tvalueConversion.toUnit,\n\t\t\t\t\t\tvalueConversion.scaleFactor,\n\t\t\t\t\t\ttrue\n\t\t\t\t )\n\t\t\t\t: bc.binLabelFormatter(stop - label_offset)\n\t\t\t// ensure that last two bin labels make sense (the last is stopunbounded)\n\t\t\treturn +v0 >= +v1 ? v0.toString() : v0 + ' to ' + v1\n\t\t}\n\t} else {\n\t\t// stop_inclusive || label_offset == 0\n\t\tconst oper0 = bin.startinclusive ? '' : '>'\n\t\tconst oper1 = bin.stopinclusive ? '' : '<'\n\t\tconst v0 = valueConversion\n\t\t\t? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(start)\n\t\t\t? start\n\t\t\t: bc.binLabelFormatter(start)\n\t\tconst v1 = valueConversion\n\t\t\t? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)\n\t\t\t: Number.isInteger(stop)\n\t\t\t? stop\n\t\t\t: bc.binLabelFormatter(stop)\n\t\t// after rounding the bin labels, the bin start may equal the last bin stop as derived from actual data\n\t\tif (+v0 >= +v1) {\n\t\t\tconst oper = bin.startinclusive ? '\u2265' : '>' // \\u2265\n\t\t\treturn oper + v0\n\t\t} else {\n\t\t\treturn oper0 + v0 + ' to ' + oper1 + v1\n\t\t}\n\t}\n}\n\n// get bin range equation from bin label and bin properties\nexport function get_bin_range_equation(bin, binconfig) {\n\tconst x = '<span style=\"font-family:Times;font-style:italic;\">x</span>'\n\tlet range_eq\n\t// should always use computed (not user-customized) bin label to determine bin range text\n\tconst copy = structuredClone(bin)\n\tcopy.label = '' // mutate only the copy, and not the original bin argument\n\tconst bin_label = get_bin_label(copy, binconfig, undefined)\n\tif (bin.startunbounded || bin.stopunbounded) {\n\t\t// first or last bins, e.g. x \u2264 14 and x > 16\n\t\trange_eq = x + ' ' + bin_label\n\t} else if (bin.startinclusive) {\n\t\t// bins with startinclusive, e.g. 14 \u2264 x < 16\n\t\trange_eq = bin_label.replace('to <', '\u2264 ' + x + ' <')\n\t} else if (bin.stopinclusive) {\n\t\t// bins with stopinclusive, e.g. 14 < x \u2264 16\n\t\trange_eq = bin_label.replace('>', '').replace('to', '< ' + x + ' \u2264')\n\t}\n\treturn range_eq\n}\n\nexport function target_percentiles(binconfig) {\n\tconst percentiles: any[] = []\n\tconst f = binconfig.first_bin\n\tif (f && isStrictNumeric(f.start_percentile)) percentiles.push(f.start_percentile)\n\tif (f && isStrictNumeric(f.stop_percentile)) percentiles.push(f.stop_percentile)\n\tconst l = binconfig.last_bin\n\tif (l && isStrictNumeric(l.start_percentile)) percentiles.push(l.start_percentile)\n\tif (l && isStrictNumeric(l.stop_percentile)) percentiles.push(l.stop_percentile)\n\treturn percentiles\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["k2c"]
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import type { Term } from '#types';
2
+ export { GENE_VARIANT, GENE_EXPRESSION, ISOFORM_EXPRESSION, SSGSEA, DNA_METHYLATION, CATEGORICAL, INTEGER, FLOAT, SNP, SNP_LIST, SNP_LOCUS, CONDITION, SURVIVAL, SAMPLELST, METABOLITE_INTENSITY, PROTEOME_ABUNDANCE, SINGLECELL_CELLTYPE, SINGLECELL_GENE_EXPRESSION, MULTIVALUE, DATE, TERM_COLLECTION, TermTypes } from '#types';
2
3
  export { TermTypeGroups } from './common.js';
3
4
  export declare const ROOT_SAMPLE_TYPE = 1;
4
5
  export declare const DEFAULT_SAMPLE_TYPE = 2;
@@ -6,31 +7,6 @@ export declare const NumericModes: {
6
7
  continuous: string;
7
8
  discrete: string;
8
9
  };
9
- export declare const CATEGORICAL = "categorical";
10
- export declare const CONDITION = "condition";
11
- export declare const DATE = "date";
12
- export declare const DNA_METHYLATION = "dnaMethylation";
13
- export declare const FLOAT = "float";
14
- export declare const GENE_VARIANT = "geneVariant";
15
- export declare const GENE_EXPRESSION = "geneExpression";
16
- export declare const ISOFORM_EXPRESSION = "isoformExpression";
17
- export declare const INTEGER = "integer";
18
- export declare const METABOLITE_INTENSITY = "metaboliteIntensity";
19
- export declare const MULTIVALUE = "multivalue";
20
- export declare const SAMPLELST = "samplelst";
21
- export declare const SINGLECELL_CELLTYPE = "singleCellCellType";
22
- export declare const SINGLECELL_GENE_EXPRESSION = "singleCellGeneExpression";
23
- export declare const SNP = "snp";
24
- export declare const SNP_LIST = "snplst";
25
- export declare const SNP_LOCUS = "snplocus";
26
- export declare const SSGSEA = "ssGSEA";
27
- export declare const SURVIVAL = "survival";
28
- export declare const TERM_COLLECTION = "termCollection";
29
- export declare const PROTEOME_ABUNDANCE = "proteomeAbundance";
30
- export declare const PROTEOME_DAP = "proteomeDAP";
31
- export declare const TermTypes: {
32
- [key: string]: string;
33
- };
34
10
  export declare const dtTermTypes: Set<string>;
35
11
  export declare const NUMERIC_DICTIONARY_TERM = "numericDictTerm";
36
12
  export declare const TermTypes2Dt: {
@@ -64,7 +40,7 @@ export declare const typeGroup: {
64
40
  singleCellGeneExpression: string;
65
41
  };
66
42
  export declare const numericTypes: Set<string>;
67
- export declare const annoNumericTypes: Set<string>;
43
+ export declare const dictionaryNumericTypes: Set<string>;
68
44
  export declare function isSingleCellTerm(term: any): boolean;
69
45
  export declare function isNumericTerm(term: Term): boolean;
70
46
  export declare function isCategoricalTerm(term: Term): boolean;
@@ -75,6 +51,7 @@ export declare function equals(t1: any, t2: any): boolean;
75
51
  export declare function getBin(lst: any[], value: number): number;
76
52
  export declare function getSampleType(term: any, ds: any): any;
77
53
  export declare function getParentType(types: Set<string>, ds: any): any;
54
+ export declare function isParentType(term: any, ds: any): boolean;
78
55
  export declare function termType2label(type: string): string;
79
56
  export declare function getDateFromNumber(value: number): Date;
80
57
  export declare function getDateStrFromNumber(value: number): string;
package/dist/src/terms.js CHANGED
@@ -7,36 +7,7 @@ import {
7
7
  TermTypeGroups,
8
8
  dtTerms
9
9
  } from "./common.js";
10
- import { TermTypeGroups as TermTypeGroups2 } from "./common.js";
11
- const ROOT_SAMPLE_TYPE = 1;
12
- const DEFAULT_SAMPLE_TYPE = 2;
13
- const NumericModes = {
14
- continuous: "continuous",
15
- discrete: "discrete"
16
- };
17
- const CATEGORICAL = "categorical";
18
- const CONDITION = "condition";
19
- const DATE = "date";
20
- const DNA_METHYLATION = "dnaMethylation";
21
- const FLOAT = "float";
22
- const GENE_VARIANT = "geneVariant";
23
- const GENE_EXPRESSION = "geneExpression";
24
- const ISOFORM_EXPRESSION = "isoformExpression";
25
- const INTEGER = "integer";
26
- const METABOLITE_INTENSITY = "metaboliteIntensity";
27
- const MULTIVALUE = "multivalue";
28
- const SAMPLELST = "samplelst";
29
- const SINGLECELL_CELLTYPE = "singleCellCellType";
30
- const SINGLECELL_GENE_EXPRESSION = "singleCellGeneExpression";
31
- const SNP = "snp";
32
- const SNP_LIST = "snplst";
33
- const SNP_LOCUS = "snplocus";
34
- const SSGSEA = "ssGSEA";
35
- const SURVIVAL = "survival";
36
- const TERM_COLLECTION = "termCollection";
37
- const PROTEOME_ABUNDANCE = "proteomeAbundance";
38
- const PROTEOME_DAP = "proteomeDAP";
39
- const TermTypes = {
10
+ import {
40
11
  GENE_VARIANT,
41
12
  GENE_EXPRESSION,
42
13
  ISOFORM_EXPRESSION,
@@ -57,7 +28,39 @@ const TermTypes = {
57
28
  SINGLECELL_GENE_EXPRESSION,
58
29
  MULTIVALUE,
59
30
  DATE,
60
- TERM_COLLECTION
31
+ TERM_COLLECTION,
32
+ TermTypes
33
+ } from "#types";
34
+ import {
35
+ GENE_VARIANT as GENE_VARIANT2,
36
+ GENE_EXPRESSION as GENE_EXPRESSION2,
37
+ ISOFORM_EXPRESSION as ISOFORM_EXPRESSION2,
38
+ SSGSEA as SSGSEA2,
39
+ DNA_METHYLATION as DNA_METHYLATION2,
40
+ CATEGORICAL as CATEGORICAL2,
41
+ INTEGER as INTEGER2,
42
+ FLOAT as FLOAT2,
43
+ SNP as SNP2,
44
+ SNP_LIST as SNP_LIST2,
45
+ SNP_LOCUS as SNP_LOCUS2,
46
+ CONDITION as CONDITION2,
47
+ SURVIVAL as SURVIVAL2,
48
+ SAMPLELST as SAMPLELST2,
49
+ METABOLITE_INTENSITY as METABOLITE_INTENSITY2,
50
+ PROTEOME_ABUNDANCE as PROTEOME_ABUNDANCE2,
51
+ SINGLECELL_CELLTYPE as SINGLECELL_CELLTYPE2,
52
+ SINGLECELL_GENE_EXPRESSION as SINGLECELL_GENE_EXPRESSION2,
53
+ MULTIVALUE as MULTIVALUE2,
54
+ DATE as DATE2,
55
+ TERM_COLLECTION as TERM_COLLECTION2,
56
+ TermTypes as TermTypes2
57
+ } from "#types";
58
+ import { TermTypeGroups as TermTypeGroups2 } from "./common.js";
59
+ const ROOT_SAMPLE_TYPE = 1;
60
+ const DEFAULT_SAMPLE_TYPE = 2;
61
+ const NumericModes = {
62
+ continuous: "continuous",
63
+ discrete: "discrete"
61
64
  };
62
65
  const dtTermTypes = new Set(dtTerms.map((t) => t.type));
63
66
  for (const dtTermType of dtTermTypes) {
@@ -123,7 +126,7 @@ const numericTypes = /* @__PURE__ */ new Set([
123
126
  SINGLECELL_GENE_EXPRESSION,
124
127
  DATE
125
128
  ]);
126
- const annoNumericTypes = /* @__PURE__ */ new Set([INTEGER, FLOAT, DATE]);
129
+ const dictionaryNumericTypes = /* @__PURE__ */ new Set([INTEGER, FLOAT, DATE]);
127
130
  const categoricalTypes = /* @__PURE__ */ new Set([CATEGORICAL, SNP]);
128
131
  const singleCellTerms = /* @__PURE__ */ new Set([SINGLECELL_CELLTYPE, SINGLECELL_GENE_EXPRESSION]);
129
132
  function isSingleCellTerm(term) {
@@ -215,6 +218,18 @@ function getParentType(types, ds) {
215
218
  }
216
219
  return null;
217
220
  }
221
+ function isParentType(term, ds) {
222
+ if (!ds.cohort.termdb.hasSampleAncestry) return false;
223
+ const sampleType = getSampleType(term, ds);
224
+ if (!sampleType) throw "sample type is not defined";
225
+ const sampleTypeObj = ds.cohort.termdb.sampleTypes[sampleType];
226
+ if (!sampleTypeObj) throw "invalid sample type";
227
+ if (Number.isInteger(sampleTypeObj.parent_id)) {
228
+ return false;
229
+ } else {
230
+ return true;
231
+ }
232
+ }
218
233
  const typeMap = {
219
234
  categorical: "Categorical",
220
235
  condition: "Condition",
@@ -274,36 +289,35 @@ function getDaysInYear(year) {
274
289
  return days;
275
290
  }
276
291
  export {
277
- CATEGORICAL,
278
- CONDITION,
279
- DATE,
292
+ CATEGORICAL2 as CATEGORICAL,
293
+ CONDITION2 as CONDITION,
294
+ DATE2 as DATE,
280
295
  DEFAULT_SAMPLE_TYPE,
281
- DNA_METHYLATION,
282
- FLOAT,
283
- GENE_EXPRESSION,
284
- GENE_VARIANT,
285
- INTEGER,
286
- ISOFORM_EXPRESSION,
287
- METABOLITE_INTENSITY,
288
- MULTIVALUE,
296
+ DNA_METHYLATION2 as DNA_METHYLATION,
297
+ FLOAT2 as FLOAT,
298
+ GENE_EXPRESSION2 as GENE_EXPRESSION,
299
+ GENE_VARIANT2 as GENE_VARIANT,
300
+ INTEGER2 as INTEGER,
301
+ ISOFORM_EXPRESSION2 as ISOFORM_EXPRESSION,
302
+ METABOLITE_INTENSITY2 as METABOLITE_INTENSITY,
303
+ MULTIVALUE2 as MULTIVALUE,
289
304
  NUMERIC_DICTIONARY_TERM,
290
305
  NumericModes,
291
- PROTEOME_ABUNDANCE,
292
- PROTEOME_DAP,
306
+ PROTEOME_ABUNDANCE2 as PROTEOME_ABUNDANCE,
293
307
  ROOT_SAMPLE_TYPE,
294
- SAMPLELST,
295
- SINGLECELL_CELLTYPE,
296
- SINGLECELL_GENE_EXPRESSION,
297
- SNP,
298
- SNP_LIST,
299
- SNP_LOCUS,
300
- SSGSEA,
301
- SURVIVAL,
302
- TERM_COLLECTION,
308
+ SAMPLELST2 as SAMPLELST,
309
+ SINGLECELL_CELLTYPE2 as SINGLECELL_CELLTYPE,
310
+ SINGLECELL_GENE_EXPRESSION2 as SINGLECELL_GENE_EXPRESSION,
311
+ SNP2 as SNP,
312
+ SNP_LIST2 as SNP_LIST,
313
+ SNP_LOCUS2 as SNP_LOCUS,
314
+ SSGSEA2 as SSGSEA,
315
+ SURVIVAL2 as SURVIVAL,
316
+ TERM_COLLECTION2 as TERM_COLLECTION,
303
317
  TermTypeGroups2 as TermTypeGroups,
304
- TermTypes,
318
+ TermTypes2 as TermTypes,
305
319
  TermTypes2Dt,
306
- annoNumericTypes,
320
+ dictionaryNumericTypes,
307
321
  dtTermTypes,
308
322
  equals,
309
323
  getBin,
@@ -319,6 +333,7 @@ export {
319
333
  isNonDictionaryType,
320
334
  isNumTermCollection,
321
335
  isNumericTerm,
336
+ isParentType,
322
337
  isSingleCellTerm,
323
338
  numericTypes,
324
339
  termType2label,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/terms.ts"],
4
- "sourcesContent": ["import type { Term } from '#types'\nimport {\n\tdtgeneexpression,\n\tdtssgsea,\n\tdtdnamethylation,\n\tdtmetaboliteintensity,\n\tdtproteomeabundance,\n\tTermTypeGroups,\n\tdtTerms\n} from './common.js'\n\n// moved TermTypeGroups to `server/src/common.js`, so now has to re-export\nexport { TermTypeGroups } from './common.js'\n\n/*\nFor datasets with multiple types of samples the ROOT_SAMPLE_TYPE is used to represent the root sample type, for example, \nthe type patient, that has one or more samples associated to it. This should be the id used as sample_type, when generating the db to identify the root samples\nin sampleidmap or the terms annotating root samples in the terms table.\nThe samples associated to a patient have annotations that are specific to a timepoint, for example, the age of the patient,\nthe doses of the drugs the patient was taking at the time of the data collection, etc. These annotations are associated to a sample.\n*/\nexport const ROOT_SAMPLE_TYPE = 1\n\n//For datasets with one sample type the DEFAULT_SAMPLE_TYPE is used to represent the sample type\nexport const DEFAULT_SAMPLE_TYPE = 2\n\nexport const NumericModes = {\n\tcontinuous: 'continuous',\n\tdiscrete: 'discrete'\n}\n\nexport const CATEGORICAL = 'categorical'\nexport const CONDITION = 'condition'\nexport const DATE = 'date'\nexport const DNA_METHYLATION = 'dnaMethylation'\nexport const FLOAT = 'float'\nexport const GENE_VARIANT = 'geneVariant'\nexport const GENE_EXPRESSION = 'geneExpression'\nexport const ISOFORM_EXPRESSION = 'isoformExpression'\nexport const INTEGER = 'integer'\nexport const METABOLITE_INTENSITY = 'metaboliteIntensity'\nexport const MULTIVALUE = 'multivalue'\nexport const SAMPLELST = 'samplelst'\nexport const SINGLECELL_CELLTYPE = 'singleCellCellType'\nexport const SINGLECELL_GENE_EXPRESSION = 'singleCellGeneExpression'\nexport const SNP = 'snp'\nexport const SNP_LIST = 'snplst'\nexport const SNP_LOCUS = 'snplocus'\nexport const SSGSEA = 'ssGSEA'\nexport const SURVIVAL = 'survival'\nexport const TERM_COLLECTION = 'termCollection'\nexport const PROTEOME_ABUNDANCE = 'proteomeAbundance'\nexport const PROTEOME_DAP = 'proteomeDAP'\n\n//Term types should be used gradually using these constants instead of hardcoding the values,\n// eg: type == CATEGORICAL instead of type == 'categorical'\nexport const TermTypes: { [key: string]: string } = {\n\tGENE_VARIANT,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tCATEGORICAL,\n\tINTEGER,\n\tFLOAT,\n\tSNP,\n\tSNP_LIST,\n\tSNP_LOCUS,\n\tCONDITION,\n\tSURVIVAL,\n\tSAMPLELST,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tMULTIVALUE,\n\tDATE,\n\tTERM_COLLECTION\n}\nexport const dtTermTypes: Set<string> = new Set(dtTerms.map((t: any) => t.type))\nfor (const dtTermType of dtTermTypes) {\n\tTermTypes[dtTermType.toUpperCase()] = dtTermType\n}\n\nexport const NUMERIC_DICTIONARY_TERM = 'numericDictTerm'\n\nexport const TermTypes2Dt = {\n\t[GENE_EXPRESSION]: dtgeneexpression,\n\t[SSGSEA]: dtssgsea,\n\t[DNA_METHYLATION]: dtdnamethylation,\n\t[METABOLITE_INTENSITY]: dtmetaboliteintensity,\n\t[PROTEOME_ABUNDANCE]: dtproteomeabundance\n}\n\n// maps term type to group (as is shown as toggles in search ui)\nexport const typeGroup = {\n\t[CATEGORICAL]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[CONDITION]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[FLOAT]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[INTEGER]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[SAMPLELST]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[SURVIVAL]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[DATE]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[MULTIVALUE]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[GENE_VARIANT]: TermTypeGroups.MUTATION_CNV_FUSION,\n\t[SNP]: TermTypeGroups.SNP,\n\t[SNP_LIST]: TermTypeGroups.SNP_LIST,\n\t[SNP_LOCUS]: TermTypeGroups.SNP_LOCUS,\n\t[GENE_EXPRESSION]: TermTypeGroups.GENE_EXPRESSION,\n\t[ISOFORM_EXPRESSION]: TermTypeGroups.ISOFORM_EXPRESSION,\n\t[SSGSEA]: TermTypeGroups.SSGSEA,\n\t[DNA_METHYLATION]: TermTypeGroups.DNA_METHYLATION,\n\t[METABOLITE_INTENSITY]: TermTypeGroups.METABOLITE_INTENSITY,\n\t[PROTEOME_ABUNDANCE]: TermTypeGroups.PROTEOME_ABUNDANCE,\n\t[TERM_COLLECTION]: TermTypeGroups.TERM_COLLECTION,\n\t[SINGLECELL_CELLTYPE]: TermTypeGroups.SINGLECELL_CELLTYPE,\n\t[SINGLECELL_GENE_EXPRESSION]: TermTypeGroups.SINGLECELL_GENE_EXPRESSION\n}\n\nconst nonDictTypes = new Set([\n\tSNP,\n\tSNP_LIST,\n\tSNP_LOCUS,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tGENE_VARIANT,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION\n])\n\nfor (const dtTermType of dtTermTypes) {\n\tnonDictTypes.add(TermTypes[dtTermType.toUpperCase()])\n}\n\nexport const numericTypes = new Set([\n\tINTEGER,\n\tFLOAT,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tDATE\n])\n\n// available termdb numeric table names used as anno_<term.type>,\n// for example anno_integer, anno_float, anno_date\nexport const annoNumericTypes = new Set([INTEGER, FLOAT, DATE])\n\nconst categoricalTypes = new Set([CATEGORICAL, SNP])\n\nconst singleCellTerms = new Set([SINGLECELL_CELLTYPE, SINGLECELL_GENE_EXPRESSION])\n\nexport function isSingleCellTerm(term: any) {\n\tif (!term) return false\n\treturn singleCellTerms.has(term.type)\n}\nexport function isNumericTerm(term: Term) {\n\tif (!term) return false\n\treturn numericTypes.has(term.type)\n}\nexport function isCategoricalTerm(term: Term) {\n\tif (!term) return false\n\treturn categoricalTypes.has(term.type)\n}\n\nexport function isDictionaryType(type: string) {\n\treturn !isNonDictionaryType(type)\n}\n\nexport function isNonDictionaryType(type: string) {\n\tif (!type) throw new Error('Type is not defined')\n\treturn nonDictTypes.has(type)\n}\n\nexport function isNumTermCollection(term: Term) {\n\tif (!term || !term.type) throw new Error('Term or term type is not defined')\n\t//Enable this check when memberType is added to term collection\n\t// return term.type === TERM_COLLECTION && term.memberType == 'numeric'\n\treturn term.type === TERM_COLLECTION\n}\n\nexport function equals(t1: any, t2: any) {\n\tif (!t1) throw new Error('First term is not defined ')\n\tif (!t2) throw new Error('Second term is not defined ')\n\tif (t1.type !== t2.type) return false //term types are different\n\tif (isDictionaryType(t1.type) && isDictionaryType(t2.type) && t1.type != SAMPLELST) return t1.id === t2.id\n\tswitch (t1.type) {\n\t\tcase GENE_EXPRESSION:\n\t\t\treturn t1.gene == t2.gene\n\t\tcase ISOFORM_EXPRESSION:\n\t\t\treturn t1.isoform == t2.isoform\n\t\tcase SSGSEA:\n\t\t\treturn t1.id == t2.id\n\t\tcase DNA_METHYLATION:\n\t\t\treturn t1.chr == t2.chr && t1.start == t2.start && t1.stop == t2.stop\n\t\tcase METABOLITE_INTENSITY:\n\t\tcase PROTEOME_ABUNDANCE:\n\t\t\treturn t1.name == t2.name\n\t\tcase GENE_VARIANT:\n\t\t\treturn t1.gene == t2.gene || (t1.chr == t2.chr && t1.start == t2.start && t1.stop == t2.stop)\n\n\t\t// TO DO: Add more cases\n\t\t// case SNP_LIST:\n\t\t// case SNP_LOCUS:\n\t\t// case SAMPLELST:\n\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\nexport function getBin(lst: any[], value: number) {\n\tlet bin = lst.findIndex(\n\t\tb => (b.startunbounded && value < b.stop) || (b.startunbounded && b.stopinclusive && value == b.stop)\n\t)\n\tif (bin == -1)\n\t\tbin = lst.findIndex(\n\t\t\tb => (b.stopunbounded && value > b.start) || (b.stopunbounded && b.startinclusive && value == b.start)\n\t\t)\n\tif (bin == -1)\n\t\tbin = lst.findIndex(\n\t\t\tb =>\n\t\t\t\t(value > b.start && value < b.stop) ||\n\t\t\t\t(b.startinclusive && value == b.start) ||\n\t\t\t\t(b.stopinclusive && value == b.stop)\n\t\t)\n\treturn bin\n}\n//Terms may have a sample type associated to them, in datasets with multiple types of samples.\n//For example the gender is associated to the patient while the age is associated to the type sample. This function is used\n//for example when calling getData or getFilter, to return either the parent or the child samples, depending on the use case.\nexport function getSampleType(term: any, ds: any) {\n\tif (!term) return null\n\t//non dict terms annotate only samples, eg: gene expression, metabolite intensity, gene variant.\n\t//Their sample type is the default sample type that may or may not have a parent type, depending on the dataset\n\tif (term.type && isNonDictionaryType(term.type)) return DEFAULT_SAMPLE_TYPE\n\t//dictionary terms may annotate different types of samples, eg: patient and sample or mouse and crop.\n\tif (term.id) return ds.cohort.termdb.term2SampleType.get(term.id)\n\tif (term.type == 'samplelst') {\n\t\tconst key = Object.keys(term.values)[0]\n\t\tconst sampleId = term.values[key].list[0]?.sampleId\n\t\tif (sampleId) return ds.sampleId2Type.get(Number(sampleId) || sampleId)\n\t\telse return DEFAULT_SAMPLE_TYPE\n\t}\n\t// samplelst or non dict terms\n\treturn DEFAULT_SAMPLE_TYPE //later own term needs to know what type annotates based on the samples\n}\n\nexport function getParentType(types: Set<string>, ds: any) {\n\tif (Object.keys(ds.cohort.termdb.sampleTypes).length == 0) return null //dataset only has one type of sample\n\tconst ids = Array.from(types)\n\tif (!ids || ids.length == 0) return null\n\tfor (const id of ids) {\n\t\tconst typeObj = ds.cohort.termdb.sampleTypes[id]\n\t\tif (!typeObj) continue\n\t\tif (typeObj.parent_id == null) return id //this is the root type\n\t\t//if my parent is in the list, then I am not the parent\n\t\tif (ids.includes(typeObj.parent_id)) continue\n\t\telse return typeObj.parent_id //my parent is not in the list, so I am the parent\n\t}\n\treturn null //no parent found\n}\n\n//Returns human readable label for each term type; label is just for printing and not computing\nconst typeMap: { [key: string]: string } = {\n\tcategorical: 'Categorical',\n\tcondition: 'Condition',\n\tfloat: 'Numerical',\n\tinteger: 'Numerical',\n\tgeneExpression: 'Gene Expression',\n\tisoformExpression: 'Isoform Expression',\n\tssGSEA: 'Geneset Expression',\n\tdnaMethylation: 'DNA Methylation',\n\tgeneVariant: 'Gene Variant',\n\tmetaboliteIntensity: 'Metabolite Intensity',\n\tproteomeAbundance: 'Proteome Abundance',\n\tproteomeDAP: 'Proteome DAP',\n\tmultivalue: 'Multi Value',\n\tsingleCellGeneExpression: 'Single Cell, Gene Expression',\n\tsingleCellCellType: 'Single Cell, Cell Type',\n\tsnplocus: 'SNP Locus',\n\tsnp: 'SNP',\n\tsnplst: 'SNP List',\n\tnumericDictTerm: 'Numeric Dictionary Term',\n\ttermCollection: 'Term Collection'\n}\n\nexport function termType2label(type: string) {\n\treturn typeMap[type] || 'Unknown term type'\n}\n\nexport function getDateFromNumber(value: number) {\n\tconst year = Math.floor(value)\n\tconst january1st = new Date(year, 0, 1)\n\tconst totalDays = getDaysInYear(year)\n\tconst time = Math.round((value - year) * totalDays) * oneDayTime\n\tconst date = new Date(january1st.getTime() + time)\n\treturn date\n}\n/*\nValue is a decimal year.\nA decimal year is a way of expressing a date or time period as a year with a decimal part, where the decimal portion \nrepresents the fraction of the year that has elapsed. \nExample:\n2025.0 represents the beginning of the year 2025. \n2025.5 represents the middle of the year 2025. \n */\nconst oneDayTime = 24 * 60 * 60 * 1000\n\nexport function getDateStrFromNumber(value: number) {\n\tconst date = getDateFromNumber(value)\n\n\t//Omit day to deidentify the patients\n\treturn date.toLocaleDateString('en-US', {\n\t\tyear: 'numeric',\n\t\tmonth: 'long'\n\t})\n}\n\n//The value returned is a decimal year\n//A decimal year is a way of expressing a date or time period as a year with a decimal part, where the decimal portion\n//represents the fraction of the year that has elapsed.\nexport function getNumberFromDateStr(str: string) {\n\tconst date = new Date(str)\n\treturn getNumberFromDate(date)\n}\n\nexport function getNumberFromDate(date: Date) {\n\tconst year = date.getFullYear()\n\tconst january1st: Date = new Date(year, 0, 1)\n\tconst diffDays = (date.getTime() - january1st.getTime()) / oneDayTime\n\tconst daysTotal = getDaysInYear(year)\n\tconst decimal = diffDays / daysTotal\n\treturn year + decimal\n}\n\nexport function getDaysInYear(year: number) {\n\tconst isLeap = new Date(year, 1, 29).getMonth() === 1\n\tconst days = isLeap ? 366 : 365\n\treturn days\n}\n"],
5
- "mappings": "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",
6
- "names": ["TermTypeGroups"]
4
+ "sourcesContent": ["import type { Term } from '#types'\nimport {\n\tdtgeneexpression,\n\tdtssgsea,\n\tdtdnamethylation,\n\tdtmetaboliteintensity,\n\tdtproteomeabundance,\n\tTermTypeGroups,\n\tdtTerms\n} from './common.js'\nimport {\n\tGENE_VARIANT,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tCATEGORICAL,\n\tINTEGER,\n\tFLOAT,\n\tSNP,\n\tSNP_LIST,\n\tSNP_LOCUS,\n\tCONDITION,\n\tSURVIVAL,\n\tSAMPLELST,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tMULTIVALUE,\n\tDATE,\n\tTERM_COLLECTION,\n\tTermTypes\n} from '#types'\n\n// legacy support - comsumers should import directly from #types (aka @sjcrh/proteinpaint-types)\nexport {\n\tGENE_VARIANT,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tCATEGORICAL,\n\tINTEGER,\n\tFLOAT,\n\tSNP,\n\tSNP_LIST,\n\tSNP_LOCUS,\n\tCONDITION,\n\tSURVIVAL,\n\tSAMPLELST,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tMULTIVALUE,\n\tDATE,\n\tTERM_COLLECTION,\n\tTermTypes\n} from '#types'\n\n// moved TermTypeGroups to `server/src/common.js`, so now has to re-export\nexport { TermTypeGroups } from './common.js'\n\n/*\nFor datasets with multiple types of samples the ROOT_SAMPLE_TYPE is used to represent the root sample type, for example, \nthe type patient, that has one or more samples associated to it. This should be the id used as sample_type, when generating the db to identify the root samples\nin sampleidmap or the terms annotating root samples in the terms table.\nThe samples associated to a patient have annotations that are specific to a timepoint, for example, the age of the patient,\nthe doses of the drugs the patient was taking at the time of the data collection, etc. These annotations are associated to a sample.\n*/\nexport const ROOT_SAMPLE_TYPE = 1\n\n//For datasets with one sample type the DEFAULT_SAMPLE_TYPE is used to represent the sample type\nexport const DEFAULT_SAMPLE_TYPE = 2\n\nexport const NumericModes = {\n\tcontinuous: 'continuous',\n\tdiscrete: 'discrete'\n}\n\nexport const dtTermTypes: Set<string> = new Set(dtTerms.map((t: any) => t.type))\nfor (const dtTermType of dtTermTypes) {\n\tTermTypes[dtTermType.toUpperCase()] = dtTermType\n}\n\nexport const NUMERIC_DICTIONARY_TERM = 'numericDictTerm'\n\nexport const TermTypes2Dt = {\n\t[GENE_EXPRESSION]: dtgeneexpression,\n\t[SSGSEA]: dtssgsea,\n\t[DNA_METHYLATION]: dtdnamethylation,\n\t[METABOLITE_INTENSITY]: dtmetaboliteintensity,\n\t[PROTEOME_ABUNDANCE]: dtproteomeabundance\n}\n\n// maps term type to group (as is shown as toggles in search ui)\nexport const typeGroup = {\n\t[CATEGORICAL]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[CONDITION]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[FLOAT]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[INTEGER]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[SAMPLELST]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[SURVIVAL]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[DATE]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[MULTIVALUE]: TermTypeGroups.DICTIONARY_VARIABLES,\n\t[GENE_VARIANT]: TermTypeGroups.MUTATION_CNV_FUSION,\n\t[SNP]: TermTypeGroups.SNP,\n\t[SNP_LIST]: TermTypeGroups.SNP_LIST,\n\t[SNP_LOCUS]: TermTypeGroups.SNP_LOCUS,\n\t[GENE_EXPRESSION]: TermTypeGroups.GENE_EXPRESSION,\n\t[ISOFORM_EXPRESSION]: TermTypeGroups.ISOFORM_EXPRESSION,\n\t[SSGSEA]: TermTypeGroups.SSGSEA,\n\t[DNA_METHYLATION]: TermTypeGroups.DNA_METHYLATION,\n\t[METABOLITE_INTENSITY]: TermTypeGroups.METABOLITE_INTENSITY,\n\t[PROTEOME_ABUNDANCE]: TermTypeGroups.PROTEOME_ABUNDANCE,\n\t[TERM_COLLECTION]: TermTypeGroups.TERM_COLLECTION,\n\t[SINGLECELL_CELLTYPE]: TermTypeGroups.SINGLECELL_CELLTYPE,\n\t[SINGLECELL_GENE_EXPRESSION]: TermTypeGroups.SINGLECELL_GENE_EXPRESSION\n}\n\nconst nonDictTypes = new Set([\n\tSNP,\n\tSNP_LIST,\n\tSNP_LOCUS,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tGENE_VARIANT,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_CELLTYPE,\n\tSINGLECELL_GENE_EXPRESSION\n])\n\nfor (const dtTermType of dtTermTypes) {\n\tnonDictTypes.add(TermTypes[dtTermType.toUpperCase()])\n}\n\nexport const numericTypes = new Set([\n\tINTEGER,\n\tFLOAT,\n\tGENE_EXPRESSION,\n\tISOFORM_EXPRESSION,\n\tSSGSEA,\n\tDNA_METHYLATION,\n\tMETABOLITE_INTENSITY,\n\tPROTEOME_ABUNDANCE,\n\tSINGLECELL_GENE_EXPRESSION,\n\tDATE\n])\n\n// dictionary numeric term types, exists in db tables, exclude non-dictionary term types\nexport const dictionaryNumericTypes = new Set([INTEGER, FLOAT, DATE])\n\nconst categoricalTypes = new Set([CATEGORICAL, SNP])\n\nconst singleCellTerms = new Set([SINGLECELL_CELLTYPE, SINGLECELL_GENE_EXPRESSION])\n\nexport function isSingleCellTerm(term: any) {\n\tif (!term) return false\n\treturn singleCellTerms.has(term.type)\n}\nexport function isNumericTerm(term: Term) {\n\tif (!term) return false\n\treturn numericTypes.has(term.type)\n}\nexport function isCategoricalTerm(term: Term) {\n\tif (!term) return false\n\treturn categoricalTypes.has(term.type)\n}\n\nexport function isDictionaryType(type: string) {\n\treturn !isNonDictionaryType(type)\n}\n\nexport function isNonDictionaryType(type: string) {\n\tif (!type) throw new Error('Type is not defined')\n\treturn nonDictTypes.has(type)\n}\n\nexport function isNumTermCollection(term: Term) {\n\tif (!term || !term.type) throw new Error('Term or term type is not defined')\n\t//Enable this check when memberType is added to term collection\n\t// return term.type === TERM_COLLECTION && term.memberType == 'numeric'\n\treturn term.type === TERM_COLLECTION\n}\n\nexport function equals(t1: any, t2: any) {\n\tif (!t1) throw new Error('First term is not defined ')\n\tif (!t2) throw new Error('Second term is not defined ')\n\tif (t1.type !== t2.type) return false //term types are different\n\tif (isDictionaryType(t1.type) && isDictionaryType(t2.type) && t1.type != SAMPLELST) return t1.id === t2.id\n\tswitch (t1.type) {\n\t\tcase GENE_EXPRESSION:\n\t\t\treturn t1.gene == t2.gene\n\t\tcase ISOFORM_EXPRESSION:\n\t\t\treturn t1.isoform == t2.isoform\n\t\tcase SSGSEA:\n\t\t\treturn t1.id == t2.id\n\t\tcase DNA_METHYLATION:\n\t\t\treturn t1.chr == t2.chr && t1.start == t2.start && t1.stop == t2.stop\n\t\tcase METABOLITE_INTENSITY:\n\t\tcase PROTEOME_ABUNDANCE:\n\t\t\treturn t1.name == t2.name\n\t\tcase GENE_VARIANT:\n\t\t\treturn t1.gene == t2.gene || (t1.chr == t2.chr && t1.start == t2.start && t1.stop == t2.stop)\n\n\t\t// TO DO: Add more cases\n\t\t// case SNP_LIST:\n\t\t// case SNP_LOCUS:\n\t\t// case SAMPLELST:\n\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\nexport function getBin(lst: any[], value: number) {\n\tlet bin = lst.findIndex(\n\t\tb => (b.startunbounded && value < b.stop) || (b.startunbounded && b.stopinclusive && value == b.stop)\n\t)\n\tif (bin == -1)\n\t\tbin = lst.findIndex(\n\t\t\tb => (b.stopunbounded && value > b.start) || (b.stopunbounded && b.startinclusive && value == b.start)\n\t\t)\n\tif (bin == -1)\n\t\tbin = lst.findIndex(\n\t\t\tb =>\n\t\t\t\t(value > b.start && value < b.stop) ||\n\t\t\t\t(b.startinclusive && value == b.start) ||\n\t\t\t\t(b.stopinclusive && value == b.stop)\n\t\t)\n\treturn bin\n}\n//Terms may have a sample type associated to them, in datasets with multiple types of samples.\n//For example the gender is associated to the patient while the age is associated to the type sample. This function is used\n//for example when calling getData or getFilter, to return either the parent or the child samples, depending on the use case.\nexport function getSampleType(term: any, ds: any) {\n\tif (!term) return null\n\t//non dict terms annotate only samples, eg: gene expression, metabolite intensity, gene variant.\n\t//Their sample type is the default sample type that may or may not have a parent type, depending on the dataset\n\tif (term.type && isNonDictionaryType(term.type)) return DEFAULT_SAMPLE_TYPE\n\t//dictionary terms may annotate different types of samples, eg: patient and sample or mouse and crop.\n\tif (term.id) return ds.cohort.termdb.term2SampleType.get(term.id)\n\tif (term.type == 'samplelst') {\n\t\tconst key = Object.keys(term.values)[0]\n\t\tconst sampleId = term.values[key].list[0]?.sampleId\n\t\tif (sampleId) return ds.sampleId2Type.get(Number(sampleId) || sampleId)\n\t\telse return DEFAULT_SAMPLE_TYPE\n\t}\n\t// samplelst or non dict terms\n\treturn DEFAULT_SAMPLE_TYPE //later own term needs to know what type annotates based on the samples\n}\n\nexport function getParentType(types: Set<string>, ds: any) {\n\tif (Object.keys(ds.cohort.termdb.sampleTypes).length == 0) return null //dataset only has one type of sample\n\tconst ids = Array.from(types)\n\tif (!ids || ids.length == 0) return null\n\tfor (const id of ids) {\n\t\tconst typeObj = ds.cohort.termdb.sampleTypes[id]\n\t\tif (!typeObj) continue\n\t\tif (typeObj.parent_id == null) return id //this is the root type\n\t\t//if my parent is in the list, then I am not the parent\n\t\tif (ids.includes(typeObj.parent_id)) continue\n\t\telse return typeObj.parent_id //my parent is not in the list, so I am the parent\n\t}\n\treturn null //no parent found\n}\n\n// whether the term annotates parent samples\nexport function isParentType(term: any, ds: any) {\n\tif (!ds.cohort.termdb.hasSampleAncestry) return false\n\tconst sampleType = getSampleType(term, ds)\n\tif (!sampleType) throw 'sample type is not defined'\n\tconst sampleTypeObj = ds.cohort.termdb.sampleTypes[sampleType]\n\tif (!sampleTypeObj) throw 'invalid sample type'\n\tif (Number.isInteger(sampleTypeObj.parent_id)) {\n\t\t// sample type has parent, so it is child sample type\n\t\treturn false\n\t} else {\n\t\t// sample type does not have parent, so it is parent sample type\n\t\treturn true\n\t}\n}\n\n//Returns human readable label for each term type; label is just for printing and not computing\nconst typeMap: { [key: string]: string } = {\n\tcategorical: 'Categorical',\n\tcondition: 'Condition',\n\tfloat: 'Numerical',\n\tinteger: 'Numerical',\n\tgeneExpression: 'Gene Expression',\n\tisoformExpression: 'Isoform Expression',\n\tssGSEA: 'Geneset Expression',\n\tdnaMethylation: 'DNA Methylation',\n\tgeneVariant: 'Gene Variant',\n\tmetaboliteIntensity: 'Metabolite Intensity',\n\tproteomeAbundance: 'Proteome Abundance',\n\tproteomeDAP: 'Proteome DAP',\n\tmultivalue: 'Multi Value',\n\tsingleCellGeneExpression: 'Single Cell, Gene Expression',\n\tsingleCellCellType: 'Single Cell, Cell Type',\n\tsnplocus: 'SNP Locus',\n\tsnp: 'SNP',\n\tsnplst: 'SNP List',\n\tnumericDictTerm: 'Numeric Dictionary Term',\n\ttermCollection: 'Term Collection'\n}\n\nexport function termType2label(type: string) {\n\treturn typeMap[type] || 'Unknown term type'\n}\n\nexport function getDateFromNumber(value: number) {\n\tconst year = Math.floor(value)\n\tconst january1st = new Date(year, 0, 1)\n\tconst totalDays = getDaysInYear(year)\n\tconst time = Math.round((value - year) * totalDays) * oneDayTime\n\tconst date = new Date(january1st.getTime() + time)\n\treturn date\n}\n/*\nValue is a decimal year.\nA decimal year is a way of expressing a date or time period as a year with a decimal part, where the decimal portion \nrepresents the fraction of the year that has elapsed. \nExample:\n2025.0 represents the beginning of the year 2025. \n2025.5 represents the middle of the year 2025. \n */\nconst oneDayTime = 24 * 60 * 60 * 1000\n\nexport function getDateStrFromNumber(value: number) {\n\tconst date = getDateFromNumber(value)\n\n\t//Omit day to deidentify the patients\n\treturn date.toLocaleDateString('en-US', {\n\t\tyear: 'numeric',\n\t\tmonth: 'long'\n\t})\n}\n\n//The value returned is a decimal year\n//A decimal year is a way of expressing a date or time period as a year with a decimal part, where the decimal portion\n//represents the fraction of the year that has elapsed.\nexport function getNumberFromDateStr(str: string) {\n\tconst date = new Date(str)\n\treturn getNumberFromDate(date)\n}\n\nexport function getNumberFromDate(date: Date) {\n\tconst year = date.getFullYear()\n\tconst january1st: Date = new Date(year, 0, 1)\n\tconst diffDays = (date.getTime() - january1st.getTime()) / oneDayTime\n\tconst daysTotal = getDaysInYear(year)\n\tconst decimal = diffDays / daysTotal\n\treturn year + decimal\n}\n\nexport function getDaysInYear(year: number) {\n\tconst isLeap = new Date(year, 1, 29).getMonth() === 1\n\tconst days = isLeap ? 366 : 365\n\treturn days\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["GENE_VARIANT", "GENE_EXPRESSION", "ISOFORM_EXPRESSION", "SSGSEA", "DNA_METHYLATION", "CATEGORICAL", "INTEGER", "FLOAT", "SNP", "SNP_LIST", "SNP_LOCUS", "CONDITION", "SURVIVAL", "SAMPLELST", "METABOLITE_INTENSITY", "PROTEOME_ABUNDANCE", "SINGLECELL_CELLTYPE", "SINGLECELL_GENE_EXPRESSION", "MULTIVALUE", "DATE", "TERM_COLLECTION", "TermTypes", "TermTypeGroups"]
7
7
  }
package/package.json CHANGED
@@ -1,43 +1,44 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-shared",
3
- "version": "2.189.0",
3
+ "version": "2.190.1",
4
4
  "description": "ProteinPaint code that is shared between server and client-side workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
7
7
  "imports": {
8
8
  "#types": "@sjcrh/proteinpaint-types",
9
- "#types/*": "@sjcrh/proteinpaint-types/*",
10
- "#types/checkers": "@sjcrh/proteinpaint-types/checkers"
9
+ "#types/*": "@sjcrh/proteinpaint-types/*"
11
10
  },
12
- "//": "all export aliases below are meant for runtime code, except for devTs-aliased subpaths",
11
+ "//": "all export aliases below are meant for runtime code, except for the devTs-aliased subpath",
13
12
  "exports": {
14
- ".": "./dist/src/index.js",
15
- "./*.ts": "./*.ts_SHOULD_BE_js",
16
- "./constants/*.ts": "./dist/constants/*.ts_SHOULD_BE_js",
17
- "./*": "./dist/src/*",
18
- "./constants/*": "./dist/constants/*",
19
- "./devTs": "./devTs.ts",
20
- "./devTs/*": "./constants/*"
13
+ ".": {
14
+ "sjpp/dev": "./src/index.ts",
15
+ "default": "./dist/src/index.js"
16
+ },
17
+ "./*.ts": {
18
+ "sjpp/dev": "./src/*.ts",
19
+ "default": "./*.ts_SHOULD_BE_js"
20
+ },
21
+ "./*": {
22
+ "sjpp/dev": "./src/*",
23
+ "default": "./dist/src/*"
24
+ }
21
25
  },
22
26
  "scripts": {
23
- "build": "esbuild src/*.* constants/*.ts --platform=node --outdir=dist --format=esm --sourcemap",
24
- "prepack": "rm -rf dist && npm run build && tsc --sourcemap --skipLibCheck --preserveWatchOutput",
25
- "dev": "rm -rf dist && concurrently 'npm run build -- --watch' 'npx tsc --watch --skipLibCheck --preserveWatchOutput'",
27
+ "ts2js": "esbuild src/*.* src/**/*.* --platform=node --outdir=dist/src --format=esm --sourcemap",
28
+ "build": "rm -rf dist && npm run ts2js && rm -rf dist/**/*.spec.* dist/**/test && tsc --sourcemap --skipLibCheck",
29
+ "prepack": "npm run build",
30
+ "dev0": "rm -rf dist && 'npx tsc --watch --skipLibCheck --preserveWatchOutput'",
31
+ "dev": "echo '--- now using raw shared/utils/src exports under conditions=sjpp/dev ---\n'",
26
32
  "pretest": "mkdir -p test && node emitImports > test/internals-test.ts",
27
- "test": "tsx test/internals-test.ts",
33
+ "test": "tsx --conditions=sjpp/dev test/internals-test.ts",
28
34
  "test-x": "ls src/test/*.spec* | xargs -I % bash -c '{ tsx %; sleep 0.001; }'",
29
35
  "spec:coverage": "node test/relevant.js"
30
36
  },
31
37
  "author": "",
32
38
  "license": "ISC",
33
39
  "files": [
34
- "constants",
35
- "devTs.ts",
36
40
  "dist"
37
41
  ],
38
- "devDependencies": {
39
- "esbuild": "^0.25.9"
40
- },
41
42
  "repository": {
42
43
  "type": "git",
43
44
  "url": "https://github.com/stjude/proteinpaint",
@@ -1,31 +0,0 @@
1
- export const FlagStatus = {
2
- Normal: 0,
3
- Skipped: 1,
4
- Flagged: 2,
5
- Deleted: 3
6
- } as const
7
-
8
- export type FlagStatusValues = (typeof FlagStatus)[keyof typeof FlagStatus]
9
-
10
- export const FeaturePrefixes = {
11
- Star: 'annotation-star-',
12
- Square: 'annotation-square-',
13
- Border: 'annotation-border-',
14
- PredBorder: 'prediction-border-'
15
- } as const
16
-
17
- export type FeaturePrefixValues = (typeof FeaturePrefixes)[keyof typeof FeaturePrefixes]
18
-
19
- export const SelectionPrefixes = {
20
- TileSelection: 'ts_',
21
- Prediction: 'pred_',
22
- Annotation: 'anno_'
23
- } as const
24
-
25
- export type SelectionPrefixValues = (typeof SelectionPrefixes)[keyof typeof SelectionPrefixes]
26
- //Didn't add Deleted to FlagStatusMessages because deleted annotations dont exist and deleted predictons are filtered out in /Users/jsimps98/dev/sjpp/proteinpaint/server/routes/aiProjectSelectedWSImages.ts line 119
27
- export const FlagStatusMessages = {
28
- [FlagStatus.Normal]: '',
29
- [FlagStatus.Skipped]: '(Skipped)',
30
- [FlagStatus.Flagged]: '(Flagged)'
31
- }
@@ -1,11 +0,0 @@
1
- # Shared Constants
2
-
3
- The goal of the `constants` code is to allow shared/types code to be able to import type definitions
4
- from shared/utils with no bundling or tsc compilation issues. Ideally, there would be a separate
5
- `shared/constants` workspace. However, due to time constraints and effort required to set up a
6
- new workspace, this `shared/utils/constants` directory was created instead.
7
-
8
- The code files in proteinpaint/shared/utils/constants:
9
- - typescript files
10
- - must have no imports from outside this folder, to prevent cyclical references/imports that crash bundling and/or tsc compilation
11
- - must export constants that can be transitively exported to devTs.ts file and shared/utils/src/index.js
package/devTs.ts DELETED
@@ -1,3 +0,0 @@
1
- // please list in alphanumeric order for readability
2
- // use .ts file extension for all code files below
3
- export * from './constants/AiHisto.ts'
@@ -1,25 +0,0 @@
1
- export declare const FlagStatus: {
2
- readonly Normal: 0;
3
- readonly Skipped: 1;
4
- readonly Flagged: 2;
5
- readonly Deleted: 3;
6
- };
7
- export type FlagStatusValues = (typeof FlagStatus)[keyof typeof FlagStatus];
8
- export declare const FeaturePrefixes: {
9
- readonly Star: "annotation-star-";
10
- readonly Square: "annotation-square-";
11
- readonly Border: "annotation-border-";
12
- readonly PredBorder: "prediction-border-";
13
- };
14
- export type FeaturePrefixValues = (typeof FeaturePrefixes)[keyof typeof FeaturePrefixes];
15
- export declare const SelectionPrefixes: {
16
- readonly TileSelection: "ts_";
17
- readonly Prediction: "pred_";
18
- readonly Annotation: "anno_";
19
- };
20
- export type SelectionPrefixValues = (typeof SelectionPrefixes)[keyof typeof SelectionPrefixes];
21
- export declare const FlagStatusMessages: {
22
- 0: string;
23
- 1: string;
24
- 2: string;
25
- };
@@ -1,29 +0,0 @@
1
- const FlagStatus = {
2
- Normal: 0,
3
- Skipped: 1,
4
- Flagged: 2,
5
- Deleted: 3
6
- };
7
- const FeaturePrefixes = {
8
- Star: "annotation-star-",
9
- Square: "annotation-square-",
10
- Border: "annotation-border-",
11
- PredBorder: "prediction-border-"
12
- };
13
- const SelectionPrefixes = {
14
- TileSelection: "ts_",
15
- Prediction: "pred_",
16
- Annotation: "anno_"
17
- };
18
- const FlagStatusMessages = {
19
- [FlagStatus.Normal]: "",
20
- [FlagStatus.Skipped]: "(Skipped)",
21
- [FlagStatus.Flagged]: "(Flagged)"
22
- };
23
- export {
24
- FeaturePrefixes,
25
- FlagStatus,
26
- FlagStatusMessages,
27
- SelectionPrefixes
28
- };
29
- //# sourceMappingURL=AiHisto.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../constants/AiHisto.ts"],
4
- "sourcesContent": ["export const FlagStatus = {\n\tNormal: 0,\n\tSkipped: 1,\n\tFlagged: 2,\n\tDeleted: 3\n} as const\n\nexport type FlagStatusValues = (typeof FlagStatus)[keyof typeof FlagStatus]\n\nexport const FeaturePrefixes = {\n\tStar: 'annotation-star-',\n\tSquare: 'annotation-square-',\n\tBorder: 'annotation-border-',\n\tPredBorder: 'prediction-border-'\n} as const\n\nexport type FeaturePrefixValues = (typeof FeaturePrefixes)[keyof typeof FeaturePrefixes]\n\nexport const SelectionPrefixes = {\n\tTileSelection: 'ts_',\n\tPrediction: 'pred_',\n\tAnnotation: 'anno_'\n} as const\n\nexport type SelectionPrefixValues = (typeof SelectionPrefixes)[keyof typeof SelectionPrefixes]\n//Didn't add Deleted to FlagStatusMessages because deleted annotations dont exist and deleted predictons are filtered out in /Users/jsimps98/dev/sjpp/proteinpaint/server/routes/aiProjectSelectedWSImages.ts line 119\nexport const FlagStatusMessages = {\n\t[FlagStatus.Normal]: '',\n\t[FlagStatus.Skipped]: '(Skipped)',\n\t[FlagStatus.Flagged]: '(Flagged)'\n}\n"],
5
- "mappings": "AAAO,MAAM,aAAa;AAAA,EACzB,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACV;AAIO,MAAM,kBAAkB;AAAA,EAC9B,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AACb;AAIO,MAAM,oBAAoB;AAAA,EAChC,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,YAAY;AACb;AAIO,MAAM,qBAAqB;AAAA,EACjC,CAAC,WAAW,MAAM,GAAG;AAAA,EACrB,CAAC,WAAW,OAAO,GAAG;AAAA,EACtB,CAAC,WAAW,OAAO,GAAG;AACvB;",
6
- "names": []
7
- }
@@ -1,5 +0,0 @@
1
- import { type TileSelection } from '@sjcrh/proteinpaint-types';
2
- import type { FeaturePrefixValues, SelectionPrefixValues } from '../constants/AiHisto.js';
3
- export declare function createSelectionID(prefix: SelectionPrefixValues, coordinates: [number, number]): string;
4
- export declare function checkSelectionType(tileSelection: TileSelection, suspectedPrefix: SelectionPrefixValues): boolean;
5
- export declare function createFeatureID(featurePrefix: FeaturePrefixValues, coords: [number, number]): string;
@@ -1,15 +0,0 @@
1
- function createSelectionID(prefix, coordinates) {
2
- return prefix + JSON.stringify(coordinates);
3
- }
4
- function checkSelectionType(tileSelection, suspectedPrefix) {
5
- return tileSelection.id.startsWith(suspectedPrefix);
6
- }
7
- function createFeatureID(featurePrefix, coords) {
8
- return featurePrefix + JSON.stringify(coords);
9
- }
10
- export {
11
- checkSelectionType,
12
- createFeatureID,
13
- createSelectionID
14
- };
15
- //# sourceMappingURL=aiHisto.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/aiHisto.ts"],
4
- "sourcesContent": ["import { type TileSelection } from '@sjcrh/proteinpaint-types'\nimport type { FeaturePrefixValues, SelectionPrefixValues } from '../constants/AiHisto.js'\n\nexport function createSelectionID(prefix: SelectionPrefixValues, coordinates: [number, number]): string {\n\treturn prefix + JSON.stringify(coordinates)\n}\n\nexport function checkSelectionType(tileSelection: TileSelection, suspectedPrefix: SelectionPrefixValues): boolean {\n\treturn tileSelection.id.startsWith(suspectedPrefix)\n}\n\nexport function createFeatureID(featurePrefix: FeaturePrefixValues, coords: [number, number]) {\n\treturn featurePrefix + JSON.stringify(coords)\n}\n"],
5
- "mappings": "AAGO,SAAS,kBAAkB,QAA+B,aAAuC;AACvG,SAAO,SAAS,KAAK,UAAU,WAAW;AAC3C;AAEO,SAAS,mBAAmB,eAA8B,iBAAiD;AACjH,SAAO,cAAc,GAAG,WAAW,eAAe;AACnD;AAEO,SAAS,gBAAgB,eAAoC,QAA0B;AAC7F,SAAO,gBAAgB,KAAK,UAAU,MAAM;AAC7C;",
6
- "names": []
7
- }