@genspectrum/dashboard-components 0.6.18 → 0.7.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.
Files changed (118) hide show
  1. package/README.md +5 -12
  2. package/custom-elements.json +22 -22
  3. package/dist/assets/mutationOverTimeWorker-BOCXtKzd.js.map +1 -0
  4. package/dist/dashboard-components.js +301 -302
  5. package/dist/dashboard-components.js.map +1 -1
  6. package/dist/genspectrum-components.d.ts +60 -10
  7. package/dist/style.css +3 -2
  8. package/package.json +13 -4
  9. package/src/index.ts +1 -0
  10. package/src/operator/FetchInsertionsOperator.ts +2 -2
  11. package/src/operator/FetchSubstitutionsOrDeletionsOperator.ts +3 -3
  12. package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +53 -38
  13. package/src/preact/dateRangeSelector/computeInitialValues.ts +17 -23
  14. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +46 -32
  15. package/src/preact/dateRangeSelector/date-range-selector.tsx +24 -26
  16. package/src/preact/dateRangeSelector/dateRangeOption.ts +65 -0
  17. package/src/preact/dateRangeSelector/selectableOptions.ts +17 -66
  18. package/src/preact/mutationComparison/fetchMutationData.spec.ts +3 -3
  19. package/src/preact/mutationComparison/getMutationComparisonTableData.spec.ts +11 -11
  20. package/src/preact/mutationComparison/getMutationComparisonTableData.ts +4 -4
  21. package/src/preact/mutationComparison/mutation-comparison-table.tsx +2 -2
  22. package/src/preact/mutationFilter/mutation-filter.tsx +27 -18
  23. package/src/preact/mutationFilter/parseAndValidateMutation.ts +4 -4
  24. package/src/preact/mutationFilter/parseMutation.spec.ts +17 -17
  25. package/src/preact/mutations/getInsertionsTableData.spec.ts +3 -3
  26. package/src/preact/mutations/getMutationsGridData.spec.ts +9 -9
  27. package/src/preact/mutations/getMutationsTableData.spec.ts +7 -7
  28. package/src/preact/mutations/mutations-insertions-table.tsx +3 -3
  29. package/src/preact/mutations/mutations-table.tsx +3 -3
  30. package/src/preact/mutationsOverTime/MutationOverTimeData.ts +20 -0
  31. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +45686 -0
  32. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +58989 -0
  33. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +103991 -0
  34. package/src/preact/mutationsOverTime/__mockData__/mockConversion.ts +54 -0
  35. package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +63690 -0
  36. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +177 -161
  37. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +17 -59
  38. package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +27 -0
  39. package/src/preact/mutationsOverTime/mutationOverTimeWorker.ts +29 -0
  40. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +13 -14
  41. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +9 -334
  42. package/src/preact/mutationsOverTime/mutations-over-time.tsx +59 -54
  43. package/src/preact/numberSequencesOverTime/getNumberOfSequencesOverTimeTableData.ts +3 -3
  44. package/src/preact/prevalenceOverTime/getPrevalenceOverTimeTableData.spec.ts +5 -5
  45. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +1 -1
  46. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +2 -2
  47. package/src/preact/shared/sort/sortInsertions.spec.ts +11 -11
  48. package/src/preact/shared/sort/sortInsertions.ts +2 -2
  49. package/src/preact/shared/sort/sortSubstitutionsAndDeletions.spec.ts +13 -13
  50. package/src/preact/shared/sort/sortSubstitutionsAndDeletions.ts +7 -4
  51. package/src/preact/webWorkers/useWebWorker.ts +51 -0
  52. package/src/preact/webWorkers/workerFunction.ts +14 -0
  53. package/src/query/queryAggregatedDataOverTime.ts +3 -3
  54. package/src/query/queryMutationsOverTime.spec.ts +272 -51
  55. package/src/query/queryMutationsOverTime.ts +114 -47
  56. package/src/query/queryPrevalenceOverTime.ts +2 -2
  57. package/src/query/queryRelativeGrowthAdvantage.ts +3 -3
  58. package/src/types.ts +25 -5
  59. package/src/utils/map2d.spec.ts +79 -12
  60. package/src/utils/map2d.ts +25 -5
  61. package/src/utils/mutations.spec.ts +20 -20
  62. package/src/utils/mutations.ts +80 -17
  63. package/src/utils/sort.ts +5 -2
  64. package/src/utils/temporal.spec.ts +27 -24
  65. package/src/utils/{temporal.ts → temporalClass.ts} +170 -72
  66. package/src/utils/temporalTestHelpers.ts +3 -3
  67. package/src/web-components/input/gs-date-range-selector.stories.ts +16 -28
  68. package/src/web-components/input/gs-date-range-selector.tsx +17 -32
  69. package/src/web-components/introduction.mdx +46 -0
  70. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +6 -699
  71. package/src/web-components/visualization/gs-mutations-over-time.tsx +2 -2
  72. package/standalone-bundle/dashboard-components.js +12011 -12778
  73. package/standalone-bundle/dashboard-components.js.map +1 -1
  74. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_01.json +0 -13
  75. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_02.json +0 -13
  76. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_03.json +0 -13
  77. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_04.json +0 -13
  78. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_05.json +0 -13
  79. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_06.json +0 -13
  80. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_07.json +0 -13
  81. package/src/preact/mutationsOverTime/__mockData__/aggregated_20_01_2024.json +0 -13
  82. package/src/preact/mutationsOverTime/__mockData__/aggregated_21_01_2024.json +0 -13
  83. package/src/preact/mutationsOverTime/__mockData__/aggregated_22_01_2024.json +0 -13
  84. package/src/preact/mutationsOverTime/__mockData__/aggregated_23_01_2024.json +0 -13
  85. package/src/preact/mutationsOverTime/__mockData__/aggregated_24_01_2024.json +0 -13
  86. package/src/preact/mutationsOverTime/__mockData__/aggregated_25_01_2024.json +0 -13
  87. package/src/preact/mutationsOverTime/__mockData__/aggregated_26_01_2024.json +0 -13
  88. package/src/preact/mutationsOverTime/__mockData__/aggregated_byDay.json +0 -38
  89. package/src/preact/mutationsOverTime/__mockData__/aggregated_byWeek.json +0 -122
  90. package/src/preact/mutationsOverTime/__mockData__/aggregated_date.json +0 -642
  91. package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations.json +0 -1470
  92. package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations_total.json +0 -13
  93. package/src/preact/mutationsOverTime/__mockData__/aggregated_week3_2024.json +0 -13
  94. package/src/preact/mutationsOverTime/__mockData__/aggregated_week4_2024.json +0 -13
  95. package/src/preact/mutationsOverTime/__mockData__/aggregated_week5_2024.json +0 -13
  96. package/src/preact/mutationsOverTime/__mockData__/aggregated_week6_2024.json +0 -13
  97. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_20_01_2024.json +0 -6778
  98. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_21_01_2024.json +0 -7129
  99. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_22_01_2024.json +0 -4681
  100. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_23_01_2024.json +0 -10738
  101. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_24_01_2024.json +0 -11710
  102. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_25_01_2024.json +0 -11557
  103. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_26_01_2024.json +0 -8596
  104. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_byDayOverall.json +0 -4726
  105. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json +0 -1747
  106. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json +0 -1774
  107. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json +0 -1819
  108. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_04.json +0 -1864
  109. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json +0 -1927
  110. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json +0 -1864
  111. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json +0 -9
  112. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json +0 -11143
  113. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byWeekOverall.json +0 -9154
  114. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_tooManyMutations.json +0 -16453
  115. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week3_2024.json +0 -8812
  116. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week4_2024.json +0 -9730
  117. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week5_2024.json +0 -9865
  118. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week6_2024.json +0 -11314
