@apollo-annotation/shared 0.3.8 → 0.3.9

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 (38) hide show
  1. package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.d.ts +3 -0
  2. package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.js +5 -4
  3. package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.js.map +1 -1
  4. package/dist/Changes/AddFeatureChange.js +14 -2
  5. package/dist/Changes/AddFeatureChange.js.map +1 -1
  6. package/dist/Changes/AddFeaturesFromFileChange.d.ts +3 -0
  7. package/dist/Changes/AddFeaturesFromFileChange.js +5 -2
  8. package/dist/Changes/AddFeaturesFromFileChange.js.map +1 -1
  9. package/dist/Changes/AddRefSeqAliasesChange.js +1 -4
  10. package/dist/Changes/AddRefSeqAliasesChange.js.map +1 -1
  11. package/dist/Changes/FromFileBaseChange.js +6 -6
  12. package/dist/Changes/FromFileBaseChange.js.map +1 -1
  13. package/dist/Checks/CDSCheck.js +5 -4
  14. package/dist/Checks/CDSCheck.js.map +1 -1
  15. package/dist/Checks/TranscriptCheck.js +12 -12
  16. package/dist/Checks/TranscriptCheck.js.map +1 -1
  17. package/dist/Checks/util.d.ts +2 -0
  18. package/dist/Checks/util.js +15 -0
  19. package/dist/Checks/util.js.map +1 -0
  20. package/dist/GFF3/annotationFeatureToGFF3.js +3 -1
  21. package/dist/GFF3/annotationFeatureToGFF3.js.map +1 -1
  22. package/dist/GFF3/gff3ToAnnotationFeature.js +1 -1
  23. package/dist/GFF3/gff3ToAnnotationFeature.js.map +1 -1
  24. package/dist/GFF3/gff3ToAnnotationFeature.test.js +3 -3
  25. package/dist/GFF3/gff3ToAnnotationFeature.test.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +6 -6
  28. package/src/Changes/AddAssemblyAndFeaturesFromFileChange.ts +6 -5
  29. package/src/Changes/AddFeatureChange.ts +16 -2
  30. package/src/Changes/AddFeaturesFromFileChange.ts +6 -1
  31. package/src/Changes/AddRefSeqAliasesChange.ts +1 -5
  32. package/src/Changes/FromFileBaseChange.ts +6 -6
  33. package/src/Checks/CDSCheck.ts +6 -4
  34. package/src/Checks/TranscriptCheck.ts +11 -12
  35. package/src/Checks/util.ts +13 -0
  36. package/src/GFF3/annotationFeatureToGFF3.ts +4 -1
  37. package/src/GFF3/gff3ToAnnotationFeature.test.ts +3 -3
  38. package/src/GFF3/gff3ToAnnotationFeature.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo-annotation/shared",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "main": "./dist/index.js",
5
5
  "scripts": {
6
6
  "build": "yarn clean && tsc --build",
@@ -10,10 +10,10 @@
10
10
  "test:ci": "NODE_V8_COVERAGE=./coverage glob -c \"tsx --test --test-reporter spec --experimental-test-coverage \" \"**/*.test.ts\""
11
11
  },
