A few days ago I had a simple, slightly weird idea:
What if every string was just an array of single letters… and I could rebuild words, paths, and API links by pointing at index ranges?
That experiment turned into string-range-tokens (srTokens).
It’s a tiny, zero-dependency ESM package that does one thing well:
Turn any string into a letter-per-index token array, then reassemble pieces of it with clean range calls.
No regex. No string slicing gymnastics. Just indexes.
The 30-second version
import { toTokens, sub, build } from 'string-range-tokens';
const t = toTokens('https://api.example.com/v1/users');
sub(t, 0, 4); // "https"
sub(t, 23, 25, [26, 31]); // "/v1/users"
sub(t, -5, -1); // "users"
build(t, [0, 4], [23, 25]); // "https/v1"
That’s the core. Everything else is built on top of it.
What you can actually use it for
1. Assemble API paths from a shared pool
Instead of hard-coding every endpoint, keep one master string and pull the parts you need:
const pool = toTokens('https://api.myapp.com/v1/users/orders/products/settings');
const users = build(pool, [0, 22], [23, 27]); // https://api.myapp.com/v1/users
const orders = build(pool, [0, 22], [29, 34]); // https://api.myapp.com/v1/orders
const products = build(pool, [0, 22], [36, 43]); // https://api.myapp.com/v1/products
Useful when you want a single source of truth for the base URL + version and then compose endpoints on the fly.
2. Build words from a letter pool (like a tiny anagram engine)
const letters = toTokens('the quick brown fox jumps over the lazy dog');
const ranges = plan(letters, 'fox jumps');
build(letters, ...ranges); // "fox jumps"
plan(letters, 'lazy dog'); // finds the ranges
plan(letters, 'brown'); // finds "brown"
plan() does a greedy longest-match search and returns the ranges that reconstruct the target. If a character is missing, it throws. Great for puzzles, teaching, or constrained string generation.
3. Negative indexes for “from the end” slicing
const t = toTokens('invoice-2026-10-09-final.pdf');
sub(t, -9, -1); // "final.pdf"
sub(t, -14, -10); // "10-09"
sub(t, 0, 6, [-9, -1]); // "invoice-final.pdf"
Feels natural once you start thinking of the string as a fixed array.
4. Scatter & gather — deterministic scramble
Sometimes you want to shuffle a string in a repeatable way and still be able to put it back together:
const { pool, key } = scatter('/api/private/orders', 42);
console.log(pool.join(''));
// something like "r/paeivtr/o/psordo" (order depends on seed)
gather(pool, key);
// "/api/private/orders" ← perfect round-trip
Same seed always produces the same shuffle. Handy for demos, tests, or playful obfuscation (remember: this is not security).
5. Tiny templating from a fixed alphabet
const alphabet = toTokens('abcdefghijklmnopqrstuvwxyz0123456789-_.');
// build a fake ID
const id = build(alphabet,
[0, 0], [1, 1], [2, 2], [10, 10], [26, 26], [27, 27]
); // "abc k01" → wait, better with plan or explicit ranges
// or more expressively:
const ranges = plan(alphabet, 'user-42');
build(alphabet, ...ranges); // "user-42"
You can keep a single character pool and generate many different strings from it.
6. Teaching indexing and ranges
Because everything is explicit indexes, it’s surprisingly good for explaining:
- zero-based indexing
- inclusive ranges
- negative indexing
- how
Array.fromhandles Unicode
toTokens('a😳b'); // ['a', '😳', 'b'] — emoji is one token
sub(toTokens('hello'), 1, 3); // "ell"
Students can see exactly which characters are being selected.
Full API at a glance
| Function | What it does |
|---|---|
toTokens(str) |
String → array of single characters (Unicode-safe) |
sub(tokens, s, e, …) |
Join one or more inclusive ranges |
build(tokens, …ranges) |
Same idea, ranges-only style |
plan(tokens, target) |
Find ranges that rebuild target
|
scatter(str, seed) |
Seeded shuffle → { pool, key }
|
gather(pool, key) |
Reconstruct original from scatter output |
All functions are pure. Zero dependencies. Works in Node 18+, Deno, Bun, and modern browsers.
TypeScript types are included, and you can even import the .ts source directly if your runtime supports it.
Install
npm i string-range-tokens
Or skip the install and just copy src/index.js — it’s a single file.
CDN:
import { toTokens, sub, build, plan, scatter, gather }
from 'https://cdn.jsdelivr.net/npm/string-range-tokens@1.0.0/src/index.js';
Important note about “hiding” strings
This is string assembly, not encryption.
Anything that ends up in the browser can be read by the user. Never try to hide real API keys, tokens, or secrets with it. Keep those on the server (Node Functions services, Cloudflare Workers, etc.).
Use srTokens for clarity, composition, teaching, and fun — not for security theater.
Links
- npm: string-range-tokens
- GitHub: Figsh/srTokens
If you build something interesting with it — a path builder, a puzzle, a teaching demo, or something completely unexpected — I’d love to see it.
Happy ranging.
Top comments (0)