@@ -2,7 +2,7 @@ import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
2
2
  import { MapOperator } from '../operator/MapOperator';
3
3
  import { RenameFieldOperator } from '../operator/RenameFieldOperator';
4
4
  import { type LapisFilter } from '../types';
5
- import { getMinMaxTemporal, TemporalCache, type YearMonthDay } from '../utils/temporal';
5
+ import { getMinMaxTemporal, TemporalCache, type YearMonthDayClass } from '../utils/temporalClass';
6
6
 
7
7
  export type RelativeGrowthAdvantageData = Awaited<ReturnType<typeof queryRelativeGrowthAdvantage>>;
8
8
 
@@ -33,13 +33,13 @@ export async function queryRelativeGrowthAdvantage<LapisDateField extends string
33
33
  return null;
34
34
  }
35
35
 
36
- const numeratorCounts = new Map<YearMonthDay, number>();
36
+ const numeratorCounts = new Map<YearMonthDayClass, number>();
37
37
  numeratorData.content.forEach((d) => {
38
38
  if (d.date) {
39
39
  numeratorCounts.set(d.date, d.count);
40
40
  }
41
41
  });
42
- const denominatorCounts = new Map<YearMonthDay, number>();
42
+ const denominatorCounts = new Map<YearMonthDayClass, number>();
43
43
  const requestData = {
44
44
  t: [] as number[],
45
45
  n: [] as number[],
package/src/types.ts CHANGED
@@ -1,4 +1,11 @@
1
- import { type Deletion, type Insertion, type Substitution } from './utils/mutations';
1
+ import {
2
+ type Deletion,
3
+ type DeletionClass,
4
+ type Insertion,
5
+ type InsertionClass,
6
+ type Substitution,
7
+ type SubstitutionClass,
8
+ } from './utils/mutations';
2
9
 
3
10
  export type LapisFilter = Record<string, string | number | null | boolean>;
4
11
 
@@ -15,12 +22,25 @@ export type SubstitutionOrDeletion = 'substitution' | 'deletion';
15
22
 
16
23
  export type MutationType = SubstitutionOrDeletion | 'insertion';
17
24
 
18
- export type SubstitutionEntry = { type: 'substitution'; mutation: Substitution; count: number; proportion: number };
25
+ export type SubstitutionEntry<T extends Substitution = SubstitutionClass> = {
26
+ type: 'substitution';
27
+ mutation: T;
28
+ count: number;
29
+ proportion: number;
30
+ };
19
31
 
20
- export type DeletionEntry = { type: 'deletion'; mutation: Deletion; count: number; proportion: number };
32
+ export type DeletionEntry<T extends Deletion = DeletionClass> = {
33
+ type: 'deletion';
34
+ mutation: T;
35
+ count: number;
36
+ proportion: number;
37
+ };
21
38
 
22
- export type InsertionEntry = { type: 'insertion'; mutation: Insertion; count: number };
39
+ export type InsertionEntry<T extends Insertion = InsertionClass> = { type: 'insertion'; mutation: T; count: number };
23
40
 
24
- export type SubstitutionOrDeletionEntry = SubstitutionEntry | DeletionEntry;
41
+ export type SubstitutionOrDeletionEntry<
42
+ S extends Substitution = SubstitutionClass,
43
+ D extends Deletion = DeletionClass,
44
+ > = SubstitutionEntry<S> | DeletionEntry<D>;
25
45
 
26
46
  export type MutationEntry = SubstitutionEntry | DeletionEntry | InsertionEntry;
@@ -2,22 +2,31 @@ import { describe, expect, it } from 'vitest';
2
2
 
3
3
  import { Map2dBase, Map2dView } from './map2d';
4
4
 
5
- describe('Map2dContainer', () => {
5
+ describe('Map2dBase', () => {
6
6
  it('should add a value and return it', () => {
7
- const map2d = new Map2dBase<string, string, number>();
7
+ const map2d = new Map2dBase<string, string, number>(
8
+ (value) => value,
9
+ (value) => value,
10
+ );
8
11
  map2d.set('a', 'b', 2);
9
12
  expect(map2d.get('a', 'b')).toBe(2);
10
13
  });
11
14
 
12
15
  it('should update a value', () => {
13
- const map2d = new Map2dBase<string, string, number>();
16
+ const map2d = new Map2dBase<string, string, number>(
17
+ (value) => value,
18
+ (value) => value,
19
+ );
14
20
  map2d.set('a', 'b', 2);
15
21
  map2d.set('a', 'b', 3);
16
22
  expect(map2d.get('a', 'b')).toBe(3);
17
23
  });
18
24
 
19
25
  it('should return the data as an array', () => {
20
- const map2d = new Map2dBase<string, string, number>();
26
+ const map2d = new Map2dBase<string, string, number>(
27
+ (value) => value,
28
+ (value) => value,
29
+ );
21
30
  map2d.set('a', 'b', 1);
22
31
  map2d.set('a', 'd', 2);
23
32
  map2d.set('c', 'b', 3);
@@ -30,7 +39,10 @@ describe('Map2dContainer', () => {
30
39
  });
31
40
 
32
41
  it('should fill empty values with the given value', () => {
33
- const map2d = new Map2dBase<string, string, number>();
42
+ const map2d = new Map2dBase<string, string, number>(
43
+ (value) => value,
44
+ (value) => value,
45
+ );
34
46
  map2d.set('a', 'b', 2);
35
47
  map2d.set('c', 'd', 4);
36
48
  expect(map2d.getAsArray(0)).toEqual([
@@ -40,7 +52,10 @@ describe('Map2dContainer', () => {
40
52
  });
41
53
 
42
54
  it('should return the keys from the first axis', () => {
43
- const map2d = new Map2dBase<string, string, number>();
55
+ const map2d = new Map2dBase<string, string, number>(
56
+ (value) => value,
57
+ (value) => value,
58
+ );
44
59
  map2d.set('a', 'b', 2);
45
60
  map2d.set('c', 'd', 4);
46
61
 
@@ -48,7 +63,10 @@ describe('Map2dContainer', () => {
48
63
  });
49
64
 
50
65
  it('should return the keys from the second axis', () => {
51
- const map2d = new Map2dBase<string, string, number>();
66
+ const map2d = new Map2dBase<string, string, number>(
67
+ (value) => value,
68
+ (value) => value,
69
+ );
52
70
  map2d.set('a', 'b', 2);
53
71
  map2d.set('c', 'd', 4);
54
72
 
@@ -56,7 +74,10 @@ describe('Map2dContainer', () => {
56
74
  });
57
75
 
58
76
  it('should work with objects as keys', () => {
59
- const map2d = new Map2dBase<{ a: string }, { b: string }, number>();
77
+ const map2d = new Map2dBase<{ a: string }, { b: string }, number>(
78
+ ({ a }) => a,
79
+ ({ b }) => b,
80
+ );
60
81
  map2d.set({ a: 'a' }, { b: 'b' }, 2);
61
82
  map2d.set({ a: 'second' }, { b: 'second' }, 3);
62
83
 
@@ -65,14 +86,20 @@ describe('Map2dContainer', () => {
65
86
  });
66
87
 
67
88
  it('should update a value with objects as keys', () => {
68
- const map2d = new Map2dBase<{ a: string }, { b: string }, number>();
89
+ const map2d = new Map2dBase<{ a: string }, { b: string }, number>(
90
+ ({ a }) => a,
91
+ ({ b }) => b,
92
+ );
69
93
  map2d.set({ a: 'a' }, { b: 'b' }, 2);
70
94
  map2d.set({ a: 'a' }, { b: 'b' }, 3);
71
95
  expect(map2d.get({ a: 'a' }, { b: 'b' })).toBe(3);
72
96
  });
73
97
 
74
98
  it('should return a row by key', () => {
75
- const map2d = new Map2dBase<string, string, number>();
99
+ const map2d = new Map2dBase<string, string, number>(
100
+ (value) => value,
101
+ (value) => value,
102
+ );
76
103
  map2d.set('a', 'b', 2);
77
104
  map2d.set('c', 'd', 4);
78
105
 
@@ -81,11 +108,48 @@ describe('Map2dContainer', () => {
81
108
  });
82
109
 
83
110
  it('should return an empty array when the row does not exist', () => {
84
- const map2d = new Map2dBase<string, string, number>();
111
+ const map2d = new Map2dBase<string, string, number>(
112
+ (value) => value,
113
+ (value) => value,
114
+ );
85
115
  map2d.set('a', 'b', 2);
86
116
 
87
117
  expect(map2d.getRow('c', 0)).toEqual([]);
88
118
  });
119
+
120
+ it('should return content as object', () => {
121
+ const map2d = new Map2dBase<string, string, number>(
122
+ (value) => value,
123
+ (value) => value,
124
+ );
125
+ map2d.set('a', 'b', 2);
126
+ map2d.set('c', 'd', 4);
127
+
128
+ const content = map2d.getContents();
129
+ expect(Array.from(content.keysFirstAxis.values())).to.deep.equal(['a', 'c']);
130
+ expect(Array.from(content.keysSecondAxis.values())).to.deep.equal(['b', 'd']);
131
+ expect(content.data.get('a')?.get('b')).toBe(2);
132
+ });
133
+
134
+ it('should use initial data', () => {
135
+ const map2d = new Map2dBase<string, string, number>(
136
+ (value) => value,
137
+ (value) => value,
138
+ );
139
+ map2d.set('a', 'b', 2);
140
+ map2d.set('c', 'd', 4);
141
+
142
+ const content = map2d.getContents();
143
+
144
+ const mapWithInitialData = new Map2dBase<string, string, number>(
145
+ (value) => value,
146
+ (value) => value,
147
+ content,
148
+ );
149
+
150
+ expect(mapWithInitialData.get('a', 'b')).toBe(2);
151
+ expect(mapWithInitialData.get('c', 'd')).toBe(4);
152
+ });
89
153
  });
90
154
 
91
155
  describe('Map2dView', () => {
@@ -146,7 +210,10 @@ describe('Map2dView', () => {
146
210
  });
147
211
 
148
212
  function createBaseContainer() {
149
- const container = new Map2dBase<string, string, number>();
213
+ const container = new Map2dBase<string, string, number>(
214
+ (value) => value,
215
+ (value) => value,
216
+ );
150
217
  container.set('a', 'b', 1);
151
218
  container.set('c', 'b', 3);
152
219
  container.set('c', 'd', 4);
@@ -1,5 +1,3 @@
1
- import hash from 'object-hash';
2
-
3
1
  export interface Map2d<Key1, Key2, Value> {
4
2
  get(keyFirstAxis: Key1, keySecondAxis: Key2): Value | undefined;
5
3
 
@@ -16,12 +14,19 @@ export interface Map2d<Key1, Key2, Value> {
16
14
  getAsArray(fillEmptyWith: Value): Value[][];
17
15
 
18
16
  serializeFirstAxis(key: Key1): string;
17
+
19
18
  serializeSecondAxis(key: Key2): string;
20
19
 
21
20
  readonly keysFirstAxis: Map<string, Key1>;
22
21
  readonly keysSecondAxis: Map<string, Key2>;
23
22
  }
24
23
 
24
+ export type Map2DContents<Key1, Key2, Value> = {
25
+ keysFirstAxis: Map<string, Key1>;
26
+ keysSecondAxis: Map<string, Key2>;
27
+ data: Map<string, Map<string, Value>>;
28
+ };
29
+
25
30
  export class Map2dBase<Key1 extends object | string, Key2 extends object | string, Value>
26
31
  implements Map2d<Key1, Key2, Value>
27
32
  {
@@ -30,9 +35,16 @@ export class Map2dBase<Key1 extends object | string, Key2 extends object | strin
30
35
  readonly keysSecondAxis = new Map<string, Key2>();
31
36
 
32
37
  constructor(
33
- readonly serializeFirstAxis: (key: Key1) => string = (key) => (typeof key === 'string' ? key : hash(key)),
34
- readonly serializeSecondAxis: (key: Key2) => string = (key) => (typeof key === 'string' ? key : hash(key)),
35
- ) {}
38
+ readonly serializeFirstAxis: (key: Key1) => string,
39
+ readonly serializeSecondAxis: (key: Key2) => string,
40
+ initialContent?: Map2DContents<Key1, Key2, Value>,
41
+ ) {
42
+ if (initialContent) {
43
+ this.keysFirstAxis = new Map(initialContent.keysFirstAxis);
44
+ this.keysSecondAxis = new Map(initialContent.keysSecondAxis);
45
+ this.data = new Map(initialContent.data);
46
+ }
47
+ }
36
48
 
37
49
  get(keyFirstAxis: Key1, keySecondAxis: Key2) {
38
50
  const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
@@ -84,6 +96,14 @@ export class Map2dBase<Key1 extends object | string, Key2 extends object | strin
84
96
  });
85
97
  });
86
98
  }
99
+
100
+ getContents(): Map2DContents<Key1, Key2, Value> {
101
+ return {
102
+ keysFirstAxis: this.keysFirstAxis,
103
+ keysSecondAxis: this.keysSecondAxis,
104
+ data: this.data,
105
+ };
106
+ }
87
107
  }
88
108
 
89
109
  export class Map2dView<Key1 extends object | string, Key2 extends object | string, Value>
@@ -1,21 +1,21 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
- import { Deletion, Insertion, Substitution } from './mutations';
3
+ import { DeletionClass, InsertionClass, SubstitutionClass } from './mutations';
4
4
 
5
- describe('Substitution', () => {
5
+ describe('SubstitutionClass', () => {
6
6
  it('should be parsed from string', () => {
7
- expect(Substitution.parse('A1T')).deep.equal(new Substitution(undefined, 'A', 'T', 1));
8
- expect(Substitution.parse('seg1:A1T')).deep.equal(new Substitution('seg1', 'A', 'T', 1));
7
+ expect(SubstitutionClass.parse('A1T')).deep.equal(new SubstitutionClass(undefined, 'A', 'T', 1));
8
+ expect(SubstitutionClass.parse('seg1:A1T')).deep.equal(new SubstitutionClass('seg1', 'A', 'T', 1));
9
9
  });
10
10
 
11
11
  it('should render to string correctly', () => {
12
12
  const substitutions = [
13
13
  {
14
- substitution: new Substitution(undefined, 'A', 'T', 1),
14
+ substitution: new SubstitutionClass(undefined, 'A', 'T', 1),
15
15
  expected: 'A1T',
16
16
  },
17
- { substitution: new Substitution('segment', 'A', 'T', 1), expected: 'segment:A1T' },
18
- { substitution: new Substitution(undefined, undefined, undefined, 1), expected: '1' },
17
+ { substitution: new SubstitutionClass('segment', 'A', 'T', 1), expected: 'segment:A1T' },
18
+ { substitution: new SubstitutionClass(undefined, undefined, undefined, 1), expected: '1' },
19
19
  ];
20
20
 
21
21
  for (const { substitution, expected } of substitutions) {
@@ -24,20 +24,20 @@ describe('Substitution', () => {
24
24
  });
25
25
  });
26
26
 
27
- describe('Deletion', () => {
27
+ describe('DeletionClass', () => {
28
28
  it('should be parsed from string', () => {
29
- expect(Deletion.parse('A1-')).deep.equal(new Deletion(undefined, 'A', 1));
30
- expect(Deletion.parse('seg1:A1-')).deep.equal(new Deletion('seg1', 'A', 1));
29
+ expect(DeletionClass.parse('A1-')).deep.equal(new DeletionClass(undefined, 'A', 1));
30
+ expect(DeletionClass.parse('seg1:A1-')).deep.equal(new DeletionClass('seg1', 'A', 1));
31
31
  });
32
32
 
33
33
  it('should render to string correctly', () => {
34
34
  const substitutions = [
35
35
  {
36
- deletion: new Deletion(undefined, 'A', 1),
36
+ deletion: new DeletionClass(undefined, 'A', 1),
37
37
  expected: 'A1-',
38
38
  },
39
- { deletion: new Deletion('segment', 'A', 1), expected: 'segment:A1-' },
40
- { deletion: new Deletion(undefined, undefined, 1), expected: '1-' },
39
+ { deletion: new DeletionClass('segment', 'A', 1), expected: 'segment:A1-' },
40
+ { deletion: new DeletionClass(undefined, undefined, 1), expected: '1-' },
41
41
  ];
42
42
 
43
43
  for (const { deletion, expected } of substitutions) {
@@ -46,19 +46,19 @@ describe('Deletion', () => {
46
46
  });
47
47
  });
48
48
 
49
- describe('Insertion', () => {
49
+ describe('InsertionClass', () => {
50
50
  it('should be parsed from string', () => {
51
- expect(Insertion.parse('ins_1:A')).deep.equal(new Insertion(undefined, 1, 'A'));
52
- expect(Insertion.parse('ins_seg1:1:A')).deep.equal(new Insertion('seg1', 1, 'A'));
51
+ expect(InsertionClass.parse('ins_1:A')).deep.equal(new InsertionClass(undefined, 1, 'A'));
52
+ expect(InsertionClass.parse('ins_seg1:1:A')).deep.equal(new InsertionClass('seg1', 1, 'A'));
53
53
  });
54
54
 
55
55
  it('should be parsed with case insensitive ins prefix', () => {
56
- expect(Insertion.parse('INS_1:A')).deep.equal(new Insertion(undefined, 1, 'A'));
57
- expect(Insertion.parse('iNs_1:A')).deep.equal(new Insertion(undefined, 1, 'A'));
56
+ expect(InsertionClass.parse('INS_1:A')).deep.equal(new InsertionClass(undefined, 1, 'A'));
57
+ expect(InsertionClass.parse('iNs_1:A')).deep.equal(new InsertionClass(undefined, 1, 'A'));
58
58
  });
59
59
 
60
60
  it('should be parsed with the other parts not case insensitive', () => {
61
- expect(Insertion.parse('ins_geNe1:1:A')).deep.equal(new Insertion('geNe1', 1, 'A'));
62
- expect(Insertion.parse('ins_1:aA')).deep.equal(new Insertion(undefined, 1, 'aA'));
61
+ expect(InsertionClass.parse('ins_geNe1:1:A')).deep.equal(new InsertionClass('geNe1', 1, 'A'));
62
+ expect(InsertionClass.parse('ins_1:aA')).deep.equal(new InsertionClass(undefined, 1, 'aA'));
63
63
  });
64
64
  });
@@ -1,12 +1,14 @@
1
1
  import { type MutationType, type SequenceType } from '../types';
2
2
 
3
3
  export interface Mutation {
4
- readonly segment: string | undefined;
5
4
  readonly position: number;
6
5
  readonly code: string;
7
6
  readonly type: MutationType;
7
+ readonly segment?: string;
8
+ }
8
9
 
9
- equals(other: Mutation): boolean;
10
+ export interface MutationClass extends Mutation {
11
+ equals(other: MutationClass): boolean;
10
12
 
11
13
  toString(): string;
12
14
  }
@@ -14,7 +16,13 @@ export interface Mutation {
14
16
  export const substitutionRegex =
15
17
  /^((?<segment>[A-Za-z0-9_-]+)(?=:):)?(?<valueAtReference>[A-Za-z])?(?<position>\d+)(?<substitutionValue>[A-Za-z.])?$/;
16
18
 
17
- export class Substitution implements Mutation {
19
+ export interface Substitution extends Mutation {
20
+ type: 'substitution';
21
+ valueAtReference: string | undefined;
22
+ substitutionValue: string | undefined;
23
+ }
24
+
25
+ export class SubstitutionClass implements MutationClass, Substitution {
18
26
  readonly code;
19
27
  readonly type = 'substitution';
20
28
 
@@ -30,8 +38,8 @@ export class Substitution implements Mutation {
30
38
  this.code = `${segmentString}${valueAtReferenceString}${this.position}${substitutionValueString}`;
31
39
  }
32
40
 
33
- equals(other: Mutation): boolean {
34
- if (!(other instanceof Substitution)) {
41
+ equals(other: MutationClass): boolean {
42
+ if (!(other instanceof SubstitutionClass)) {
35
43
  return false;
36
44
  }
37
45
  return (
@@ -46,12 +54,12 @@ export class Substitution implements Mutation {
46
54
  return this.code;
47
55
  }
48
56
 
49
- static parse(mutationStr: string): Substitution | null {
57
+ static parse(mutationStr: string): SubstitutionClass | null {
50
58
  const match = mutationStr.match(substitutionRegex);
51
59
  if (match === null || match.groups === undefined) {
52
60
  return null;
53
61
  }
54
- return new Substitution(
62
+ return new SubstitutionClass(
55
63
  match.groups.segment,
56
64
  match.groups.valueAtReference,
57
65
  match.groups.substitutionValue,
@@ -62,7 +70,12 @@ export class Substitution implements Mutation {
62
70
 
63
71
  export const deletionRegex = /^((?<segment>[A-Za-z0-9_-]+)(?=:):)?(?<valueAtReference>[A-Za-z])?(?<position>\d+)(-)$/;
64
72
 
65
- export class Deletion implements Mutation {
73
+ export interface Deletion extends Mutation {
74
+ type: 'deletion';
75
+ valueAtReference: string | undefined;
76
+ }
77
+
78
+ export class DeletionClass implements MutationClass, Deletion {
66
79
  readonly code;
67
80
  readonly type = 'deletion';
68
81
 
@@ -76,8 +89,8 @@ export class Deletion implements Mutation {
76
89
  this.code = `${segmentString}${valueAtReferenceString}${this.position}-`;
77
90
  }
78
91
 
79
- equals(other: Mutation): boolean {
80
- if (!(other instanceof Deletion)) {
92
+ equals(other: MutationClass): boolean {
93
+ if (!(other instanceof DeletionClass)) {
81
94
  return false;
82
95
  }
83
96
  return (
@@ -91,20 +104,29 @@ export class Deletion implements Mutation {
91
104
  return this.code;
92
105
  }
93
106
 
94
- static parse(mutationStr: string): Deletion | null {
107
+ static parse(mutationStr: string): DeletionClass | null {
95
108
  const match = mutationStr.match(deletionRegex);
96
109
  if (match === null || match.groups === undefined) {
97
110
  return null;
98
111
  }
99
112
 
100
- return new Deletion(match.groups.segment, match.groups.valueAtReference, parseInt(match.groups.position, 10));
113
+ return new DeletionClass(
114
+ match.groups.segment,
115
+ match.groups.valueAtReference,
116
+ parseInt(match.groups.position, 10),
117
+ );
101
118
  }
102
119
  }
103
120
 
104
121
  export const insertionRegexp =
105
122
  /^ins_((?<segment>[A-Za-z0-9_-]+)(?=:):)?(?<position>\d+):(?<insertedSymbols>(([A-Za-z?]|(\.\*))+))$/i;
106
123
 
107
- export class Insertion implements Mutation {
124
+ export interface Insertion extends Mutation {
125
+ type: 'insertion';
126
+ insertedSymbols: string;
127
+ }
128
+
129
+ export class InsertionClass implements MutationClass {
108
130
  readonly code;
109
131
  readonly type = 'insertion';
110
132
 
@@ -116,8 +138,8 @@ export class Insertion implements Mutation {
116
138
  this.code = `ins_${this.segment ? `${this.segment}:` : ''}${this.position}:${this.insertedSymbols}`;
117
139
  }
118
140
 
119
- equals(other: Mutation): boolean {
120
- if (!(other instanceof Insertion)) {
141
+ equals(other: MutationClass): boolean {
142
+ if (!(other instanceof InsertionClass)) {
121
143
  return false;
122
144
  }
123
145
  return (
@@ -131,13 +153,54 @@ export class Insertion implements Mutation {
131
153
  return this.code;
132
154
  }
133
155
 
134
- static parse(mutationStr: string): Insertion | null {
156
+ static parse(mutationStr: string): InsertionClass | null {
135
157
  const match = mutationStr.match(insertionRegexp);
136
158
  if (match === null || match.groups === undefined) {
137
159
  return null;
138
160
  }
139
161
 
140
- return new Insertion(match.groups.segment, parseInt(match.groups.position, 10), match.groups.insertedSymbols);
162
+ return new InsertionClass(
163
+ match.groups.segment,
164
+ parseInt(match.groups.position, 10),
165
+ match.groups.insertedSymbols,
166
+ );
167
+ }
168
+ }
169
+
170
+ export function toMutation(
171
+ mutationClass: SubstitutionClass | DeletionClass | InsertionClass,
172
+ ): Substitution | Deletion | Insertion {
173
+ if (mutationClass.type === 'insertion') {
174
+ return {
175
+ type: 'insertion' as const,
176
+ code: mutationClass.code,
177
+ segment: mutationClass.segment,
178
+ position: mutationClass.position,
179
+ insertedSymbols: mutationClass.insertedSymbols,
180
+ };
181
+ }
182
+ return toSubstitutionOrDeletion(mutationClass);
183
+ }
184
+
185
+ export function toSubstitutionOrDeletion(mutation: SubstitutionClass | DeletionClass): Substitution | Deletion {
186
+ switch (mutation.type) {
187
+ case 'substitution':
188
+ return {
189
+ type: 'substitution' as const,
190
+ code: mutation.code,
191
+ segment: mutation.segment,
192
+ position: mutation.position,
193
+ valueAtReference: mutation.valueAtReference,
194
+ substitutionValue: mutation.substitutionValue,
195
+ };
196
+ case 'deletion':
197
+ return {
198
+ type: 'deletion' as const,
199
+ code: mutation.code,
200
+ segment: mutation.segment,
201
+ position: mutation.position,
202
+ valueAtReference: mutation.valueAtReference,
203
+ };
141
204
  }
142
205
  }
143
206
 
package/src/utils/sort.ts CHANGED
@@ -1,6 +1,9 @@
1
- import type { Temporal } from './temporal';
1
+ import type { TemporalClass } from './temporalClass';
2
2
 
3
- export function sortNullToBeginningThenByDate(a: { dateRange: Temporal | null }, b: { dateRange: Temporal | null }) {
3
+ export function sortNullToBeginningThenByDate(
4
+ a: { dateRange: TemporalClass | null },
5
+ b: { dateRange: TemporalClass | null },
6
+ ) {
4
7
  return a.dateRange === null
5
8
  ? -1
6
9
  : b.dateRange === null