Project configuration
trbconfig.jsonc is the source of truth for the project target, source and
output directories, TypeRB packages, and native target dependencies. It
accepts line and block comments. Trailing commas are not allowed.
Example
{
// One target and package ecosystem for the whole project.
"name": "my-app",
"version": "0.1.0",
"mode": "go",
"sourceDir": ".",
"outDir": "build",
"copyFiles": true,
"packageManagement": "managed",
"packages": {
"acme/contracts": "v1.2.3"
},
"dependencies": {
"example.com/acme/library": "v1.2.3"
},
"devDependencies": {},
"go": {
"module": "example.com/my-app",
"version": "1.27",
"rootPackage": "main"
}
}
Use trb init to create a valid starting config:
trb init --mode go --module example.com/my-app .
trb init --mode ruby .
trb init --mode typescript .
Modes
A project declares one mode: go, ruby, or typescript. The mode selects
the backend, target toolchain, and package ecosystem. It does not select a
grammar variant or loosen portable type checking. Target-specific capabilities
require an explicit trb/platform/<mode>/* import.
Go mode owns go.mod, Ruby mode owns Gemfile and .ruby-version, and
TypeScript mode owns package.json. These files are deterministic views of
the native dependencies and devDependencies in trbconfig.jsonc. Edit
native dependencies through the config or trb add --native and
trb remove --native, then run trb sync.
go.version must be Go 1.27 or later. Generated Go uses native generic
methods introduced in Go 1.27, so projects targeting Go 1.26 are rejected.
TypeScript toolchain
New managed TypeScript projects use ESM, npm, and the latest compatible TypeScript 6 patch:
{
"devDependencies": {
"typescript": "^6.0.0"
},
"typescript": {
"packageManager": "npm",
"moduleType": "module",
"runtime": "node"
}
}
TypeScript 6 is the supported toolchain while the TypeScript 7 programmatic API and its surrounding package ecosystem stabilize. The range accepts compatible 6.x patch releases without moving a project to 7.x. Browser applications are the primary TypeScript use case; APIs tied to a particular runtime remain explicit platform packages.
After native dependencies are installed, trb install also indexes supported
.d.ts exports into .trb/native-types.json. TypeRB source can import those
configured packages directly; ordinary builds and completion use the cached
index and generated TypeScript keeps the original package specifier. Native
declaration indexing requires TypeScript 6.x and reports the installed version
when another major version is detected.
typescript.runtime selects browser, bun, or node and defaults to
node. browser projects compile source for a browser application or bundler
and cannot use trb run as a process entrypoint. Bun server projects can
select both runtime and package manager without additional manifest files:
{
"mode": "typescript",
"typescript": {
"runtime": "bun",
"packageManager": "bun",
"moduleType": "module"
}
}
When packageManager is omitted, Bun runtime projects default to bun; other
TypeScript projects default to npm. The two settings remain independent, so
an explicitly configured npm installation can still be executed by Bun.
TypeRB packages
packages declares portable TypeRB source packages. A short key defaults to a
GitHub repository, while the lock records its canonical manifest identity:
{
"packages": {
"acme/contracts": "v1.2.3",
"company/auth": {
"source": "gitlab.com/company/auth",
"version": "v2.0.0"
},
"local/widgets": {
"path": "../widgets"
}
}
}
acme/contracts resolves from github.com/acme/contracts by default and stays
the explicit source import. Full repository paths are also valid keys. Remote
packages accept an exact semantic-version tag, latest, or a Git revision.
latest is pinned until trb update is run. Local paths are development
inputs and are intentionally not content locked.
trb update PACKAGE... accepts direct keys from this packages map. It
re-resolves each selected package and its transitive graph while preserving the
locked graphs of unselected direct packages. The complete graph is re-resolved
when no package is named.
trb.lock records canonical package names, transitive dependencies, Git commit
IDs, and SHA-256 content checksums. Commit it to version control. Resolved
content lives below the ignored .trb/packages directory. Builds and the REPL
never contact a package source; run trb install after changing packages.
See the package guide.
Lint configuration
The optional lint section selects the small built-in ruleset and overrides
individual rule levels:
{
"lint": {
"preset": "recommended",
"rules": {
"trb/prefer-conditional-transfer": "warning",
"trb/omit-terminal-void-return": "warning"
}
}
}
preset is recommended by default. Set it to none to start with every
built-in rule disabled, then enable selected rules explicitly. A rule level is
off, warning, or error. Unknown rule IDs and invalid levels are
configuration errors rather than silently ignored options.
Built-in lint behavior ships with the trb binary and uses the TypeRB release
version; there is no separate linter version. Machine-readable lint output has
its own numeric diagnostic schema version so report consumers can negotiate
shape independently from rule behavior. See the linting guide.
Native package management
The default "packageManagement": "managed" lets TypeRB generate and use the
target manifest from dependencies and devDependencies.
When TypeRB is embedded in an application that already owns its manifest, set:
{
"packageManagement": "external"
}
trb build then generates source without reading or modifying the host
manifest. Native sync, add, remove, and install operations are disabled in
this mode. TypeRB source packages remain available because their lock and
source cache are independent of the target package manager.
Database schema workflow
The optional db section configures mode-independent schema commands:
{
"db": {
"adapter": "sqlite",
"database": "db/development.sqlite3",
"schema": "db/schema.sql",
"lock": "db/schema.lock.json",
"sqldef": {
"command": "sqlite3def",
"version": "3.11.19"
}
}
}
database may instead be { "environment": "DATABASE_URL" }. Adapter
defaults select sqlite3def, psqldef, or mysqldef and the sqldef version
supported by the current TypeRB release. sqldef.arguments adds project-owned
command options. See the database schema guide.
Job adapter composition
Projects using trb/jobs select an adapter through one typed composition
module:
{
"jobs": {
"configuration": "config/jobs"
}
}
The module returns the portable JobAdapter contract. The official SQL
adapter keeps database and worker choices outside application Job definitions:
import { JobAdapter } from trb/jobs
import { SQLAdapter, SQLDialect } from trb/jobs/sql
import { Duration } from trb/std/time
JOBS_ADAPTER: JobAdapter := SQLAdapter.new(
dialect: SQLDialect::PostgreSQL,
source: "postgres://localhost/jobs",
source_environment: "JOBS_DATABASE_URL",
poll_interval: Duration.seconds(1),
)
configuration is relative to sourceDir; a trailing .trb is optional.
Normal Job modules import only trb/jobs. Importing trb/jobs/sql in the
composition module also selects the target-native database dependency. The
initial compiler integration accepts one explicitly typed JOBS_ADAPTER
constant initialized directly with SQLAdapter.new(...); duration and scalar
options are compile-time values. The configuration module initializes this
application-scoped adapter once, and all generated enqueue wrappers reuse the
same instance. It is not a factory evaluated for each enqueue.
This keeps native dependency and generated SQL selection deterministic while
the external adapter protocol is still under development.
Derived Job enqueue methods call the configured JobAdapter through its
portable enqueue and enqueue_at methods. Portable generated TypeRB owns
payload serialization and relative scheduling validation; the adapter owns ID
generation, persistence, and native error mapping. Worker lifecycle remains a
separate bundled integration in this alpha contract.
Project entrypoint
A runnable project defines exactly one top-level def main(). main is a
language convention rather than a configurable entrypoint, so the config has no
entrypoint field. A library project may omit main.
In Go mode, source modules generated in another directory cannot import the
module that owns main(). The runnable module becomes the generated program
package, which Go does not allow another package to import. trb check and
trb build report this at the authored import. Move declarations shared with a
nested package into a separate module without main().
Local packages
Map a portable source-only directory into the project import graph with
localPackages:
{
"localPackages": {
"acme/contracts": "../../packages/contracts/src"
}
}
An index.trb file is the package entry module. Projects in different modes can
import the same package without copying its source. New reusable packages
should instead contain trbpackage.json and use a packages path requirement,
which exercises the same manifest and dependency graph as a remote package.
Ruby toolchain and loader
Ruby projects default to the current supported Ruby release and can select how generated imports are loaded:
{
"ruby": {
"version": "4.0.6",
"loader": "zeitwerk"
}
}
For a managed Ruby project, trb sync writes the configured version to both
Gemfile and .ruby-version. Bundler itself uses the version included with
that Ruby release.
The require_relative loader emits explicit relative requires. The zeitwerk
loader keeps project imports as compile-time dependencies without emitting
Ruby require calls.