npm package: @jmbg-labs/jmbg
Description: Validate Serbian unique master citizen number (JMBG - Jedinstveni Matični Broj Građana)
Author: Dušan Veličković
Latest version: 1.0.1

# JMBG JavaScript/TypeScript Library

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Node Version](https://img.shields.io/badge/Node-%3E%3D16.0.0-blue)](https://nodejs.org/)

A JavaScript/TypeScript library for validating and parsing Serbian unique master citizen numbers (JMBG - Jedinstveni Matični Broj Građana).

## Features

- ✅ Validate JMBG numbers with comprehensive checks
- ✅ Extract birth date, region, and gender information
- ✅ Support for all Serbian regions
- ✅ Calculate age from JMBG
- ✅ Full TypeScript support with type definitions
- ✅ Fully tested with Vitest
- ✅ Zero dependencies
- ✅ CommonJS and ESM support

## Installation

Install via npm:

```bash
npm install @jmbg-labs/jmbg
```

Or with yarn:

```bash
yarn add @jmbg-labs/jmbg
```

Or with pnpm:

```bash
pnpm add @jmbg-labs/jmbg
```

## Usage

### Basic Validation

```typescript
import { Jmbg } from '@jmbg-labs/jmbg';

// Quick validation
if (Jmbg.valid('0101000710009')) {
    console.log('Valid JMBG');
}

// Parse and validate
try {
    const jmbg = Jmbg.parse('0101000710009');
    console.log('Valid JMBG:', jmbg.format());
} catch (error) {
    console.error('Invalid JMBG:', error.message);
}
```

### Extract Information

```typescript
import { Jmbg } from '@jmbg-labs/jmbg';

const jmbg = new Jmbg('0101000710009');

// Get birth date
const date = jmbg.getDate(); // Date object
console.log(date.toISOString()); // 2000-01-01T00:00:00.000Z

// Get age
console.log(jmbg.getAge()); // e.g., 26

// Check gender
if (jmbg.isMale()) {
    console.log('Male');
}

if (jmbg.isFemale()) {
    console.log('Female');
}

// Access individual parts
console.log(jmbg.get('day'));         // 1
console.log(jmbg.get('month'));       // 1
console.log(jmbg.get('year'));        // 2000
console.log(jmbg.get('region'));      // 71
console.log(jmbg.get('regionText'));  // "Belgrade"
console.log(jmbg.get('country'));     // "Serbia"
console.log(jmbg.get('unique'));      // 0
console.log(jmbg.get('checksum'));    // 9
```

### Using Property Access

```typescript
const jmbg = new Jmbg('1505995800002');

// String conversion
console.log(jmbg.toString()); // "1505995800002"
console.log(jmbg.format());   // "1505995800002"

// Access original parts
console.log(jmbg.get('original'));        // "1505995800002"
console.log(jmbg.get('dayOriginal'));     // "15"
console.log(jmbg.get('monthOriginal'));   // "05"
console.log(jmbg.get('yearOriginal'));    // "995"
console.log(jmbg.get('regionOriginal'));  // "80"
console.log(jmbg.get('uniqueOriginal'));  // "000"

// Check property existence
if (jmbg.has('regionText')) {
    console.log(jmbg.get('regionText')); // "Novi Sad"
}
```

## JMBG Format

JMBG consists of 13 digits: `DDMMYYYRRBBBC`

- **DD** - Day of birth (01-31)
- **MM** - Month of birth (01-12)
- **YYY** - Year of birth (last 3 digits)
- **RR** - Region code
- **BBB** - Unique number (000-499 for males, 500-999 for females)
- **C** - Checksum digit

### Supported Regions

The library supports all Serbian and ex-Yugoslav regions including (beware: ex-Yugoslav regions codes may have changed since the breakup):

- **Serbia** (71-79): Belgrade, Kragujevac, Niš, etc.
- **Serbia/Vojvodina** (80-89): Novi Sad, Subotica, Pančevo, etc.
- **Serbia/Kosovo** (91-96): Priština, Peć, Prizren, etc.
- **Bosnia and Herzegovina** (10-19)
- **Montenegro** (21-29)
- **Croatia** (30-39)
- **Macedonia** (41-49)
- **Slovenia** (50)

## Validation Rules

The library performs comprehensive validation:

1. **Length check** - Must be exactly 13 digits
2. **Format check** - Must contain only numeric characters
3. **Date validation** - Birth date must be valid (including leap year support)
4. **Region validation** - Region code must exist in the registry
5. **Checksum validation** - Modulo 11 algorithm verification

## Development

### Running Tests

```bash
# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage
```

### Building

```bash
# Build the library
npm run build

# Build in watch mode
npm run dev
```

### Code Style

```bash
# Lint code
npm run lint

# Fix linting issues
npm run lint:fix

# Format code
npm run format

# Check formatting
npm run format:check
```

## Requirements

- Node.js >=16.0.0
- No external dependencies (for production use)

## TypeScript Support

This library is written in TypeScript and includes full type definitions. No additional `@types` package is needed.

```typescript
import { Jmbg, JmbgError, JmbgParts } from '@jmbg-labs/jmbg';

// Full type safety
const jmbg: Jmbg = new Jmbg('0101000710009');
const parts: JmbgParts | null = jmbg.get('day');
```

## Contributing

Contributions are welcome! Please ensure:

1. All tests pass (`npm test`)
2. Code follows the style guide (`npm run lint`)
3. Add tests for new features
4. Update documentation as needed

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Credits

Developed by [JMBG Labs](https://github.com/jmbg-labs)

## Support

- 🐛 [Report Issues](https://github.com/jmbg-labs/js/issues)
- 📖 [Source Code](https://github.com/jmbg-labs/js)

## Examples

### Validate Multiple JMBGs

```typescript
const jmbgs = ['0710003730015', '1705978730032', 'invalid'];

for (const jmbgString of jmbgs) {
    if (Jmbg.valid(jmbgString)) {
        const jmbg = Jmbg.parse(jmbgString);
        console.log(
            `${jmbg.format()} - Born: ${jmbg.getDate().toISOString().split('T')[0]}, ` +
            `Region: ${jmbg.get('regionText')}, ` +
            `Gender: ${jmbg.isMale() ? 'Male' : 'Female'}`
        );
    } else {
        console.log(`${jmbgString} - Invalid`);
    }
}
```

### Age Calculation

```typescript
const jmbg = new Jmbg('0710003730015');
const age = jmbg.getAge();

if (age >= 18) {
    console.log('Adult');
} else {
    console.log('Minor');
}
```

### Error Handling

```typescript
import { Jmbg, JmbgError } from '@jmbg-labs/jmbg';

try {
    const jmbg = new Jmbg('1234567890123');
} catch (error) {
    if (error instanceof JmbgError) {
        // Handle specific validation errors
        console.error('Validation failed:', error.message);
        // Possible messages:
        // - "Input string must have 13 digits."
        // - "JMBG string must have 13 digits."
        // - "Date '...' is not valid."
        // - "Region '...' is not valid for Serbian JMBG."
        // - "Checksum is not valid."
    }
}
```

### Using in CommonJS

```javascript
const { Jmbg } = require('@jmbg-labs/jmbg');

const jmbg = new Jmbg('0101000710009');
console.log(jmbg.getAge());
```

### Using in ES Modules

```javascript
import { Jmbg } from '@jmbg-labs/jmbg';

const jmbg = new Jmbg('0101000710009');
console.log(jmbg.getAge());
```