@datagrok/bio 2.25.9 → 2.25.10

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.
Files changed (97) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/CLAUDE.md +380 -0
  3. package/detectors.js +9 -1
  4. package/dist/111.js +1 -1
  5. package/dist/111.js.map +1 -1
  6. package/dist/234.js +1 -1
  7. package/dist/234.js.map +1 -1
  8. package/dist/242.js +1 -1
  9. package/dist/242.js.map +1 -1
  10. package/dist/284.js +1 -1
  11. package/dist/284.js.map +1 -1
  12. package/dist/455.js +1 -1
  13. package/dist/455.js.map +1 -1
  14. package/dist/589.js +1 -1
  15. package/dist/589.js.map +1 -1
  16. package/dist/603.js +1 -1
  17. package/dist/603.js.map +1 -1
  18. package/dist/682.js +1 -1
  19. package/dist/682.js.map +1 -1
  20. package/dist/705.js +1 -1
  21. package/dist/705.js.map +1 -1
  22. package/dist/731.js +1 -1
  23. package/dist/731.js.map +1 -1
  24. package/dist/778.js +1 -1
  25. package/dist/778.js.map +1 -1
  26. package/dist/793.js +1 -1
  27. package/dist/793.js.map +1 -1
  28. package/dist/810.js +1 -1
  29. package/dist/810.js.map +1 -1
  30. package/dist/950.js +1 -1
  31. package/dist/950.js.map +1 -1
  32. package/dist/980.js +1 -1
  33. package/dist/980.js.map +1 -1
  34. package/dist/package-test.js +5 -5
  35. package/dist/package-test.js.map +1 -1
  36. package/dist/package.js +3 -3
  37. package/dist/package.js.map +1 -1
  38. package/package.json +11 -4
  39. package/scripts/read-tree-pkl.py +1 -1
  40. package/src/demo/bio03-atomic-level.ts +1 -1
  41. package/src/package-test.ts +4 -2
  42. package/src/package.g.ts +49 -13
  43. package/src/package.ts +63 -36
  44. package/src/substructure-search/substructure-search.ts +1 -1
  45. package/src/tests/Palettes-test.ts +1 -1
  46. package/src/tests/WebLogo-layout-tests.ts +1 -1
  47. package/src/tests/WebLogo-positions-test.ts +1 -1
  48. package/src/tests/WebLogo-project-tests.ts +1 -1
  49. package/src/tests/_first-tests.ts +1 -1
  50. package/src/tests/activity-cliffs-tests.ts +1 -1
  51. package/src/tests/activity-cliffs-utils.ts +1 -1
  52. package/src/tests/biln-tests.ts +1 -1
  53. package/src/tests/bio-tests.ts +1 -1
  54. package/src/tests/checkInputColumn-tests.ts +1 -1
  55. package/src/tests/converters-test.ts +1 -1
  56. package/src/tests/detectors-benchmark-tests.ts +1 -1
  57. package/src/tests/detectors-tests.ts +1 -1
  58. package/src/tests/detectors-weak-and-likely-tests.ts +1 -1
  59. package/src/tests/fasta-export-tests.ts +1 -1
  60. package/src/tests/fasta-handler-test.ts +1 -1
  61. package/src/tests/helm-tests.ts +1 -1
  62. package/src/tests/lib-tests.ts +1 -1
  63. package/src/tests/mm-distance-tests.ts +1 -1
  64. package/src/tests/monomer-libraries-tests.ts +1 -1
  65. package/src/tests/msa-tests.ts +1 -1
  66. package/src/tests/pepsea-tests.ts +1 -1
  67. package/src/tests/renderers-monomer-placer-tests.ts +1 -1
  68. package/src/tests/renderers-test.ts +1 -1
  69. package/src/tests/scoring.ts +1 -1
  70. package/src/tests/seq-handler-get-helm-tests.ts +1 -1
  71. package/src/tests/seq-handler-get-region-tests.ts +1 -1
  72. package/src/tests/seq-handler-splitted-tests.ts +1 -1
  73. package/src/tests/seq-handler-tests.ts +1 -1
  74. package/src/tests/sequence-space-test.ts +1 -1
  75. package/src/tests/sequence-space-utils.ts +1 -1
  76. package/src/tests/similarity-diversity-tests.ts +1 -1
  77. package/src/tests/splitters-test.ts +1 -1
  78. package/src/tests/substructure-filters-tests.ts +1 -1
  79. package/src/tests/to-atomic-level-tests.ts +1 -1
  80. package/src/tests/to-atomic-level-ui-tests.ts +1 -1
  81. package/src/tests/utils/detectors-utils.ts +1 -1
  82. package/src/tests/utils/sequences-generators.ts +1 -1
  83. package/src/tests/utils.ts +1 -1
  84. package/src/tests/viewers.ts +1 -1
  85. package/src/utils/detect-macromolecule-probe.ts +1 -1
  86. package/src/utils/monomer-lib/lib-manager.ts +2 -2
  87. package/src/utils/monomer-lib/monomer-lib-base.ts +7 -3
  88. package/src/utils/multiple-sequence-alignment-ui.ts +1 -1
  89. package/src/utils/seq-helper/seq-handler.ts +22 -10
  90. package/src/utils/split-to-monomers.ts +1 -1
  91. package/src/viewers/vd-regions-viewer.ts +1 -1
  92. package/src/viewers/web-logo-viewer.ts +1 -1
  93. package/src/widgets/bio-substructure-filter-helm.ts +1 -1
  94. package/src/widgets/bio-substructure-filter.ts +1 -1
  95. package/src/widgets/to-atomic-level-widget.ts +1 -7
  96. package/test-console-output-1.log +1279 -3049
  97. package/test-record-1.mp4 +0 -0
