Billion‑Context Text Compression Demo in TypeScript

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.

Before you start

Minimal Clone Architecture
LOCAL RUNTIME read/write POST /compress starts Express TS Server Handles /compress endpoint SQLite DB Stores word‑to‑token map Static HTML UI Test page with textarea ts-node CLI Runs the server
1

Phase 1: Scaffold the project

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 es2022
2

Phase 2: Build the compression model

Now 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));
  })();
}
3

Phase 3: Expose an API & UI

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}`);
});
4

Phase 4: Run locally

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.ts
5

Phase 5: Verify & extend

With 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."

Verifying the Stack

CheckCommand / Why it matters
Server starts without errorsRun npx ts-node src/server.ts and see console log "🚀 http://localhost:3000"
UI loads in browserOpen http://localhost:3000 and see a textarea page
Compression returns token stringRun the curl command above; response JSON contains a non‑empty compressed field

What’s next?

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!