@genspectrum/dashboard-components 0.6.18 → 0.6.19

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 (108) hide show
  1. package/README.md +5 -12
  2. package/custom-elements.json +4 -4
  3. package/dist/assets/mutationOverTimeWorker-BdzqDqvO.js.map +1 -0
  4. package/dist/dashboard-components.js +216 -214
  5. package/dist/dashboard-components.js.map +1 -1
  6. package/dist/genspectrum-components.d.ts +40 -40
  7. package/dist/style.css +3 -2
  8. package/package.json +13 -2
  9. package/src/operator/FetchInsertionsOperator.ts +2 -2
  10. package/src/operator/FetchSubstitutionsOrDeletionsOperator.ts +3 -3
  11. package/src/preact/mutationComparison/fetchMutationData.spec.ts +3 -3
  12. package/src/preact/mutationComparison/getMutationComparisonTableData.spec.ts +11 -11
  13. package/src/preact/mutationComparison/getMutationComparisonTableData.ts +4 -4
  14. package/src/preact/mutationComparison/mutation-comparison-table.tsx +2 -2
  15. package/src/preact/mutationFilter/mutation-filter.tsx +27 -18
  16. package/src/preact/mutationFilter/parseAndValidateMutation.ts +4 -4
  17. package/src/preact/mutationFilter/parseMutation.spec.ts +17 -17
  18. package/src/preact/mutations/getInsertionsTableData.spec.ts +3 -3
  19. package/src/preact/mutations/getMutationsGridData.spec.ts +9 -9
  20. package/src/preact/mutations/getMutationsTableData.spec.ts +7 -7
  21. package/src/preact/mutations/mutations-insertions-table.tsx +3 -3
  22. package/src/preact/mutations/mutations-table.tsx +3 -3
  23. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +45686 -0
  24. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +58989 -0
  25. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +103991 -0
  26. package/src/preact/mutationsOverTime/__mockData__/mockConversion.ts +54 -0
  27. package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +63690 -0
  28. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +176 -159
  29. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +17 -59
  30. package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +27 -0
  31. package/src/preact/mutationsOverTime/mutationOverTimeWorker.ts +29 -0
  32. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +13 -14
  33. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +9 -334
  34. package/src/preact/mutationsOverTime/mutations-over-time.tsx +68 -52
  35. package/src/preact/numberSequencesOverTime/getNumberOfSequencesOverTimeTableData.ts +3 -3
  36. package/src/preact/prevalenceOverTime/getPrevalenceOverTimeTableData.spec.ts +5 -5
  37. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +1 -1
  38. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +2 -2
  39. package/src/preact/shared/sort/sortInsertions.spec.ts +11 -11
  40. package/src/preact/shared/sort/sortInsertions.ts +2 -2
  41. package/src/preact/shared/sort/sortSubstitutionsAndDeletions.spec.ts +13 -13
  42. package/src/preact/shared/sort/sortSubstitutionsAndDeletions.ts +7 -4
  43. package/src/preact/webWorkers/useWebWorker.ts +51 -0
  44. package/src/preact/webWorkers/workerFunction.ts +14 -0
  45. package/src/query/queryAggregatedDataOverTime.ts +3 -3
  46. package/src/query/queryMutationsOverTime.spec.ts +272 -51
  47. package/src/query/queryMutationsOverTime.ts +114 -45
  48. package/src/query/queryPrevalenceOverTime.ts +2 -2
  49. package/src/query/queryRelativeGrowthAdvantage.ts +3 -3
  50. package/src/types.ts +25 -5
  51. package/src/utils/map2d.spec.ts +29 -1
  52. package/src/utils/map2d.ts +22 -1
  53. package/src/utils/mutations.spec.ts +20 -20
  54. package/src/utils/mutations.ts +80 -17
  55. package/src/utils/sort.ts +5 -2
  56. package/src/utils/temporal.spec.ts +27 -24
  57. package/src/utils/{temporal.ts → temporalClass.ts} +170 -72
  58. package/src/utils/temporalTestHelpers.ts +3 -3
  59. package/src/web-components/introduction.mdx +46 -0
  60. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +6 -699
  61. package/src/web-components/visualization/gs-mutations-over-time.tsx +2 -2
  62. package/standalone-bundle/dashboard-components.js +13763 -13754
  63. package/standalone-bundle/dashboard-components.js.map +1 -1
  64. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_01.json +0 -13
  65. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_02.json +0 -13
  66. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_03.json +0 -13
  67. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_04.json +0 -13
  68. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_05.json +0 -13
  69. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_06.json +0 -13
  70. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_07.json +0 -13
  71. package/src/preact/mutationsOverTime/__mockData__/aggregated_20_01_2024.json +0 -13
  72. package/src/preact/mutationsOverTime/__mockData__/aggregated_21_01_2024.json +0 -13
  73. package/src/preact/mutationsOverTime/__mockData__/aggregated_22_01_2024.json +0 -13
  74. package/src/preact/mutationsOverTime/__mockData__/aggregated_23_01_2024.json +0 -13
  75. package/src/preact/mutationsOverTime/__mockData__/aggregated_24_01_2024.json +0 -13
  76. package/src/preact/mutationsOverTime/__mockData__/aggregated_25_01_2024.json +0 -13
  77. package/src/preact/mutationsOverTime/__mockData__/aggregated_26_01_2024.json +0 -13
  78. package/src/preact/mutationsOverTime/__mockData__/aggregated_byDay.json +0 -38
  79. package/src/preact/mutationsOverTime/__mockData__/aggregated_byWeek.json +0 -122
  80. package/src/preact/mutationsOverTime/__mockData__/aggregated_date.json +0 -642
  81. package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations.json +0 -1470
  82. package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations_total.json +0 -13
  83. package/src/preact/mutationsOverTime/__mockData__/aggregated_week3_2024.json +0 -13
  84. package/src/preact/mutationsOverTime/__mockData__/aggregated_week4_2024.json +0 -13
  85. package/src/preact/mutationsOverTime/__mockData__/aggregated_week5_2024.json +0 -13
  86. package/src/preact/mutationsOverTime/__mockData__/aggregated_week6_2024.json +0 -13
  87. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_20_01_2024.json +0 -6778
  88. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_21_01_2024.json +0 -7129
  89. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_22_01_2024.json +0 -4681
  90. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_23_01_2024.json +0 -10738
  91. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_24_01_2024.json +0 -11710
  92. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_25_01_2024.json +0 -11557
  93. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_26_01_2024.json +0 -8596
  94. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_byDayOverall.json +0 -4726
  95. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json +0 -1747
  96. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json +0 -1774
  97. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json +0 -1819
  98. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_04.json +0 -1864
  99. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json +0 -1927
  100. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json +0 -1864
  101. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json +0 -9
  102. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json +0 -11143
  103. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byWeekOverall.json +0 -9154
  104. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_tooManyMutations.json +0 -16453
  105. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week3_2024.json +0 -8812
  106. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week4_2024.json +0 -9730
  107. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week5_2024.json +0 -9865
  108. 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,7 +2,7 @@ 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
