TYTX for Dummies

Typed Text eXchange — send a Decimal from Python, receive a Decimal in JavaScript. Zero boilerplate.

The Type Problem Nobody Talks About

You build a web app. Your Python backend calculates a price: Decimal("99.99"). You serialize it to JSON. JSON has no Decimal type. Your 99.99 arrives in JavaScript as a float — and 0.1 + 0.2 === 0.30000000000000004. Your invoice is wrong. Your accountant is angry.

So you convert Decimal to string before sending. Now the frontend needs to know which strings are "actually numbers" and convert them back. You add a schema. Or a convention. Or a separate metadata field. For every type. In every endpoint.

Dates? Same story. datetime(2025, 1, 15, 10, 30) becomes "2025-01-15T10:30:00" — is that UTC? Local? The frontend guesses. Half your bug tracker is timezone issues.

TYTX solves this by embedding type information directly in the value. "99.99::N" is a Decimal. "2025-01-15::D" is a date. The sender tags, the receiver hydrates. No schema negotiation, no out-of-band metadata, no guessing.

PYTHON Decimal, date, datetime, time JAVASCRIPT Big.js, Date, Date, Date TYTX WIRE FORMAT "99.99::N" • "2025-01-15::D" WITHOUT TYTX Decimal("99.99") → JSON float 99.99 99.99 (float) precision lost 99.99000000000001 wrong invoice WITH TYTX Decimal("99.99") "99.99::N" Big("99.99")

When does this actually matter?

Financial data

Prices, invoices, tax calculations — Decimal roundtrips without floating-point surprises. "999.99::N" stays 999.99, always.

Date/time across timezones

Dates stay dates, datetimes are always UTC with explicit ::DHZ suffix. No more "is this local or UTC?" bugs.

Multiple transports

Same typed data over JSON, XML, MessagePack, or query strings. Switch transport, keep types.

Multi-language apps

Python backend, JavaScript frontend. Types survive the round trip. Same library on both sides.

Zero-config

No schema files, no code generation, no build step. pip install genro-tytx and go.

Backward compatible

TYTX values are valid JSON strings. Any JSON parser can read them. Type-unaware code just sees strings.

Quick Start

Install

# Python
pip install genro-tytx              # Core
pip install genro-tytx[fast]        # With orjson (faster JSON)
pip install genro-tytx[msgpack]     # With MessagePack support
pip install genro-tytx[all]         # Everything

# JavaScript (from GitHub)
npm install genropy/genro-tytx

Encode & Decode (Python)

from decimal import Decimal
from datetime import date, datetime
from genro_tytx import to_tytx, from_tytx

# Encode a Python dict with typed values
order = {
    "product": "Widget",
    "price": Decimal("99.99"),
    "date": date(2025, 1, 15),
}

wire = to_tytx(order)
# '{"product": "Widget", "price": "99.99::N", "date": "2025-01-15::D"}::JS'

# Decode back — types are automatically restored
result = from_tytx(wire)
# {"product": "Widget", "price": Decimal("99.99"), "date": date(2025, 1, 15)}

Encode & Decode (JavaScript)

import { toTytx, fromTytx } from 'genro-tytx';
import { createDecimal, setDecimalLibrary } from 'genro-tytx/src/registry.js';

setDecimalLibrary('big.js');

const order = {
    product: "Widget",
    price: createDecimal("99.99"),
    date: new Date(Date.UTC(2025, 0, 15)),
};

const wire = toTytx(order);
// '{"product":"Widget","price":"99.99::N","date":"2025-01-15::D"}::JS'

const result = fromTytx(wire);
// { product: "Widget", price: Big("99.99"), date: Date(2025-01-15) }

HTTP Integration (Python ASGI)

from genro_tytx import asgi_data, to_tytx

async def handle_request(scope, receive, send):
    # Automatically decodes TYTX from query, headers, cookies, body
    data = await asgi_data(scope, receive)

    price = data["body"]["price"]   # Already a Decimal!
    total = price * 3

    response = to_tytx({"total": total})
    # Send response...

HTTP Integration (JavaScript)

import { fetchTytx } from 'genro-tytx';

const result = await fetchTytx('/api/order', {
    body: { price: createDecimal("99.99"), qty: 3 },
    transport: 'json',
});
// result.total is a Big (Decimal), ready to use

Key insight: You never write type-conversion code. Tag on one side, hydrate on the other. Every value keeps its real type through the entire roundtrip.

