@datagrok/bio 2.27.7 → 2.27.8

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
@@ -5,7 +5,7 @@
5
5
  "name": "Davit Rizhinashvili",
6
6
  "email": "drizhinashvili@datagrok.ai"
7
7
  },
8
- "version": "2.27.7",
8
+ "version": "2.27.8",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
@@ -44,7 +44,7 @@
44
44
  ],
45
45
  "dependencies": {
46
46
  "@biowasm/aioli": "^3.1.0",
47
- "@datagrok-libraries/bio": "^5.65.0",
47
+ "@datagrok-libraries/bio": "^5.65.1",
48
48
  "@datagrok-libraries/chem-meta": "^1.2.9",
49
49
  "@datagrok-libraries/math": "^1.2.6",
50
50
  "@datagrok-libraries/ml": "^6.10.11",
@@ -547,6 +547,30 @@ category('toAtomicLevelHelmRna', async () => {
547
547
  expect(pCount, 0, `expected 0 phosphates with both terminals: ${smiles}`);
548
548
  });
549
549
 
550
+ // LNA (2,4-BNA) regression. The 2,4-O-CH2 bridge sits ABOVE C1' once the
551
+ // sugar is oriented with R1/R2 atoms horizontal, so the natural R3 vector
552
+ // points sideways instead of up. Without the abnormal-sugar override the
553
+ // base ends up sideways from the sugar (or worse, overlapping it). The
554
+ // assertion here is structural: must produce a valid single-fragment
555
+ // SMILES with the LNA-specific bridge oxygen plus the normal nucleoside
556
+ // features. Coordinates aren't checked — only connectivity.
557
+ test('rna-helm-lna-base-above-sugar', async () => {
558
+ const smiles = await helmRnaLinearToSmiles(`RNA1{[lna](A)p.[lna](T)}$$$$V2.0`);
559
+ expect(smiles !== 'MALFORMED_INPUT_VALUE' && smiles.length > 10, true,
560
+ `valid SMILES expected: ${smiles}`);
561
+ expect(smiles.indexOf('.') === -1, true,
562
+ `expected single fragment: ${smiles}`);
563
+ // Sanity check for nitrogens — adenine brings 5 (4 ring + 1 NH2) and
564
+ // thymine brings 2 (both ring), so at least 7 total. Match both
565
+ // uppercase (N, [nH]) and lowercase aromatic (n) — N atoms in heterocyclic
566
+ // SMILES are written lowercase when aromatic.
567
+ const nCount = (smiles.match(/[Nn]/g) || []).length;
568
+ expect(nCount >= 7, true, `expected at least 7 nitrogens: ${smiles}`);
569
+ // One inter-nucleotide phosphate.
570
+ const pCount = (smiles.match(/P/g) || []).length;
571
+ expect(pCount, 1, `expected exactly 1 phosphate: ${smiles}`);
572
+ });
573
+
550
574
  // GalNAc oxygen-count regression. Previously the R1 placeholder atom
551
575
  // (substituted to 'O' from the "OH" cap) was left in the assembly,
552
576
  // adding a stray OH on the chain-attach carbon. lna(T)GalNAc has known
@@ -583,6 +607,40 @@ category('toAtomicLevelHelmRna', async () => {
583
607
  expect(/S/.test(smiles), true, `expected sulfur from sp: ${smiles}`);
584
608
  });
585
609
 
610
+ // Regression: H-cap phosphates (sp et al.) used to drop the bridging O
611
+ // on the 3' side of the linkage. The previous sugar's 3'-O is removed
612
+ // unconditionally during sugar processing on the assumption that the
613
+ // following linker brings its own bridging oxygen via the R1 cap; with
614
+ // an H cap that assumption breaks and the chain ended up as
615
+ // C3'-P(=O)(SH)-O-C5' instead of the proper C3'-O-P(=O)(SH)-O-C5'.
616
+ // The fix promotes the H cap to an O so the bridging atom always exists.
617
+ // Use m(2'-OMe ribose) so we can also verify the methoxy group survives
618
+ // the sp chain assembly.
619
+ test('rna-helm-sp-bridging-o-preserved', async () => {
620
+ const smiles = await helmRnaLinearToSmiles(`RNA1{m(A)[sp].r(A)[sp]}$$$$V2.0`);
621
+ expect(smiles !== 'MALFORMED_INPUT_VALUE' && smiles.length > 10, true,
622
+ `valid SMILES expected: ${smiles}`);
623
+ // Single connected fragment.
624
+ expect(smiles.indexOf('.') === -1, true,
625
+ `expected single fragment: ${smiles}`);
626
+ // 2 sp linkers → 2 phosphorus atoms.
627
+ const pCount = (smiles.match(/P/g) || []).length;
628
+ expect(pCount, 2, `expected exactly 2 phosphates: ${smiles}`);
629
+ // 2 sulfurs (one per sp).
630
+ const sCount = (smiles.match(/S/g) || []).length;
631
+ expect(sCount, 2, `expected exactly 2 sulfurs (one per sp): ${smiles}`);
632
+ // No C-P bond — every P should be bordered by O on both chain sides.
633
+ // A P preceded directly by an aliphatic carbon (lowercase 'c' is
634
+ // aromatic; capital 'C' is sp3) means the bridging O was lost.
635
+ expect(/C\d*P|CP|cP/.test(smiles), false,
636
+ `expected no direct C-P bond (bridging O missing): ${smiles}`);
637
+ // 2' methoxy on m must survive — methyl ether oxygen plus three oxygens
638
+ // per nucleotide gives plenty of O atoms; the structural assertion
639
+ // above is the strict one. Sanity-check that a methyl ether (OC) is
640
+ // present somewhere.
641
+ expect(/OC|CO/.test(smiles), true, `expected methoxy fragment: ${smiles}`);
642
+ });
643
+
586
644
  // R-group swap heuristic: a single-R-group terminal monomer can be placed
587
645
  // at either end of a HELM chain, even if its R-group label "should" only
588
646
  // belong at one end. The conversion swaps rNodes so the existing