7
  const map2d = new Map2dBase<string, string, number>();
8
8
  map2d.set('a', 'b', 2);
@@ -86,6 +86,34 @@ describe('Map2dContainer', () => {
86
86
 
87
87
  expect(map2d.getRow('c', 0)).toEqual([]);
88
88
  });
89
+
90
+ it('should return content as object', () => {
91
+ const map2d = new Map2dBase<string, string, number>();
92
+ map2d.set('a', 'b', 2);
93
+ map2d.set('c', 'd', 4);
94
+
95
+ const content = map2d.getContents();
96
+ expect(Array.from(content.keysFirstAxis.values())).to.deep.equal(['a', 'c']);
97
+ expect(Array.from(content.keysSecondAxis.values())).to.deep.equal(['b', 'd']);
98
+ expect(content.data.get('a')?.get('b')).toBe(2);
99
+ });
100
+
101
+ it('should use initial data', () => {
102
+ const map2d = new Map2dBase<string, string, number>();
103
+ map2d.set('a', 'b', 2);
104
+ map2d.set('c', 'd', 4);
105
+
106
+ const content = map2d.getContents();
107
+
108
+ const mapWithInitialData = new Map2dBase<string, string, number>(
109
+ (value) => value,
110
+ (value) => value,
111
+ content,
112
+ );
113
+
114
+ expect(mapWithInitialData.get('a', 'b')).toBe(2);
115
+ expect(mapWithInitialData.get('c', 'd')).toBe(4);
116
+ });
89
117
  });
