@apollo-annotation/mst 0.1.17 → 0.1.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.
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@apollo-annotation/mst",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "main": "./dist/index.js",
5
5
  "scripts": {
6
6
  "build": "tsc"
7
7
  },
8
8
  "dependencies": {
9
- "@jbrowse/core": "^2.7.0",
9
+ "@jbrowse/core": "^2.13.1",
10
10
  "mobx": "^6.6.1",
11
11
  "mobx-state-tree": "^5.1.7",
12
+ "react-dom": "^18.2.0",
12
13
  "rxjs": "^7.4.0",
13
14
  "tslib": "^2.3.1"
14
15
  },
15
16
  "devDependencies": {
16
- "typescript": "^5.1.6"
17
+ "typescript": "^5.5.3"
17
18
  },
18
19
  "publishConfig": {
19
20
  "access": "public"
@@ -0,0 +1,293 @@
1
+ import { intersection2 } from '@jbrowse/core/util'
2
+ import {
3
+ IAnyModelType,
4
+ IMSTMap,
5
+ Instance,
6
+ SnapshotIn,
7
+ SnapshotOrInstance,
8
+ cast,
9
+ getParentOfType,
10
+ getSnapshot,
11
+ types,
12
+ } from 'mobx-state-tree'
13
+
14
+ import { ApolloAssembly } from '.'
15
+
16
+ const LateAnnotationFeature = types.late(
17
+ (): IAnyModelType => AnnotationFeatureModel,
18
+ )
19
+
20
+ export const AnnotationFeatureModel = types
21
+ .model('AnnotationFeatureModel', {
22
+ _id: types.identifier,
23
+ /** Unique ID of the reference sequence on which this feature is located */
24
+ refSeq: types.string,
25
+ /**
26
+ * Type of feature. Can be any string, but is usually an ontology term,
27
+ * e.g. "gene" from the
28
+ * {@link http://sequenceontology.org/browser/current_release/term/SO:0000704 |Sequence Ontology}.
29
+ */
30
+ type: types.string,
31
+ /**
32
+ * Coordinate of the edge of the feature that is closer to the beginning of
33
+ * the reference sequence. This can be thought of as the "start" of features
34
+ * on the positive strand. Uses interbase (0-based half-open) coordinates.
35
+ */
36
+ min: types.number,
37
+ /**
38
+ * Coordinate of the edge of the feature that is closer to the end of the
39
+ * reference sequence. This can be thought of as the "end" of features on
40
+ * the positive strand. Uses interbase (0-based half-open) coordinates.
41
+ */
42
+ max: types.number,
43
+ /**
44
+ * The strand on which the feature is located. `+1` for the positive (a.k.a.
45
+ * plus or forward) and `-1` for the negative (a.k.a minus or reverse)
46
+ * strand.
47
+ */
48
+ strand: types.maybe(types.union(types.literal(1), types.literal(-1))),
49
+ /** Child features of this feature */
50
+ children: types.maybe(types.map(LateAnnotationFeature)),
51
+ /**
52
+ * Additional attributes of the feature. This could include name, source,
53
+ * note, dbxref, etc.
54
+ */
55
+ attributes: types.map(types.array(types.string)),
56
+ })
57
+ .views((self) => ({
58
+ get length() {
59
+ return self.max - self.min
60
+ },
61
+ get featureId() {
62
+ return self.attributes.get('id')
63
+ },
64
+ /**
65
+ * Possibly different from `min` because "The GFF3 format does not enforce a
66
+ * rule in which features must be wholly contained within the location of
67
+ * their parents"
68
+ */
69
+ get minWithChildren() {
70
+ let { min } = self
71
+ const children = self.children as Children
72
+ if (!children) {
73
+ return min
74
+ }
75
+ for (const [, child] of children) {
76
+ min = Math.min(min, child.min)
77
+ }
78
+ return min
79
+ },
80
+ /**
81
+ * Possibly different from `max` because "The GFF3 format does not enforce a
82
+ * rule in which features must be wholly contained within the location of
83
+ * their parents"
84
+ */
85
+ get maxWithChildren() {
86
+ let { max } = self
87
+ const children = self.children as Children
88
+ if (!children) {
89
+ return max
90
+ }
91
+ for (const [, child] of children) {
92
+ max = Math.max(max, child.max)
93
+ }
94
+ return max
95
+ },
96
+ hasDescendant(featureId: string) {
97
+ const children = self.children as Children
98
+ if (!children) {
99
+ return false
100
+ }
101
+ for (const [id, child] of children) {
102
+ if (id === featureId) {
103
+ return true
104
+ }
105
+ if (child.hasDescendant(featureId)) {
106
+ return true
107
+ }
108
+ }
109
+ return false
110
+ },
111
+ get cdsLocations(): { min: number; max: number; phase: 0 | 1 | 2 }[][] {
112
+ if (self.type !== 'mRNA') {
113
+ throw new Error(
114
+ 'Only features of type "mRNA" or equivalent can calculate CDS locations',
115
+ )
116
+ }
117
+ const children = self.children as Children
118
+ if (!children) {
119
+ throw new Error('no CDS or exons in mRNA')
120
+ }
121
+ const cdsChildren = [...children.values()].filter(
122
+ (child) => child.type === 'CDS',
123
+ )
124
+ if (cdsChildren.length === 0) {
125
+ throw new Error('no CDS in mRNA')
126
+ }
127
+ const cdsLocations: { min: number; max: number; phase: 0 | 1 | 2 }[][] =
128
+ []
129
+ for (const cds of cdsChildren) {
130
+ const { max: cdsMax, min: cdsMin } = cds
131
+ const locs: { min: number; max: number }[] = []
132
+ for (const [, child] of children) {
133
+ if (child.type !== 'exon') {
134
+ continue
135
+ }
136
+ const [start, end] = intersection2(
137
+ cdsMin,
138
+ cdsMax,
139
+ child.min,
140
+ child.max,
141
+ )
142
+ if (start !== undefined && end !== undefined) {
143
+ locs.push({ min: start, max: end })
144
+ }
145
+ }
146
+ locs.sort(({ min: a }, { min: b }) => a - b)
147
+ if (self.strand === -1) {
148
+ locs.reverse()
149
+ }
150
+ let nextPhase: 0 | 1 | 2 = 0
151
+ const phasedLocs = locs.map((loc) => {
152
+ const phase = nextPhase
153
+ nextPhase = ((3 - ((loc.max - loc.min - phase + 3) % 3)) % 3) as
154
+ | 0
155
+ | 1
156
+ | 2
157
+ return { ...loc, phase }
158
+ })
159
+ cdsLocations.push(phasedLocs)
160
+ }
161
+ return cdsLocations
162
+ },
163
+ }))
164
+ .actions((self) => ({
165
+ setAttributes(attributes: Map<string, string[]>) {
166
+ self.attributes.clear()
167
+ for (const [key, value] of attributes.entries()) {
168
+ self.attributes.set(key, value)
169
+ }
170
+ },
171
+ setAttribute(key: string, value: string[]) {
172
+ self.attributes.merge({ [key]: value })
173
+ },
174
+ setType(type: string) {
175
+ self.type = type
176
+ },
177
+ setRefSeq(refSeq: string) {
178
+ self.refSeq = refSeq
179
+ },
180
+ setMin(min: number) {
181
+ if (min > self.max) {
182
+ throw new Error(`Min "${min}" is greater than max "${self.max}"`)
183
+ }
184
+ if (self.min !== min) {
185
+ self.min = min
186
+ }
187
+ },
188
+ setMax(max: number) {
189
+ if (max < self.min) {
190
+ throw new Error(`Max "${max}" is less than Min "${self.min}"`)
191
+ }
192
+ if (self.max !== max) {
193
+ self.max = max
194
+ }
195
+ },
196
+ setStrand(strand?: 1 | -1 | undefined) {
197
+ self.strand = strand
198
+ },
199
+ addChild(childFeature: AnnotationFeatureSnapshot) {
200
+ if (self.children && self.children.size > 0) {
201
+ const existingChildren = getSnapshot<
202
+ Record<string, AnnotationFeatureSnapshot>
203
+ >(self.children)
204
+ self.children.clear()
205
+ for (const [, child] of Object.entries({
206
+ ...existingChildren,
207
+ [childFeature._id]: childFeature,
208
+ }).sort(([, a], [, b]) => a.min - b.min)) {
209
+ self.children.put(child)
210
+ }
211
+ } else {
212
+ self.children = cast({})
213
+ self.children?.put(childFeature)
214
+ }
215
+ },
216
+ deleteChild(childFeatureId: string) {
217
+ self.children?.delete(childFeatureId)
218
+ },
219
+ }))
220
+ .actions((self) => ({
221
+ update({
222
+ children,
223
+ max,
224
+ min,
225
+ refSeq,
226
+ strand,
227
+ }: {
228
+ refSeq: string
229
+ min: number
230
+ max: number
231
+ strand?: 1 | -1
232
+ children?: SnapshotOrInstance<typeof self.children>
233
+ }) {
234
+ self.setRefSeq(refSeq)
235
+ self.setMin(min)
236
+ self.setMax(max)
237
+ self.setStrand(strand)
238
+ if (children) {
239
+ self.children = cast(children)
240
+ }
241
+ },
242
+ }))
243
+ // This views block has to be last to avoid:
244
+ // "'parent' is referenced directly or indirectly in its own type annotation."
245
+ .views((self) => ({
246
+ get parent(): AnnotationFeature | undefined {
247
+ let parent: AnnotationFeature | undefined
248
+ try {
249
+ parent = getParentOfType(self, AnnotationFeatureModel)
250
+ } catch {
251
+ // pass
252
+ }
253
+ return parent
254
+ },
255
+ get topLevelFeature(): AnnotationFeature {
256
+ let feature = self
257
+ let parent
258
+ do {
259
+ try {
260
+ parent = getParentOfType(feature, AnnotationFeatureModel)
261
+ feature = parent
262
+ } catch {
263
+ parent = undefined
264
+ }
265
+ } while (parent)
266
+ return feature as AnnotationFeature
267
+ },
268
+ get assemblyId(): string {
269
+ return getParentOfType(self, ApolloAssembly)._id
270
+ },
271
+ }))
272
+
273
+ export type Children = IMSTMap<typeof AnnotationFeatureModel> | undefined
274
+
275
+ // eslint disables because of
276
+ // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
277
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
278
+ interface AnnotationFeatureRaw
279
+ extends Instance<typeof AnnotationFeatureModel> {}
280
+ // This type isn't exactly right, since "children" is actually an IMSTMap and
281
+ // not a Map, but it's better than typing it as any.
282
+ export interface AnnotationFeature
283
+ extends Omit<AnnotationFeatureRaw, 'children'> {
284
+ children?: Map<string, AnnotationFeature>
285
+ }
286
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
287
+ interface AnnotationFeatureSnapshotRaw
288
+ extends SnapshotIn<typeof AnnotationFeatureModel> {}
289
+ export interface AnnotationFeatureSnapshot
290
+ extends AnnotationFeatureSnapshotRaw {
291
+ /** Child features of this feature */
292
+ children?: Record<string, AnnotationFeatureSnapshot>
293
+ }
@@ -2,6 +2,11 @@ import { Instance, SnapshotIn, types } from 'mobx-state-tree'
2
2
 
3
3
  import { ApolloRefSeq } from './ApolloRefSeq'
4
4
 
5
+ export type BackendDriverType =
6
+ | 'CollaborationServerDriver'
7
+ | 'InMemoryFileDriver'
8
+ | 'DesktopFileDriver'
9
+
5
10
  export const ApolloAssembly = types
6
11
  .model('ApolloAssembly', {
7
12
  _id: types.identifier,
@@ -30,5 +35,10 @@ export const ApolloAssembly = types
30
35
  },
31
36
  }))