The Type System

TYTX adds a ::SUFFIX to values that JSON can't represent natively. The suffix tells the receiver how to reconstruct the original type.

Non-Native Types (always suffixed)

These types have no JSON equivalent. Without the suffix, the receiver has no way to know what they are.

SuffixMeaningPythonJavaScriptWire Example
::NDecimalDecimalBig / Decimal"99.99::N"
::DDatedateDate (midnight UTC)"2025-01-15::D"
::DHZDateTime (UTC)datetimeDate"2025-01-15T10:30:00.000Z::DHZ"
::HTimetimeDate (epoch 1970-01-01)"14:30:00.000::H"

JSON-Native Types (suffixed only in XML/QS)

These types exist natively in JSON. Suffixes are added only when the transport doesn't have native support (XML, query strings).

SuffixMeaningTypeWire Example
::LInteger (Long)int / number"42::L"
::RFloat (Real)float / number"3.14::R"
::BBooleanbool / boolean"true::B"
::TText (explicit)str / string"hello::T"
::NNNullNone / null"::NN"

Structure Markers

SuffixMeaningExample
::JSJSON structure with typed values{"price": "99.99::N"}::JS
::QSQuery string formatprice=99.99::N&qty=3::L::QS

The ::JS suffix is the signal. When a JSON string ends with ::JS, the decoder knows to parse it and walk the tree looking for typed values. Without ::JS, it's just a regular JSON string — no processing needed.

DateTime Precision

JavaScript Date has millisecond precision. TYTX truncates Python microseconds to milliseconds during encoding to ensure perfect roundtrips.

# Python: microseconds truncated to milliseconds
to_tytx(datetime(2025, 1, 15, 10, 30, 45, 123456))
# "2025-01-15T10:30:45.123Z::DHZ"  (123456 μs → 123 ms)

# Naive datetimes are treated as UTC
to_tytx(datetime(2025, 1, 15, 10, 30))
# "2025-01-15T10:30:00.000Z::DHZ"

JSON Transport

JSON is the default transport. When your data contains typed values (Decimal, date, etc.), TYTX wraps them as strings with type suffixes and marks the whole structure with ::JS.

How encoding works

from decimal import Decimal
from datetime import date
from genro_tytx import to_tytx

# Dict with typed values → ::JS suffix
to_tytx({"price": Decimal("99.99"), "date": date(2025, 1, 15)})
# '{"price": "99.99::N", "date": "2025-01-15::D"}::JS'

# Dict with only native JSON types → no suffix
to_tytx({"name": "Widget", "count": 42})
# '{"name": "Widget", "count": 42}'

# Scalar typed value → quoted with suffix, no ::JS
to_tytx(Decimal("100.50"))
# '"100.50::N"'

# Plain scalar → quoted JSON
to_tytx("hello")
# '"hello"'

How decoding works

from genro_tytx import from_tytx

# TYTX structure → types restored
from_tytx('{"price": "99.99::N", "date": "2025-01-15::D"}::JS')
# {"price": Decimal("99.99"), "date": date(2025, 1, 15)}

# Scalar with suffix
from_tytx('"100.50::N"')
# Decimal("100.50")

# Plain JSON (no TYTX suffixes) → parsed as standard JSON
from_tytx('{"name": "Widget", "count": 42}')
# {"name": "Widget", "count": 42}

With explicit transport

When receiving data from HTTP (via asgi_data / wsgi_data), the transport is set to "json" based on the Content-Type. This tells from_tytx to expect JSON-encoded data.

# to_tytx with transport="json" wraps output in quotes
to_tytx({"price": Decimal("99.99")}, transport="json")
# '"{\\"price\\": \\"99.99::N\\"}::JS"'

# from_tytx with transport="json" strips the outer quotes
from_tytx('"{\\"price\\": \\"99.99::N\\"}::JS"', transport="json")
# {"price": Decimal("99.99")}

orjson acceleration

If orjson is installed, TYTX uses it automatically for faster JSON parsing. No code changes needed.

pip install genro-tytx[fast]   # installs orjson

XML Transport

When you need XML (SOAP, legacy integrations, config files), TYTX embeds type suffixes directly in text content and attribute values.

Data structure convention

XML elements map to Python dicts with a fixed structure:

{"tag_name": {"attrs": {...}, "value": ...}}

Encoding

