armanion
v9.0.0
Published
A professional-grade data system with proprietary .arm storage format
Maintainers
Readme
Armanion Data System
A professional-grade Node.js library with proprietary .arm storage format
Armanion is not just a file format—it's a complete Data System and State Engine designed for production use. Built with TypeScript in strict mode, it offers a minimalistic API that masks sophisticated internal architecture.
Philosophy
Armanion treats data as a living system rather than static storage:
- Data System: Self-contained storage with its own philosophy and identity
- State Engine: Dynamic state management with intelligent change tracking
- Zero Compromise: Production-ready from day one with crash prevention guarantees
- Elegant Simplicity: Complex internals, simple interface
Core Principles
- Deterministic: Predictable behavior across all scenarios
- Safe: Zero tolerance for unsafe types (no
any) - Atomic: All operations are transactional
- Encrypted: Native security without external dependencies
- Extensible: Plugin architecture without touching core code
Installation
npm install armanionQuick Start
Basic Usage
import { Armanion } from 'armanion';
// Create new data system
const arm = Armanion.create({
user: {
name: 'John',
age: 30
}
});
// Read data
const name = arm.get('user.name'); // "John"
// Write data
arm.set('user.email', '[email protected]');
// Save to file
await arm.save('data.arm');Load Existing File
import { Armanion } from 'armanion';
// Load from .arm file
const arm = await Armanion.load('data.arm');
// Access data
const user = arm.get('user');
console.log(user);Mental Model
Think of Armanion as a smart document that:
- Remembers its state and tracks every change
- Validates operations before executing them
- Protects data with built-in encryption
- Prevents corruption through atomic transactions
- Extends functionality through plugins
Data Paths
Access data using dot notation paths:
// Simple path
arm.get('user.name');
// Nested path
arm.get('settings.display.theme');
// Arrays work too
arm.get('users.0.name');Paths are:
- Normalized automatically
- Validated before use
- Protected from injection
Core Features
1. Change Tracking
Every modification is tracked:
arm.set('user.name', 'Jane');
arm.set('user.age', 31);
// View all changes
const changes = arm.getChanges();
// [
// { path: 'user.name', operation: 'set', oldValue: 'John', newValue: 'Jane' },
// { path: 'user.age', operation: 'set', oldValue: 30, newValue: 31 }
// ]
// Check if modified
const hasChanges = arm.hasChanges(); // true2. Transactions
Atomic operations with automatic rollback:
await arm.transaction(async (arm) => {
arm.set('account.balance', 1000);
arm.set('account.status', 'active');
// If any error occurs, all changes rollback
if (someCondition) {
throw new Error('Validation failed');
}
// Changes only persist if transaction completes
});3. Lazy Loading
Efficient memory usage for large datasets:
const arm = Armanion.create(hugData, {
lazyThreshold: 5000 // Load chunks larger than 5KB on-demand
});
// Data loaded only when accessed
const section = arm.get('large.section');4. Data Operations
// Set value
arm.set('user.name', 'John');
// Get value
const name = arm.get('user.name');
// Delete value
arm.delete('user.temp');
// Merge objects
arm.merge('user', {
email: '[email protected]',
verified: true
});
// Check existence
if (arm.has('user.email')) {
console.log('Email exists');
}5. Auto-Save
Automatic persistence on changes:
const arm = Armanion.create(data, {
autoSave: true
});
// File is automatically saved after each change
arm.set('user.name', 'Jane');Security & Encryption
Password-Based Encryption
// Create with encryption
const arm = Armanion.create(data, {
encryption: {
password: 'your-secure-password'
}
});
// Save encrypted
await arm.save('secure.arm');
// Load encrypted
const loaded = await Armanion.load('secure.arm', {
encryption: {
password: 'your-secure-password'
}
});Key-Based Encryption
import { KeyDerivation } from 'armanion';
// Generate strong key
const key = KeyDerivation.deriveKey(
'password',
KeyDerivation.generateSalt(),
32 // 256-bit key
);
const arm = Armanion.create(data, {
encryption: { key }
});Selective Field Encryption
Encrypt only sensitive fields:
const arm = Armanion.create(data, {
encryption: {
password: 'secret',
selectiveFields: ['user.password', 'user.ssn', 'payment.cardNumber']
}
});
// Only specified fields are encrypted
// Other fields remain in plaintext for performanceCustom Encryption Algorithms
import { EncryptionAlgorithm } from 'armanion';
class CustomAlgorithm implements EncryptionAlgorithm {
readonly keySize = 32;
readonly name = 'Custom-AES';
encrypt(data: Buffer, key: Buffer): Buffer {
// Your encryption logic
}
decrypt(data: Buffer, key: Buffer): Buffer {
// Your decryption logic
}
}
const arm = Armanion.create(data, {
encryption: {
key: myKey,
algorithm: new CustomAlgorithm()
}
});Extensibility
Hooks System
React to system events:
import { HookEvent } from 'armanion';
const arm = Armanion.create();
// Before save
arm.on(HookEvent.BEFORE_SAVE, async (context) => {
console.log('Saving to:', context.path);
});
// After load
arm.on(HookEvent.AFTER_LOAD, async (context) => {
console.log('Loaded:', context.data);
});
// On error
arm.on(HookEvent.ERROR, async (context) => {
console.error('Error:', context.error);
});Available events:
BEFORE_LOAD/AFTER_LOADBEFORE_SAVE/AFTER_SAVEBEFORE_GET/AFTER_GETBEFORE_SET/AFTER_SETBEFORE_DELETE/AFTER_DELETEBEFORE_COMMIT/AFTER_COMMITBEFORE_ROLLBACK/AFTER_ROLLBACKERROR
Plugins
Extend functionality without modifying core:
import { Plugin, HookEvent } from 'armanion';
class AuditPlugin implements Plugin {
name = 'audit';
version = '1.0.0';
install(hooks) {
hooks.on(HookEvent.AFTER_SET, async (context) => {
console.log(`Audit: Set ${context.path} = ${context.value}`);
});
}
}
const arm = Armanion.create();
await arm.use(new AuditPlugin());Built-in Plugins
Validation Plugin
import { ValidationPlugin } from 'armanion';
const validator = new ValidationPlugin();
validator.addValidator('user.age', (value) => {
return typeof value === 'number' && value >= 0 && value <= 150;
});
validator.addValidator('user.email', (value) => {
return typeof value === 'string' && value.includes('@');
});
await arm.use(validator);
// Now these will be validated
arm.set('user.age', 25); // ✓ OK
arm.set('user.age', -5); // ✗ Error: Validation failedLogging Plugin
import { LoggingPlugin } from 'armanion';
await arm.use(new LoggingPlugin(console.log));
// All operations will be logged
arm.set('user.name', 'John');
// Logs: [BEFORE_SET] { path: 'user.name', value: 'John', ... }
// Logs: [AFTER_SET] { path: 'user.name', value: 'John', ... }Error Handling
Structured Errors
All errors are deterministic and structured:
import { ArmanionError, ErrorCode } from 'armanion';
try {
arm.get('nonexistent.path');
} catch (error) {
if (error instanceof ArmanionError) {
console.log(error.code); // ErrorCode.DATA_NOT_FOUND
console.log(error.message); // "Data not found at path: nonexistent.path"
console.log(error.recoverable); // false
console.log(error.context); // Full error context
}
}Error Codes
ErrorCode.FILE_NOT_FOUND
ErrorCode.FILE_CORRUPTED
ErrorCode.INVALID_PATH
ErrorCode.DATA_NOT_FOUND
ErrorCode.VALIDATION_FAILED
ErrorCode.TRANSACTION_FAILED
ErrorCode.ENCRYPTION_FAILED
ErrorCode.DECRYPTION_FAILED
ErrorCode.OPERATION_NOT_PERMITTED
ErrorCode.INTERNAL_ERRORCrash Prevention
Armanion guarantees your application won't crash:
// Safe execution wrapper
import { safeExecute } from 'armanion';
const result = safeExecute(
() => riskyOperation(),
(error) => ErrorFactory.internalError('Operation failed')
);
// Async version
const result = await safeExecuteAsync(
async () => await riskyAsyncOperation()
);Performance
Optimizations
- Lazy Loading: Large data chunks load on-demand
- No-Op Detection: Identical values don't trigger changes
- Smart Serialization: Efficient binary format
- Minimal I/O: Operations batched automatically
- Change Tracking: Only modified data is processed
Benchmarks
// Large dataset handling
const arm = Armanion.create(dataWith10MBSize);
// Fast access (O(1) for paths)
const value = arm.get('deep.nested.path'); // ~0.1ms
// Efficient saves (only changed data)
await arm.save('data.arm'); // ~50ms for 10MB
// Memory efficient (lazy loading)
console.log(process.memoryUsage()); // ~15MB for 10MB fileBest Practices
1. Use Transactions for Multiple Operations
// ✓ Good - Atomic
await arm.transaction(async (arm) => {
arm.set('user.name', 'John');
arm.set('user.email', '[email protected]');
arm.delete('user.temp');
});
// ✗ Bad - Non-atomic
arm.set('user.name', 'John');
arm.set('user.email', '[email protected]');
arm.delete('user.temp');2. Use Type Safety
// ✓ Good - Type safe
interface User {
name: string;
age: number;
}
const user = arm.get<User>('user');
// ✗ Bad - Untyped
const user = arm.get('user');3. Validate Before Save
// ✓ Good
import { ValidationPlugin } from 'armanion';
const validator = new ValidationPlugin();
validator.addValidator('user.age', (v) => typeof v === 'number');
await arm.use(validator);
// ✗ Bad - No validation
arm.set('user.age', 'invalid'); // Could cause issues4. Handle Errors Explicitly
// ✓ Good
try {
await arm.save('data.arm');
} catch (error) {
if (error instanceof ArmanionError) {
handleArmanionError(error);
}
}
// ✗ Bad - Silent failures
arm.save('data.arm').catch(() => {});5. Use Encryption for Sensitive Data
// ✓ Good
const arm = Armanion.create(data, {
encryption: {
password: process.env.ENCRYPTION_KEY,
selectiveFields: ['password', 'ssn', 'creditCard']
}
});
// ✗ Bad - Plaintext sensitive data
const arm = Armanion.create(data);ARM File Format
The .arm format is a proprietary binary format:
[Header: 12 bytes]
- Magic: 'ARM\x01' (4 bytes)
- Version: 1 byte
- Flags: 1 byte (encrypted, compressed, indexed)
- Reserved: 2 bytes
- Checksum: 4 bytes
[Metadata Section]
- Length: 4 bytes
- JSON metadata
[Data Section]
- Length: 4 bytes
- Data (plaintext or encrypted)
[Index Section] (optional)
- Length: 4 bytes
- Index dataFeatures:
- Corruption Detection: CRC-32 checksum
- Version Control: Format version embedded
- Metadata: Created/modified timestamps, encryption info
- Compact: Binary format, minimal overhead
- Extensible: Reserved bytes for future features
Advanced Usage
Custom Serialization
import { ARMSerializer, ARMStructure } from 'armanion';
// Create custom structure
const structure = ARMSerializer.create(
Buffer.from(JSON.stringify(data)),
{ custom: { author: 'John' } },
{ encrypted: true }
);
// Serialize to buffer
const buffer = ARMSerializer.serialize(structure);
// Deserialize from buffer
const loaded = ARMSerializer.deserialize(buffer);Direct Engine Access
// For advanced users who need full control
const arm = Armanion.create();
const engine = arm['engine']; // Access private engine
// Use engine directly (bypasses hooks)
engine.set('path', value);
const changes = engine.getChanges();Custom Plugins
class CompressionPlugin implements Plugin {
name = 'compression';
version = '1.0.0';
install(hooks) {
// Compress before save
hooks.on(HookEvent.BEFORE_SAVE, async (context) => {
if (context.structure) {
context.structure.data = compress(context.structure.data);
context.structure.header.flags.compressed = true;
}
});
// Decompress after load
hooks.on(HookEvent.AFTER_LOAD, async (context) => {
if (context.structure?.header.flags.compressed) {
context.data = decompress(context.data);
}
});
}
}TypeScript Support
Full TypeScript support with strict typing:
import { Armanion, ArmanionConfig, HookEvent } from 'armanion';
interface AppData {
users: User[];
settings: Settings;
}
const config: ArmanionConfig = {
encryption: {
password: 'secret'
},
autoSave: true
};
const arm = Armanion.create<AppData>({
users: [],
settings: {}
}, config);
// Type-safe access
const users = arm.get<User[]>('users');JavaScript (CommonJS) Support
Works seamlessly with CommonJS:
// CommonJS
const { Armanion } = require('armanion');
const arm = Armanion.create({ data: 'value' });
arm.save('data.arm');API Reference
Armanion Class
Static Methods
create(data?, config?)- Create new instanceload(filePath, config?)- Load from .arm file
Instance Methods
get<T>(path)- Get value at pathset(path, value)- Set value at pathdelete(path)- Delete value at pathmerge(path, object)- Merge object at pathhas(path)- Check if path existssave(filePath?)- Save to .arm filetransaction(operation)- Execute in transactiongetChanges()- Get all changeshasChanges()- Check if has changesexport()- Export all dataon(event, handler)- Register hookuse(plugin)- Install pluginunuse(pluginName)- Uninstall plugin
License
MIT
Contributing
Contributions are welcome! Please ensure:
- All tests pass
- TypeScript strict mode is satisfied
- No
anytypes are used - Code is documented
- Error handling is comprehensive
Support
For issues, questions, or contributions, please open an issue on GitHub.
Armanion - Professional data management, simplified.
