@sjcrh/proteinpaint-server 2.211.0 → 2.212.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.211.0",
3
+ "version": "2.212.0",
4
4
  "type": "module",
5
5
  "description": "a genomics visualization tool for exploring a cohort's genotype and phenotype data",
6
6
  "main": "src/app.js",
@@ -19,6 +19,7 @@
19
19
  "start": "tsx watch ./start.js",
20
20
  "test:unit": "tsx emitImports.js unit > serverTests.js && c8 tsx --conditions=sjpp/dev serverTests.js && rm -rf ./cache",
21
21
  "test": "tsc && npm run test:unit",
22
+ "test:request": "code=0; for f in src/test/*.request.integration.spec.ts; do tsx --conditions=sjpp/dev $f || code=1; done; exit $code",
22
23
  "precombined:coverage": "tsx emitImports.js unit > serverTests.js",
23
24
  "combined:coverage": "coverageKey=test c8 --all --src=proteinpaint/server --experimental-monocart -r=v8 -r=html -r=json -r=markdown-summary -r=markdown-details -o=./.coverage tsx ./coverage.js & ",
24
25
  "postcombined:coverage": "rm -rf ./cache",
@@ -58,10 +59,10 @@
58
59
  "dependencies": {
59
60
  "@sjcrh/augen": "2.210.1",
60
61
  "@sjcrh/proteinpaint-python": "2.209.0",
61
- "@sjcrh/proteinpaint-r": "2.207.1",
62
+ "@sjcrh/proteinpaint-r": "2.212.0",
62
63
  "@sjcrh/proteinpaint-rust": "2.209.0",
63
- "@sjcrh/proteinpaint-shared": "2.210.1",
64
- "@sjcrh/proteinpaint-types": "2.211.0",
64
+ "@sjcrh/proteinpaint-shared": "2.211.1-0",
65
+ "@sjcrh/proteinpaint-types": "2.212.0",
65
66
  "@types/express": "^5.0.0",
66
67
  "@types/express-session": "^1.18.1",
67
68
  "better-sqlite3": "^12.4.1",
@@ -121,5 +122,5 @@
121
122
  ]
122
123
  ]
123
124
  },
124
- "_buildTime": "2026-09-24T02:12:34Z"
125
+ "_buildTime": "2026-09-26T07:30:19Z"
125
126
  }
@@ -3,6 +3,7 @@ import { get_ds_tdb } from "#src/termdb.js";
3
3
  import * as utils from "#src/utils.js";
4
4
  import { mayLimitSamples } from "#src/mds3.filter.js";
5
5
  import serverconfig from "#src/serverconfig.js";
6
+ import { sql } from "#src/sql.ts";
6
7
  import { readGeneRows, baseUniProtAcc } from "../src/routes/termdb.bubbleHeatmap.ts";
7
8
  const missingDapWarned = /* @__PURE__ */ new Set();
8
9
  function init({ genomes }) {
@@ -180,7 +181,7 @@ async function validate_query_proteome(ds) {
180
181
  }
181
182
  }
182
183
  }
