@hpx7/delta-pack
v0.2.8
Published
A TypeScript code generator and runtime for binary serialization based on schemas.
Maintainers
Readme
Delta-Pack TypeScript
TypeScript implementation of delta-pack, a compact binary serialization format with efficient delta compression for real-time state synchronization.
Installation
npm install @hpx7/delta-packQuick Start
Delta-pack provides three approaches for working with schemas:
- Interpreter Mode - Runtime schema parsing with dynamic API
- Decorator Mode - Use TypeScript decorators for class-based schemas (builds on interpreter mode)
- Codegen Mode - Generate TypeScript code from schemas for compile-time type safety
Interpreter Mode (Recommended for prototyping)
import { ObjectType, StringType, IntType, load, Infer } from "@hpx7/delta-pack";
// Define schema type directly
const Player = ObjectType("Player", {
id: StringType(),
name: StringType(),
score: IntType(),
});
// Infer TypeScript type
type Player = Infer<typeof Player>;
// Result: { id: string; name: string; score: number }
// Load API for the type
const PlayerApi = load(Player);
// Use the API
const player = { id: "p1", name: "Alice", score: 100 };
const encoded = PlayerApi.encode(player);
const decoded = PlayerApi.decode(encoded);Decorator Mode (Recommended for class-based code)
import { StringType, IntType, loadClass } from "@hpx7/delta-pack";
class Player {
@StringType()
id: string = "";
@StringType()
name: string = "";
@IntType()
score: number = 0;
}
// Load API from decorated class
const PlayerApi = loadClass(Player);
// Use the API
const player = new Player();
player.id = "p1";
player.name = "Alice";
player.score = 100;
const encoded = PlayerApi.encode(player);
const decoded = PlayerApi.decode(encoded);Codegen Mode (Recommended for production)
Create a YAML schema file (schema.yml):
# yaml-language-server: $schema=https://raw.githubusercontent.com/hpx7/delta-pack/refs/heads/main/schema.json
Player:
id: string
name: string
score: intGenerate TypeScript code using the CLI:
npx delta-pack generate schema.yml -l typescript -o generated.tsThen use the generated code:
import { Player } from "./generated";
const player: Player = { id: "p1", name: "Alice", score: 100 };
const encoded = Player.encode(player);
const decoded = Player.decode(encoded);Schema Definition
Schemas can be defined in two ways:
- YAML - Human-readable format, useful for defining schemas separately from code. Parse with
parseSchemaYml()for interpreter mode. - TypeScript - Define schemas using the type constructor functions. Works with both interpreter and codegen modes. Required for using the
Infer<>type utility.
Note: The Infer<> type utility only works with TypeScript-defined schemas, not YAML-parsed schemas, since it requires compile-time type information.
YAML Schema
Create a schema.yml file:
# yaml-language-server: $schema=https://raw.githubusercontent.com/hpx7/delta-pack/refs/heads/main/schema.json
# Enums (list of string literals)
Team:
- RED
- BLUE
# Objects
Player:
id: string
name: string
score: int
team: Team
position: Position?
Position:
x: float(precision=0.1)
y: float(precision=0.1)
# Union types (list of type references)
# A union is a list where all items are defined type names
MoveAction:
x: int
y: int
AttackAction:
targetId: string
damage: uint # shorthand for int(min=0)
GameAction:
- MoveAction
- AttackAction
# Complex types
GameState:
players: <string, Player>
actions: GameAction[]
round: uint
phase: string
# Bounded integers
BoundedExample:
health: int(min=0, max=100) # validated in fromJson, encoded as unsigned
level: int(min=1, max=99)
temperature: int(min=-50, max=50)Parse YAML schemas with:
import { parseSchemaYml } from "@hpx7/delta-pack";
import { readFileSync } from "fs";
const schemaYml = readFileSync("schema.yml", "utf8");
const schema = parseSchemaYml(schemaYml);See the main README for complete schema syntax reference.
TypeScript Schema
Define schemas using the type constructor functions. Named types (ObjectType, EnumType, UnionType) take a required name as the first parameter:
import {
ObjectType,
StringType,
IntType,
UIntType,
FloatType,
ArrayType,
OptionalType,
RecordType,
EnumType,
UnionType,
ReferenceType,
} from "@hpx7/delta-pack";
// Enum type
const Team = EnumType("Team", ["RED", "BLUE"]);
// Object types - define in dependency order
const Position = ObjectType("Position", {
x: FloatType({ precision: 0.1 }),
y: FloatType({ precision: 0.1 }),
});
const Player = ObjectType("Player", {
id: StringType(),
name: StringType(),
score: IntType(),
team: ReferenceType(Team),
position: OptionalType(ReferenceType(Position)),
});
// Union type - options are direct type references
const MoveAction = ObjectType("MoveAction", {
x: IntType(),
y: IntType(),
});
const AttackAction = ObjectType("AttackAction", {
targetId: StringType(),
damage: UIntType(),
});
const GameAction = UnionType("GameAction", [MoveAction, AttackAction]);
// Using unions in other types
const GameState = ObjectType("GameState", {
players: RecordType(StringType(), ReferenceType(Player)),
actions: ArrayType(ReferenceType(GameAction)),
round: UIntType(),
phase: StringType(),
});Union value format: Union values use a flattened discriminated union format with _type:
// Creating a union value
const action: GameAction = { _type: "MoveAction", x: 10, y: 20 };
// Type narrowing with discriminant
if (action._type === "MoveAction") {
console.log(action.x, action.y);
}Interpreter API
The interpreter mode provides a runtime API for working with schemas.
Loading a Schema
import { ObjectType, StringType, IntType, load, Infer } from "@hpx7/delta-pack";
// Define schema type
const Player = ObjectType("Player", {
id: StringType(),
name: StringType(),
score: IntType(),
});
// Infer TypeScript type
type Player = Infer<typeof Player>;
// Result: { id: string; name: string; score: number }
// Load interpreter API
const PlayerApi = load(Player);API Methods
Every loaded type provides these methods:
fromJson(obj: Record<string, unknown>): T
Validates and parses JSON data, throwing if invalid. Use this when parsing untrusted or untyped data:
// Parse unvalidated JSON data
const jsonData = JSON.parse(networkResponse);
const player = Player.fromJson(jsonData);For most cases, prefer using TypeScript types directly:
// Preferred: use TypeScript types for compile-time safety
const player: Player = { id: "p1", name: "Alice", score: 100 };toJson(obj: T): Record<string, unknown>
Converts an object to JSON-serializable format. Useful for serializing to JSON or sending over HTTP:
const player: Player = { id: "p1", name: "Alice", score: 100 };
const json = Player.toJson(player);
const jsonString = JSON.stringify(json);Format notes:
- Maps (RecordType) are converted to plain objects
- Optional object properties with
undefinedvalues are excluded from the JSON - Unions are converted to protobuf format:
{ TypeName: {...} }
Example with unions:
const action: GameAction = { _type: "MoveAction", x: 10, y: 20 };
const json = GameAction.toJson(action);
// Result: { MoveAction: { x: 10, y: 20 } }The JSON output uses protobuf format ({ TypeName: {...} }) which can be parsed back with fromJson().
encode(obj: T): Uint8Array
Serializes an object to binary format:
const player = { id: "p1", name: "Alice", score: 100 };
const bytes = Player.encode(player);
console.log(`Encoded size: ${bytes.length} bytes`);decode(bytes: Uint8Array): T
Deserializes binary data back to an object:
const decoded = Player.decode(bytes);
// decoded = { id: 'p1', name: 'Alice', score: 100 }encodeDiff(oldObj: T, newObj: T): Uint8Array
Encodes only the differences between two objects:
const oldPlayer = { id: "p1", name: "Alice", score: 100 };
const newPlayer = { id: "p1", name: "Alice", score: 150 };
const diff = Player.encodeDiff(oldPlayer, newPlayer);
console.log(`Diff size: ${diff.length} bytes`); // Much smaller!decodeDiff(oldObj: T, diffBytes: Uint8Array): T
Applies a diff to reconstruct the new object:
const reconstructed = Player.decodeDiff(oldPlayer, diff);
// reconstructed = { id: 'p1', name: 'Alice', score: 150 }equals(a: T, b: T): boolean
Deep equality comparison with appropriate tolerance for floats:
const isEqual = Player.equals(player1, player2);For quantized floats (with precision), equality uses quantized value comparison. For non-quantized floats, equality uses epsilon-based comparison (0.00001 tolerance).
clone(obj: T): T
Creates a deep clone of an object:
const player1 = { id: "p1", name: "Alice", score: 100 };
const player2 = Player.clone(player1);
// Modifying the clone doesn't affect the original
player2.score = 200;
console.log(player1.score); // 100
console.log(player2.score); // 200Important notes:
- Creates deep copies of all nested objects, arrays, and maps
- Primitives (strings, numbers, booleans) are copied by value
- The
_dirtyfield is not preserved in clones (clones always start clean) - Useful for creating modified copies without mutating the original state
Decorator API
The decorator mode provides a class-based API using TypeScript decorators. Schemas are inferred from decorated classes at runtime.
Decorators
The same type functions used to build schemas (StringType(), IntType(), ArrayType(), etc.) also work as property decorators. See Type Reference for the complete list.
Additional decorator-specific features:
| Function | Description |
| ----------------------- | --------------------------------------------- |
| EnumType("Name", []) | Define an enum type |
| UnionType("Name", []) | Define a union from a list of variant classes |
| ReferenceType(ref) | Reference a class, enum, or union |
Referencing classes, enums, and unions:
- Use
ReferenceType(ClassName)to reference another class - Use
EnumType("Name", [...])to define an enum, thenReferenceType(EnumDef)to use it - Use
UnionType("Name", [ClassA, ClassB])to define a union, thenReferenceType(UnionDef)to use it
Example
import {
StringType,
IntType,
UIntType,
FloatType,
BooleanType,
ReferenceType,
ArrayType,
RecordType,
OptionalType,
EnumType,
UnionType,
Infer,
loadClass,
} from "@hpx7/delta-pack";
// Enum type
const Team = EnumType("Team", ["RED", "BLUE"]);
type Team = Infer<typeof Team>;
// Nested object
class Position {
@FloatType({ precision: 0.1 }) x: number = 0;
@FloatType({ precision: 0.1 }) y: number = 0;
}
// Union types - define variant classes, then combine with UnionType
class MoveAction {
@IntType() x: number = 0;
@IntType() y: number = 0;
}
class AttackAction {
@StringType() targetId: string = "";
@UIntType() damage: number = 0;
}
// Define union type - combines variant classes
const GameAction = UnionType("GameAction", [MoveAction, AttackAction]);
type GameAction = Infer<typeof GameAction>;
// Main class
class Player {
@StringType() id: string = "";
@StringType() name: string = "";
@IntType() score: number = 0;
@BooleanType() isOnline: boolean = false;
@ReferenceType(Position) position: Position = new Position();
@ReferenceType(Team) team: Team = Team.RED;
@ArrayType(StringType()) tags: string[] = [];
@ArrayType(ReferenceType(Position)) waypoints: Position[] = [];
@RecordType(StringType(), IntType()) inventory: Map<string, number> = new Map();
@OptionalType(StringType()) nickname?: string;
@OptionalType(ReferenceType(GameAction)) lastAction?: GameAction;
}
// Load API and use
const PlayerApi = loadClass(Player);
const player = new Player();
player.name = "Alice";
const encoded = PlayerApi.encode(player);
const decoded = PlayerApi.decode(encoded);Advanced patterns (nested containers, number modifiers):
class AdvancedExample {
@ArrayType(UIntType()) unsignedInts: number[] = [];
@ArrayType(FloatType({ precision: 0.01 })) floats: number[] = [];
@ArrayType(ArrayType(IntType())) matrix: number[][] = [];
@RecordType(StringType(), ArrayType(IntType())) vectorsById: Map<string, number[]> = new Map();
@OptionalType(ReferenceType(Player)) partner?: Player; // Self-reference
}Working with Union Types
With the decorator API, union types are defined using UnionType() to combine variant classes:
// Define variant classes (regular decorated classes)
class JoinMessage {
@StringType() name: string = "";
}
class InputMessage {
@ReferenceType(ClientInput) input: ClientInput = new ClientInput();
}
// Define union type - combines variants
const ClientMessage = UnionType("ClientMessage", [JoinMessage, InputMessage]);
type ClientMessage = Infer<typeof ClientMessage>;
// Load the API from the union
const ClientMessageApi = loadClass(ClientMessage);Encoding: Pass variant class instances directly:
const joinMsg = new JoinMessage();
joinMsg.name = "Alice";
const encoded = ClientMessageApi.encode(joinMsg);Decoding: Returns variant class instances. Use instanceof to narrow the type:
const message = ClientMessageApi.decode(encoded);
if (message instanceof JoinMessage) {
console.log(message.name); // TypeScript knows this is JoinMessage
} else if (message instanceof InputMessage) {
console.log(message.input);
}buildSchema()
If you need access to the generated schema (e.g., for inspection or to use with load() directly), use buildSchema():
import { buildSchema, load } from "@hpx7/delta-pack";
const schema = buildSchema(Player);
console.log(schema); // { Player: { type: "object", properties: { ... }, name: "Player" } }
// Use with load() for more control
const PlayerApi = load(schema["Player"]);Requirements
tsconfig.json: Enable "experimentalDecorators": true
Class requirements:
- Parameterless constructor (instantiable with
new Class()) - Decorated properties must have default values (discovered via
Object.keys(new Class())) - At least one property must have a type decorator
- Unique class names (used as schema identifiers)
Note: Properties without decorators are ignored (not serialized). Methods are also allowed. This lets you add helper methods or transient state to your classes.
API Methods
loadClass() returns the same API as load() in interpreter mode:
| Method | Description |
| ----------------------- | ------------------------------------ |
| encode(obj) | Serialize to binary |
| decode(bytes) | Deserialize from binary |
| encodeDiff(old, new) | Encode delta between two states |
| decodeDiff(old, diff) | Apply delta to reconstruct new state |
| equals(a, b) | Deep equality comparison |
| clone(obj) | Deep clone |
| toJson(obj) | Convert to JSON-serializable format |
| fromJson(obj) | Validate and parse JSON data |
Key difference: In decorator mode, decode, decodeDiff, clone, and fromJson return proper class instances—not plain objects. This includes union variants (e.g., decoding a ClientMessage returns a JoinMessage or InputMessage instance).
Schema API vs Decorator API
| Aspect | Schema API | Decorator API |
| ----------------- | -------------------------------------------- | ------------------------------------------- |
| Type constructors | StringType(), ArrayType(), etc. | Same - work as decorators too |
| Object types | ObjectType("Name", { ... }) | Class with decorated properties |
| Enums | EnumType("Name", [...]) | Same - EnumType("Name", [...]) |
| Unions | UnionType("Name", [TypeA, TypeB]) | UnionType("Name", [ClassA, ClassB]) |
| References | Type references: ReferenceType(PlayerType) | Class/enum/union refs: ReferenceType(ref) |
| Loading | load(RootType) | loadClass(Class) or loadClass(UnionDef) |
| Return types | Plain objects | Class instances with methods |
Codegen API
The codegen mode generates TypeScript code from schemas for compile-time type safety. Code generation is handled by the @hpx7/delta-pack-cli package.
Generating Code
# Install CLI globally or use npx
npm install -g @hpx7/delta-pack-cli
# Generate from YAML schema
delta-pack generate schema.yml -l typescript -o generated.ts
# Or use npx
npx delta-pack generate schema.yml -l typescript -o generated.tsFor programmatic code generation, import from the CLI package:
import { codegenTypescript } from "@hpx7/delta-pack-cli/codegen";
import { parseSchemaYml } from "@hpx7/delta-pack";
import { readFileSync, writeFileSync } from "fs";
const schemaYml = readFileSync("schema.yml", "utf8");
const schema = parseSchemaYml(schemaYml);
const code = codegenTypescript(schema);
writeFileSync("generated.ts", code);Using Generated Code
The generated code exports TypeScript types and runtime objects:
import { Player, GameState } from "./generated";
// TypeScript types are available
const player: Player = {
id: "p1",
name: "Alice",
score: 100,
};
// Runtime objects provide the same API as interpreter mode
const encoded = Player.encode(player);
const decoded = Player.decode(encoded);Generated API
The generated code provides the same methods as interpreter mode:
Player.fromJson(obj)- Validate and parse JSON dataPlayer.toJson(obj)- Convert to JSON-serializable formatPlayer.encode(obj)- Serialize to binaryPlayer.decode(bytes)- Deserialize from binaryPlayer.encodeDiff(old, new)- Encode deltaPlayer.decodeDiff(old, diff)- Apply deltaPlayer.equals(a, b)- Deep equalityPlayer.clone(obj)- Deep clone objectPlayer.default()- Default instance
Complete Example
Multiplayer Game State Sync
Using Interpreter Mode:
import {
ObjectType,
StringType,
UIntType,
FloatType,
IntType,
EnumType,
ReferenceType,
RecordType,
load,
Infer,
} from "@hpx7/delta-pack";
// Define schema types
const Team = EnumType("Team", ["RED", "BLUE"]);
const Position = ObjectType("Position", {
x: FloatType(),
y: FloatType(),
});
const Player = ObjectType("Player", {
id: StringType(),
username: StringType(),
team: ReferenceType(Team),
position: ReferenceType(Position),
health: UIntType(),
score: IntType(),
});
const GameState = ObjectType("GameState", {
players: RecordType(StringType(), ReferenceType(Player)),
round: UIntType(),
timeRemaining: FloatType(),
});
// Infer types
type GameStateType = Infer<typeof GameState>;
// Load API
const GameStateApi = load(GameState);
// Create initial state
const state1: GameStateType = {
players: new Map([
[
"p1",
{
id: "p1",
username: "Alice",
team: "RED",
position: { x: 100, y: 100 },
health: 100,
score: 0,
},
],
]),
round: 1,
timeRemaining: 600.0,
};
// Updated state (player moved)
const state2: GameStateType = {
...state1,
players: new Map([["p1", { ...state1.players.get("p1")!, position: { x: 105, y: 102 } }]]),
timeRemaining: 599.0,
};
// Delta encoding
const diff = GameStateApi.encodeDiff(state1, state2);
const reconstructed = GameStateApi.decodeDiff(state1, diff);Using Decorator Mode:
import {
StringType,
UIntType,
FloatType,
IntType,
ReferenceType,
RecordType,
EnumType,
Infer,
loadClass,
} from "@hpx7/delta-pack";
const Team = EnumType("Team", ["RED", "BLUE"]);
type Team = Infer<typeof Team>;
class Position {
@FloatType() x: number = 0;
@FloatType() y: number = 0;
}
class Player {
@StringType() id: string = "";
@StringType() username: string = "";
@ReferenceType(Team) team: Team = "RED";
@ReferenceType(Position) position: Position = new Position();
@UIntType() health: number = 0;
@IntType() score: number = 0;
}
class GameState {
@RecordType(StringType(), ReferenceType(Player)) players: Map<string, Player> = new Map();
@UIntType() round: number = 0;
@FloatType() timeRemaining: number = 0;
}
// Load API
const GameStateApi = loadClass(GameState);
// Create initial state
const state1 = new GameState();
state1.round = 1;
state1.timeRemaining = 600.0;
const player = new Player();
player.id = "p1";
player.username = "Alice";
player.team = "RED";
player.position.x = 100;
player.position.y = 100;
player.health = 100;
state1.players.set("p1", player);
// Updated state (player moved)
const state2 = GameStateApi.clone(state1);
state2.players.get("p1")!.position.x = 105;
state2.players.get("p1")!.position.y = 102;
state2.timeRemaining = 599.0;
// Delta encoding
const diff = GameStateApi.encodeDiff(state1, state2);
const reconstructed = GameStateApi.decodeDiff(state1, diff);Using Codegen Mode:
Create a schema file (game.schema.yml):
# yaml-language-server: $schema=https://raw.githubusercontent.com/hpx7/delta-pack/refs/heads/main/schema.json
Team:
- RED
- BLUE
Position:
x: float
y: float
Player:
id: string
username: string
team: Team
position: Position
health: uint
score: int
GameState:
players: <string, Player>
round: uint
timeRemaining: floatGenerate the code:
npx delta-pack generate game.schema.yml -l typescript -o generated.tsThen use the generated code:
import { GameState, Player } from "./generated";
// TypeScript types are available at compile time
const state: GameState = GameState.default();
state.players.set("p1", {
id: "p1",
username: "Alice",
team: "RED",
position: { x: 100, y: 100 },
health: 100,
score: 0,
});
// Same API as interpreter mode
const encoded = GameState.encode(state);
const decoded = GameState.decode(encoded);Performance Tips
Delta Compression
Delta encoding is most effective when:
- State changes are incremental (only a few fields change per update)
- You send updates frequently (e.g., 60 times per second in games)
- Objects are medium to large (>50 bytes)
Typical bandwidth savings:
- Position-only updates: 90-95% smaller
- Single field changes: 85-90% smaller
- Multiple field changes: 70-85% smaller
Dirty Tracking with track()
For maximum encodeDiff performance, use track() to automatically track which fields have changed. This allows delta encoding to skip comparison checks entirely:
import { track, clearTracking } from "@hpx7/delta-pack";
// Wrap state with tracking
const state = track({
tick: 0,
player: { x: 0, y: 0 },
players: new Map([["p1", { x: 0, y: 0 }]]),
});
// Mutations are automatically tracked
state.tick = 1; // Marks "tick" dirty
state.player.x = 100; // Marks "x" on player, "player" on state
state.players.get("p1")!.x = 50; // Marks "x" on player, "p1" on players, "players" on state
// Efficient delta encoding - only changed fields are compared
const diff = GameStateApi.encodeDiff(oldState, state);
// Clear tracking for next update cycle
clearTracking(state);Deep tracking: Changes to nested objects, arrays, and maps automatically propagate up to parents. This works with:
- Nested objects:
state.player.x = 100marks bothplayer.xandstate.player - Arrays:
state.items[0].value = 99marks the item, index 0, andstate.items - Maps:
state.players.get("p1")!.x = 50marks the player, key "p1", andstate.players - Array methods:
push,pop,shift,unshift,spliceall mark appropriate indices
Game loop pattern:
class Game {
private state = track(new GameState());
tick() {
// Mutations are automatically tracked
this.state.tick++;
for (const [id, input] of this.inputs) {
const player = this.state.players.get(id)!;
player.x += input.vx;
player.y += input.vy;
}
}
broadcast() {
for (const client of this.clients) {
const diff = GameStateApi.encodeDiff(client.lastState, this.state);
client.send(diff);
client.lastState = GameStateApi.clone(this.state);
}
clearTracking(this.state); // Reset for next tick
}
}The Tracked<T> type: The track() function returns a Tracked<T> which recursively adds _dirty sets at every level of the object tree.
When to use tracking:
- High-frequency updates (e.g., 20-60 times per second)
- Large state objects where full comparison is expensive
- Mutable state that changes incrementally each tick
Manual _dirty (alternative): You can also set _dirty directly without using track():
// Objects: track changed fields
player.score = 150;
player._dirty = new Set(["score"]);
// Arrays: track changed indices
items[5] = newItem;
items._dirty = new Set([5]);
// Maps: track changed keys
players.set("p1", updatedPlayer);
players._dirty = new Set(["p1"]);The _dirty field is:
- Optional: If absent, full comparison is performed
- Type-safe:
Set<keyof T>for objects,Set<number>for arrays,Set<K>for maps - Included in types: Both codegen and interpreter types include
_dirty - Not serialized: The
_dirtyfield is never encoded in the binary format
Quantized Floats
Use precision for floats to reduce size:
const Position = ObjectType("Position", {
x: FloatType({ precision: 0.1 }), // ~10cm precision
y: FloatType({ precision: 0.1 }),
});This enables delta compression to skip encoding unchanged floats even if they differ slightly due to floating-point imprecision.
String Dictionary
Strings are automatically deduplicated within each encoding operation. Reuse common strings (player IDs, item names, etc.) to benefit from dictionary compression.
Map vs Array
- Use
RecordType(maps) when entities have unique IDs - Use
ArrayTypewhen order matters or IDs aren't meaningful
Maps enable efficient delta encoding for entity collections (only changed entities are encoded).
Examples
See the examples/ directory for complete examples:
examples/primitives/- Basic primitive typesexamples/user/- User profile with unions and optionalsexamples/game/- Multiplayer game with complex state
Each example includes:
schema.yml- Schema definitionstate1.json,state2.json, ... - Example states demonstrating delta compression
Type Reference
Primitive Types
| Function | TypeScript Type | Description |
| -------------------------- | --------------- | ------------------------------------------- |
| StringType() | string | UTF-8 encoded string |
| IntType() | number | Variable-length signed integer (zigzag) |
| IntType({ min }) | number | Bounded integer, encoded as unsigned offset |
| IntType({ min, max }) | number | Bounded integer with validation |
| UIntType() | number | Shorthand for IntType({ min: 0 }) |
| FloatType() | number | 32-bit IEEE 754 float |
| FloatType({ precision }) | number | Quantized float with specified precision |
| BooleanType() | boolean | Single bit boolean |
Bounded integers: When min is specified, values are encoded as value - min using unsigned varints, which is more compact for values near the minimum. The max parameter adds validation in fromJson() but doesn't affect encoding.
Container Types
| Function | TypeScript Type | Description |
| ------------------ | ---------------- | ------------------------------------ |
| ArrayType(T) | T[] | Array of type T |
| OptionalType(T) | T \| undefined | Optional value of type T |
| RecordType(K, V) | Map<K, V> | Map with key type K and value type V |
Complex Types
| Function | TypeScript Type | Description |
| ----------------------------- | ------------------------ | -------------------------------- |
| ObjectType("Name", { ... }) | { ... } | Object with defined properties |
| EnumType("Name", [...]) | Union of string literals | Enumerated string values |
| UnionType("Name", [...]) | { _type: string } & T | Tagged union of named types |
| ReferenceType(Type) | Named type | Reference to another named type |
| SelfReferenceType() | Self | Reference to the containing type |
Development
Running Tests
npm test # Run all tests
npm run test:coverage # Run with coverage report
npm run test:ui # Open Vitest UIType Checking
npm run typecheckFormatting
npm run format # Format code
npm run format:check # Check formattingAPI Documentation
For detailed API documentation and schema syntax, see the main README.
License
MIT