from genro_tytx import to_tytx

data = {
    "order": {
        "attrs": {"id": 123, "created": date(2025, 1, 15)},
        "value": {
            "total": {"attrs": {}, "value": Decimal("299.97")}
        }
    }
}

to_tytx(data, transport="xml")

Produces:

<order id="123::L" created="2025-01-15::D">
  <total>299.97::N</total>
</order>

Decoding

xml_data = '<order id="123::L"><total>299.97::N</total></order>'

from_tytx(xml_data, transport="xml")
# {"order": {"attrs": {"id": 123}, "value": {"total": {"attrs": {}, "value": Decimal("299.97")}}}}

Configuration

ParameterTypeDefaultDescription
transportstrNoneSet to "xml" for XML transport

All type suffixes are active in XML — including ::L, ::R, ::B for native JSON types. Since XML is all text, every type needs an explicit suffix.

MessagePack Transport

When you need compact binary encoding (WebSocket, IoT, high-throughput APIs), TYTX uses MessagePack native extension types — no string wrapping, zero overhead.

Ext codeTypePayload
Timestamp (built-in)datetimeNative msgpack Timestamp (UTC)
1DecimalUTF-8 string (e.g. "99.99")
2dateISO string "YYYY-MM-DD"
3timeISO string "HH:MM:SS.ffffff"

Install

# Python
pip install genro-tytx[msgpack]

# JavaScript
npm install @msgpack/msgpack

Usage

from genro_tytx import to_tytx, from_tytx

# Encode → bytes (compact binary with ext types)
data = {"price": Decimal("99.99"), "date": date(2025, 1, 15)}
packed = to_tytx(data, transport="msgpack")
# b'...'  binary payload: Decimal as ext 1, date as ext 2

# Decode → dict with real types
result = from_tytx(packed, transport="msgpack")
# {"price": Decimal("99.99"), "date": date(2025, 1, 15)}

Direct functions

from genro_tytx import to_msgpack, from_msgpack

packed = to_msgpack({"total": Decimal("500")})
result = from_msgpack(packed)
# {"total": Decimal("500")}

MessagePack is ~30-50% smaller than JSON on typical payloads, and faster to parse. Using native ext types (no string tagging) keeps the payload lean and parsing direct.

Query String Transport

When you need to pass typed data in URLs (query parameters, form data), TYTX encodes key-value pairs with type suffixes.

Object mode (key=value)

from genro_tytx import to_tytx, from_tytx
from genro_tytx import to_qs, from_qs

# Encode with qs=True
to_tytx({"price": Decimal("99.99"), "date": date(2025, 12, 14)}, qs=True)
# 'price=99.99::N&date=2025-12-14::D::QS'

# Or use direct functions
to_qs({"price": Decimal("99.99"), "active": True})
# 'price=99.99::N&active=true::B'

# Decode
from_qs('price=99.99::N&active=true::B')
# {"price": Decimal("99.99"), "active": True}

Array mode (no keys)

to_qs(["alpha", "beta", "gamma"])
# 'alpha&beta&gamma'

from_qs('alpha&beta&gamma')
# ["alpha", "beta", "gamma"]

Object and array modes can't be mixed. If some items have = and some don't, to_qs raises ValueError.

HTTP Integration

TYTX provides ready-made utilities to decode typed data from HTTP requests — query parameters, headers, cookies, and body — in both ASGI and WSGI applications.

ASGI (async)

from genro_tytx import asgi_data

async def app(scope, receive, send):
    data = await asgi_data(scope, receive)

    data["query"]    # dict — decoded query parameters
    data["headers"]  # dict — decoded headers
    data["cookies"]  # dict — decoded cookies
    data["body"]     # any  — decoded body (based on Content-Type)

WSGI (sync)

from genro_tytx import wsgi_data

def app(environ, start_response):
    data = wsgi_data(environ)

    data["query"]    # same structure as ASGI
    data["headers"]
    data["cookies"]
    data["body"]

Auto-detection

Transport is detected automatically from Content-Type:

Content-TypeTransport
application/json"json"
application/xml"xml"
application/msgpack"msgpack"

JavaScript: fetchTytx

import { fetchTytx } from 'genro-tytx';

// POST with automatic TYTX encoding
const result = await fetchTytx('/api/order', {
    body: { price: createDecimal("99.99"), qty: 3 },
    transport: 'json',     // default
});
// result is already decoded with real types