183
- const geneIndexHint = q.db.prepare("SELECT 1 FROM sqlite_master WHERE type = ? AND name = ?").get("index", "proteome_abundance_gene") ? " INDEXED BY proteome_abundance_gene" : "";
184
+ const geneIndexHint = q.db.prepare("SELECT 1 FROM sqlite_master WHERE type = ? AND name = ?").get("index", "proteome_abundance_gene") ? sql` INDEXED BY proteome_abundance_gene` : sql``;
184
185
  q.find = async (arg) => {
185
186
  const proteins = arg?.proteins;
186
187
  if (!Array.isArray(proteins) || proteins.length == 0) throw "queries.proteome.find arg.proteins[] missing";
@@ -215,16 +216,15 @@ async function validate_query_proteome(ds) {
215
216
  const upperToken = `${token}\uFFFF`;
216
217
  const rawRows = [];
217
218
  if (filters?.length) {
218
- const { conditions, params } = buildFilterClause(filters);
219
- const sql = `SELECT DISTINCT gene, identifier FROM proteome_abundance${geneIndexHint} WHERE gene >= ? COLLATE NOCASE AND gene < ? COLLATE NOCASE AND ${conditions.join(
220
- " AND "
219
+ const query = sql`SELECT DISTINCT gene, identifier FROM proteome_abundance${geneIndexHint} WHERE gene >= ${token} COLLATE NOCASE AND gene < ${upperToken} COLLATE NOCASE AND ${buildFilterClause(
220
+ filters
221
221
  )} LIMIT ${MAX_FIND_RESULTS}`;
222
- rawRows.push(...q.db.prepare(sql).all(token, upperToken, ...params));
222
+ rawRows.push(...q.db.prepare(query).all());
223
223
  } else {
224
224
  rawRows.push(
225
225
  ...q.db.prepare(
226
- `SELECT DISTINCT gene, identifier FROM proteome_abundance WHERE gene >= ? COLLATE NOCASE AND gene < ? COLLATE NOCASE LIMIT ${MAX_FIND_RESULTS}`
227
- ).all(token, upperToken)
226
+ sql`SELECT DISTINCT gene, identifier FROM proteome_abundance WHERE gene >= ${token} COLLATE NOCASE AND gene < ${upperToken} COLLATE NOCASE LIMIT ${MAX_FIND_RESULTS}`
227
+ ).all()
228
228
  );
229
229
  }
230
230
  for (const row of rawRows) {
@@ -256,14 +256,10 @@ function resolveColumnName(idx) {
256
256
  return name;
257
257
  }
258
258
  function buildFilterClause(filters) {
259
- const conditions = [];
260
- const params = [];
261
- for (const f of filters) {
262
- const colName = resolveColumnName(f.columnIdx);
263
- conditions.push(`${colName} = ?`);
264
- params.push(f.columnValue);
265
- }
266
- return { conditions, params };
259
+ return sql.join(
260
+ filters.map((f) => sql`${sql.id(resolveColumnName(f.columnIdx))} = ${f.columnValue}`),
261
+ sql` AND `
262
+ );
267
263
  }
268
264
  function listCohortSamples(db, filters) {
269
265
  if (!filters?.length) throw "listCohortSamples: filters must not be empty";
@@ -272,26 +268,22 @@ function listCohortSamples(db, filters) {
272
268
  const key = JSON.stringify(filters);
273
269
  const hit = perDb.get(key);
274
270
  if (hit) return hit;
275
- const { conditions, params } = buildFilterClause(filters);
276
- const samples = db.prepare(`SELECT DISTINCT sample FROM proteome_abundance WHERE ${conditions.join(" AND ")}`).all(...params).map((r) => String(r.sample));
271
+ const samples = db.prepare(sql`SELECT DISTINCT sample FROM proteome_abundance WHERE ${buildFilterClause(filters)}`).all().map((r) => String(r.sample));
277
272
  perDb.set(key, samples);
278
273
  return samples;
279
274
  }
280
275
  const cohortSampleCache = /* @__PURE__ */ new WeakMap();
281
276
  function listIdentifierAnnotations(db, gene, filters) {
282
- const { conditions, params } = buildFilterClause(filters);
283
277
  const rows = db.prepare(
284
- `SELECT identifier, modsite, isoform FROM proteome_abundance WHERE gene = ? COLLATE NOCASE${conditions.length ? " AND " + conditions.join(" AND ") : ""} GROUP BY identifier`
285
- ).all(gene, ...params);
278
+ sql`SELECT identifier, modsite, isoform FROM proteome_abundance WHERE gene = ${gene} COLLATE NOCASE${filters.length ? sql` AND ${buildFilterClause(filters)}` : sql``} GROUP BY identifier`
279
+ ).all();
286
280
  return new Map(rows.map((r) => [r.identifier, { modsite: r.modsite, isoform: r.isoform }]));
287
281
  }
288
282
  function queryDbRows(db, identifier, filters) {
289
- const { conditions, params } = buildFilterClause(filters);
290
- const allConditions = [`identifier = ? COLLATE NOCASE`, ...conditions];
291
- const sql = `SELECT organism, disease, identifier, protein_accession, isoform, modsite, gene, sample, value, brain_region
283
+ const query = sql`SELECT organism, disease, identifier, protein_accession, isoform, modsite, gene, sample, value, brain_region
292
284
  FROM proteome_abundance
293
- WHERE ${allConditions.join(" AND ")}`;
294
- return db.prepare(sql).all(identifier, ...params);
285
+ WHERE identifier = ${identifier} COLLATE NOCASE${filters.length ? sql` AND ${buildFilterClause(filters)}` : sql``}`;
286
+ return db.prepare(query).all();
295
287
  }
296
288
  async function getProteomeValuesFromCohort(ds, param, q) {
297
289
  const db = ds.queries.proteome.db;