90
118
 
91
119
  describe('Map2dView', () => {
@@ -22,6 +22,12 @@ export interface Map2d<Key1, Key2, Value> {
22
22
  readonly keysSecondAxis: Map<string, Key2>;
23
23
  }
24
24
 
25
+ export type Map2DContents<Key1, Key2, Value> = {
26
+ keysFirstAxis: Map<string, Key1>;
27
+ keysSecondAxis: Map<string, Key2>;
28
+ data: Map<string, Map<string, Value>>;
29
+ };
30
+
25
31
  export class Map2dBase<Key1 extends object | string, Key2 extends object | string, Value>
26
32
  implements Map2d<Key1, Key2, Value>
27
33
  {
@@ -32,7 +38,14 @@ export class Map2dBase<Key1 extends object | string, Key2 extends object | strin
32
38
  constructor(
33
39
  readonly serializeFirstAxis: (key: Key1) => string = (key) => (typeof key === 'string' ? key : hash(key)),
34
40
  readonly serializeSecondAxis: (key: Key2) => string = (key) => (typeof key === 'string' ? key : hash(key)),
35
- ) {}
41
+ initialContent?: Map2DContents<Key1, Key2, Value>,
42
+ ) {
43
+ if (initialContent) {
44
+ this.keysFirstAxis = new Map(initialContent.keysFirstAxis);
45
+ this.keysSecondAxis = new Map(initialContent.keysSecondAxis);
46
+ this.data = new Map(initialContent.data);
47
+ }
48
+ }
36
49
 
37
50
  get(keyFirstAxis: Key1, keySecondAxis: Key2) {
38
51
  const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
@@ -84,6 +97,14 @@ export class Map2dBase<Key1 extends object | string, Key2 extends object | strin
84
97
  });
85
98
  });
86
99
  }
100
+
101
+ getContents(): Map2DContents<Key1, Key2, Value> {
102
+ return {
103
+ keysFirstAxis: this.keysFirstAxis,
104
+ keysSecondAxis: this.keysSecondAxis,
105
+ data: this.data,
106
+ };
107
+ }
87
108
  }
88
109
 
89
110
  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
@@ -5,23 +5,26 @@ import {
5
5
  generateAllMonthsInRange,
6
6
  generateAllYearsInRange,
7
7
  TemporalCache,
8
- Year,
9
- YearMonth,
10
- YearMonthDay,
11
- YearWeek,
12
- } from './temporal';
8
+ YearClass,
9
+ YearMonthClass,
10
+ YearMonthDayClass,
11
+ YearWeekClass,
12
+ } from './temporalClass';
13
13
 
14
14
  const cache = TemporalCache.getInstance();
15
15
 
16
16
  describe('generateAllDaysInRange', () => {
17
17
  it('should return all days in range', () => {
18
18
  expect(
19
- generateAllDaysInRange(YearMonthDay.parse('2020-01-01', cache), YearMonthDay.parse('2020-01-04', cache)),
19
+ generateAllDaysInRange(
20
+ YearMonthDayClass.parse('2020-01-01', cache),
21
+ YearMonthDayClass.parse('2020-01-04', cache),
22
+ ),
20
23
  ).deep.equal([
21
- YearMonthDay.parse('2020-01-01', cache),
22
- YearMonthDay.parse('2020-01-02', cache),
23
- YearMonthDay.parse('2020-01-03', cache),
24
- YearMonthDay.parse('2020-01-04', cache),
24
+ YearMonthDayClass.parse('2020-01-01', cache),
25
+ YearMonthDayClass.parse('2020-01-02', cache),
26
+ YearMonthDayClass.parse('2020-01-03', cache),
27
+ YearMonthDayClass.parse('2020-01-04', cache),
25
28
  ]);
26
29
  });
27
30
  });
