Skip to content

sebastian-software/effective-rison

 
 

Repository files navigation

rison

npm CI TypeScript Types Included Zero Dependencies Module Node pnpm Prettier Vitest Made in Germany

Open Demo

Modern, TypeScript-first, ESM-only Rison: compact serialization for JSON-like structures — great for URLs and for compact storage. Based on Rison (Original).

Rison is a slight variation of JSON that looks vastly superior after URI encoding — ideal for compact state in URLs and also useful for storing structured data in localStorage/sessionStorage.

Why Rison for URLs

  • Encodes nested objects/arrays compactly and URI‑friendly (minimal escaping).
  • Ideal for client state in query or fragment (filters, sorting, columns, pagination).
  • Practical URL limits are infrastructure‑driven: conservative target ≈ 2 KB total; common defaults ≈ 8 KB. Fragments (#state=…) aren’t sent to servers and bypass those limits.
  • This package includes native compression helpers (gzip/deflate via CompressionStream) with an auto mode that selects the shortest token.

Quick Start

import { encode, decode } from "@effective/rison";

encode({ any: "json", yes: true });
// -> (any:json,yes:!t)

decode("(any:json,yes:!t)");
// -> { any: 'json', yes: true }

Optional URL compression helpers (native CompressionStream in modern browsers):

import { compressToUrl, decompressFromUrl } from "@effective/rison";

// mode: 'auto' | 'deflate' | 'none' (default: 'auto')
const compact = await compressToUrl({ page: 1, filters: { active: true } }, { mode: 'auto' });
// -> safe, compact string for query params/fragments

const value = await decompressFromUrl(compact);

### Compression formats & prefixes

- `none`: returns raw Rison (no prefix)
- `deflate`: returns a token prefixed with `d:` and base64url‑encoded (deflate‑raw)
- `auto`: compares raw Rison vs deflate and picks the shortest
// -> original value

Storage (localStorage) Usage

For storage, URL compatibility isn’t required. This library offers high‑density UTF‑16 packing (Base32768‑like) that maps 15‑bit chunks to non‑surrogate code points, giving better density than base64. Storage helpers default to base32768 and keep the same compression prefixes.

import { compressForStorage, decompressFromStorage } from "@effective/rison";

// mode: 'auto' | 'deflate' | 'none' (default: 'auto')
// encoding: 'base32768' | 'base64' (default: 'base32768')
const token = await compressForStorage({ theme: 'dark', tabs: [1, 2, 3] }, { encoding: 'base32768' });
// store in localStorage yourself
localStorage.setItem('app:state', token);

// later…
const restored = await decompressFromStorage(localStorage.getItem('app:state')!, { encoding: 'base32768' });

Convenience wrappers are also available if you prefer direct storage calls:

import { saveToLocalStorage, loadFromLocalStorage } from "@effective/rison";

await saveToLocalStorage("app:state", { theme: "dark" }, { encoding: "base32768" });
const state = await loadFromLocalStorage("app:state", { encoding: "base32768" });

Installation

pnpm add @effective/rison
# or
npm install @effective/rison

Requirements & Support

  • Browsers: modern engines support CompressionStream/DecompressionStream.
  • Node.js: 20+ provides CompressionStream in runtime; this repo builds/tests on Node 22+.
  • ESM-only package.

API

  • encode(value) → string
  • decode(string) → any
  • encodeUri(value) → string (Rison-encode + relaxed URI escaping)
  • compressToUrl(value, { mode }) → Promise
  • decompressFromUrl(string) → Promise
  • compressForStorage(value, { mode, encoding }) → Promise
  • decompressFromStorage(string, { encoding }) → Promise
  • saveToLocalStorage(key, value, { mode, encoding }) → Promise
  • loadFromLocalStorage(key, { encoding }) → Promise<any | null>

Notes:

  • Storage encoding defaults to base32768 for maximum density with simple decoding. You can switch to base64 for interoperability.
  • Compression prefixes remain the same: d: (deflate-raw) or none (raw rison).

Types are published via dist/rison.d.ts.

Demos & Examples

  • Live demo: 3‑pane source → converted → restored
    • Radio options for compression: auto, deflate, none
    • Three presets (Short/Medium/Long) to test compression impact

Nuqs Integration

Use Rison with nuqs to manage URL state in React apps.

import { useQueryState } from "nuqs";
import { encode, decode } from "@effective/rison";

// Rison serializer for nuqs
const risonSerializer = {
  parse: (value: string) => decode(value),
  serialize: (value: unknown) => encode(value)
};

export function Example() {
  // e.g. ?state=(filter:(active:!t),page:3)
  const [state, setState] = useQueryState("state", risonSerializer as any);

  return (
    <div>
      <pre>{JSON.stringify(state, null, 2)}</pre>
      <button onClick={() => setState({ filter: { active: true }, page: 3 })}>Set State</button>
    </div>
  );
}

Compression helpers are async and not suitable for nuqs serializers; if you need compression with nuqs, use a custom async layer outside of serializer calls.

For smaller URLs, use the compressed variant above.

Breaking changes in v1

  • ESM-only package, Node 22+ recommended.
  • Removed default export; use named imports.
  • Removed legacy O-/A-Rison helpers; use encode/decode only.

Contributing

  • Run tests: pnpm test
  • Typecheck: pnpm run typecheck
  • Format: pnpm run format

Releasing

Manual, assisted by release-it. Ensure your npm and GitHub tokens are configured locally.

pnpm install
pnpm run release

This runs typecheck/tests, builds, bumps the version, publishes to npm (public), tags the release, and publishes a GitHub Release.

License

MIT — see LICENSE.md.

  • Copyright © 2007–2009 Metaweb Technologies, Inc.
  • Copyright © 2025–present, Sebastian Software GmbH, Germany.

Acknowledgments

  • Originally created and published by Metaweb Technologies, Inc. (Google).
  • Encoder inspired by Douglas Crockford's json.js; decoder inspired by Oliver Steele's JSON for OpenLaszlo.
  • Thanks to all contributors over the years.

About

JavaScript/Python/etc Rison

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 100.0%