// GET (no body → auto GET)
const data = await fetchTytx('/api/products');

Configuration

ParameterTypeDefaultDescription
bodyanyundefinedData to send (auto-encoded)
transportstring"json""json", "xml", or "msgpack"
methodstringautoHTTP method (auto: GET if no body, POST if body)
headersobject{}Additional headers (merged with Content-Type)

Plain JSON works too. If a non-TYTX client sends standard JSON like {"key": "value"}, it's parsed correctly. TYTX decoding is backward-compatible — it only activates type hydration when it detects TYTX suffixes.

How It All Fits Together

The encode/decode flow

ENCODING (to_tytx) 1. INPUT Python/JS object Decimal, date, dict, list... 2. WALK TREE Serialize each value Decimal → "99.99::N" 3. TRANSPORT JSON / XML / MsgPack Serialize structure 4. WIRE str / bytes '{"p":"99::N"}::JS' DECODING (from_tytx) 1. WIRE str / bytes 2. PARSE Strip ::JS, parse JSON 3. HYDRATE Walk tree, find ::SUFFIX "99.99::N" → Decimal 4. OUTPUT Python/JS object Real types restored TRANSPORT SELECTION JSON (default) XML MessagePack Query String

Module architecture

registry

Type registry mapping Python/JS types to suffixes and serializers. The single source of truth for type encoding.

encode

to_tytx() — walks data, serializes typed values with suffixes, wraps in chosen transport.

decode

from_tytx() — parses transport, walks tree, hydrates string values back to native types.

utils

Low-level helpers: raw_encode, raw_decode, walk, tytx_equivalent.

http

ASGI/WSGI request decoders, JS fetchTytx wrapper. Auto-detects transport from Content-Type.

xml / msgpack / qs

Transport-specific encoders and decoders. Same typed values, different wire formats.

JavaScript decimal libraries

JavaScript has no built-in Decimal. TYTX lets you choose your library:

import { setDecimalLibrary } from 'genro-tytx/src/registry.js';

setDecimalLibrary('decimal.js');  // Full-featured, larger bundle
setDecimalLibrary('big.js');      // Lightweight, recommended
setDecimalLibrary('number');     // Native Number fallback (loses precision)

Auto-detection: TYTX tries decimal.js first, then big.js, then falls back to Number. If you install one of the libraries, it just works.

Cheat Sheet

Python

I want to…How
Encode any value to TYTXto_tytx(value)
Decode TYTX back to Pythonfrom_tytx(data)
Encode to XMLto_tytx(value, transport="xml")
Encode to MessagePackto_tytx(value, transport="msgpack")
Encode to query stringto_tytx(value, qs=True)
Encode without TYTX suffixesto_tytx(value, raw=True)
Decode an ASGI requestawait asgi_data(scope, receive)
Decode a WSGI requestwsgi_data(environ)
Use XML functions directlyto_xml(value) / from_xml(data)
Use MessagePack directlyto_msgpack(value) / from_msgpack(data)
Use QS directlyto_qs(value) / from_qs(data)
Encode a single scalarraw_encode(value)(True, "99.99::N")
Decode a single scalarraw_decode("99.99::N")(True, Decimal("99.99"))
Enable orjson accelerationpip install genro-tytx[fast]

JavaScript

I want to…How
Encode any value to TYTXtoTytx(value)
Decode TYTX back to JSfromTytx(data)
Encode to XMLtoTytx(value, "xml")
Encode to MessagePacktoTytx(value, "msgpack")
Encode to query stringtoTytx(value, null, {qs: true})
Fetch with TYTX (auto encode/decode)await fetchTytx(url, {body, transport})
Create a Decimal valuecreateDecimal("99.99")
Check if value is DecimalisDecimal(value)
Choose decimal librarysetDecimalLibrary("big.js")
Detect transport from Content-TypegetTransport("application/json")"json"

Type suffixes

SuffixTypeExample
::NDecimal"99.99::N"
::DDate"2025-01-15::D"
::DHZDateTime (UTC)"2025-01-15T10:30:00.000Z::DHZ"
::HTime"14:30:00.000::H"
::LInteger"42::L"
::RFloat"3.14::R"
::BBoolean"true::B"
::TText"hello::T"
::NNNull"::NN"
::JSJSON Structure{"p":"99::N"}::JS
::QSQuery Stringp=99::N::QS