@@ -1,7 +1,7 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
- import {expect} from '@datagrok-libraries/utils/src/test';
4
+ import {expect} from '@datagrok-libraries/test/src/test';
5
5
  import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
6
6
  import {BYPASS_LARGE_DATA_WARNING} from '@datagrok-libraries/ml/src/functionEditors/consts';
7
7
  import {DimReductionMethods} from '@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/types';
@@ -1,7 +1,7 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
- import {category, test, expect, awaitCheck, delay} from '@datagrok-libraries/utils/src/test';
4
+ import {category, test, expect, awaitCheck, delay} from '@datagrok-libraries/test/src/test';
5
5
  import {SequenceSimilarityViewer} from '../analysis/sequence-similarity-viewer';
6
6
  import {SequenceDiversityViewer} from '../analysis/sequence-diversity-viewer';
7
7
 
@@ -4,7 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import wu from 'wu';
6
6
 
7
- import {after, before, category, test, expect, expectArray} from '@datagrok-libraries/utils/src/test';
7
+ import {after, before, category, test, expect, expectArray} from '@datagrok-libraries/test/src/test';
8
8
  import {TAGS as bioTAGS, splitterAsFasta} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
9
  import {splitterAsHelm} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
10
10
  import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
@@ -7,7 +7,7 @@ import * as DG from 'datagrok-api/dg';
7
7
  import $ from 'cash-dom';
8
8
  import wu from 'wu';
9
9
 