32
37
 
33
- export type ApolloAssemblyI = Instance<typeof ApolloAssembly>
34
- export type ApolloAssemblySnapshot = SnapshotIn<typeof ApolloAssembly>
38
+ // eslint disables because of
39
+ // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
40
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
41
+ export interface ApolloAssemblyI extends Instance<typeof ApolloAssembly> {}
42
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
43
+ export interface ApolloAssemblySnapshot
44
+ extends SnapshotIn<typeof ApolloAssembly> {}
@@ -7,9 +7,9 @@ import {
7
7
  } from 'mobx-state-tree'
8
8
 
9
9
  import {
10
- AnnotationFeature,
10
+ AnnotationFeatureModel,
11
11
  AnnotationFeatureSnapshot,
12
- } from './AnnotationFeature'
12
+ } from './AnnotationFeatureModel'
13
13
 
14
14
  export const Sequence = types.model({
15
15
  start: types.number,
@@ -28,7 +28,7 @@ export const ApolloRefSeq = types
28
28
  _id: types.identifier,
29
29
  name: types.string,
30
30
  description: '',
31
- features: types.map(AnnotationFeature),
31
+ features: types.map(AnnotationFeatureModel),
32
32
  sequence: types.array(Sequence),
33
33
  })
34
34
  .actions((self) => ({
@@ -112,5 +112,9 @@ export const ApolloRefSeq = types
112
112
  },
113
113
  }))
