Lightweight gzip-compressed JSON container with magic-byte framing.
Compress, export, and restore typed JSON payloads as Blob, ArrayBuffer, or base64 — built for Cloudflare Workers and browser environments.
const cj = MajikCompressedJSON.create({ id: 1, messages: [...] });
const blob = cj.toMJKCJSON(); // upload to R2
const back = await MajikCompressedJSON.decompress<MyType>(blob); // restore original- Majik CJSON
majik-cjson wraps any JSON object or array into an immutable, gzip-compressed binary container. Every payload is framed with a 4-byte magic header (MJKC) that allows fast identity probing without full decompression.
The container can be exported as a Blob (for object storage uploads), an ArrayBuffer (for raw binary handling), or a base64 string (for embedding in JSON fields or URLs). Any of those forms can be fed back into fromMJKCJSON() to restore the original typed payload.
It is a single-dependency package (fflate) with no WASM, no native bindings, and no Node.js-only APIs — it runs anywhere that has TextEncoder, Blob, and atob/btoa.
- gzip compression via fflate — pure JS, no WASM, no cold-start overhead
- Magic-byte framing — every payload starts with
MJKC(0x4D 0x4A 0x4B 0x43) for cheap identity probing - Typed generics — full
Tsupport so your payload type flows through compress → decompress - Flexible I/O — accepts and exports
Blob,ArrayBuffer,Uint8Array, andbase64strings - Immutable instances — private constructor + JS
#private fields; state is truly inaccessible at runtime - Input validation — throws descriptive errors on invalid JSON, bad magic headers, and decompression failures
- Zero Node.js dependency — works in Cloudflare Workers, browsers, and any Web API–compatible runtime
- Compression introspection —
compressionRatioandbyteSizegetters for observability
npm install majik-cjsonpnpm add majik-cjsonyarn add majik-cjsonRequirements: ESM-compatible runtime with TextEncoder, Blob, atob/btoa (Cloudflare Workers, modern browsers, Node.js ≥ 18).
import { MajikCompressedJSON } from "majik-cjson";Pass a plain object, array, or a valid JSON string. An error is thrown for invalid input.
// From an object
const cj = MajikCompressedJSON.create({ id: 1, name: "Zelijah" });
// From an array
const cj = MajikCompressedJSON.create([1, 2, 3]);
// From a JSON string
const cj = MajikCompressedJSON.create('{"id":1,"name":"Zelijah"}');
// With a custom type
type ThreadExport = { threadId: string; messages: Message[] };
const cj = MajikCompressedJSON.create<ThreadExport>(exportPayload);// Blob — for uploading to Cloudflare R2 or any object store
const blob = cj.toMJKCJSON();
await r2.put("exports/thread-123.mjkcjson", blob);
// ArrayBuffer — for raw binary handling or passing directly to fetch()
const buffer = cj.toBinary();
// Base64 string — for embedding in JSON fields, database columns, or URLs
const b64 = cj.toBase64();
// Plain object with both payloads (base64-encoded compressed form)
const obj = cj.toJSON();
// → { payload: { ... }, compressed: "MJKC..." }fromMJKCJSON() accepts any form — Blob, ArrayBuffer, Uint8Array, or base64 string.
// From Blob (e.g. fetched from R2)
const instance = await MajikCompressedJSON.fromMJKCJSON<ThreadExport>(blob);
// From base64 string
const instance = await MajikCompressedJSON.fromMJKCJSON<ThreadExport>(b64);
// From ArrayBuffer
const instance = await MajikCompressedJSON.fromMJKCJSON<ThreadExport>(buffer);
// Access the original payload
console.log(instance.payload); // ThreadExportIf you only need the original payload and don't need the instance, use decompress() directly.
const original = await MajikCompressedJSON.decompress<ThreadExport>(blob);
console.log(original.threadId);isCompressed() checks the magic header cheaply — no decompression, no JSON parsing.
const valid = await MajikCompressedJSON.isCompressed(blob); // true | falseUseful as a pre-flight guard before calling fromMJKCJSON(), e.g. when processing user-uploaded files.
const cj = MajikCompressedJSON.create(largePayload);
cj.payload // T — original deserialized JSON
cj.compressedBytes // Uint8Array — framed binary (MAGIC + gzip)
cj.byteSize // number — byte size of compressed payload
cj.compressionRatio // number — compressedSize / originalSize (lower = better)
cj.isEmpty // boolean — true if compressed payload is empty
console.log(`Reduced by ${((1 - cj.compressionRatio) * 100).toFixed(1)}%`);
// → "Reduced by 87.3%"| Method | Description |
|---|---|
create<T>(input: JSONInput) |
Compress a JSON object, array, or JSON string. Returns a new MajikCompressedJSON<T> instance. |
fromMJKCJSON<T>(input: MJKCJSONInput) |
Reconstruct an instance from a Blob, ArrayBuffer, Uint8Array, or base64 string. |
decompress<T>(input: MJKCJSONInput) |
Convenience wrapper — decompresses and returns the payload directly. |
isCompressed(input: MJKCJSONInput) |
Returns true if the input has a valid MJKC magic header. No decompression performed. |
| Method | Returns | Description |
|---|---|---|
toMJKCJSON() |
Blob |
Framed binary as a Blob (application/octet-stream). Use for R2 / object store uploads. |
toBinary() |
ArrayBuffer |
Framed binary as an ArrayBuffer. |
toBase64() |
string |
Framed binary encoded as base64. |
toJSON() |
{ payload: T; compressed: string } |
Plain serialisable object with both payloads. |
| Getter | Type | Description |
|---|---|---|
payload |
T |
The original deserialized JSON payload. |
compressedBytes |
Uint8Array |
The framed compressed binary. |
byteSize |
number |
Byte size of the compressed payload. |
compressionRatio |
number |
compressedSize / originalSize. Lower is better. |
isEmpty |
boolean |
true if the compressed payload contains no data beyond the magic header. |
type JSONInput = Record<string, unknown> | unknown[] | string;
type MJKCJSONInput = Blob | ArrayBuffer | Uint8Array | string;Every MJKCJSON payload follows this layout:
┌─────────────────────┬──────────────────────────────────────┐
│ MAGIC (4 bytes) │ gzip-compressed JSON (variable) │
│ 4D 4A 4B 43 │ fflate gzipSync, level 6 │
│ "M" "J" "K" "C" │ │
└─────────────────────┴──────────────────────────────────────┘
The magic header allows format detection without decompression. The gzip payload is standard RFC 1952 — compatible with any gzip implementation.
If you want to contribute or help extend support to more platforms, reach out via email. All contributions are welcome!
Apache-2.0 — free for personal and commercial use.
Made with 💙 by @thezelijah
- Developer: Josef Elijah Fabian
- GitHub: https://github.com/jedlsf
- Project Repository: https://github.com/Majikah/majik-cjson
- Business Email: business@thezelijah.world
- Official Website: https://www.thezelijah.world