10
- import {after, before, category, test, expect, delay, testEvent, awaitCheck} from '@datagrok-libraries/utils/src/test';
10
+ import {after, before, category, test, expect, delay, testEvent, awaitCheck} from '@datagrok-libraries/test/src/test';
11
11
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/types/monomer-library';
12
12
  import {
13
13
  getUserLibSettings, setUserLibSettings
@@ -5,7 +5,7 @@ import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import wu from 'wu';
7
7
 
8
- import {before, after, category, test, expectArray, expect} from '@datagrok-libraries/utils/src/test';
8
+ import {before, after, category, test, expectArray, expect} from '@datagrok-libraries/test/src/test';
9
9
  import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
10
10
  import {_toAtomicLevel} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
11
11
  import {IMonomerLib} from '@datagrok-libraries/bio/src/types/monomer-library';
@@ -1,7 +1,7 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
- import {after, before, category, expect, expectArray, test} from '@datagrok-libraries/utils/src/test';
4
+ import {after, before, category, expect, expectArray, test} from '@datagrok-libraries/test/src/test';
5
5
  import {IMonomerLib} from '@datagrok-libraries/bio/src/types/monomer-library';
6
6
  import {sequenceToMolfile} from '../utils/sequence-to-mol';
7
7
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/types/monomer-library';
@@ -2,7 +2,7 @@ import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
4
  import {ALIGNMENT, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
5
- import {expect} from '@datagrok-libraries/utils/src/test';
5
+ import {expect} from '@datagrok-libraries/test/src/test';
6
6
  import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
7
7
 
8
8
  export type DetectorTestData = { [testName: string]: { csv: string, neg?: string[], pos?: { [colName: string]: PosCol } } };
@@ -3,7 +3,7 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {ALIGNMENT, ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
6
- import {expect} from '@datagrok-libraries/utils/src/test';
6
+ import {expect} from '@datagrok-libraries/test/src/test';
7
7
 
8
8
  import {awaitGrid} from '../utils';
9
9
 
@@ -1,7 +1,7 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
 
4
- import {delay, expect, testEvent} from '@datagrok-libraries/utils/src/test';
4
+ import {delay, expect, testEvent} from '@datagrok-libraries/test/src/test';
5
5
  import {asRenderer, IRenderer, isRenderer} from '@datagrok-libraries/bio/src/types/renderer';
6
6
 
7
7
  import {_package} from '../package-test';
@@ -2,7 +2,7 @@ import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
  //import * as ui from 'datagrok-api/ui';
4
4
 
5
- import {category, test, testViewer} from '@datagrok-libraries/utils/src/test';
5
+ import {category, test, testViewer} from '@datagrok-libraries/test/src/test';
6
6
  import {readDataframe} from './utils';
7
7
 
8
8
 
@@ -4,7 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
 
6
6
  import {_package} from '../package';
7
- import {delay} from '@datagrok-libraries/utils/src/test';
7
+ import {delay} from '@datagrok-libraries/test/src/test';
8
8
 
9
9
  type IDetectorReport = { categoriesSample: any[], rejectReason: string };
10
10
 
@@ -4,7 +4,7 @@ import * as grok from 'datagrok-api/grok';
4
4
  import * as ui from 'datagrok-api/ui';
5
5
  import * as DG from 'datagrok-api/dg';
6
6
 
7
- import {delay} from '@datagrok-libraries/utils/src/test';
7
+ import {delay} from '@datagrok-libraries/test/src/test';
8
8
  import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
9
9
  import {DEFAULT_FILES_LIB_PROVIDER_NAME, findProviderWithLibraryName, IMonomerLib, IMonomerSet} from '@datagrok-libraries/bio/src/types/monomer-library';
10
10
  import {
@@ -103,7 +103,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
103
103
  private _monomerLibProviders: IMonomerLibProvider[] | null = null;
104
104
  public async getProviders(): Promise<IMonomerLibProvider[]> {
105
105
  if (this._monomerLibProviders == null) {
106
- const providerFuncs = DG.Func.find({tags: ['monomer-lib-provider']});
106
+ const providerFuncs = DG.Func.find({meta: {role: 'monomer-lib-provider'}});
107
107
  this._monomerLibProviders = await Promise.all(providerFuncs.map(async (func) => {
108
108
  return (await func.apply({})) as IMonomerLibProvider;
109
109
  }));
@@ -64,7 +64,7 @@ export class MonomerLibBase implements IMonomerLibBase {
64
64
  }
65
65
 
66
66
  getMonomerSymbolsByType(polymerType: PolymerType): string[] {
67
- const res = Object.keys(this._monomers[polymerType]);
67
+ const res = Object.keys(this._monomers[polymerType] ?? {});
68
68
  if (this._smilesMonomerCache[polymerType])
69
69
  res.push(...Object.keys(this._smilesMonomerCache[polymerType]));
70
70
  return res;
@@ -130,7 +130,7 @@ export class MonomerLibBase implements IMonomerLibBase {
130
130
  }
131
131
 
132
132
  getMonomer(polymerType: PolymerType | null, argMonomerSymbol: string): Monomer | null {
133
- const logPrefix = `Bio: MonomerLib.getMonomer()`;
133
+ // const logPrefix = `Bio: MonomerLib.getMonomer()`;
134
134
  // Adjust RNA's 'R' for ribose to 'r' and 'P' for phosphate to 'p' for case-sensitive monomer names.
135
135
  // There are uppercase 'R' and 'P' at RNA samples in test data 'helm2.csv' but lowercase in HELMCoreLibrary.json
136
136
  let monomerSymbol = argMonomerSymbol;
@@ -142,7 +142,7 @@ export class MonomerLibBase implements IMonomerLibBase {
142
142
  let res: Monomer | null = null;
143
143
 
144
144
  if (!polymerType) {
145
- _package.logger.warning(`${logPrefix} symbol '${argMonomerSymbol}', polymerType not specified.`);
145
+ // _package.logger.warning(`${logPrefix} symbol '${argMonomerSymbol}', polymerType not specified.`);
146
146
  // Assume any polymer type
147
147
  for (const [_polymerType, dict] of Object.entries(this._monomers)) {
148
148
  res = dict[monomerSymbol];
@@ -154,6 +154,8 @@ export class MonomerLibBase implements IMonomerLibBase {
154
154
  monomerSymbol = this._smilesMonomerCache[polymerType][monomerSymbol];
155
155
  const dict = this._monomers[polymerType];
156
156
  res = dict?.[monomerSymbol] ?? null;
157
+ if (!res)
158
+ return this.getMonomer(null, monomerSymbol); // fallback to search without polymer type
157
159
  }
158
160
  return res;
159
161
  }
@@ -252,6 +254,8 @@ export class MonomerLibBase implements IMonomerLibBase {
252
254
  .replace(/_/g, ' ')
253
255
  .replace(/-/g, ' ')
254
256
  .replace(/([a-z])([a-z])([A-Z])/g, '$1$2 $3');
257
+ if (monomer.polymerType)
258
+ res.append(ui.divText(monomer.polymerType, {style: {fontStyle: 'italic'}}));
255
259
  res.append(ui.divText(source));
256
260
  }
257
261
  }
@@ -6,7 +6,7 @@ import * as DG from 'datagrok-api/dg';
6
6
  import * as ui from 'datagrok-api/ui';
7
7
 
8
8
  import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
9
- import {delay} from '@datagrok-libraries/utils/src/test';
9
+ import {delay} from '@datagrok-libraries/test/src/test';
10
10
  import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
11
11
  import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
12
12
 
@@ -149,8 +149,7 @@ export class SeqHandler implements ISeqHandler {
149
149
 
150
150
  const stats = getStats(categoriesSample, 3, (s) => s.split(this.separator!));
151
151
  let invalidateRequired = false;
152
-
153
- const refinerList = DG.Func.find({tags: ['notationRefiner']});
152
+ const refinerList = DG.Func.find({meta: {role: 'notationRefiner'}});
154
153
 
155
154
  for (const refineFuncFind of refinerList) {
156
155
  try {
@@ -773,14 +772,25 @@ export class SeqHandler implements ISeqHandler {
773
772
  public convert(tgtNotation: NOTATION, tgtSeparator?: string): DG.Column<string> {
774
773
  // Get joiner from the source column units handler (this) knowing about the source sequence.
775
774
  // For example, converting DNA Helm to fasta requires removing the r(X)p decoration.
776
- const joiner: JoinerFunc = this.getJoiner({notation: tgtNotation, separator: tgtSeparator});
777
- const newColumn = this.getNewColumn(tgtNotation, tgtSeparator);
778
- // assign the values to the newly created empty column
779
- newColumn.init((rowIdx: number) => {
780
- const srcSS = this.getSplitted(rowIdx);
781
- return joiner(srcSS);
782
- });
783
- return newColumn;
775
+ if (!this.isCustom() || tgtNotation !== NOTATION.HELM) {
776
+ const joiner: JoinerFunc = this.getJoiner({notation: tgtNotation, separator: tgtSeparator});
777
+ const newColumn = this.getNewColumn(tgtNotation, tgtSeparator);
778
+ // assign the values to the newly created empty column
779
+ newColumn.init((rowIdx: number) => {
780
+ const srcSS = this.getSplitted(rowIdx);
781
+ return joiner(srcSS);
782
+ });
783
+ return newColumn;
784
+ } else {
785
+ if (!this.notationProvider || !this.notationProvider.getHelm)
786
+ throw new Error('Notation provider with getHelm function is required for custom notation conversion.');
787
+ const newColumn = this.getNewColumn(tgtNotation, tgtSeparator);
788
+ newColumn.init((rowIdx: number) => {
789
+ const srcSeq = this.column.get(rowIdx);
790
+ return this.notationProvider!.getHelm(srcSeq, {});
791
+ });
792
+ return newColumn;
793
+ }
784
794
  }
785
795
 
786
796
  /**
@@ -932,6 +942,8 @@ export class SeqHandler implements ISeqHandler {
932
942
 
933
943
  private convertToHelm(src: string): string {
934
944
  if (this.notation == NOTATION.HELM) return src;
945
+ if (this.isCustom() && this.notationProvider?.getHelm)
946
+ return this.notationProvider.getHelm(src, {});
935
947
 
936
948
  const wrappers = this.getHelmWrappers();
937
949
 
@@ -2,7 +2,7 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {delay} from '@datagrok-libraries/utils/src/test';
5
+ import {delay} from '@datagrok-libraries/test/src/test';
6
6
  import {checkInputColumnUI} from './check-input-column';
7
7
  import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
8
8
  import * as C from './constants';
@@ -4,7 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
6
6
 
7
- import {testEvent} from '@datagrok-libraries/utils/src/test';
7
+ import {testEvent} from '@datagrok-libraries/test/src/test';
8
8
  import {
9
9
  IVdRegionsViewer,
10
10
  VdRegion, VdRegionType,
@@ -22,7 +22,7 @@ import {
22
22
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
23
23
  import {intToHtmlA} from '@datagrok-libraries/utils/src/color';
24
24
  import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
25
- import {testEvent} from '@datagrok-libraries/utils/src/test';
25
+ import {testEvent} from '@datagrok-libraries/test/src/test';
26
26
  import {PromiseSyncer} from '@datagrok-libraries/bio/src/utils/syncer';
27
27
  import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
28
28
  import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/monomer-library';
@@ -9,7 +9,7 @@ import {App, IHelmWebEditor} from '@datagrok-libraries/bio/src/helm/types';
9
9
  import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
10
10
  import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
11
11
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
12
- import {delay} from '@datagrok-libraries/utils/src/test';
12
+ import {delay} from '@datagrok-libraries/test/src/test';
13
13
  import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
14
14
 
15
15
  import {updateDivInnerHTML} from '../utils/ui-utils';
@@ -14,7 +14,7 @@ import wu from 'wu';
14
14
  import {Observable, Subject, Unsubscribable} from 'rxjs';
15
15
 
16
16
  import {TAGS as bioTAGS, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
17
- import {delay, testEvent} from '@datagrok-libraries/utils/src/test';
17
+ import {delay, testEvent} from '@datagrok-libraries/test/src/test';
18
18
  import {IRenderer} from '@datagrok-libraries/bio/src/types/renderer';
19
19
  import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
20
20
  import {PromiseSyncer} from '@datagrok-libraries/bio/src/utils/syncer';
@@ -18,12 +18,6 @@ export async function toAtomicLevelSingle(sequence: DG.SemanticValue): Promise<{
18
18
  errorText = 'Atomic level conversion requeires a sequence column';
19
19
  return {errorText, mol: ''};
20
20
  }
21
- const supportedUnits: string[] = [NOTATION.FASTA, NOTATION.SEPARATOR, NOTATION.HELM, NOTATION.BILN];
22
- //todo: add support for custom notations
23
- if (!supportedUnits.includes(sequence.cell.column.meta.units?.toLowerCase() ?? '')) {
24
- errorText = 'Unsupported sequence notation. please use Bio | Polytool | Convert';
25
- return {errorText, mol: ''};
26
- }
27
21
  const seqHelper = await PackageFunctions.getSeqHelper();
28
22
  const seqSh = seqHelper.getSeqHandler(sequence.cell.column);
29
23
  if (!seqSh) {
@@ -46,7 +40,7 @@ export async function toAtomicLevelSingle(sequence: DG.SemanticValue): Promise<{
46
40
  singleValCol.temp[SeqTemps.notationProvider] = sequence.cell.column.temp[SeqTemps.notationProvider];
47
41
  // helm and biln will have cyclization marks, so we need to use POM to convert them
48
42
  const seqSplitted = seqSh.getSplitted(sequence.cell.rowIndex);
49
- const shouldUsePOM = (seqSplitted.graphInfo?.connections?.length ?? 0) > 0;
43
+ const shouldUsePOM = (seqSplitted.graphInfo?.connections?.length ?? 0) > 0 || seqSh.units === NOTATION.CUSTOM;
50
44
  const isHelmWithMultiplePolymerTypes = seqSh.isHelm() &&
51
45
  (new Set((seqSplitted.graphInfo?.polymerTypes ?? []))).size > 1;
52
46