sequaljs
Version:
JavaScript/TypeScript library for parsing and manipulating ProForma peptide sequence notation
629 lines (497 loc) • 18.4 kB
Markdown
# ProForma 2.1 Upgrade Plan for SequalJS (TypeScript)
## Overview
This document outlines the plan to upgrade the TypeScript ProForma parser from version 2.0 to 2.1, implementing all six new features specified in the ProForma 2.1 specification.
**Reference Implementation**: The Go implementation at `/mnt/d/GoLandProjects/sequal` has already been upgraded to ProForma 2.1 and serves as the reference for this upgrade.
---
## Implementation Phases
### Phase 1: Charged Formulas (Section 11.1)
**Priority**: High
**Files to Modify**: `src/modification.ts`, `src/proforma.ts`
#### Description
Add support for formulas with charge notation, e.g., `[Formula:Zn1:z+2]`, `[Formula:Cu1:z-1]`
#### Implementation Details
**1.1 PipeValue Class Enhancement** (`src/modification.ts`)
```typescript
class PipeValue {
// Add new fields
charge?: string; // e.g., "z+2", "z-1"
chargeValue?: number; // e.g., 2, -1
// Update constructor to parse charge notation
constructor(value: string, valueType: PipeValueType) {
// Existing code...
// ProForma 2.1: Handle charged formulas (Section 11.1)
if (valueType === PipeValueType.FORMULA) {
const chargeMatch = value.match(/:z([+-]\d+)$/);
if (chargeMatch) {
this.charge = 'z' + chargeMatch[1];
this.chargeValue = parseInt(chargeMatch[1]);
this.value = value.replace(/:z[+-]\d+$/, '');
}
}
}
// Add getters
getCharge(): string | undefined { return this.charge; }
getChargeValue(): number | undefined { return this.chargeValue; }
}
```
**1.2 Update toProforma() Method**
```typescript
toProforma(): string {
let result = this.value;
// ProForma 2.1: Add charge notation
if (this.charge) {
result += ':' + this.charge;
}
return result;
}
```
**1.3 Test Cases** (`src/__test__/proforma_2_1_charged_formulas.test.ts`)
- Basic charged formula: `PEPT[Formula:Zn1:z+2]IDE`
- Negative charge: `PEPT[Formula:Cu1:z-1]IDE`
- Multiple charged formulas in sequence
- Round-trip conversion
- Combined with other modifications
**Expected Outcome**: Parser can handle formulas with `:z+N` or `:z-N` charge notation
---
### Phase 2: Named Entity Definitions (Section 11.3)
**Priority**: High
**Files to Modify**: `src/modification.ts`, `src/proforma.ts`
#### Description
Support named entity references like `[#g1:Glycan]`, `[#p1:Peptide]`
#### Implementation Details
**2.1 Modification Class Enhancement** (`src/modification.ts`)
```typescript
class Modification extends BaseBlock {
// Add new field
fullName?: string; // Named entity full name
constructor(params) {
// Existing code...
this.fullName = params.fullName;
}
// Add getter
getFullName(): string | undefined { return this.fullName; }
}
```
**2.2 Parser Enhancement** (`src/proforma.ts`)
```typescript
static _createModification(modStr: string, options: any): Modification {
// Check for named entity pattern: #id:Name
const namedEntityMatch = modStr.match(/^#([^:]+):(.+)$/);
if (namedEntityMatch) {
const [, entityId, entityName] = namedEntityMatch;
return new Modification({
value: modStr,
fullName: entityName,
modType: options.modType || 'static',
// ... other params
});
}
// Existing code...
}
```
**2.3 Update Serialization**
```typescript
toProforma(): string {
if (this.fullName) {
// Already in #id:Name format
return this.value;
}
// Existing code...
}
```
**2.4 Test Cases** (`src/__test__/proforma_2_1_named_entities.test.ts`)
- Basic named entity: `[#g1:Glycan]-PEPTIDE`
- Multiple named entities
- Named entities with modifications
- Round-trip conversion
**Expected Outcome**: Parser recognizes and preserves named entity definitions
---
### Phase 3: Custom Monosaccharide Definitions (Section 11.4)
**Priority**: Medium
**Files to Modify**: `src/modification.ts`, `src/proforma.ts`
#### Description
Support custom glycan building blocks: `[#d1:dHex]`, `[#h1:Hex(5)]`
#### Implementation Details
**3.1 Same approach as Named Entities**
- Custom monosaccharides use the same `#id:Name` syntax
- Already handled by Phase 2 implementation
- Just need specific validation for glycan context
**3.2 Additional Validation** (`src/modification.ts`)
```typescript
// In Modification constructor
if (this.fullName && this.modType === 'glycan') {
// Validate glycan definition format
this.validateGlycanDefinition();
}
private validateGlycanDefinition(): void {
// Check for valid glycan composition
const glycanPattern = /^[A-Za-z]+(\(\d+\))?$/;
if (!glycanPattern.test(this.fullName!)) {
throw new Error(`Invalid glycan definition: ${this.fullName}`);
}
}
```
**3.3 Test Cases** (`src/__test__/proforma_2_1_custom_monosaccharides.test.ts`)
- Custom monosaccharide: `[#d1:dHex]`
- With counts: `[#h1:Hex(5)]`
- Multiple custom definitions
- Round-trip conversion
**Expected Outcome**: Parser handles custom glycan building block definitions
---
### Phase 4: Terminal Global Modifications (Section 11.5)
**Priority**: Medium
**Files to Modify**: `src/proforma.ts`
#### Description
Allow global modifications on termini: `<[Acetyl]@N-term>`, `<[Amidation]@C-term>`
#### Implementation Details
**4.1 Parser Enhancement** (`src/proforma.ts`)
```typescript
// In global modification parsing section
const globalModMatch = cleanedProforma.match(/^<\[([^\]]+)\]@([^>]+)>/);
if (globalModMatch) {
const [fullMatch, modValue, targets] = globalModMatch;
// ProForma 2.1: Check for terminal targets
if (targets === 'N-term' || targets === 'C-term') {
// Create terminal modification instead of global
const terminalMod = this._createModification(modValue, {
modType: 'terminal'
});
// Add to appropriate terminal position
if (targets === 'N-term') {
modifications.set(-1, [terminalMod]);
} else {
modifications.set(-2, [terminalMod]);
}
} else {
// Existing global mod handling
// ...
}
}
```
**4.2 Test Cases** (`src/__test__/proforma_2_1_terminal_global.test.ts`)
- N-terminal global: `<[Acetyl]@N-term>PEPTIDE`
- C-terminal global: `<[Amidation]@C-term>PEPTIDE`
- Both terminals
- Round-trip conversion
**Expected Outcome**: Global modification syntax works for terminal positions
---
### Phase 5: Placement Controls (Section 11.2)
**Priority**: High
**Files to Modify**: `src/modification.ts`, `src/proforma.ts`
#### Description
Add placement control tags for global modifications:
- `Position:M` - Position constraint
- `Limit:2` - Max modifications per position
- `CoMKP` - Colocalize with known positions
- `CoMUP` - Colocalize with unknown positions
#### Implementation Details
**5.1 Modification Class Enhancement** (`src/modification.ts`)
```typescript
class Modification extends BaseBlock {
// ProForma 2.1: Placement controls (Section 11.2)
positionConstraint?: string[]; // e.g., ["M"], ["S", "T", "Y"]
limitPerPosition?: number; // e.g., 2
colocalizeKnown: boolean = false; // CoMKP
colocalizeUnknown: boolean = false; // CoMUP
constructor(params) {
// Existing code...
this.positionConstraint = params.positionConstraint;
this.limitPerPosition = params.limitPerPosition;
this.colocalizeKnown = params.colocalizeKnown ?? false;
this.colocalizeUnknown = params.colocalizeUnknown ?? false;
}
// Add getters
getPositionConstraint(): string[] | undefined { return this.positionConstraint; }
getLimitPerPosition(): number | undefined { return this.limitPerPosition; }
getColocalizeKnown(): boolean { return this.colocalizeKnown; }
getColocalizeUnknown(): boolean { return this.colocalizeUnknown; }
}
```
**5.2 GlobalModification Class Update**
```typescript
class GlobalModification extends Modification {
constructor(params) {
super({
...params,
positionConstraint: params.positionConstraint,
limitPerPosition: params.limitPerPosition,
colocalizeKnown: params.colocalizeKnown,
colocalizeUnknown: params.colocalizeUnknown,
});
}
}
```
**5.3 Parser Enhancement** (`src/proforma.ts`)
```typescript
// In global modification parsing
if (modValue.includes('|')) {
const modParts = modValue.split('|');
const modName = modParts[0];
// ProForma 2.1: Parse placement control tags
let positionConstraint: string[] | undefined;
let limitPerPosition: number | undefined;
let colocalizeKnown = false;
let colocalizeUnknown = false;
for (let i = 1; i < modParts.length; i++) {
const part = modParts[i];
if (part.startsWith('Position:')) {
positionConstraint = part.substring(9).split(',');
} else if (part.startsWith('Limit:')) {
limitPerPosition = parseInt(part.substring(6));
} else if (part === 'CoMKP' || part === 'ColocaliseModificationsOfKnownPosition') {
colocalizeKnown = true;
} else if (part === 'CoMUP' || part === 'ColocaliseModificationsOfUnknownPosition') {
colocalizeUnknown = true;
}
}
// Create global modification with placement controls
globalMods.push(new GlobalModification({
value: modName,
targetResidues: targets.split(','),
globalModType: 'fixed',
positionConstraint,
limitPerPosition,
colocalizeKnown,
colocalizeUnknown,
}));
}
```
**5.4 Serialization Update** (`src/modification.ts`)
```typescript
toProforma(): string {
let result = this.modValue.toProforma();
// ProForma 2.1: Add placement control tags
const tags: string[] = [];
if (this.positionConstraint && this.positionConstraint.length > 0) {
tags.push(`Position:${this.positionConstraint.join(',')}`);
}
if (this.limitPerPosition !== undefined) {
tags.push(`Limit:${this.limitPerPosition}`);
}
if (this.colocalizeKnown) {
tags.push('CoMKP');
}
if (this.colocalizeUnknown) {
tags.push('CoMUP');
}
if (tags.length > 0) {
result += '|' + tags.join('|');
}
return result;
}
```
**5.5 Test Cases** (`src/__test__/proforma_2_1_placement_controls.test.ts`)
- Position constraint: `<[Oxidation|Position:M]@M>PEPTIDE`
- Multiple positions: `<[Phospho|Position:S,T,Y]@S,T,Y>PEPTIDES`
- Limit: `<[Phospho|Limit:2]@S,T,Y>STYSTY`
- CoMKP: `<[Oxidation|CoMKP]@M>PEPTIDE`
- CoMUP: `<[Oxidation|CoMUP]@M>PEPTIDE`
- Combined: `<[Phospho|Position:S,T,Y|Limit:2|CoMKP]@S,T,Y>PEPTIDES`
- Serialization tests
- Round-trip tests
**Expected Outcome**: Full placement control support for global modifications
---
### Phase 6: Ion Notation (Section 11.6)
**Priority**: High
**Files to Modify**: `src/modification.ts`, `src/proforma.ts`
#### Description
Add semantic flag for ion type modifications: `[a-type-ion]`, `[b-type-ion]`, etc.
#### Implementation Details
**6.1 Modification Class Enhancement** (`src/modification.ts`)
```typescript
class Modification extends BaseBlock {
// ProForma 2.1: Ion notation (Section 11.6)
isIonType: boolean = false;
constructor(params) {
// Existing code...
this.isIonType = params.isIonType ?? false;
}
// Add getter
getIsIonType(): boolean { return this.isIonType; }
}
```
**6.2 Ion Type Detection** (`src/proforma.ts`)
```typescript
static isIonTypeModification(modStr: string): boolean {
const modStrLower = modStr.toLowerCase();
// Check for -type-ion suffix
if (modStrLower.endsWith('-type-ion')) {
return true;
}
// Known Unimod ion type IDs
const ionTypeUnimodIds = new Set([
'140', // a-type-ion
'2132', // b-type-ion
'4', // c-type-ion
'24', // x-type-ion
'2133', // y-type-ion
'23', // z-type-ion
]);
// Check for Unimod references
if (modStr.startsWith('UNIMOD:') || modStr.startsWith('U:')) {
const unimodId = modStr.split(':')[1];
return ionTypeUnimodIds.has(unimodId);
}
return false;
}
```
**6.3 Parser Integration**
```typescript
static _createModification(modStr: string, options: any): Modification {
const isIonType = this.isIonTypeModification(modStr);
return new Modification({
value: modStr,
isIonType,
// ... other params
});
}
```
**6.4 Test Cases** (`src/__test__/proforma_2_1_ion_notation.test.ts`)
- All ion types: `[a-type-ion]`, `[b-type-ion]`, `[c-type-ion]`, `[x-type-ion]`, `[y-type-ion]`, `[z-type-ion]`
- Unimod references: `[UNIMOD:140]`, `[UNIMOD:2132]`
- Short form: `[U:140]`, `[U:2132]`
- Case insensitive: `[A-TYPE-ION]`, `[B-Type-Ion]`
- Non-ion mods return false
- Round-trip preservation
**Expected Outcome**: Ion type modifications are flagged and preserved
---
## Integration Testing
### Create Comprehensive Integration Test Suite
**File**: `src/__test__/proforma_2_1_integration.test.ts`
#### Test Coverage
1. **Placement Controls with Ion Notation**
- `<[Phospho|Position:S,T,Y|Limit:2]@S,T,Y>PEPT[a-type-ion]IDE`
2. **Charged Formulas with Ion Types**
- `PE[a-type-ion]PT[Formula:Zn1:z+2]IDE`
3. **Named Entities with Charged Formulas**
- `[#g1:Glycan]-PEPT[Formula:Zn1:z+2]IDE`
4. **Complex Global Modifications**
- `<[Phospho|Position:S,T,Y|CoMKP]@S,T,Y><[Oxidation|Position:M|CoMUP]@M>STMYST`
5. **All Features Combined**
- `<[Phospho|Position:S,T,Y|Limit:2|CoMKP]@S,T,Y>[#g1:Glycan]-PE[a-type-ion]PT[Formula:Zn1:z+2]IDE`
6. **Serialization Consistency**
- Multiple round-trip tests ensuring stability
7. **Edge Cases**
- Empty position constraints
- Multiple ion types in sequence
- Terminal global with placement controls
---
## Testing Strategy
### 1. Unit Tests
- Each phase gets its own test file
- Minimum 10-15 test cases per feature
- Cover parsing, serialization, and round-trip
### 2. Integration Tests
- Test feature combinations
- Complex real-world scenarios
- Round-trip stability
### 3. Regression Tests
- **CRITICAL**: Run all existing ProForma 2.0 tests after each phase
- Ensure zero breaking changes
- Fix any regressions immediately before proceeding
### 4. Test Naming Convention
```
src/__test__/
proforma_2_1_charged_formulas.test.ts
proforma_2_1_named_entities.test.ts
proforma_2_1_custom_monosaccharides.test.ts
proforma_2_1_terminal_global.test.ts
proforma_2_1_placement_controls.test.ts
proforma_2_1_ion_notation.test.ts
proforma_2_1_integration.test.ts
```
---
## Documentation Updates
### 1. README.md Updates
- Change version from "ProForma 2.0" to "ProForma 2.1"
- Add ProForma 2.1 Features section with examples
- Update feature list
- Add 2.1 code examples
- Update compliance section
### 2. Code Documentation
- Add JSDoc comments for new fields
- Document ProForma 2.1 sections in comments
- Add examples in class documentation
### 3. CHANGELOG.md
Create new file documenting:
- Version bump to 2.1.0
- All new features
- Breaking changes (if any)
- Migration guide
---
## Implementation Order
### Recommended Sequence
1. **Phase 1: Charged Formulas** - Foundation for formula enhancements
2. **Phase 6: Ion Notation** - Simple boolean flag, minimal risk
3. **Phase 5: Placement Controls** - Core feature, test thoroughly
4. **Phase 2: Named Entities** - Moderate complexity
5. **Phase 3: Custom Monosaccharides** - Builds on named entities
6. **Phase 4: Terminal Global** - Lower priority, niche use case
7. **Integration Tests** - Comprehensive feature combinations
---
## Success Criteria
### Phase Completion Checklist
For each phase:
- [ ] Feature implementation complete
- [ ] Unit tests written (10+ test cases)
- [ ] All unit tests passing
- [ ] All existing ProForma 2.0 tests still passing (zero regressions)
- [ ] Serialization/round-trip tests passing
- [ ] Code documentation updated
- [ ] Examples added to README (if applicable)
### Final Completion
- [ ] All 6 phases implemented
- [ ] Integration test suite complete (20+ test cases)
- [ ] All tests passing (100% pass rate)
- [ ] Zero regressions in ProForma 2.0 features
- [ ] README.md updated
- [ ] CHANGELOG.md created
- [ ] Version bumped to 2.1.0 in package.json
---
## Risk Mitigation
### Critical Considerations
1. **Backward Compatibility**: Must maintain 100% ProForma 2.0 compatibility
2. **Pipe Value Handling**: Don't break existing pipe value parsing for placement controls
3. **Terminal Modifications**: Ensure terminal global mods don't conflict with existing terminal mod handling
4. **TypeScript Types**: Add proper type definitions for all new fields
5. **Jest Configuration**: Ensure test coverage includes new files
### Testing Checkpoints
- After each phase: Run full test suite
- Before integration tests: Verify zero regressions
- Before documentation: Verify all features work in combination
- Before release: Full manual testing with complex examples
---
## Reference Implementation
**Go Implementation**: `/mnt/d/GoLandProjects/sequal`
- All 6 phases completed
- 37+ new tests
- Zero regressions
- Can be used as reference for TypeScript port
**Key Files to Reference**:
- `sequal/proforma.go` - Parser implementation
- `sequal/modification.go` - Modification data structures
- `sequal/global_modification.go` - Global modification handling
- `sequal/proforma_2_1_*_test.go` - Test patterns and cases
---
## Timeline Estimate
**Per Phase**: 2-4 hours (implementation + testing)
**Total Estimate**: 12-24 hours for complete upgrade
**Breakdown**:
- Phase 1 (Charged Formulas): 2 hours
- Phase 2 (Named Entities): 3 hours
- Phase 3 (Custom Monosaccharides): 2 hours
- Phase 4 (Terminal Global): 2 hours
- Phase 5 (Placement Controls): 4 hours
- Phase 6 (Ion Notation): 2 hours
- Integration Tests: 3 hours
- Documentation: 2 hours
- Final QA: 2 hours
---
## Notes
- Use strict TypeScript typing for all new fields
- Follow existing code style and patterns
- Maintain JSDoc documentation standards
- Use Jest for all testing
- Keep test files focused and organized
- Commit after each phase completion
- Tag release as v2.1.0 when complete