npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@human-synthesis/norns-tron

v0.0.3

Published

TRON serialization for the Norns ecosystem — token-efficient, faster-than-JSON wire format for APIs and LLM-facing output.

Readme

@human-synthesis/norns-tron

TRON serialization for the Norns ecosystem — a token-efficient, faster-than-JSON wire format for APIs and LLM-facing output. Zero runtime dependencies.

TRON cuts 11–61% of tokens vs JSON and, used correctly, beats JSON.parse / JSON.stringify on decode, encode, and round-trip. Plain JSON is valid TRON, so adoption is progressive and rollback is trivial. See PERFORMANCE.md for measured numbers and usage guidance.

The encoder/decoder core is absorbed from the apitron research library; this package adds the Norns framework glue as subpath exports.

@human-synthesis/norns-tron           encode / decode / defineSchema / registry / columnar
@human-synthesis/norns-tron/server    tronSerializer() for norns route()
@human-synthesis/norns-tron/client    createApi() fetch wrapper that speaks TRON
@human-synthesis/norns-tron/valibot   derive wire schemas from valibot schemas

Turn it on app-wide

# src/hooks.server.c
import { boot } from '@human-synthesis/norns/server'
import { tronSerializer } from '@human-synthesis/norns-tron/server'

app := await boot
  features: import.meta.glob('./lib/*/server/module.c', eager: true)
  serializer: tronSerializer()

Every route() response is now content-negotiated: clients that send Accept: application/tron get TRON, everyone else (curl, third parties, existing code) keeps getting JSON. Nothing breaks. Remove the serializer line to roll the whole thing back.

Per-route control:

export GET  := route serializer: tronSerializer({ schema: noteWire }), handler: ...   # schema mode
export GET  := route serializer: tronSerializer({ columnar: true }), handler: ...     # numeric tables
export POST := route serializer: null, handler: ...                                   # force plain JSON

Call it from the client

import { api, createApi } from '@human-synthesis/norns-tron/client'

users := await api.get '/api/users'          # sends Accept: application/tron
await api.post '/api/notes', { title, body } # body goes out as TRON too

# inside a load function, keep SvelteKit's fetch semantics:
api := createApi { fetch }

# endpoints served in schema mode tag the wire; hand the client the contracts:
api := createApi { schemas: [noteWire, userWire] }   # or a createRegistry()

Responses are decoded by content type: self-describing TRON on its own, tagged schema-mode documents (#notes.v1) through the matching compiled schema, JSON as JSON. A tag with no registered schema throws instead of handing you positional arrays. Non-2xx responses throw ApiError with the decoded body (status, body.message, body.issues from a route() 400).

The 1.7 KB WASM scanner ships embedded as base64 — no asset wiring, works in Node, Bun, and the browser. Under a strict CSP without unsafe-eval the decoder transparently falls back to the JS scanner (~1.1–1.5x).

Where it pays off

| Payload | Mode | Why | |---|---|---| | Paged / sorted tables (DataTable, admin lists) | schema (tronSerializer({ schema }) + createApi({ schemas })) | no shape on the wire, row constructor compiled once; pairs with norns listQuery() / listResult() and norns-ui useList() | | Picker options (Autocomplete / MultiSelect source) | default | small responses ship as JSON automatically; TRON kicks in past ~1 KB | | Charts, aggregations, exports of numbers | columnar (tronSerializer({ columnar: true }) + api.tape()) | decodes into one Float64Array, ~3x faster than JSON.parse, no row objects | | Agent / LLM-facing reads (search results, indexes) | self-describing (tronSerializer()) | a cold reader gets the declarations in-band; 26–61% fewer tokens on tabular data | | Cached lists on Workers (route({ cache: { ttl } })) | any | the encoded body is stored per Accept variant, so encode runs once per TTL and clients revalidate with ETag |

The norns-demo has one page per row under /examples/tron.

Schema mode — the fastest path for internal endpoints

Both ends already know the shape from the feature contract, so nothing descriptive needs to travel. Derive the wire schema from the valibot schema you already have in shared/schema.c:

# src/lib/notes/shared/schema.c
import * as v from 'valibot'
import { tronSchemaFromValibot } from '@human-synthesis/norns-tron/valibot'

export noteSchema := v.object
  id: v.number()
  title: v.string()
  status: v.picklist ['draft', 'published']

export noteWire := tronSchemaFromValibot noteSchema, { id: 'notes.v1', path: '$.data' }

picklist/enum fields become dictionary columns (integers on the wire), booleans become 0/1. Compile once at module scope — never per request. The #notes.v1 tag makes version mismatches fail loudly instead of misdecoding; use createRegistry() (or the schemas array) on a client that consumes several shapes. Derivation is for flat object schemas; nested rows still work through the self-describing mode.

Columnar mode — numbers without objects

For a table whose values are all numbers (or dictionary-able strings / booleans), skip row objects entirely:

# server
export GET := route
  serializer: tronSerializer({ columnar: true })
  cache: { ttl: 30 }
  handler: ({ container }) => stats(container).perDay()   # [{ day, count, chars }, …]

# client
{ fields, rows, cols, tape } := await api.tape '/api/stats'
count := tape[i * cols + fields.indexOf('count')]

api.tape() always returns the same { fields, rows, cols, tape } shape: the WASM fast path when the server answered columnar TRON, and a packed fallback (tableFromRows) when it answered JSON or WASM is unavailable.

Semantics and limits

  • Same value semantics as JSON: toJSON() is honored, so a Date arrives as its ISO string (not a Date — same as response.json()). Map/Set serialize as {} and BigInt throws, exactly like JSON.stringify; dev mode logs a warning when a route returns them.
  • Not for load / form actions — SvelteKit serializes those with devalue (which preserves Dates, Maps, Sets) and only dispatches form-encoded POSTs to actions. This package targets route() endpoints, LLM-facing output, and service-to-service payloads; norns-ui's Form has an API mode (submit) for posting through api.post().
  • Payloads under ~1 KB are emitted as plain JSON automatically (still decoded transparently) — below that size TRON's fixed costs don't pay for themselves.
  • The wire content type is application/tron, not application/json: a TRON body may carry a declaration preamble that is not valid JSON.
  • Schema mode uses new Function for the row constructor where allowed and a closure fallback elsewhere (Cloudflare Workers, CSP-restricted browsers).

Development

bun test              # 50 tests: core roundtrips, Date regression, server/client glue, schema registry, columnar tape, valibot derivation
bun run embed-wasm    # regenerate src/core/wasm-bytes.js after replacing wasm/parserTron2.wasm

License: MIT.