UNPKG

sequaljs

Version:

JavaScript/TypeScript library for parsing and manipulating ProForma peptide sequence notation

629 lines (497 loc) 18.4 kB
# 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