@@ -29,30 +32,30 @@ describe('generateAllDaysInRange', () => {
29
32
  describe('generateAllMonthsInRange', () => {
30
33
  it('should return all months in range', () => {
31
34
  expect(
32
- generateAllMonthsInRange(YearMonth.parse('2020-01', cache), YearMonth.parse('2020-04', cache)),
35
+ generateAllMonthsInRange(YearMonthClass.parse('2020-01', cache), YearMonthClass.parse('2020-04', cache)),
33
36
  ).deep.equal([
34
- YearMonth.parse('2020-01', cache),
35
- YearMonth.parse('2020-02', cache),
36
- YearMonth.parse('2020-03', cache),
37
- YearMonth.parse('2020-04', cache),
37
+ YearMonthClass.parse('2020-01', cache),
38
+ YearMonthClass.parse('2020-02', cache),
39
+ YearMonthClass.parse('2020-03', cache),
40
+ YearMonthClass.parse('2020-04', cache),
38
41
  ]);
39
42
  });
40
43
  });
41
44
 
42
45
  describe('generateAllYearsInRange', () => {
43
46
  it('should return all years in range', () => {
44
- expect(generateAllYearsInRange(Year.parse('2020-01', cache), Year.parse('2023', cache))).deep.equal([
45
- Year.parse('2020', cache),
46
- Year.parse('2021', cache),
47
- Year.parse('2022', cache),
48
- Year.parse('2023', cache),
47
+ expect(generateAllYearsInRange(YearClass.parse('2020-01', cache), YearClass.parse('2023', cache))).deep.equal([
48
+ YearClass.parse('2020', cache),
49
+ YearClass.parse('2021', cache),
50
+ YearClass.parse('2022', cache),
51
+ YearClass.parse('2023', cache),
49
52
  ]);
50
53
  });
51
54
  });
52
55
 
53
56
  describe('YearMonthDay', () => {
54
57
  it('should parse from string', () => {
55
- const underTest = YearMonthDay.parse('2020-01-01', cache);
58
+ const underTest = YearMonthDayClass.parse('2020-01-01', cache);
56
59
 
57
60
  expect(underTest.yearNumber).equal(2020);
58
61
  expect(underTest.monthNumber).equal(1);
@@ -66,7 +69,7 @@ describe('YearMonthDay', () => {
66
69
 
67
70
  describe('YearWeek', () => {
68
71
  it('should parse from string', () => {
69
- const underTest = YearWeek.parse('2020-W02', cache);
72
+ const underTest = YearWeekClass.parse('2020-W02', cache);
70
73
 
71
74
  expect(underTest.isoYearNumber).equal(2020);
72
75
  expect(underTest.isoWeekNumber).equal(2);
@@ -78,7 +81,7 @@ describe('YearWeek', () => {
78
81
 
79
82
  describe('YearMonth', () => {
80
83
  it('should parse from string', () => {
81
- const underTest = YearMonth.parse('2020-01', cache);
84
+ const underTest = YearMonthClass.parse('2020-01', cache);
82
85
 
83
86
  expect(underTest.yearNumber).equal(2020);
84
87
  expect(underTest.monthNumber).equal(1);
@@ -90,7 +93,7 @@ describe('YearMonth', () => {
90
93
 
91
94
  describe('Year', () => {
92
95
  it('should parse from string', () => {
93
- const underTest = Year.parse('2020', cache);
96
+ const underTest = YearClass.parse('2020', cache);
94
97
 
95
98
  expect(underTest.year).equal(2020);
96
99
  expect(underTest.text).equal('2020');