12
12
  "dependencies": {
13
- "@apollo-annotation/common": "^0.3.8",
14
- "@apollo-annotation/mst": "^0.3.8",
15
- "@apollo-annotation/schemas": "^0.3.8",
16
- "@gmod/gff": "1.2.0",
13
+ "@apollo-annotation/common": "^0.3.9",
14
+ "@apollo-annotation/mst": "^0.3.9",
15
+ "@apollo-annotation/schemas": "^0.3.9",
16
+ "@gmod/gff": "^2.0.0",
17
17
  "@gmod/indexedfasta": "^2.0.4",
18
18
  "@jbrowse/core": "^3.6.5",
19
19
  "bson-objectid": "^2.0.4",
@@ -25,7 +25,7 @@
25
25
  "@nestjs/common": "^10.1.0",
26
26
  "@nestjs/core": "^10.1.0",
27
27
  "@types/chai": "^4.3.19",
28
- "@types/node": "^18.14.2",
28
+ "@types/node": "^20.19.15",
29
29
  "chai": "^5.1.1",
30
30
  "chai-exclude": "^3.0.0",
31
31
  "glob": "^11.0.0",
@@ -19,6 +19,7 @@ export interface SerializedAddAssemblyAndFeaturesFromFileChangeBase
19
19
  export interface AddAssemblyAndFeaturesFromFileChangeDetails {
20
20
  assemblyName: string
21
21
  fileIds: { fa: string }
22
+ parseOptions?: { bufferSize: number }
22
23
  }
23
24
 
24
25
  export interface SerializedAddAssemblyAndFeaturesFromFileChangeSingle
@@ -68,7 +69,7 @@ export class AddAssemblyAndFeaturesFromFileChange extends FromFileBaseChange {
68
69
  const { assemblyModel, checkModel, fileModel, filesService, user } = backend
69
70
  const { assembly, changes, logger } = this
70
71
  for (const change of changes) {
71
- const { assemblyName, fileIds } = change
72
+ const { assemblyName, fileIds, parseOptions } = change
72
73
  const fileId = fileIds.fa
73
74
 
74
75
  const { FILE_UPLOAD_FOLDER } = process.env
@@ -110,13 +111,13 @@ export class AddAssemblyAndFeaturesFromFileChange extends FromFileBaseChange {
110
111
  backend,
111
112
  )
112
113
 
113
- // Loop all features
114
- logger.debug?.(
115
- `**************** LOOPATAAN KAIKKI FEATURET SEURAAVAKSI File type: "${fileDoc.type}"`,
116
- )
114
+ const { bufferSize = 10_000 } = parseOptions ?? {}
117
115
  const featureStream = filesService.parseGFF3(
118
116
  filesService.getFileStream(fileDoc),
117
+ { bufferSize },
119
118
  )
119
+ // @ts-expect-error type is wrong here
120
+ // eslint-disable-next-line @typescript-eslint/await-thenable
120
121
  for await (const f of featureStream) {
121
122
  const gff3Feature = f as GFF3Feature
122
123
  logger.verbose?.(`ENTRY=${JSON.stringify(gff3Feature)}`)
@@ -167,9 +167,23 @@ export class AddFeatureChange extends FeatureChange {
167
167
  for (const change of changes) {
168
168
  const { addedFeature, parentFeatureId } = change
169
169
  if (parentFeatureId) {
170
- const parentFeature = dataStore.getFeature(parentFeatureId)
170
+ let parentFeature = dataStore.getFeature(parentFeatureId)
171
+ // maybe the parent feature hasn't been loaded yet
171
172
  if (!parentFeature) {
172
- throw new Error(`Could not find parent feature "${parentFeatureId}"`)
173
+ await dataStore.loadFeatures([
174
+ {
175
+ assemblyName: assembly,
176
+ refName: addedFeature.refSeq,
177
+ start: addedFeature.min,
178
+ end: addedFeature.max,
179
+ },
180
+ ])
181
+ parentFeature = dataStore.getFeature(parentFeatureId)
182
+ if (!parentFeature) {
183
+ throw new Error(
184
+ `Could not find parent feature "${parentFeatureId}"`,
185
+ )
186
+ }
173
187
  }
174
188
  // create an ID for the parent feature if it does not have one
175
189
  if (!parentFeature.attributes.get('_id')) {
@@ -18,6 +18,7 @@ export interface SerializedAddFeaturesFromFileChangeBase
18
18
 
19
19
  export interface AddFeaturesFromFileChangeDetails {
20
20
  fileId: string
21
+ parseOptions?: { bufferSize: number }
21
22
  }
22
23
 
23
24
  export interface SerializedAddFeaturesFromFileChangeSingle
@@ -75,7 +76,7 @@ export class AddFeaturesFromFileChange extends FromFileBaseChange {
75
76
  }
76
77
 
77
78
  for (const change of changes) {
78
- const { fileId } = change
79
+ const { fileId, parseOptions } = change
79
80
 
80
81
  const { FILE_UPLOAD_FOLDER } = process.env
81
82
  if (!FILE_UPLOAD_FOLDER) {
@@ -89,10 +90,14 @@ export class AddFeaturesFromFileChange extends FromFileBaseChange {
89
90
  logger.debug?.(`FileId "${fileId}", checksum "${fileDoc.checksum}"`)
90
91
 
91
92
  // Read data from compressed file and parse the content
93
+ const { bufferSize = 10_000 } = parseOptions ?? {}
92
94
  const featureStream = filesService.parseGFF3(
93
95
  filesService.getFileStream(fileDoc),
96
+ { bufferSize },
94
97
  )
95
98
  let featureCount = 0
99
+ // @ts-expect-error type is wrong here
100
+ // eslint-disable-next-line @typescript-eslint/await-thenable
96
101
  for await (const f of featureStream) {
97
102
  const gff3Feature = f as GFF3Feature
98
103
 
@@ -35,11 +35,7 @@ export class AddRefSeqAliasesChange extends AssemblySpecificChange {
35
35
  if (!assembly) {
36
36
  throw new Error(`assembly ${this.assembly} not found`)
37
37
  }
38
- const sessionAliases = assembly.refNameAliases
39
-
40
- if (!sessionAliases) {
41
- throw new Error('Session refNameAliases not found in assembly')
42
- }
38
+ const sessionAliases = assembly.refNameAliases ?? {}
43
39
 
44
40
  for (const refSeqAlias of this.refSeqAliases) {
45
41
  const { aliases, refName } = refSeqAlias
@@ -1,7 +1,4 @@
1
- /* eslint-disable @typescript-eslint/restrict-plus-operands */
2
1
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
- /* eslint-disable @typescript-eslint/no-unsafe-call */
5
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
6
3
  import {
7
4
  AssemblySpecificChange,
@@ -38,8 +35,11 @@ export abstract class FromFileBaseChange extends AssemblySpecificChange {
38
35
  let parsingStarted = false
39
36
  logger.debug?.('starting sequence stream')
40
37
  let lineCount = 0
38
+ const decoder = new TextDecoder()
39
+ // @ts-expect-error type is wrong here
40
+ // eslint-disable-next-line @typescript-eslint/await-thenable
41
41
  for await (const data of sequenceStream) {
42
- const chunk = data.toString()
42
+ const chunk = decoder.decode(data)
43
43
  lastLineIsIncomplete = !chunk.endsWith('\n')
44
44
  // chunk is small enough that you can split the whole thing into lines without having to make it into smaller chunks first.
45
45
  const lines = chunk.split(/\r?\n/)
@@ -48,9 +48,9 @@ export abstract class FromFileBaseChange extends AssemblySpecificChange {
48
48
  incompleteLine = ''
49
49
  }
50
50
  if (lastLineIsIncomplete) {
51
- incompleteLine = lines.pop() || ''
51
+ incompleteLine = lines.pop() ?? ''
52
52
  }
53
- for await (const line of lines) {
53
+ for (const line of lines) {
54
54
  lineCount++
55
55
  if (lineCount % 1_000_000 === 0) {
56
56
  logger.debug?.(`Processed ${lineCount} lines`)
@@ -6,6 +6,8 @@ import {
6
6
  import { intersection2 } from '@jbrowse/core/util'
7
7
  import ObjectID from 'bson-objectid'
8
8
 
9
+ import { getPrintableId } from './util'
10
+
9
11
  enum STOP_CODONS {
10
12
  'TAG',
11
13
  'TAA',
@@ -175,7 +177,7 @@ async function checkMRNA(
175
177
  refSeq: refSeq.toString(),
176
178
  start: cdsStart,
177
179
  end: cdsStart,
178
- message: `Unexpected start codon in feature "${_id}": ${start_codon}`,
180
+ message: `Unexpected start codon "${start_codon}" in feature "${getPrintableId(feature)}": `,
179
181
  })
180
182
  }
181
183
 
@@ -189,7 +191,7 @@ async function checkMRNA(
189
191
  refSeq: refSeq.toString(),
190
192
  start: cdsEnd,
191
193
  end: cdsEnd,
192
- message: `Missing stop codon for feature "${_id}"`,
194
+ message: `Missing stop codon in feature "${getPrintableId(feature)}"`,
193
195
  })
194
196
  }
195
197
  } else {
@@ -201,7 +203,7 @@ async function checkMRNA(
201
203
  refSeq: refSeq.toString(),
202
204
  start: cdsEnd,
203
205
  end: cdsEnd,
204
- message: `Missing stop codon: The coding sequence for feature "${_id}" is not a multiple of three`,
206
+ message: `Missing stop codon in feature "${getPrintableId(feature)}"`,
205
207
  })
206
208
  }
207
209
  for (const [idx, codon] of codons.entries()) {
@@ -220,7 +222,7 @@ async function checkMRNA(
220
222
  refSeq: refSeq.toString(),
221
223
  start: codonStart,
222
224
  end: codonEnd,
223
- message: `The coding sequence for feature "${_id}" has an internal stop codon`,
225
+ message: `Internal stop codon in feature "${getPrintableId(feature)}"`,
224
226
  })
225
227
  }
226
228
  }
@@ -6,6 +6,8 @@ import {
6
6
  import { revcom } from '@jbrowse/core/util'
7
7
  import ObjectID from 'bson-objectid'
8
8
 
9
+ import { getPrintableId } from './util'
10
+
9
11
  interface SpliceSequence {
10
12
  fivePrimeSeq: string
11
13
  fivePrimeMin: number
@@ -35,15 +37,17 @@ async function getSpliceSequences(
35
37
  if (exons.length < 2) {
36
38
  return []
37
39
  }
40
+ exons.sort((a, b) => (a.min < b.min ? -1 : 1))
38
41
 
39
42
  const spliceSeq: SpliceSequence[] = []
40
43
  for (let i = 0; i < exons.length - 1; i++) {
41
44
  let fivePrimeMin = exons[i].max
42
- let threePrimeMin = exons[i + 1].min - 2
45
+ let threePrimeMin = exons[i + 1].min
43
46
  if (transcript.strand === -1) {
44
- const _fivePrimeMin = fivePrimeMin
45
- fivePrimeMin = threePrimeMin
46
- threePrimeMin = _fivePrimeMin
47
+ ;[threePrimeMin, fivePrimeMin] = [fivePrimeMin, threePrimeMin]
48
+ fivePrimeMin -= 2
49
+ } else {
50
+ threePrimeMin -= 2
47
51
  }
48
52
 
49
53
  let fivePrimeSeq = await getSequence(fivePrimeMin, fivePrimeMin + 2)
@@ -53,12 +57,7 @@ async function getSpliceSequences(
53
57
  fivePrimeSeq = revcom(fivePrimeSeq)
54
58
  }
55
59
 
56
- spliceSeq.push({
57
- fivePrimeSeq,
58
- fivePrimeMin,
59
- threePrimeSeq,
60
- threePrimeMin,
61
- })
60
+ spliceSeq.push({ fivePrimeSeq, fivePrimeMin, threePrimeSeq, threePrimeMin })
62
61
  }
63
62
  return spliceSeq
64
63
  }
@@ -82,7 +81,7 @@ async function checkTranscript(
82
81
  refSeq: feature.refSeq.toString(),
83
82
  start: spliceSequence.fivePrimeMin,
84
83
  end: spliceSequence.fivePrimeMin + 2,
85
- message: `Unexpected 5' splice site in "${feature._id}". Expected: ${[...VALID_FIVE_PRIME_SEQ].join('|')}, got: ${spliceSequence.fivePrimeSeq}`,
84
+ message: `Unexpected 5 splice site in "${getPrintableId(feature)}". Expected: ${[...VALID_FIVE_PRIME_SEQ].join('|')}, got: ${spliceSequence.fivePrimeSeq}`,
86
85
  })
87
86
  }
88
87
  if (
@@ -96,7 +95,7 @@ async function checkTranscript(
96
95
  refSeq: feature.refSeq.toString(),
97
96
  start: spliceSequence.threePrimeMin,
98
97
  end: spliceSequence.threePrimeMin + 2,
99
- message: `Unexpected 3' splice site in "${feature._id}". Expected: ${[...VALID_THREE_PRIME_SEQ].join('|')}, got: ${spliceSequence.threePrimeSeq}`,
98
+ message: `Unexpected 3 splice site in "${getPrintableId(feature)}". Expected: ${[...VALID_THREE_PRIME_SEQ].join('|')}, got: ${spliceSequence.threePrimeSeq}`,
100
99
  })
101
100
  }
102
101
  }
@@ -0,0 +1,13 @@
1
+ import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
2
+
3
+ export function getPrintableId(feature: AnnotationFeatureSnapshot): string {
4
+ const gff_id = feature.attributes?.gff_id?.join(', ')
5
+ if (gff_id) {
6
+ return `${gff_id} (_id: ${feature._id.toString()})`
7
+ }
8
+ const gff_name = feature.attributes?.gff_name?.join(', ')
9
+ if (gff_name) {
10
+ return `${gff_name} (_id: ${feature._id.toString()})`
11
+ }
12
+ return `_id: ${feature._id.toString()}`
13
+ }
@@ -101,7 +101,10 @@ export function annotationFeatureToGFF3(
101
101
  score,
102
102
  strand: feature.strand ? (feature.strand === 1 ? '+' : '-') : null,
103
103
  phase: null,
104
- attributes: Object.keys(attributes).length > 0 ? attributes : null,
104
+ attributes:
105
+ Object.keys(attributes).length > 0
106
+ ? (attributes as Record<string, string[]>)
107
+ : null,
105
108
  derived_features: [],
106
109
  child_features: prepareChildFeatures(
107
110
  feature,
@@ -3,7 +3,7 @@ import { readFileSync } from 'node:fs'
3
3
  import { describe, it } from 'node:test'
4
4
 
5
5
  import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
6
- import gff, { type GFF3Feature } from '@gmod/gff'
6
+ import { type GFF3Feature, parseStringSync } from '@gmod/gff'
7
7
  import { assert, use } from 'chai'
8
8
  import chaiExclude from 'chai-exclude'
9
9
 
@@ -116,7 +116,7 @@ function readFeatureFile(fn: string): GFF3Feature[] {
116
116
  feature.push(line)
117
117
  }
118
118
  }
119
- const inGff = gff.parseStringSync(feature.join('\n')) as GFF3Feature[]
119
+ const inGff = parseStringSync(feature.join('\n')) as GFF3Feature[]
120
120
  return inGff
121
121
  }
122
122
 
@@ -232,7 +232,7 @@ describe('gff3ToAnnotationFeature', () => {
232
232
  for (const testCase of testCases) {
233
233
  const [description, featureLine, convertedFeature] = testCase
234
234
  it(`converts ${description}`, () => {
235
- const gff3Feature = gff.parseStringSync(featureLine, {
235
+ const gff3Feature = parseStringSync(featureLine, {
236
236
  parseSequences: false,
237
237
  })
238
238
  const feature = gff3ToAnnotationFeature(gff3Feature[0])
@@ -115,7 +115,7 @@ function convertFeatureAttributes(
115
115
  if (attributesCollections.length > 0) {
116
116
  for (const attributesCollection of attributesCollections) {
117
117
  for (const [key, val] of Object.entries(attributesCollection)) {
118
- if (!val || key === 'Parent') {
118
+ if (key === 'Parent') {
119
119
  continue
120
120
  }
121
121
  const newKey = isGFFReservedAttribute(key) ? gffToInternal[key] : key