A minimal TypeScript clone that compresses large text streams locally
Welcome to another official UVF IT guide! In this quick‑fire tutorial we’ll build a tiny TypeScript demo that mimics the spirit of the billion‑context compression plugin – a small server that accepts a big string and returns a compressed representation using a simple dictionary‑based algorithm.
You’ll have a working Express API in under 45 minutes, no paid services required, and you’ll see how the real project achieves billions‑of‑token sessions by persisting a tiny dictionary in SQLite.
We start by creating a fresh folder, initializing it as an npm workspace, and pulling in the core tools: TypeScript for static typing, Express to expose an HTTP endpoint, and better‑sqlite3 for a lightweight embedded database. This gives us a clean, typed foundation that can be run directly with ts-node, so we don’t need a separate build step yet.
The commands below also add useful dev helpers like nodemon (auto‑restart) and @types definitions so our editor can offer autocomplete and error checking right away.
mkdir billion-context-demo && cd billion-context-demo
npm init -y
npm install express better-sqlite3
npm install -D typescript ts-node @types/node @types/express nodemon
npx tsc --init --rootDir src --outDir dist --esModuleInterop true --module commonjs --target es2022Now we create a tiny compressor that turns repeated words into short numeric tokens. The first time a word appears we store it in a SQLite table and assign it the next integer ID; subsequent occurrences are replaced with that ID. This mirrors the original plugin’s idea of a persistent dictionary that lets the model reuse tokens across requests without re‑learning each time.
The module exports a single async function compress(text:string):Promise<string> that returns a space‑separated string of tokens. It also ensures the SQLite file (./data/dict.db) is created on first run.
src/compressor.ts
import Database from 'better-sqlite3';
// Initialise (or open) the SQLite DB that holds the dictionary
const db = new Database('./data/dict.db');
// Ensure the table exists
db.exec(`CREATE TABLE IF NOT EXISTS dict (
id INTEGER PRIMARY KEY AUTOINCREMENT,
word TEXT UNIQUE NOT NULL
);`);
/**
* Compress a plain‑text string by replacing each word with a numeric token.
* New words are inserted into the dictionary and get a fresh ID.
* Returns a space‑separated token string.
*/
export async function compress(text: string): Promise<string> {
const words = text.split(/\s+/).filter(Boolean);
const tokens: string[] = [];
const insert = db.prepare('INSERT INTO dict (word) VALUES (?)');
const select = db.prepare('SELECT id FROM dict WHERE word = ?');
for (const w of words) {
let row = select.get(w);
if (!row) {
const info = insert.run(w);
row = { id: info.lastInsertRowid };
}
tokens.push(String(row.id));
}
return tokens.join(' ');
}
// Simple test when run directly
if (require.main === module) {
(async () => {
const sample = 'hello world hello uvf it demo demo';
console.log('input :', sample);
console.log('output:', await compress(sample));
})();
}We’ll spin up a tiny Express server that offers a POST /compress endpoint. The endpoint receives raw text, runs the previously built tokenizer/compressor, and returns a space‑separated token string in JSON. To make testing easy we also serve a minimal HTML page with a textarea and a button that calls the API via fetch. This keeps the whole demo self‑contained and runnable with just ts-node.
The server also serves static assets from a public folder, so the HTML, a tiny CSS file, and a client‑side script live together. By keeping everything in src/server.ts we avoid extra configuration and keep the codebase easy to follow for newcomers.
src/server.ts
import express from 'express';
import path from 'path';
import bodyParser from 'body-parser';
// Assume compressText is exported from the previous phase's module
import { compressText } from './compressor';
const app = express();
const PORT = 3000;
app.use(bodyParser.text({ type: '*/*' }));
// Serve static UI files
app.use(express.static(path.join(__dirname, '..', 'public')));
app.post('/compress', async (req, res) => {
try {
const input = req.body as string;
if (!input) return res.status(400).json({ error: 'No text provided' });
const compressed = compressText(input);
res.json({ compressed });
} catch (e) {
res.status(500).json({ error: (e as Error).message });
}
});
app.listen(PORT, () => {
console.log(`🚀 http://localhost:${PORT}`);
});Now that the server code is ready, we can launch it directly with ts-node. This avoids a separate build step and lets you see changes instantly. After starting the server, open your browser to http://localhost:3000 – you should see a simple page with a textarea and a "Compress" button.
When you type or paste large text into the textarea and hit the button, the client script will POST the content to /compress and display the token string below. This live feedback confirms the whole pipeline works from UI to backend.
npm install express body-parser @types/express @types/body-parser ts-node typescript
npx ts-node src/server.tsWith the server running, a quick curl request proves the API returns the expected format. You should receive a JSON object whose compressed field contains a space‑separated list of tokens – that’s the core output of the Billion‑Context algorithm.
If you want to go further, add a /decompress route that calls a decompressText function (the inverse of compressText). The same UI can be extended with another button that posts to this new endpoint, letting you round‑trip text and see the original restored.
curl -X POST http://localhost:3000/compress -H "Content-Type: text/plain" --data "Hello world, this is a test of the compression demo."| Check | Command / Why it matters |
|---|---|
| Server starts without errors | Run npx ts-node src/server.ts and see console log "🚀 http://localhost:3000" |
| UI loads in browser | Open http://localhost:3000 and see a textarea page |
| Compression returns token string | Run the curl command above; response JSON contains a non‑empty compressed field |
You now have a functional clone of a text‑compression service. Try adding a decompression endpoint, switch SQLite to Postgres, or package the app as a Docker container. Happy hacking!