Tinox
A compiled, object-oriented programming language with Java-like syntax, LLVM backend, integrated HTTP server, and complete IDE tooling – designed for high-performance backend services.
What is Tinox?
Tinox is an independently developed, statically typed programming language that compiles directly to native machine instructions. The language combines a Java-like, annotation-driven syntax with the performance advantages of an LLVM-based compiler and a runtime system written in C.
The main focus is on the simple development of high-performance
HTTP REST services: a controller handler requires only a few lines of code,
and the built-in HTTP server is based on epoll and
SO_REUSEPORT – technologies also used in production-ready
web servers such as Nginx.
import tinox.core.http_server
import tinox.core.json
class SortController
{
@POST
@Path("/sort")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
fn sort(ctx: HttpContext) -> Nothing
{
const root: JsonValue = Json.parse(ctx.request.body)
const numbers: List<Int64> = Json.intArrayFromJson(root.getField("numbers"))
ctx.response.body = Json.intArrayWrap("sorted", numbers.sort())
}
}
This code represents a fully functional REST endpoint – no framework configuration overhead, no XML files, no boilerplate.
Technical Fundamentals
LLVM as Compiler Backend
The Tinox compiler does not generate native machine instructions directly, but rather LLVM Intermediate Representation (LLVM IR) – a platform-independent, typed assembly language. The LLVM toolchain then handles the optimization and translation to machine code.
LLVM is also used by Clang (C/C++), Rust, Swift, Julia, and Kotlin Native – it is one of the most mature compiler backends in the industry.
Rust as Implementation Language
The entire compiler – lexer, parser, type checker, code generator, and LSP server – is implemented in Rust. This brings the following advantages:
- Safety No undefined behavior, no memory errors in the compiler itself
- Performance Fast compile times of the compiler (no GC pauses)
- Concurrency Thread-safe data structures (DashMap) for the LSP server
C as Runtime System
The runtime system (runtime.c) is written in C and provides all
operating system-level functions: memory management, strings, arrays,
maps, file I/O, networking, and the HTTP server. C functions are called directly from
LLVM IR – no ABI overhead through wrapper layers.
Key Concepts
Static Typing
All types are checked at compile time. No runtime type errors possible.
AOT Compilation
Ahead-of-Time: the source code is fully compiled to a native binary before execution. No interpreter, no JIT.
Boehm GC
Tinox uses the Boehm Garbage Collector – a conservative, thread-safe GC. Memory is freed automatically; the developer does not need to manage anything manually.
1:1 Threading
Every spawn call creates a real OS thread (pthread).
Full parallelism on multiple CPU cores.
Statement Termination
A statement ends at the end of its line – there is no statement
terminator. A ; that terminates a statement is a hard compile error
(“semicolons were removed”), and several statements cannot share a line.
The only remaining ; separates the parts of a C-style
for (init; cond; update) header.
A statement continues on the next line only if the line ends with a binary
operator, a comma or an opening bracket, or if the next line starts with
. (method chains). Inside ( ) and [ ]
newlines are plain whitespace. A line starting with (,
[, - or + directly after a complete
expression is a hard error – the compiler never guesses whether it continues
the previous line. Enum variants are written one per line.
const total = first +
second +
third
const name = user
.profile()
.displayName()
Components Overview
Tinox consists of several Rust crates (libraries) that together form the compiler and tooling, plus the C runtime system and the standard library written in Tinox itself.
| Component | Language | Purpose | Size |
|---|---|---|---|
tinox-common | Rust | Shared data types (Span, Error, AST nodes) | ~250 lines |
tinox-lexer | Rust | Tokenization (Lexer) | ~800 lines |
tinox-parser | Rust | AST construction + code formatter | ~2,500 lines |
tinox-typecheck | Rust | Type checking + annotation processing | ~2,000 lines |
tinox-codegen | Rust | AST → LLVM IR | ~6,000 lines |
tinox (CLI) | Rust | Compiler control, REPL, project manager | ~1,700 lines |
tinox-lsp | Rust | Language Server Protocol Server | ~1,000 lines |
tinox-eclipse | Java | Eclipse IDE Plugin | ~400 lines |
runtime.c | C | Runtime system: I/O, HTTP, memory, strings | ~1,700 lines |
tinox-core | Tinox | Standard library (55+ modules) | ~200 KB |
Compiler Pipeline
The Tinox compiler transforms a .tnx source file in several
steps into an executable native binary:
Lexer (tinox-lexer)
Reads the UTF-8 source text and breaks it into tokens (smallest
meaningful units): keywords, identifiers, literals,
operators, brackets. Each token carries precise position information
(line, column, byte offset) for later error messages.
Supports string interpolation ("Hello ${name}!").
Parser (tinox-parser)
Converts the token sequence into an Abstract Syntax Tree (AST) – a tree structure representing the syntactic structure of the program. Recursive descent parser: every grammar rule corresponds to a function. Recognizes classes, methods, expressions, loops, match expressions, etc.
Import Resolution (tinox CLI)
import declarations are resolved recursively: the imported
.tnx files (from the standard library or the project directory)
are loaded, parsed, and embedded into the AST. The result is a
single, consolidated AST with all required declarations.
Type Checker (tinox-typecheck)
Two-pass approach: first all declarations (classes, methods, functions) are registered, then expressions and statements are checked for type correctness. Also validates annotations (@GET, @POST, @Produces etc.), extracts REST routes, and recognizes DI components.
Code Generator (tinox-codegen)
Traverses the AST directly and generates LLVM IR as text. Generates vtable structures for interface dispatch, monomorphizes generic functions/classes, allocates closure environments, generates HTTP route handlers. No intermediate step – directly AST → IR.
llc (LLVM Compiler)
The external LLVM tool llc translates LLVM IR into
assembler. In release mode with -O3, extensive
optimizations (inlining, loop optimizations, vectorization) are performed.
Clang (C Compiler / Linker)
clang compiles runtime.c into an object file
and links it with the generated assembler into a finished
native binary. This also links -lm (math library).
Runtime (C Runtime System)
The core of the Tinox runtime system is runtime/runtime.c
(~1,700 lines of C code). It provides all primitives that the generated
LLVM IR accesses directly.
Memory Management
Tinox uses the Boehm Garbage Collector (libgc) for automatic
memory management. The GC is conservative: it scans the stack and registers for
pointers and frees objects that are no longer reachable – without the developer
having to call free() explicitly. With GC_THREADS it is
fully thread-safe and works correctly with the epoll-based
HTTP server thread pool.
For frequent operations (sort, JSON parsing), thread-local static buffers that are allocated once and then reused – this completely eliminates malloc overhead in the request-critical path.
Collections
| Type | Implementation | Special Feature |
|---|---|---|
| Array | Dynamic C array | Length stored at offset -1; grows on push |
| Map | Open-addressing hash table | FNV-1a hash, load factor 0.75, tombstones for delete |
| String | C string (char*) | Immutable; operations create new object |
Async & Concurrency
The concurrency model is based on POSIX threads (pthreads).
spawn creates a real OS thread. Channels internally use
a linked-list queue with mutex and condition variable.
Task lifetime, deadlines and cancellation
scope { ... } owns its lexical spawns. A plain spawn belongs to
its spawning thread's lifetime; that thread joins its remaining children
before finishing. On process exit, remaining joinable children are cancelled
and joined. Failures are rethrown by await, or at scope exit if
unobserved; additional failures are reported on stderr. A body's own error
wins. Repeated awaits cache the same result or error while the owner lives.
After the owning scope closes, handles expire and further use fails visibly.
Keep tasks inside their owner; return the awaited value instead of a task.
try {
scope {
taskSetDeadline(250) // Duration from now, in monotonic milliseconds.
const task: Task<String> = spawn Main::load()
println(await task)
}
} catch (e: String) { println(e) }
| Operation | Contract |
|---|---|
taskCancel(task: Task<T>) | Request cooperative cancellation of a task and its descendants. |
taskSetDeadline(ms: Int64) | Set the current scope's deadline. Ancestor deadlines still apply; 0 expires immediately. Negative durations fail visibly. |
taskCheckCancellation() | Checkpoint for CPU loops. Throws Task cancelled, Task deadline exceeded, or Task service shutdown requested. |
taskWaitReadable(fd: Int64) | Cancellable readiness wait for a real file descriptor; use before a single-reader accept/read. |
taskDetach(task: Task<T>) | Explicit fire-and-forget. Only the owning thread may detach an unjoined task. It loses inherited scope cancellation/deadlines, cannot be awaited, and any uncaught failure terminates the process visibly. |
taskRequestShutdown() | Process-wide cooperative stop request for service lifecycle/control code. Bootstrap awaits stop waiting; shutdown joins outstanding joinable tasks and preserves genuine failures. |
Cancellation also wakes sleep/sleep_ms, blocked
channel recv, and await (check latency about 10 ms,
excluding scheduling). Tokens propagate to child tasks and nested scopes.
Scope failure cancels unfinished siblings; body errors remain primary.
An explicitly cancelled task still reports cancellation unless awaited and
caught. Expected cancellation during process shutdown is successful.
Cancellation uses normal exception unwinding: finally and
defer run in their usual order; a child's transaction rolls back
before its join completes. Transactions are thread-local and never inherited.
Put a scope inside a transactional method to join before commit, and put
parent resource cleanup outside the scope so children finish first.
No asynchronous thread termination is used. Native blocking I/O, database
calls and CPU loops without checkpoints are not automatically interruptible.
Use resource-specific stop/close operations or taskWaitReadable;
after five seconds of ignored cancellation during scope shutdown, the runtime
fails visibly instead of hanging silently. Explicit await of a cancelled task and
a service shutdown request have the same five-second watchdog. With @Service, its configured common shutdown deadline replaces
the standalone five-second shutdown watchdog; see the service lifecycle below.
Detached workers are explicitly outside structured joining.
Legacy Int64 handle storage/await remains temporarily compatible
for published packages. New code and task-control operations require
Task<T>. Invalid/expired handles and cyclic awaits fail with a
diagnostic; integer handles are deprecated.
Coordinated service lifecycle
Add @Service(startupMs, shutdownMs) to class Main to
enable coordinated startup, health reporting and graceful shutdown. Both durations
are optional positive integer milliseconds (defaults: 30000 and 5000; maximum:
3600000). The coordinator starts before database initialization and eager DI.
Existing programs opt into this contract explicitly.
@ApplicationComponent
@Synchronized
class Resources {
@OnStart
fn start() -> Nothing { /* Initialize resources before listeners start. */ }
@Readiness
fn ready() -> Bool { return true }
@Liveness
fn live() -> Bool { return true }
@OnStop
fn stop() -> Nothing { /* Dispose resources after callbacks and tasks finish. */ }
}
@Service(30000, 5000)
class Main {
fnc main() -> Int32 { return 0 }
}
Import the component and annotated controllers/endpoints from the entry file.
Hooks are synchronous instance methods without parameters, on non-generic
@ApplicationComponent/@Startup classes without inheritance.
Start hooks run after injected dependencies' hooks, then by class/method name;
stop hooks run in reverse order. Each hook joins its spawned children before
the next step. All initialized owners receive stop hooks even after a startup
failure; cleanup continues after errors, preserves the first failure and exits
nonzero. Protect mutable state shared with health checks and callbacks using
@Synchronized.
States are initializing → starting → ready, with unready
for failed health contributions, then draining → stopping → stopped
(or failed). Readiness requires completed initialization/start hooks,
bound required listeners, consumer handshakes and successful readiness/liveness
contributions. Returning from Main is not a readiness barrier; with listeners,
it leaves the service running. Without listeners, returning requests shutdown.
Generated HTTP and HTTP/3 servers reserve GET /health/live and
GET /health/ready: HTTP 200 or 503 with JSON state, readiness, liveness
and monotonic startedMs, readyMs, shutdownMs.
Started means managed bootstrap entry; ready is the first successful health pass,
not process creation or banner timing. Health contributions run periodically on
the coordinator and should be quick. Unready startup exceeding its budget fails
visibly. A later false health result changes health, without restarting the process.
serviceState(), serviceStatusJson(),
serviceIsReady(), serviceIsLive(),
serviceIsStopping() and the three timestamp getters
serviceStartedMs()/serviceReadyMs()/serviceShutdownMs()
expose the same state. serviceAwaitReady(ms) returns Bool;
serviceBoundPort(index) reports the actual port of an auto-run task
in bootstrap-plan order (-1 before binding or for a broker).
tinox explain --bootstrap Main.tnx --json includes deadlines,
hook order, tasks and shutdown policy.
SIGTERM, SIGINT, serviceRequestShutdown() and
taskRequestShutdown() mark the service unready and stop admission.
Accepted HTTP responses and broker acknowledgements finish; WebSockets send close
code 1001 and run OnClose. Listeners/workers join before remaining structured
children are cancelled/joined, reverse stop hooks run and database pools close.
serviceAwaitShutdown() lets Main wait for the stop request.
One common shutdown deadline covers every stage; expiry terminates the process
with status 1, including blocked native I/O or hooks. No asynchronous thread
cancellation is used. Explicitly detached user tasks remain outside structured joining.
Use an exec-form container entrypoint so signals reach tinox. Configure a startup
probe and readiness/liveness probes against the health paths, and give the
orchestrator a termination grace period longer than the shutdown budget.
CLI @Command and @TinoxUIApp entrypoints cannot currently
combine with @Service; compilation rejects them visibly.
Shared application state
@ApplicationComponent creates one lazy singleton per component class;
@Startup eagerly creates that same kind of singleton before main.
Generated providers serialize construction, field defaults, recursive dependency
injection and publication. Every concurrent provider caller sees the same fully
initialized instance. Providers do not invoke user constructors. This initialization
guarantee does not serialize later mutation.
HTTP workers and independently spawned REST, WebSocket and AMQP components can
execute concurrently. Put request data in @HttpRequestScoped owners,
and inject application services into them. Capturing a request-scoped dependency
in a singleton shares that object; refreshing a singleton controller's injected
field during dispatch is unsafe for per-request isolation. The compiler warns
about captive request state and exposed or unguarded mutable singleton fields.
@ApplicationComponent
@Synchronized
class Counter {
private var value: Int64 = 0
fn increment() -> Int64 {
this.value = this.value + 1
return this.value
}
}
@HttpRequestScoped
class Api {
@Inject var counter: Counter
@GET @Path("/count")
fn count() -> Int64 { return this.counter.increment() }
}
@Synchronized on a non-generic application/startup class protects
all instance methods; on an instance method it protects only that method. There
is one recursive monitor per declaring class, shared by every annotated
call and every instance of that class. Nested calls on the same thread are safe.
Static methods are excluded from class-level synchronization; explicitly annotating
a static method, generic method, unscoped class, interface/trait method or class
with inheritance is rejected. This deliberately limited contract prevents silent
gaps in generated locking.
The monitor is held across the method's defer/finally cleanup and transactional commit or rollback, and released before returning a result or error. Cancellation while waiting for a monitor throws without executing the body; cancellation inside it unwinds the body and releases the monitor. Existing errors are preserved during cleanup. Dev UI state snapshots use the class monitor when one is present.
Keep state private and protect reads as well as writes. Returned mutable objects,
direct field access, static methods and unannotated methods bypass the monitor.
Take snapshots inside a protected method instead of returning internal collections.
Do not await a child that needs the monitor while holding it, and acquire different
class monitors in a consistent order. Reentrancy applies to one thread, not spawned
children. For smaller explicit critical sections, use
tinox.core.semaphore locks with defer immediately after a
successful acquisition; these native blocking locks do not become cancellable.
Annotations and diagnostics do not prove universal race freedom.
HTTP Server (Low Level)
The HTTP server in the runtime uses:
- SO_REUSEPORT Multiple threads bind the same port – the kernel distributes connections
- epoll Event multiplexing: one thread manages up to 4,096 simultaneous connections
- TCP_NODELAY Nagle algorithm disabled – immediate sending of small packets
- Keep-Alive HTTP/1.1 keep-alive, idle timeout 500ms
Type System
Primitive Types
| Tinox Type | Bit | LLVM-IR | Description |
|---|---|---|---|
Int8 / Int16 / Int32 / Int64 | 8–64 | i8–i64 | Signed integers |
UInt8 / UInt16 / UInt32 / UInt64 | 8–64 | i8–i64 | Unsigned integers |
Float32 / Float64 | 32/64 | float/double | IEEE 754 floating-point numbers |
Bool | 1 | i1 | Boolean value |
Char | 32 | i32 | Unicode code point |
String | – | i8* | UTF-8 string (C string) |
Nothing | – | void | No return value (equivalent to void) |
Never | – | void | Function never returns (throw, exit) |
s.chars() returns List<String> containing one
Unicode scalar value per element. Use for ch in s.chars() to
process complete UTF-8 characters. Combining marks and emoji sequences may
span several elements: this does not normalize text or segment graphemes.
Empty input yields an empty list; malformed UTF-8 aborts with its byte
offset. Length, indexing, charAt, and direct string iteration
retain their byte semantics. The operation takes O(bytes) time and allocates
a list plus one short string per scalar value.
Composite Types
const numbers: List<Int64> = [1, 2, 3] // Array
const ages: Map<String, Int32> = @{"Alice" => 30} // Map/Dictionary
const pair: (Int32, String) = (42, "hello") // Tuple
const name: String? = null // Nullable
const fn add: fn(Int32, Int32) -> Int32 = |a, b| a + b // Function type
Generics
Tinox supports parametric polymorphism via monomorphization: a separate function is generated for each concrete type – like C++ templates or Rust. There is no runtime overhead from type erasure (as in Java).
fn max<T>(a: T, b: T) -> T
{
if a > b { return a }
return b
}
class Box<T>
{
var value: T
}
Object Orientation
Tinox is a hybrid language – object-oriented with classes, inheritance, and interfaces, but procedural free functions and functional concepts (lambdas, higher-order functions) are also possible.
Classes
class Animal
{
var name: String
var age: Int32
fn speak() -> String // instance method
{
return "..."
}
fnc create(name: String) -> Animal // static method (fnc)
{
return Animal { name: name, age: 0 }
}
}
class Dog extends Animal // inheritance
{
fn speak() -> String
{
return "Woof!"
}
}
Interfaces & Polymorphism
interface Serializable
{
fn serialize() -> String
}
class User implements Serializable
{
var name: String
fn serialize() -> String
{
return "User:" + this.name
}
}
Interface dispatch is implemented via vtables – identical to C++ virtual functions. Every object that implements an interface receives a pointer to a table of function pointers.
Absence and recoverable errors
For new library APIs, use Option<T> for absence and
Result<T> for expected failures. Import
tinox.core.option and tinox.core.result.
Some("") means present-empty; None means absent.
Check isSome()/isNone() or choose an explicit
fallback with unwrapOr(). Do not add a nullable
T? alternative for the same lookup.
Result<T> retains its String error. Use
isOk()/isErr() and getError();
preserve operation, target and cause without exposing secrets. Do not
infer status from message contents. APIs needing stable error categories
should use documented domain outcome fields. A generic typed-error
Result and allocation changes are deferred.
Exceptions remain appropriate for documented precondition, programming
and setup failures. Option.unwrap() on None throws
"Option is None"; Result.unwrap() on Err throws
the original error. Callback exceptions propagate through combinators.
Use finally for cleanup on normal and exceptional exits.
The additive HTTP methods findHeader, findParam,
findQuery and QueryString::find return
Option<String>. Headers are case-insensitive;
path/query names are case-sensitive. Queries keep raw values, the last
duplicate, and present-empty bare keys. Existing String getters keep
their empty fallback without deprecation warnings. Use a package version
that provides the optional methods, or matching workspace sources. See the API inventory
and migration guide for examples and legacy I/O limitations.
The current checkout also provides checked filesystem alternatives:
Fs::tryReadFile, tryWriteFile, tryAppendFile,
tryCopyFile, tryMoveFile and tryDeleteFile.
Their IoResult<T> keeps status and payload together;
failure() exposes operation, target, OS code and cause.
An empty read is success; partial reads and failed flushes are failures.
Migration and compatibility contracts.
ByteStream and Http1Stream provide bounded,
deadline-aware TCP/TLS streaming with explicit ownership and cleanup.
HTTP/1.1 streaming supports one Content-Length exchange; HTTP/2/3 body
bridges preserve binary bytes but still buffer whole bodies.
Binary and streaming contracts.
Keyword Overview
| Keyword | Meaning |
|---|---|
fn | Instance method (has implicit this) |
fnc | Static method (no this, called via class name) |
const | Immutable local variable (let is still accepted as a deprecated alias) |
var | Mutable local variable or field |
class | Class declaration |
interface | Interface declaration (like Java interface) |
enum | Enumeration type |
extends | Single inheritance |
implements | Interface implementation |
spawn | Start a new thread; the expression has type Task<T> (T = the function's return type) |
await | Wait for a Task<T> and yield its T (String, Float64, Bool, classes, ... not just Int64); awaiting the same task again returns the same result. Task is a built-in type name and can no longer be used for your own classes |
scope { ... } | Structured concurrency: every spawn lexically inside the block belongs to it, and leaving the block (normally, via return/break or throw) joins them all. A child failure nobody awaited is re-thrown at the block's end; if the block itself threw, its error wins and child failures are reported on stderr. A throw inside a spawned function is re-thrown by await. scope is contextual, not a reserved word. A spawn in a closure inside the block is not owned by it |
defer | Execute code when leaving the scope |
match | Pattern matching expression |
Annotations
The annotation system is one of the core strengths of Tinox. Annotations control the behavior of the compiler, the HTTP server, and future frameworks – similar to Java/Spring or Quarkus.
HTTP REST Annotations
Every parameter of a @GET/@POST/etc. handler needs
exactly one binding annotation: @PathParam/@QueryParam
bind by the parameter's own name to the same-named :name path
segment or query string key, @PostParam deserializes the whole
JSON request body, @HttpContext gives access to the request/
response. The return type decides the response mode:
HttpContext means manual mode (the method builds
ctx.response itself), any other type gets auto-serialized as JSON.
// Auto-serialize: the return value becomes the JSON response body
@GET
@Path("/users/:id")
@Produces(MediaType.APPLICATION_JSON)
@Auth("bearer")
fn getUser(@PathParam id: Int64) -> User { ... }
// Manual mode: dynamic status code (e.g. 404), @HttpContext + -> HttpContext
@GET
@Path("/users/:id")
fn getUserOrNotFound(@PathParam id: Int64, @HttpContext ctx: HttpContext) -> HttpContext
{
// ... ctx.response.status(404).json(...) or ctx.response.json(...)
return ctx
}
Migrating from the old REST API
Before this version, every handler implicitly got exactly one unannotated
ctx: HttpContext parameter, return type Nothing, and
had to read path/query parameters and the request body manually via
ctx.request.getParam(...)/ctx.request.getQuery(...)/
Json::deserialize<T>(ctx.request.body). That shape no longer
compiles — there is no backward compatibility, every parameter now needs one
of the four binding annotations explicitly.
| Before | After |
|---|---|
fn getUser(ctx: HttpContext) -> Nothing |
fn getUser(@PathParam id: Int64) -> User |
fn createUser(ctx: HttpContext) -> Nothing |
fn createUser(@PostParam u: User) -> User |
A dynamic status code (e.g. the same route answering 200
sometimes and 404 other times) can't be expressed with a fixed return
type — manual mode stays available for that: @HttpContext ctx:
HttpContext as a parameter, -> HttpContext as the
return type, ending in return ctx. |
|
Serialization
@JsonSerializable
class User
{
var id: Int64
@JsonField("first_name") // Overrides the JSON key
var firstName: String
@Sensitive // Output as "***" in logs
var password: String
@DoNotSerialize // Excluded from JSON
var internalToken: String
}
All Built-in Annotations
| Annotation | Target | Description |
|---|---|---|
@GET @POST @PUT @PATCH @DELETE | Method | HTTP verb for REST route |
@Path("...") | Class / Method | URL path (supports :param) |
@Produces(MediaType.*) | Method | Response content type |
@Consumes(MediaType.*) | Method | Request content type |
@StatusCode(200) | Method | Default HTTP status code |
@PathParam | Parameter | Binds a :name path segment (same name as the parameter) — String/Int64/Int32/Bool/Float64/Float32 |
@QueryParam | Parameter | Binds a query string key (same name as the parameter) — same types as @PathParam |
@PostParam | Parameter | Binds the deserialized JSON request body — type must be @JsonSerializable |
@HttpContext | Parameter | Binds the request/response handle — type must be HttpContext; return type HttpContext switches on manual response mode |
@Auth("bearer") | Class / Method | Authentication required |
@JsonSerializable | Class | Generates toJson()/fromJson() |
@JsonField("name") | Field | Alternative JSON key |
@Sensitive | Field | Completely masked in logs |
@Masked | Field | Partially masked in logs |
@DoNotSerialize | Field | Exclude field from JSON |
@Log | Class | Automatically inject log field |
@Inject | Field | Provider-owned injection field; source reassignment is rejected |
@Provider("name") | Class | Names a concrete DI provider with an explicit scope |
@Qualifier("name") | Field | Selects an @Inject field's provider at compile time |
@Required / @Optional | Field | Presence/null contract for JSON request DTOs |
@Length(min, max) | Field | Inclusive Unicode-codepoint or list-element count bounds |
@Range(min, max) | Field | Inclusive numeric request-DTO bounds; @NotNull remains a database annotation |
@Config("key") | Field | Value from application.properties; values may contain ${ENV} / ${ENV:default} (environment variable, optional default; \${ = literal) |
@Synchronized | Class/method | Recursive class monitor for singleton instance methods |
@ApplicationComponent | Class | Singleton scope for DI |
@HttpRequestScoped | Class | One instance per concrete provider and synchronous REST request context |
@Test("desc") | Method | Unit test (tinox test) |
@inline | Function / Method | Forced LLVM inlining |
@deprecated("msg") | All | Deprecation warning |
@Command("name") | Class | CLI command |
@annotation | Class | Define custom annotation |
@Getter | Class / Field | Generates get<Field>() — for every field on a class, or just the annotated field on a field; never overrides an already hand-written method of the same name |
@Setter | Class / Field | Generates set<Field>(value) — same rules as @Getter |
@WebsocketEndpoint("/path"[, port]) | Class | WebSocket: generates an accept/message loop, spawned alongside class Main (multiple classes allowed, each its own port) |
@OnOpen | Method | WebSocket: called on new connection, signature fn(conn: Int64) -> Nothing |
@OnMessage | Method | WebSocket: called per text message, signature fn(conn: Int64, msg: String) -> Nothing. AMQP-1.0: called per received message, signature fn(msg: Amqp10Message) -> Nothing. AMQP-0-9-1: called per received message, signature fn(msg: AmqpMessage091) -> Nothing |
@OnClose | Method | WebSocket: called when the connection ends, signature fn(conn: Int64) -> Nothing |
@Amqp10Consumer(host, port, user, pass, address) | Class | AMQP-1.0: generates a connect/attach/receive loop, spawned alongside class Main (multiple consumer classes allowed, even against the same broker/port with different queues) |
@Amqp091Consumer(host, port, vhost, user, pass, queue) | Class | AMQP-0-9-1: generates a connect/open/qos/consume/receive loop, spawned alongside class Main (multiple consumer classes allowed); prefetch is fixed at 1 in v1 |
@TinoxUIApp@TinoxUIApp(httpPort)@TinoxUIApp(httpPort, wsPort) | Class | Tinox-UI: generates the HTTP shell/client-JS server on httpPort and a WebSocket accept loop on wsPort that calls the @View method and re-renders the component tree diff-based (only patches are sent, no full resend); at most one @TinoxUIApp class per program, with exactly one @View method. Both ports are optional: with no argument they are 8080/8081, with one argument httpPort and httpPort + 1. Every port a program binds is checked for collisions at compile time — two binders on one port do not fail loudly at runtime (SO_REUSEPORT), they silently split the traffic between them |
@View | Method | Tinox-UI: marks the method that builds/rebuilds the @TinoxUIApp's component tree, signature fn() -> Component; also serves as the fallback/404 view when @Route is used and the current route matches no pattern |
@Route("/path/:param") | Method | Tinox-UI: registers a fn() -> Component method inside a @TinoxUIApp class as that path's builder (Vaadin-style route dispatch). Requires a var currentRoute: String field; matched :name path segments are auto-assigned into a same-named field on the class, if one exists |
Current contracts: DI providers and request contexts, recursive request validation and HTTP-400 details.
Standard Library (tinox-core)
The standard library consists of over 55 modules written in Tinox itself.
Import: import tinox.core.<module>
HTTP & Web
http_server, rest, rest_framework,
http, websocket – complete HTTP/1.1 server with
routing, middleware, and content negotiation, plus a WebSocket server per
RFC 6455.
Messaging
amqp091 – AMQP-0-9-1 client (no broker) for
message-queue brokers like RabbitMQ: connect, channel, publish/consume.
amqp10 – standalone AMQP-1.0 client with a
connection/session/link hierarchy and credit-based flow control.
Data Formats
json, xml, csv, toml,
ini, base64, hex – serialization and
deserialization of common formats.
Mathematics
math, mathf, decimal,
complex – trigonometric functions, high-precision arithmetic,
complex numbers.
Collections
array, sort, set, queue,
stack, heap, graph, trie
– algorithms and data structures.
Strings & Regex
string, regex, fmt, tpl
– string manipulation, regular expressions, formatting, templating.
I/O & System
io, fs, env, process,
socket – file system, environment variables, processes,
low-level networking.
Concurrency
cron, events, pool,
semaphore, ratelimit, cache
– scheduling, event bus, thread pool, synchronization.
Cryptography
crypto, jwt, bcrypt
– hashing, JSON Web Tokens, password hashing.
LSP – Basics
The Language Server Protocol (LSP) is an open protocol developed by Microsoft in 2016 that standardizes the communication between a language server (which understands the programming language) and an editor (which shows the user interface).
The Problem Before LSP
Previously, every IDE had to implement its own plugins for every language: Eclipse plugin for Java, VS Code plugin for Java, IntelliJ plugin for Java – all separate implementations of the same logic. With n languages and m editors, n × m integrations were created.
LSP Solution: n + m Instead of n × m
With LSP you write one language server, and every LSP-compatible editor uses it automatically. The language server communicates via JSON-RPC over stdio with the editor.
Editor (Eclipse, VS Code, Neovim …)
│
│ JSON-RPC via stdin/stdout
▼
Language Server (tinox-lsp Binary)
│
│ Analyzes .tnx files
▼
Lex → Parse → Typecheck → Responses to editor
Important LSP Methods
| LSP Method | What the editor sends | What the server responds |
|---|---|---|
textDocument/hover | Cursor position | Markdown with type information |
textDocument/completion | Cursor position after . | List of completion suggestions |
textDocument/definition | Cursor on identifier | File + line of the definition |
textDocument/documentSymbol | Document URI | Hierarchical symbol list (Outline) |
textDocument/publishDiagnostics | – (server push) | Error positions + messages |
textDocument/didChange | New file content | Triggers re-analysis |
Tinox LSP Server
tinox-lsp is a complete LSP server implemented with
tower-lsp 0.20 (Rust) and Tokio as asynchronous
runtime.
Embedded Standard Library
All 26+ standard library modules are embedded directly into the LSP binary
using include_str!(). The server requires no installation
or configuration of file paths – it works immediately after copying
the binary.
Import Resolution for Diagnostics
When a .tnx file is opened, the LSP server automatically
resolves import declarations and loads the corresponding stdlib ASTs as
prelude. This way Json.parse() or HttpContext
are not marked as "undefined".
Dot Completion for Classes
When the user types Json.:
- The editor sends
textDocument/completionwith the offset after the dot - The LSP extracts the dot chain (
["Json"]) - It checks whether
Jsonis a variable (instance) or a class name - If class name: directly load the methods of
Jsonfrom the registry - Static methods (
fnc) are displayed with the hintfnc parse(...)
Technical Details
| Property | Value |
|---|---|
| Protocol | Language Server Protocol 3.17 (JSON-RPC via stdio) |
| Async Runtime | Tokio 1.x (multi-threaded) |
| Document Storage | DashMap (lock-free concurrent HashMap) |
| Stdlib embedded | Yes, via include_str!() at compile time |
| Diagnostics Mode | Full re-parse + typecheck on every save/change |
| Supported Editors | Eclipse (LSP4E), VS Code, Neovim, Emacs, … |
Eclipse Plugin
The Eclipse plugin tinox-eclipse connects the Eclipse IDE with the
tinox-lsp language server via the LSP4E framework –
the LSP client for Eclipse.
Features
Real-Time Error Highlighting
Syntax errors and type errors are underlined directly in the editor as soon as the file is saved.
Hover Information
Mouse over a function or variable: type signature and documentation appear in the tooltip.
Go to Definition
Ctrl+click or F3 jumps to the definition of a class, method, or function.
Code Completion
Ctrl+Space or after typing a dot, method and field suggestions appear.
Outline View
The Eclipse Outline View shows all classes, methods, and fields of the current file hierarchically.
Run (Ctrl+F11)
Direct compilation and execution of the current .tnx file
from within Eclipse.
Architecture
The plugin is an OSGi bundle that registers the
.tnx file extension via plugin.xml and instructs LSP4E to start the tinox-lsp process when
.tnx files are opened. Communication runs over stdin/stdout in JSON-RPC.
CLI Tools
The tinox binary is the central tool for all
development tasks:
| Command | Description |
|---|---|
tinox new <name> | Scaffold new project (folder structure, tinox.toml, main.tnx) |
tinox build <file> | Compile to native binary (release: -O3 + LTO) |
tinox run <file> | Compile and run immediately |
tinox dev <file> | Hot-reload mode: automatically recompile on file change |
tinox check <file> | Type check only, no binary generated |
tinox test <file> | Run all @Test-annotated methods |
tinox fmt <file> | Format source code (AST → formatted text) |
tinox doc | Generate HTML documentation from docstrings |
tinox graph [file] | Generate a Mermaid call-graph diagram from REST/WebSocket/AMQP/CLI entry points |
tinox repl | Interactive REPL session |
tinox add … | Add package dependency |
tinox install | Download and install dependencies |
HTTP Server & Performance
The integrated HTTP server is optimized for maximum throughput at minimal latency. It uses technologies also employed in production-ready systems such as Nginx and Envoy.
epoll – Event Multiplexing
Instead of blocking a thread for each connection, the server registers all
active connections in an epoll file descriptor. One
epoll_wait() call delivers all connections that have readable data
– in a single system call.
// Concept (simplified):
epoll_fd = epoll_create1(0)
// Register all active connections...
while (true)
{
events = epoll_wait(epoll_fd, max_events, 50) // 50ms Timeout
for event in events
{
if event.fd == server_socket { accept() }
else { handle_request(event.fd) }
}
}
SO_REUSEPORT – Kernel Load Balancing
With SO_REUSEPORT, multiple threads (or processes) can listen
on the same port simultaneously. The Linux kernel automatically distributes incoming connections
to available listeners – without locks, without bottlenecks.
Thread-Local Buffers
Each HTTP worker thread has its own static buffer for request data, response data, and JSON parsing. These buffers are never freed and are reused – this completely eliminates malloc/free in the critical request path.
Concurrency Budgets & Overload Handling
Worker and connection counts are bounded and container-aware. The effective
CPU count is the smaller of the process's CPU affinity (taskset,
cpuset) and the cgroup CPU quota (v1 and v2, e.g. docker run --cpus=1.5
gives 2), not the host's core count. Each limit can be overridden with an
environment variable; a malformed value aborts startup. The effective values
are printed in the startup banner (Limits: ...) and exported on
/metrics together with open/peak/rejected counters.
| Variable | Default | Effect when exhausted |
|---|---|---|
TINOX_CPUS | detected | – |
TINOX_HTTP_WORKERS | CPUs | – |
TINOX_MAX_CONNECTIONS | 4096 (max 4096) | HTTP/1: 503 Service Unavailable with Retry-After: 1 |
TINOX_MAX_TASKS | 4096 | spawn returns a task whose await (or enclosing scope) throws a "task budget exhausted" error |
TINOX_MAX_WS_CONNECTIONS | 1024 | the WebSocket connection is closed immediately |
TINOX_QUEUE_WAIT_MS | 0 (no queueing) | how long spawn / a WebSocket accept may wait for a free slot before being rejected |
Every setting can also be placed in application.properties as
tinox.limits.<name> (e.g. tinox.limits.max_tasks=256);
the environment variable wins. The Dev UI API exposes the effective values and live
counters at /limits. HTTP/1 rejects immediately (the epoll workers must not block);
the HTTP/2, HTTP/3 and TLS accept loops are single-threaded by design, so their backlog is
bounded by the kernel listen queue and they never grow threads. AMQP connections use
spawn and therefore count against TINOX_MAX_TASKS.
A rejected request is never retried automatically.
Bind address: by default every listener (HttpServer, @TinoxUIApp, WebSocket, HTTP/2, SSE) binds 0.0.0.0. TINOX_BIND_ADDRESS=127.0.0.1 restricts them to one IPv4 address (e.g. loopback-only for local dashboards); an invalid value aborts startup.
Benchmark: Tinox vs. Quarkus
A sort REST endpoint was measured under identical conditions against Quarkus (Java, native image):
| Metric | Tinox | Quarkus | Difference |
|---|---|---|---|
| Throughput | 26,700–27,000 req/s | 24,867–25,541 req/s | +5–7% |
| Latency (mean) | 0.44–0.45 ms | 0.52–0.55 ms | ~17–20% better |
| Error rate | 0 % | 0 % | – |
Test conditions: 5,000 requests, concurrency 20, list with 20 numbers per request, Linux x86_64.
Pros & Cons
✓ Advantages
- Very low latency and high throughput thanks to LLVM + epoll
- Boehm GC – automatic memory management without borrow-checker complexity
- Low startup time (<1ms) – ideal for serverless/Lambda
- Java-like syntax – low entry barrier for backend developers
- Annotation-driven REST: minimal boilerplate
- Complete tooling: LSP, Eclipse plugin, REPL, formatter
- Standard library written in the language itself
- Small binary footprint – no framework as runtime dependency
- Embedded stdlib in the LSP – no configuration required
- Cross-platform via LLVM (Linux, macOS, Windows possible)
✗ Disadvantages
- Conservative GC (Boehm) – can be somewhat slower than manual management for pointer-intensive workloads
- Small ecosystem – no mature third-party libraries
- No standardized specification – language changes quickly
- Compiler errors not yet as helpful as in Rust or Go
- No complete module system with real separation between projects
- No borrow-checker safety – use-after-free theoretically possible (but GC prevents leaks)
- Long compile times due to LLVM (for large projects)
- No Windows support (epoll is Linux-specific)
- No stable releases yet – pre-1.0
- Thin community and documentation so far
Comparison: Memory Safety
Tinox gives the developer full control over memory – like C –, but does not offer static guarantees like Rust's borrow checker. This means:
| Language | Memory Model | Safety | Performance |
|---|---|---|---|
| Java / Kotlin | GC | High (no UAF) | GC pauses |
| Rust | Ownership + Borrow-Checker | Very high (compile-time) | Maximum |
| Go | GC (concurrent) | High | Good |
| Tinox | Boehm GC (conservative) | GC prevents leaks | Very high |
| C/C++ | Manual | Developer responsibility | Maximum |
Use Cases
High-Performance REST APIs
Microservices with very low latency requirements (trading, real-time systems). Annotation-driven routing, no framework overhead.
Serverless / Edge Computing
Cold start <1ms thanks to native binary without JVM or Node.js. Ideal for AWS Lambda, Cloudflare Workers (native mode), edge functions.
System Tools & CLIs
With @Command/@Option/@Argument
CLI tools can be developed with minimal effort. Native binary without
runtime dependencies.
Data Processing Pipelines
Batch processing of large data volumes with automatic GC – no manual memory management required, yet high performance.
Security-Critical Services
Deterministic runtime behavior through conservative GC with predictable behavior. Annotations for sensitivity and masking built in.
Teaching & Research
Small, readable compiler codebase. Ideal for learning compiler construction, LLVM IR, and LSP development.
Less Suitable For…
- Frontend development (no browser target, no WASM focus)
- Data science / ML (no NumPy equivalent, no notebook integration)
- Mobile apps (no iOS/Android framework)
- Projects that require a huge package ecosystem (npm, Maven)
Comparison with Other Languages
| Feature | Tinox | Java / Quarkus | Go | Rust | C |
|---|---|---|---|---|---|
| Compilation | AOT → native | JVM / AOT (native image) | AOT → native | AOT → native | AOT → native |
| Garbage Collector | Yes (Boehm GC) | Yes (G1GC, ZGC) | Yes (concurrent) | No (Ownership) | No |
| Memory Safety | GC-protected (no leaks) | GC-protected | GC-protected | Compile-time | Manual |
| Syntax Style | Java-like | Java | C-like | C-like | C |
| REST Annotations | Built-in | JAX-RS / Quarkus | No standard | No standard | None |
| Startup Time | <1ms | JVM: 1–3s; Native: ~10ms | ~5ms | <1ms | <1ms |
| IDE Tooling | LSP + Eclipse | Very mature | Good (gopls) | Very good (rust-analyzer) | Medium (clangd) |
| Ecosystem | Small (growing) | Huge (Maven) | Good | Good (crates.io) | Large (mature) |
| Concurrency | Threads + Channels | Virtual Threads (Java 21) | Goroutines | async/await + Threads | pthreads |
| Backend LLVM | Yes | No (JVM / Graal) | No (custom) | Yes | Optional (clang) |
http_server
Typed JSON responses with dynamic status codes are available as Response<T>. See the typed response and OpenAPI contract guide for checkout-library setup and the annotation-first CRUD example.
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/http_server/1-0-4/docs.html
Request body size cap: 4 MiB (TINOX_MAX_BODY in runtime.c). A request whose Content-Length exceeds this is rejected up front with 413 Payload Too Large and the connection is closed — the handler is never invoked, and the body is never silently truncated and processed as if it were complete (issue #174).
http2_server
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/http2_server/1-0-2/docs.html
http3_server
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/http3_server/1-0-2/docs.html
websocket
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/websocket/1-0-3/docs.html
sse
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/sse/1-0-1/docs.html
amqp091
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/amqp091/1-0-3/docs.html
amqp10
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/amqp10/1-0-2/docs.html
smtp
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/smtp/1-0-1/docs.html
redis
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/redis/1-0-1/docs.html
hpack
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/hpack/1-0-1/docs.html
http
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/http/1-0-3/docs.html
rest.client & rest.server
rest is published as a single package covering both the client and the server API. Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/rest/1-0-3/docs.html
socket
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/socket/1-0-0/docs.html
kubernetes
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/kubernetes/1-0-3/docs.html
ui
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/ui/1-0-13/docs.html
jwt
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/jwt/1-0-2/docs.html
oauth2
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/oauth2/1-0-2/docs.html
oidc
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/oidc/1-0-2/docs.html
json
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/json/1-0-0/docs.html
msgpack
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/msgpack/1-0-1/docs.html
xml
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/xml/1-0-1/docs.html
yaml
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/yaml/1-0-2/docs.html
csv
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/csv/1-0-1/docs.html
toml
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/toml/1-0-1/docs.html
ini
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/ini/1-0-1/docs.html
base64
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/base64/1-0-0/docs.html
hex
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/hex/1-0-0/docs.html
encoding
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/encoding/1-0-0/docs.html
unicode
A String stays a byte sequence (len(), s[i], substring() count bytes). tinox.core.unicode adds the code point unit explicitly and optionally; grapheme clusters and normalization (NFC/NFD) are not provided. Malformed UTF-8 is never replaced: it raises a catchable error naming the first invalid byte offset.
| Call | Meaning |
|---|---|
Unicode::isValid(s) | well-formed UTF-8 (no overlong forms, surrogates, values above U+10FFFF) |
Unicode::codePointCount(s) | number of code points; "e" + U+0301 is two |
Unicode::codePoints(s) / fromCodePoints(list) | the scalar values as List<Int64> and back; NUL, surrogates and out-of-range values throw |
Unicode::byteOffset(s, index) | byte offset of code point index (to cut with substring on code point boundaries) |
Unicode::toUpper(s) / toLower(s) | full default mapping (ß → SS), no locale; capital sigma always becomes σ (no final-sigma rule) |
Costs: every call is O(bytes) and validates first; codePoints allocates 8 bytes per code point, toUpper/toLower up to 3x the input. The tables match Unicode::version() (16.0.0) and are regenerated with scripts/gen-unicode-case.py.
array
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/array/1-0-0/docs.html
collections
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/collections/1-0-0/docs.html
heap
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/heap/1-0-0/docs.html
queue
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/queue/1-0-0/docs.html
set
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/set/1-0-0/docs.html
sort
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/sort/1-0-0/docs.html
iter
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/iter/1-0-0/docs.html
graph
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/graph/1-0-0/docs.html
trie
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/trie/1-0-0/docs.html
math
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/math/1-0-0/docs.html
mathf
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/mathf/1-0-0/docs.html
mathx
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/mathx/1-0-0/docs.html
complex
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/complex/1-0-0/docs.html
decimal
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/decimal/1-0-0/docs.html
random
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/random/1-0-0/docs.html
string
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/string/1-0-0/docs.html
fmt
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/fmt/1-0-0/docs.html
format
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/format/1-0-0/docs.html
regex
Current workspace: Regex methods throw catchable errors for invalid patterns; isValid and WithOptions variants are available. Current Regex contract. The embedded page describes the published package version.
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/regex/1-0-0/docs.html
uri
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/uri/1-0-0/docs.html
Current workspace additions: metadata, atomic rename, and safe directory operations.
fs
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/fs/1-0-0/docs.html
io
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/io/1-0-0/docs.html
env
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/env/1-0-0/docs.html
process
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/process/1-0-0/docs.html
debug
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/debug/1-0-0/docs.html
zip
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/zip/1-0-2/docs.html
compress
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/compress/1-0-1/docs.html
asm
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/asm/1-0-0/docs.html
bitmap
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/bitmap/1-0-1/docs.html
tpl
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/tpl/1-0-1/docs.html
crypto
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/crypto/1-0-2/docs.html
hash
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/hash/1-0-0/docs.html
validation
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/validation/1-0-0/docs.html
uuid
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/uuid/1-0-0/docs.html
semaphore
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/semaphore/1-0-0/docs.html
pool
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/pool/1-0-0/docs.html
ratelimit
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/ratelimit/1-0-1/docs.html
cron
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/cron/1-0-0/docs.html
events
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/events/1-0-0/docs.html
time
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/time/1-0-0/docs.html
cache
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/cache/1-0-0/docs.html
option
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/option/1-0-0/docs.html
result
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/result/1-0-0/docs.html
logger
Request correlation, structured logging and OTLP tracing (development reference)
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/logger/1-0-0/docs.html
db
Full reference (description, dependencies, examples, classes & methods) is loaded directly from the published tinox-central package documentation, so it does not have to be maintained in two places: docs/tinox-core/db/1-0-0/docs.html
Only the query-chain classes (DB, EntityQuery) themselves — @Entity/@Table/@Column/@Id/@GeneratedValue, the connection pool, and @Transactional (since issue #191) are compiler/codegen features, not stdlib classes; see the ORM & Lambda Query DSL guide further down for their full reference.
ORM & Lambda Query DSL
Tinox provides a type-safe ORM inspired by JPA/QueryDSL. Classes are annotated as database tables, and all queries run through a type-safe lambda DSL — no string-based queries.
Annotations
| Annotation | Target | Description |
|---|---|---|
@Entity | Class | Marks the class as a database entity |
@Table("name") | Class | Sets the table name (default: lowercase class name) |
@Id | Field | Primary key |
@Column("col") | Field | Maps field to a DB column (default: field name) |
@GeneratedValue | Field | ID generated by the DB (AUTO_INCREMENT / SERIAL) |
@NotNull | Field | Column is NOT NULL |
Entity Definition
@Entity
@Table("users")
class User {
@Id @GeneratedValue
var id: Int64
@Column("name")
var name: String
@Column("age")
var age: Int64
@Column("active")
var active: Bool
}tinox.toml – [database] Configuration
# PostgreSQL
[database]
driver = "postgres"
url = "postgres://user:pass@localhost:5432/mydb"
pool = 10
# MySQL / MariaDB
[database]
driver = "mysql"
url = "mysql://user:pass@localhost:3306/mydb"
# SQLite (no DB server required)
[database]
driver = "sqlite"
url = "sqlite:///path/to/database.db"Lambda Query DSL
// All users over 18, sorted alphabetically, max 10 results
const adults: List<User> = DB::of(User)
.filter(u -> u.age > 18)
.orderBy(u -> u.name)
.limit(10)
.list()
// Read a single user
const alice: User = DB::of(User)
.filter(u -> u.name == "Alice")
.first()
// Count active users
const n: Int64 = DB::of(User)
.filter(u -> u.active == true)
.count()Lambda → SQL Translation Table
| Lambda Expression | SQL Fragment |
|---|---|
u.age > 18 | age > $1 |
u.name == "Alice" | name = $1 |
u.name != "Bob" | name != $1 |
u.name.startsWith("A") | name LIKE 'A%' |
u.name.endsWith("z") | name LIKE '%z' |
u.name.contains("oo") | name LIKE '%oo%' |
expr1 && expr2 | (pred1) AND (pred2) |
expr1 || expr2 | (pred1) OR (pred2) |
!expr | NOT (pred) |
Save, Update, Delete
// INSERT (ID assigned by the DB)
const user: User = User {}
user.name = "Alice"
user.age = 30
const saved: User = DB::of(User).save(user)
// DELETE
DB::of(User).delete(saved)
// Save multiple entities
const list: List<User> = List::new()
list.add(user1)
list.add(user2)
DB::of(User).saveAll(list)Error Handling
Database errors are currently logged to stderr. Integration with try/catch is planned for a future release. Connection errors at startup terminate the program with a descriptive message.