IVMS101.2023 TypeScript library with legacy 2020 support
npm install ivms101A TypeScript library providing type definitions, validation, and conversion utilities for the IVMS101 (interVASP Messaging Standard). Defaults to IVMS101.2023 with full support for legacy 2020 format.
- ๐ฏ 2023-first API - Modern, clean interface defaulting to the latest standard
- โ
Runtime validation - Comprehensive Zod schemas with all 12 IVMS101 constraints
- ๐ Version conversion - Bidirectional, lossless conversion between 2020 and 2023
- ๐งช Property-based testing - Fast-check arbitraries for generating compliant test data
- ๐ฆ Dual format - CommonJS and ESM builds with full TypeScript support
- ๐๏ธ Clean architecture - Independent type definitions with no circular dependencies
``bash`
npm install ivms101
Version 2.0 introduces a 2023-first API with breaking changes. Here's how to upgrade:
1. Main export now defaults to IVMS101.2023
- IVMS101 type is now IVMS101_2023.IVMS101 (not a union)
- All type exports from main package are 2023 types
2. 2020 support moved to legacy submodule
- Import 2020 types from 'ivms101/legacy' instead of main package
- Conversion functions moved to legacy module
3. New validation API
- New validate() function replaces validateIVMS101()
- Defaults to 2023 validation
#### If you're using both 2020 and 2023 (union types)
Before (v1.x):
`typescript
import { IVMS101, IVMS101_2020, IVMS101_2023 } from 'ivms101';
// IVMS101 was a union type
function process(data: IVMS101) {
// data could be either version
}
`
After (v2.0):
`typescript
import { IVMS101 } from 'ivms101'; // This is now 2023 only
import { IVMS101_2020, IVMS101Schema } from 'ivms101/legacy';
// For union type, import from legacy
import type { IVMS101Type } from 'ivms101/legacy';
function process(data: IVMS101Type) {
// data can be either version (union)
}
// OR: Keep separate functions for each version
function process2023(data: IVMS101) { / ... / }
function process2020(data: IVMS101_2020.IVMS101) { / ... / }
`
#### If you're using 2020 types
Before (v1.x):
`typescript
import { IVMS101_2020 } from 'ivms101';
const data: IVMS101_2020.IVMS101 = { / ... / };
`
After (v2.0):
`typescript
import { IVMS101_2020 } from 'ivms101/legacy';
const data: IVMS101_2020.IVMS101 = { / ... / };
`
#### If you're using conversion functions
Before (v1.x):
`typescript`
import { convertTo2023, convertFrom2023 } from 'ivms101';
After (v2.0):
`typescript`
import { convertTo2023, convertFrom2023 } from 'ivms101/legacy';
#### If you're using validation
Before (v1.x):
`typescript
import { validateIVMS101, isValidIVMS101 } from 'ivms101';
const validated = validateIVMS101(data); // accepts either version
`
After (v2.0):
`typescript
// Option 1: Use new validate() function (defaults to 2023)
import { validate } from 'ivms101';
const validated = validate(data); // validates as 2023
const validated2020 = validate(data, { version: '2020' });
// Option 2: Use legacy validateIVMS101 for union validation
import { validateIVMS101, isValidIVMS101 } from 'ivms101/legacy';
const validated = validateIVMS101(data); // accepts either version
`
#### If you're using type guards
Before (v1.x):
`typescript`
import { isValidIVMS101_2023 } from 'ivms101';
After (v2.0):
`typescript
// 2023 type guard in main package
import { isValidIVMS101_2023 } from 'ivms101';
// 2020 type guard in legacy package
import { isValidIVMS101_2020 } from 'ivms101/legacy';
`
#### Quick Migration Checklist
- [ ] Replace import { IVMS101_2020, ... } from 'ivms101' with import { IVMS101_2020, ... } from 'ivms101/legacy'import { convertTo2023, convertFrom2023 } from 'ivms101'
- [ ] Replace with import { convertTo2023, convertFrom2023 } from 'ivms101/legacy'IVMS101Type
- [ ] If using union types, import from 'ivms101/legacy' instead of using IVMS101validate()
- [ ] Update validation calls to use new function or import legacy validatorsIVMS101
- [ ] Update type annotations: is now 2023-only (not a union)
โ
All 2023 types work exactly as before (just imported from main package)
โ
All conversion logic is unchanged (just moved to legacy module)
โ
All validation schemas work the same way
โ
ensureVersion() and ivms101_version() still in main package
โ
Arbitraries still work the same way
โ
All tests pass without changes
For new code, use the 2023-first API:
`typescript`
import { validate, type IVMS101 } from 'ivms101';
For existing code that needs 2020 support, add legacy import:
`typescript`
import { validate, type IVMS101 } from 'ivms101';
import { IVMS101_2020, convertTo2023 } from 'ivms101/legacy';
`typescript
import { validate, type IVMS101, type NaturalPerson } from 'ivms101';
// The IVMS101 type is IVMS101.2023 by default
const data: IVMS101 = {
originator: {
originatorPerson: [{ / ... / }] // Note: singular "Person" in 2023
},
beneficiary: {
beneficiaryPerson: [{ / ... / }]
},
payloadMetadata: {
payloadVersion: "101.2023" // Required in 2023
}
};
// Validate (defaults to 2023)
const validated = validate(data);
// Or explicitly specify version
const validated2023 = validate(data, { version: '2023' });
`
`typescript
import { IVMS101_2020 } from 'ivms101/legacy';
import { validate } from 'ivms101';
const legacy: IVMS101_2020.IVMS101 = {
originator: {
originatorPersons: [{ / ... / }] // Note: plural "Persons" in 2020
},
beneficiary: {
beneficiaryPersons: [{ / ... / }]
}
};
// Validate as 2020 format
const validated = validate(legacy, { version: '2020' });
`
`typescript
import { ensureVersion, ivms101_version, PayloadVersionCode } from 'ivms101';
import { convertTo2023, convertFrom2023 } from 'ivms101/legacy';
// Auto-detect version
const version = ivms101_version(data);
// Convert to 2023 (default)
const data2023 = ensureVersion(data);
// Convert to specific version
const data2020 = ensureVersion(PayloadVersionCode.V2020, data);
// Explicit conversions
const converted2023 = convertTo2023(data2020);
const converted2020 = convertFrom2023(data2023);
`
`typescript
import {
// Types (all are IVMS101.2023)
type IVMS101, // Main IVMS101 type
type NaturalPerson, // Natural person type
type LegalPerson, // Legal person type
type Person, // Person (natural or legal)
type Originator, // Originator type
type Beneficiary, // Beneficiary type
// Enums
PayloadVersionCode, // Version codes ("101" | "101.2023")
// Validation
validate, // validate(data, { version?: '2020' | '2023' })
IVMS101_2023Schema, // Zod schema for 2023
isValidIVMS101_2023, // Type guard for 2023
// Utilities
ensureVersion, // Convert to specific version (defaults to 2023)
ivms101_version, // Detect version
// Testing
arbitraries, // Fast-check arbitraries
// Shared core types (also exported directly)
type CountryCode, // ISO country codes
type AddressTypeCode, // Address type codes
type NaturalPersonNameTypeCode, // Natural person name type codes
type LegalPersonNameTypeCode, // Legal person name type codes
type Address, // Address structure
type NationalIdentification, // National ID structure
// ... and other core types
} from 'ivms101';
`
`typescript
import {
// Types
IVMS101_2020, // Namespace with all 2020 types
// Conversion
convertTo2023, // Convert 2020 โ 2023
convertFrom2023, // Convert 2023 โ 2020
// Validation
IVMS101_2020Schema, // Zod schema for 2020
isValidIVMS101_2020, // Type guard for 2020
IVMS101Schema, // Union schema (2020 | 2023)
validateIVMS101, // Validate either version
isValidIVMS101, // Type guard for either version
} from 'ivms101/legacy';
`
`typescript
import { validate } from 'ivms101';
// Validate with default version (2023)
try {
const validated = validate(unknownData);
console.log('Valid IVMS101.2023 data:', validated);
} catch (error) {
console.error('Validation failed:', error.message);
}
// Validate specific version
const validated2020 = validate(data, { version: '2020' });
`
`typescript
import { isValidIVMS101_2023 } from 'ivms101';
import { isValidIVMS101_2020, isValidIVMS101 } from 'ivms101/legacy';
// Check if data is valid 2023 format
if (isValidIVMS101_2023(data)) {
// TypeScript knows data is IVMS101_2023.IVMS101
console.log(data.originator.originatorPerson);
}
// Check if data is valid 2020 format
if (isValidIVMS101_2020(data)) {
// TypeScript knows data is IVMS101_2020.IVMS101
console.log(data.originator.originatorPersons);
}
// Check if data is either version
if (isValidIVMS101(data)) {
console.log('Valid IVMS101 data (either version)');
}
`
`typescript
import { IVMS101_2023Schema } from 'ivms101';
import { IVMS101_2020Schema, IVMS101Schema } from 'ivms101/legacy';
// Safe parsing (doesn't throw)
const result = IVMS101_2023Schema.safeParse(data);
if (result.success) {
console.log('Valid data:', result.data);
} else {
console.log('Validation errors:', result.error.issues);
}
// Parse (throws on invalid data)
const validated = IVMS101_2023Schema.parse(data);
// Union schema for either version
const validatedAny = IVMS101Schema.parse(data);
`
The 2023 version introduced several breaking changes from 2020:
| Aspect | IVMS101 2020 | IVMS101.2023 |
|--------|--------------|--------------|
| Originator field | originatorPersons (plural) | originatorPerson (singular) |beneficiaryPersons
| Beneficiary field | (plural) | beneficiaryPerson (singular) |customerNumber
| Customer ID (both person types) | | customerIdentification |nameIdentifierType
| Natural person name field | | naturalPersonNameIdentifierType |payloadMetadata.payloadVersion
| Payload version field | Optional | Required () |
`typescript
// 2020 format
const data2020 = {
originator: {
originatorPersons: [{
naturalPerson: {
customerNumber: "12345",
name: {
nameIdentifier: [{
primaryIdentifier: "Doe",
secondaryIdentifier: "John",
nameIdentifierType: "LEGL"
}]
}
}
}]
},
// ... rest of structure
};
// 2023 format (after conversion)
const data2023 = {
originator: {
originatorPerson: [{
naturalPerson: {
customerIdentification: "12345",
name: {
nameIdentifier: [{
primaryIdentifier: "Doe",
secondaryIdentifier: "John",
naturalPersonNameIdentifierType: "LEGL"
}]
}
}
}]
},
payloadMetadata: {
payloadVersion: "101.2023"
}
// ... rest of structure
};
`
The library includes comprehensive fast-check arbitraries for generating valid IVMS101 test data.
`bash`
npm install --save-dev fast-check
`typescript
import { arbitraries } from 'ivms101';
import * as fc from 'fast-check';
// Generate sample data
const samples2023 = fc.sample(arbitraries.ivms101_2023Valid(), 5);
const samples2020 = fc.sample(arbitraries.ivms101_2020(), 5);
const personSamples = fc.sample(arbitraries.naturalPerson(), 10);
`
`typescript
import { arbitraries } from 'ivms101';
import { validate } from 'ivms101';
import { convertTo2023, convertFrom2023 } from 'ivms101/legacy';
import * as fc from 'fast-check';
describe('IVMS101 Properties', () => {
it('all generated 2023 data should be valid', () => {
fc.assert(fc.property(arbitraries.ivms101_2023Valid(), (data) => {
expect(() => validate(data)).not.toThrow();
}));
});
it('should preserve data through roundtrip conversion', () => {
fc.assert(fc.property(arbitraries.ivms101_2020(), (original) => {
const converted = convertTo2023(original);
const backConverted = convertFrom2023(converted);
expect(backConverted.originator.originatorPersons.length)
.toBe(original.originator.originatorPersons.length);
}));
});
});
`
#### Complete Structures
- ivms101_2020() - Generate IVMS101 2020 objectsivms101_2023()
- - Generate IVMS101 2023 objectsivms101_2023Valid()
- - Generate 2023 with guaranteed version fieldivms101()
- - Generate either version randomly
#### Person Types
- naturalPerson() - Natural person (2020 format)legalPerson()
- - Legal person (2020 format)person()
- - Either natural or legal (2020)naturalPerson2023()
- - Natural person (2023 format)legalPerson2023()
- - Legal person (2023 format)person2023()
- - Either natural or legal (2023)
#### Components
- naturalPersonNameId() - Name identifier for natural personslegalPersonNameId()
- - Name identifier for legal personsaddress()
- - Complete address objectsnaturalPersonNationalIdentification()
- - National ID for natural personslegalEntityNationalIdentification()
- - National ID for legal entities
#### Enums & Codes
- naturalPersonNameTypeCode() - Name type codeslegalPersonNameTypeCode()
- - Legal person name codesaddressTypeCode()
- - Address type codescountryCode()
- - ISO country codesnationalIdentifierTypeCode()
- - National ID type codestransliterationMethodCode()
- - Transliteration codes
The library implements all 12 IVMS101 specification constraints:
- C1: OriginatorInformationNaturalPerson - Must have address, customer ID, national ID, or date of birth
- C2: DateInPast - Birth date must be historic
- C4: OriginatorInformationLegalPerson - Must have address, customer ID, or national ID
- C5: LegalNamePresentLegalPerson - Must have at least one LEGL name type
- C6: LegalNamePresentNaturalPerson - Must have at least one LEGL name type
- C8: ValidAddress - Must have addressLine OR structured address
- C9: CompleteNationalIdentifierLegalPerson - Complex LEI/registrationAuthority rules
- C10: RegistrationAuthority format - Must match RA[0-9]{6}
- C11: ValidLEI format - Must be 20-character alphanumeric
- C12: sequentialIntegrity - Transfer path sequences must be sequential starting at 0
The library enforces these limits during validation:
| Field | Maximum | Rationale |
|-------|---------|-----------|
| Originator/Beneficiary Persons | 10 | Joint accounts + margin |
| Account Numbers | 20 | Multiple corporate wallets |
| Name Identifiers (Natural) | 5 | Multiple name variants |
| Name Identifiers (Legal) | 3 | Aligns with type codes |
| Geographic Addresses | 5 | Multiple addresses |
| Transfer Path | 5 | Intermediary VASP chain |
| Transliteration Methods | 5 | Character set conversions |
The library follows a clean, layered architecture:
``
countries.ts โ core.ts โ ivms101_2023.ts (independent)
โ ivms101_2020.ts (independent)
โ validator.ts
โ converter.ts
โ arbitraries.ts
โ index.ts (2023 exports)
โ legacy.ts (2020 exports)
Key principle: The 2020 and 2023 type definitions are completely independent, both depending only on shared core types.
This library is written in TypeScript and provides complete type definitions:
`typescript
import type {
IVMS101,
NaturalPerson,
LegalPerson,
Person,
Originator,
Beneficiary,
} from 'ivms101';
import type { IVMS101_2020 } from 'ivms101/legacy';
// Full type safety for both versions
function processData(data: IVMS101) {
// TypeScript knows this is 2023 format
console.log(data.originator.originatorPerson); // โ
}
function processLegacy(data: IVMS101_2020.IVMS101) {
// TypeScript knows this is 2020 format
console.log(data.originator.originatorPersons); // โ
}
``
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details.