LOADING...
A lightweight text version management system with differential storage and version rollback capabilities. Similar to Git's version management mechanism, but specifically optimized for text content.
本文档也有中文版本
A lightweight text version management system with differential storage and version rollback capabilities. Similar to Git's version management mechanism, but specifically optimized for text content.
Online preview: https://ravelloh.github.io/text-version
commit: Submit new version (similar to git commit)show: Display specified version content (similar to git show)log: Display version history (similar to git log)latest: Get latest version contentreset: Reset to specified version (similar to git reset --hard), keep the target version and all versions before it, delete versions after itsquash: Keep the target version and all versions after it, delete versions before it to reduce storage spaceWhen version history becomes too long, you can use the squash method to optimize storage space:
You can provide custom compression algorithms to further reduce storage space:
When snapshot content is very large, you can use separate storage for more flexible data management:
Use Cases:
Uses length-prefixed format internally:
Diff operation format:
When version name duplication occurs during submission, the system automatically adds # suffixes:
The system automatically compares the following storage methods and selects the one with minimum space:
Important: Under the new strategy, the latest version is always a snapshot, historical versions store reverse diffs from new to old.
Example:
Besides npm installation, you can also use directly via CDN:
Note: Text-Version requires the diff-match-patch library as a dependency. You need to include it before the text-version script.
Parameters:
Normal Import:
Separate Import:
Note: When using separate import, the hash value of snapshot content will be validated. An error will be thrown if it doesn't match.
Submit new version, save text changes.
Display text content of specified version.
Display version history log, get all version information.
Get text content of latest version.
Reset to specified version, keep the target version and all versions before it, delete all versions after the target version.
Note: After reset, the target version automatically becomes a snapshot (as it becomes the latest version), and previous versions are converted to differential storage.
Keep the target version and all versions after it, delete all versions before the specified version, used to reduce storage space.
Note: This operation is irreversible and will permanently delete all version history before the target version. After squash, the latest version becomes a snapshot, while the target version and intermediate versions are stored as diffs. Suitable for storage space optimization when version history becomes too long.
Export current version data.
Separate Export Explanation:
MIT
Issues and Pull Requests are welcome!
R numberI length:textD numberv1v1#v1v1#v1##version_name:R6I5:old_contentversion_name:=historical_version:R6I5:new_contentinitialStoragecompressionProvidermetadatasnapshotcompressionProvidercommit(text: string, version?: string): thistextversionthisshow(version: string): string | nullversionlog(): VersionInfo[]latest(): stringreset(targetVersion: string): thistargetVersionthissquash(targetVersion: string): thistargetVersionthisexport(mode?: "monolithic" | "separate"): string | { metadata: string; snapshot: string }mode"monolithic""separate"metadatasnapshot"monolithic"metadatasnapshotmetadata##[[hash]]##snapshothashsquashsquashnpm install text-version // or
pnpm install text-version // or
yarn add text-version// ES6 modules
import { TextVersion } from 'text-version';
// CommonJS
const { TextVersion } = require('text-version');const tv = new TextVersion();// Import
import { TextVersion } from 'text-version';
// Or CommonJS
// const { TextVersion } = require('text-version');
// Create instance
const tv = new TextVersion();
// Submit new version
tv.commit('Hello, World!', 'v1');
tv.commit('Hello, World!\\nThis is the second line.', 'v2');
tv.commit('Hello, TypeScript!\\nThis is the second line.');
// View version history
console.log(tv.log());
//[
// { version: 'v1', isSnapshot: false }, // diff
// { version: 'v2', isSnapshot: false }, // diff
// { version: 'ycdf93', isSnapshot: true } // snapshot (latest)
//]
// View specified version
console.log(tv.show('v1'));
// "Hello, World!"
// View latest version
console.log(tv.latest());
// "Hello, TypeScript!\\nThis is the second line."
// Export version data (monolithic mode - default)
const storage = tv.export();
// Or explicitly specify monolithic mode
const storage2 = tv.export("monolithic");
console.log(storage);
// 2:v1:R6D7
// 2:v2:R3D10I2:world
// :6:ycdf93:hello, TypeScript!\nThis is the second line.
// Reset to specified version
tv.reset('v2');
// Compress storage space - set v2 as snapshot, delete v1
tv.squash('v2'); // v1 version will be permanently deleted, v2 becomes new starting snapshot
// Load from existing storage
const tv2 = new TextVersion(storage);
console.log(tv2.latest()); // Can access the saved dataconst tv = new TextVersion();
// Create multiple versions
tv.commit('First version', 'v1');
tv.commit('Second version', 'v2');
tv.commit('Third version', 'v3');
tv.commit('Fourth version', 'v4');
const storage = tv.export();
console.log('Original storage size:', storage.length);
console.log('Version count:', tv.log().length); // 4 versions
// Compress to v2, delete v1
tv.squash('v2');
const newStorage = tv.export();
console.log('Compressed storage size:', newStorage.length);
console.log('Version count:', tv.log().length); // 3 versions: v2, v3, v4
// v1 version has been deleted and cannot be accessed
console.log(tv.show('v1')); // null
// v2 and later versions can still be accessed normally
console.log(tv.show('v2')); // "Second version"
// Note: After squash, v4 (latest version) is a snapshot, v2 and v3 are diffs:version_name_length:version_name:content (snapshot version)
version_name_length:version_name:operation_sequence (diff version)
version_name_length:version_name:=version_name (version reference)
version_name_length:version_name:=version_name:operation_sequence (hybrid reference)2:v1:R8D8I13:original text # v1 is reverse diff (from v2 to v1)
2:v2:R4I8:modified content D2 # v2 is reverse diff (from v3 to v2)
:2:v3:This is latest modified content # v3 is snapshot (latest version)
2:v1#:=v1 # Reference to v1import { TextVersion } from 'text-version';
// Compression usage example
const compressionProvider = {
compress: (data) => /* compression algorithm */ data,
decompress: (data) => /* decompression algorithm */ data
};
const tv = new TextVersion('', compressionProvider);
tv.commit('This is a very long text...');
console.log(tv.latest());const tv = new TextVersion();
tv.commit('First version', 'v1');
tv.commit('Second version', 'v2');
tv.commit('Very very very long latest version content...', 'v3');
// Separate export
const result = tv.export("separate");
// result = {
// metadata: "2:v1:D6I4:original text\n2:v2:D5\n:2:v3:##[[abc12345]]##",
// snapshot: "Very very very long latest version content..."
// }
// Note: ##[[abc12345]]## is a hash placeholder for snapshot content, used for integrity verification
// metadata contains placeholder, snapshot stored separately
console.log(result.metadata.length); // small
console.log(result.snapshot.length); // large
// Create instance with separated data (auto-validates hash)
const tv2 = new TextVersion(result.metadata, result.snapshot);
console.log(tv2.latest()); // "Very very very long latest version content..."
// Error will be thrown if snapshot hash doesn't match (prevents data tampering)
try {
new TextVersion(result.metadata, 'wrong snapshot content');
} catch (e) {
console.error('Hash validation failed'); // Snapshot content doesn't match hash in metadata
}<!-- Include diff-match-patch dependency first -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<!-- Then include text-version UMD version -->
<script src="https://cdn.jsdelivr.net/npm/text-version/dist/index.umd.js"></script>
<!-- Or using unpkg CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<script src="https://unpkg.com/text-version/dist/index.umd.js"></script><!DOCTYPE html>
<html>
<head>
<title>Text-Version CDN Example</title>
</head>
<body>
<h1>Text-Version Demo</h1>
<textarea id="input" placeholder="Enter text content..." rows="5" cols="50">Hello, World!</textarea><br><br>
<button onclick="commitVersion()">Commit Version</button>
<button onclick="showLatest()">Show Latest</button>
<button onclick="showLog()">Show Log</button><br><br>
<div>
<h3>Output:</h3>
<pre id="output"></pre>
</div>
<!-- Include diff-match-patch dependency first -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<!-- Then include text-version -->
<script src="https://cdn.jsdelivr.net/npm/text-version/dist/index.umd.js"></script>
<script>
// TextVersion available through global variable window.TextVersion
const tv = new window.TextVersion.TextVersion();
let versionCounter = 1;
function commitVersion() {
const text = document.getElementById('input').value;
const version = `v${versionCounter++}`;
tv.commit(text, version);
document.getElementById('output').textContent =
`Version ${version} committed\nCurrent storage: ${tv.export()}`;
}
function showLatest() {
const latest = tv.latest();
document.getElementById('output').textContent =
`Latest version content:\n${latest}`;
}
function showLog() {
const log = tv.log();
const logText = log.map(info =>
`${info.version} (${info.isSnapshot ? 'snapshot' : 'diff'})`
).join('\n');
document.getElementById('output').textContent =
`Version history:\n${logText}`;
}
</script>
</body>
</html>new TextVersion(initialStorage?: string, compressionProvider?: CompressionProvider)
new TextVersion(metadata: string, snapshot: string, compressionProvider?: CompressionProvider)interface VersionInfo {
version: string; // Version name
isSnapshot: boolean; // Whether it's a snapshot version
}
interface CompressionProvider {
compress(data: string): string;
decompress(data: string): string;
}
interface DiffOperation {
type: 'retain' | 'insert' | 'delete';
length?: number; // Number of characters for retain and delete operations
text?: string; // Text content for insert operations
}