114
114
 
115
- export type ApolloRefSeqI = Instance<typeof ApolloRefSeq>
116
- export type ApolloRefSeqSnapshot = SnapshotIn<typeof ApolloRefSeq>
115
+ // eslint disables because of
116
+ // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
117
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
118
+ export interface ApolloRefSeqI extends Instance<typeof ApolloRefSeq> {}
119
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
120
+ export interface ApolloRefSeqSnapshot extends SnapshotIn<typeof ApolloRefSeq> {}
@@ -1,11 +1,11 @@
1
1
  import { Instance, SnapshotIn, types } from 'mobx-state-tree'
2
2
 
3
- import { AnnotationFeature } from './AnnotationFeature'
3
+ import { AnnotationFeatureModel } from './AnnotationFeatureModel'
4
4
 
5
5
  export const CheckResult = types.model('CheckResult', {
6
6
  _id: types.identifier,
7
7
  name: types.string,
8
- ids: types.array(types.safeReference(AnnotationFeature)),
8
+ ids: types.array(types.safeReference(AnnotationFeatureModel)),
9
9
  refSeq: types.string,
10
10
  start: types.number,
11
11
  end: types.number,
@@ -13,5 +13,9 @@ export const CheckResult = types.model('CheckResult', {
13
13
  message: types.string,
14
14
  })
15
15
 
16
- export type CheckResultI = Instance<typeof CheckResult>
17
- export type CheckResultSnapshot = SnapshotIn<typeof CheckResult>
16
+ // eslint disables because of
17
+ // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
18
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
19
+ export interface CheckResultI extends Instance<typeof CheckResult> {}
20
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
21
+ export interface CheckResultSnapshot extends SnapshotIn<typeof CheckResult> {}
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './AnnotationFeature'
1
+ export * from './AnnotationFeatureModel'
2
2
  export * from './ApolloAssembly'
3
3
  export * from './ApolloRefSeq'
4
4
  export * from './CheckResult'
@@ -1,243 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
2
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
3
- /* eslint-disable @typescript-eslint/no-unsafe-call */
4
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
5
- import {
6
- IAnyModelType,
7
- Instance,
8
- SnapshotIn,
9
- SnapshotOrInstance,
10
- cast,
11
- getParentOfType,
12
- getSnapshot,
13
- types,
14
- } from 'mobx-state-tree'
15
-
16
- import { ApolloAssembly } from '.'
17
-
18
- export const LateAnnotationFeature = types.late(
19
- (): IAnyModelType => AnnotationFeature,
20
- )
21
-
22
- const Phase = types.maybe(
23
- types.union(types.literal(0), types.literal(1), types.literal(2)),
24
- )
25
- export const AnnotationFeature = types
26
- .model('AnnotationFeature', {
27
- _id: types.identifier,
28
- gffId: types.maybe(types.string), // ID from attributes if exists, otherwise gffId = _id
29
- /** Reference sequence name */
30
- refSeq: types.string,
31
- /** Feature type */
32
- type: types.string,
33
- /** Feature location start coordinate */
34
- start: types.number,
35
- /** Feature location end coordinate */
36
- end: types.number,
37
- /**
38
- * If the feature exists in multiple places, e.g. a CDS in a canonical SO
39
- * gene, this gives the coordinates of the individual starts and ends. The
40
- * start of the first location and the end of the last location should match
41
- * the feature's start and end.
42
- */
43
- discontinuousLocations: types.maybe(
44
- types.array(
45
- types.model({ start: types.number, end: types.number, phase: Phase }),
46
- ),
47
- ),
48
- /** The strand on which the feature is located */
49
- strand: types.maybe(types.union(types.literal(1), types.literal(-1))),
50
- /** The feature's score */
51
- score: types.maybe(types.number),
52
- /**
53
- * The feature's phase, which is required for certain features, e.g. CDS in a
54
- * canonical SO gene
55
- */
56
- phase: Phase,
57
- /** Child features of this feature */
58
- children: types.maybe(types.map(types.maybe(LateAnnotationFeature))),
59
- /**
60
- * Additional attributes of the feature. This could include name, source,
61
- * note, dbxref, etc.
62
- */
63
- attributes: types.map(types.array(types.string)),
64
- })
65
- .views((self) => ({
66
- get length() {
67
- return self.end - self.start
68
- },
69
- get featureId() {
70
- return self.attributes.get('id')
71
- },
72
- /**
73
- * Possibly different from `start` because "The GFF3 format does not enforce
74
- * a rule in which features must be wholly contained within the location of
75
- * their parents"
76
- */
77
- get min() {
78
- let min = self.start
79
- for (const [, child] of self.children ?? []) {
80
- min = Math.min(min, child.min)
81
- }
82
- return min
83
- },
84
- /**
85
- * Possibly different from `end` because "The GFF3 format does not enforce a
86
- * rule in which features must be wholly contained within the location of
87
- * their parents"
88
- */
89
- get max() {
90
- let max = self.end
91
- for (const [, child] of self.children ?? []) {
92
- max = Math.max(max, child.max)
93
- }
94
- return max
95
- },
96
- hasDescendant(featureId: string) {
97
- const { children } = self
98
- if (!children) {
99
- return false
100
- }
101
- for (const [id, child] of children) {
102
- if (id === featureId) {
103
- return true
104
- }
105
- if (child.hasDescendant(featureId)) {
106
- return true
107
- }
108
- }
109
- return false
110
- },
111
- }))
112
- .actions((self) => ({
113
- setAttributes(attributes: Map<string, string[]>) {
114
- self.attributes.clear()
115
- for (const [key, value] of attributes.entries()) {
116
- self.attributes.set(key, value)
117
- }
118
- },
119
- setAttribute(key: string, value: string[]) {
120
- self.attributes.merge({ [key]: value })
121
- },
122
- setType(type: string) {
123
- self.type = type
124
- },
125
- setRefSeq(refSeq: string) {
126
- self.refSeq = refSeq
127
- },
128
- setStart(start: number) {
129
- if (start > self.end) {
130
- throw new Error(`Start "${start}" is greater than end "${self.end}"`)
131
- }
132
- if (self.start !== start) {
133
- self.start = start
134
- }
135
- },
136
- setCDSDiscontinuousLocationStart(start: number, index: number) {
137
- const dl = self.discontinuousLocations
138
- if (dl && dl.length > 0 && dl[index].start !== start) {
139
- dl[index].start = start
140
- if (index === 0) {
141
- self.start = start
142
- }
143
- }
144
- },
145
- setEnd(end: number) {
146
- if (end < self.start) {
147
- throw new Error(`End "${end}" is less than start "${self.start}"`)
148
- }
149
- if (self.end !== end) {
150
- self.end = end
151
- }
152
- },
153
- setCDSDiscontinuousLocationEnd(end: number, index: number) {
154
- const dl = self.discontinuousLocations
155
- if (dl && dl.length > 0 && dl[index].end !== end) {
156
- dl[index].end = end
157
- if (index === dl.length - 1) {
158
- self.end = end
159
- }
160
- }
161
- },
162
- setStrand(strand?: 1 | -1) {
163
- self.strand = strand
164
- },
165
- addChild(childFeature: AnnotationFeatureSnapshot) {
166
- if (self.children && self.children.size > 0) {
167
- const existingChildren = getSnapshot(self.children) ?? {}
168
- self.children.clear()
169
- for (const [, child] of Object.entries({
170
- ...existingChildren,
171
- [childFeature._id]: childFeature,
172
- }).sort(([, a], [, b]) => a.start - b.start)) {
173
- self.children.put(child)
174
- }
175
- } else {
176
- self.children = cast({})
177
- self.children?.put(childFeature)
178
- }
179
- },
180
- deleteChild(childFeatureId: string) {
181
- self.children?.delete(childFeatureId)
182
- },
183
- }))
184
- .actions((self) => ({
185
- update({
186
- children,
187
- end,
188
- refSeq,
189
- start,
190
- strand,
191
- }: {
192
- refSeq: string
193
- start: number
194
- end: number
195
- strand?: 1 | -1
196
- children?: SnapshotOrInstance<typeof LateAnnotationFeature>
197
- }) {
198
- self.setRefSeq(refSeq)
199
- self.setStart(start)
200
- self.setEnd(end)
201
- self.setStrand(strand)
202
- if (children) {
203
- self.children = cast(children)
204
- }
205
- },
206
- }))
207
- // This views block has to be last to avoid:
208
- // "'parent' is referenced directly or indirectly in its own type annotation."
209
- .views((self) => ({
210
- get parent() {
211
- let parent: AnnotationFeatureI | undefined
212
- try {
213
- parent = getParentOfType(self, AnnotationFeature)
214
- } catch {
215
- // pass
216
- }
217
- return parent
218
- },
219
- get topLevelFeature(): AnnotationFeatureI {
220
- let feature = self
221
- let parent
222
- do {
223
- try {
224
- parent = getParentOfType(feature, AnnotationFeature)
225
- feature = parent
226
- } catch {
227
- parent = undefined
228
- }
229
- } while (parent)
230
- return feature as AnnotationFeatureI
231
- },
232
- get assemblyId(): string {
233
- return getParentOfType(self, ApolloAssembly)._id
234
- },
235
- }))
236
-
237
- export type AnnotationFeatureI = Instance<typeof AnnotationFeature>
238
- type AnnotationFeatureSnapshotRaw = SnapshotIn<typeof AnnotationFeature>
239
- export interface AnnotationFeatureSnapshot
240
- extends AnnotationFeatureSnapshotRaw {
241
- /** Child features of this feature */
242
- children?: Record<string, AnnotationFeatureSnapshot>
243
- }