Version: 2.0.6 Status: Stable & Production Ready Last Updated: 2025-11-16
Complete guide to modifying TONL documents with the Modification API.
The set() method creates or updates values at any path:
const doc = TONLDocument.fromJSON({ user: { name: 'Alice' } });
// Update existing
doc.set('user.name', 'Alice Smith');
// Add new property
doc.set('user.email', 'alice@example.com');
// Method chaining
doc
.set('user.age', 30)
.set('user.active', true)
.set('user.role', 'admin');The delete() method removes values:
// Delete property
doc.delete('user.temp');
// Delete from nested object
doc.delete('user.profile.old Data');
// Delete array element (with reindexing)
doc.delete('users[0]');if (doc.exists('user.email')) {
console.log('Email exists:', doc.get('user.email'));
}TONL automatically creates intermediate objects/arrays when setting deep paths:
const doc = TONLDocument.fromJSON({});
// Creates: { user: { profile: { settings: { theme: 'dark' } } } }
doc.set('user.profile.settings.theme', 'dark');
// Creates: { items: ['first'] }
doc.set('items[0]', 'first');
// Mixed nesting
doc.set('data.list[0].name', 'Item 1');
// Creates: { data: { list: [{ name: 'Item 1' }] } }const doc = TONLDocument.fromJSON({ items: [] });
// Push (add to end)
const newLength = doc.push('items', 1, 2, 3);
console.log('New length:', newLength); // 3
// Pop (remove from end)
const last = doc.pop('items');
console.log('Popped:', last); // 3doc.merge('user', {
email: 'alice@example.com',
verified: true,
lastLogin: '2025-01-01'
});
// Shallow merge into user object// Take a snapshot
const snapshot = doc.snapshot();
// Make changes
doc.set('version', '2.0.0');
doc.set('features.newFeature', true);
// snapshot is unchanged - independent copy
console.log(snapshot.get('version')); // Still '1.0.0'const before = TONLDocument.fromJSON({ a: 1, b: 2, c: 3 });
const after = TONLDocument.fromJSON({ a: 1, b: 3, d: 4 });
const diff = before.diff(after);
console.log(diff.summary);
// {
// added: 1, // d was added
// modified: 1, // b was changed
// deleted: 1, // c was removed
// total: 3
// }
console.log(diff.changes);
// [
// { path: 'b', type: 'modified', oldValue: 2, newValue: 3 },
// { path: 'c', type: 'deleted', oldValue: 3 },
// { path: 'd', type: 'added', newValue: 4 }
// ]console.log(before.diffString(after));
// Changes: 3
// Added: 1
// Modified: 1
// Deleted: 1
//
// ~ b: 2 → 3
// - c (was: 3)
// + d = 4// Synchronous
doc.saveSync('data.tonl');
// Asynchronous
await doc.save('data.tonl');
// With encoding options
await doc.save('data.tonl', {
delimiter: '|',
indent: 2,
includeTypes: true
});import { FileEditor } from 'tonl';
// Open file (creates backup automatically)
const editor = await FileEditor.open('config.tonl', {
backup: true,
backupSuffix: '.bak'
});
// Modify data
editor.data.app.version = '2.0.0';
editor.data.users.push({ name: 'New User' });
// Check if modified
if (editor.isModified()) {
// Save atomically (temp file + rename)
await editor.save();
}
// Restore from backup if needed
await editor.restoreBackup();function updateWithRollback(doc: TONLDocument, updates: () => void) {
const backup = doc.snapshot();
try {
updates();
return { success: true };
} catch (error) {
// Rollback: create new doc from backup
const restored = backup.snapshot();
return { success: false, error, backup: restored };
}
}
// Usage
const result = updateWithRollback(doc, () => {
doc.set('config.timeout', 5000);
doc.set('config.retries', 3);
});const auditLog: any[] = [];
const initial = doc.snapshot();
// Make changes throughout the day
doc.set('user.lastLogin', new Date().toISOString());
// ... more changes ...
// At end of day, generate audit log
const dailyChanges = doc.diff(initial);
auditLog.push({
date: new Date(),
changes: dailyChanges.changes
});import { FileEditor } from 'tonl';
// Always use FileEditor for important files
const editor = await FileEditor.open('critical-data.tonl', {
backup: true
});
try {
// Make changes
editor.data.balance += 100;
// Validate
if (editor.data.balance < 0) {
throw new Error('Invalid balance');
}
// Save (atomic write + backup)
await editor.save();
} catch (error) {
console.error('Failed to update, backup preserved');
}doc
.set('user.name', 'Alice')
.set('user.email', 'alice@example.com')
.set('user.verified', true)
.delete('user.tempToken');// Only update if exists
if (doc.exists('user.preferences')) {
doc.merge('user.preferences', { theme: 'dark' });
}
// Update with validation
function safeSet(doc: TONLDocument, path: string, value: any) {
const type = doc.typeOf(path);
if (type && typeof value !== type) {
throw new Error(`Type mismatch: expected ${type}, got ${typeof value}`);
}
doc.set(path, value);
}// Update multiple paths
const updates = [
['user.firstName', 'Alice'],
['user.lastName', 'Smith'],
['user.email', 'alice@example.com']
];
for (const [path, value] of updates) {
doc.set(path, value);
}// Lock-free update with retry
function optimisticUpdate(filePath: string, updateFn: (doc: TONLDocument) => void) {
const original = TONLDocument.fromFileSync(filePath);
const updated = original.snapshot();
updateFn(updated);
// Save and verify no concurrent changes
updated.saveSync(filePath);
}// BAD - modifies internal state without cache reset
const user = doc.get('user');
user.name = 'Bob'; // ❌ Won't work correctly
// GOOD - use set()
doc.set('user.name', 'Bob'); // ✅ Correct// BAD - changes lost
doc.set('important', 'data');
// ... program exits ...
// GOOD - always save
doc.set('important', 'data');
await doc.save('file.tonl');// BAD - can't delete root
doc.delete('$'); // ❌ Error
// GOOD - delete specific properties
doc.delete('propertyName'); // ✅ Works- Batch related changes before saving
- Use indices for repeated lookups
- Use snapshots sparingly (they create deep copies)
- Use FileEditor for large files (atomic operations)
- Stream when processing multi-GB files
- Query API - How to query data
- Indexing Guide - Fast lookups
- Streaming Guide - Large files
Happy modifying! 🎉