Skip to content

Chuks v0.0.9 — Final 0.0.x Patch

Chuks v0.0.9 closes out the 0.0.x line. The next release, 0.1.0, will bump the minor version as we move toward the first public-beta surface area.

This is a polish-and-stabilize release across the compiler, CLI, stdlib, and runtime. Nine commits since v0.0.8, all 298 tests passing on both VM and AOT.

  • CLI installs prefer the registry’s mirrored tarballs with transparent fallback to the upstream Git archive
  • Multi-package AOT is more robust cross-module slice element conversion, sibling-subclass devirt, map-literal temp shadowing all fixed
  • New repository methods: createAndReturn, updateAndReturn single round-trip insert/update + read
  • Type-aware .auto() primary key inherits its underlying column type instead of always being INTEGER
  • time.toISO is now UTC in both VM and AOT
  • crypto.sha256Raw / crypto.hmacRaw in AOT now return Buffer IDs, matching the VM contract
  • Better diagnostics: runtime panics report accurate source positions; the typechecker flags class-as-instance and unbound-method mistakes earlier
  • Cleaner shutdown with cancellable sleeps + launcher signal forwarding

CLI: registry-mirrored installs with fallback

Section titled “CLI: registry-mirrored installs with fallback”

When you publish a package to the Chuks registry, the registry now mirrors the release tarball to its own immutable, CDN-fronted storage. Starting in v0.0.9, the CLI prefers that mirror when installing:

Terminal window
$ chuks add my_package
Resolving my_package@^1.0.0...
Found my_package@1.2.3
Downloading from https://packages.chuks.org/.../package.tar.gz ...
Installed my_package@1.2.3

If the mirror is missing or temporarily unreachable, the CLI transparently falls back to the upstream Git archive no manual intervention, no failed installs:

Terminal window
Mirror unavailable (connection refused), falling back to upstream
Downloading from https://github.com/owner/repo/archive/refs/tags/v1.2.3.tar.gz ...
Installed my_package@1.2.3

This makes installs faster (CDN-cached, fewer GitHub round-trips), more reliable (mirror is immutable git tag -d && git tag -f can’t change what you installed yesterday), and backward-compatible with older registry deployments that don’t expose tarballUrl.

If you’ve been compiling larger projects to AOT, you may have hit one of these.

1. Cross-module slice element conversion. When a function in package A accepted a []User and you passed a []map[string]any from package B (e.g. raw rows from a DB query), the AOT compiler emitted code that panicked on the first element access. We now use the same __chuks_map_to_struct_ptr helper that already worked for scalar arguments.

2. Sibling-subclass devirt import cycles. Devirtualization was emitting type-assertion branches for every class in the program. When two sibling subclasses shared a base class with the same method name (e.g. ProjectRepo and ScenePresetPackRepo both extending Repository), each devirt site would force its package to import the other’s, producing an import cycle. We now skip foreign-module classes in devirt; the existing reflection fallback handles cross-package dispatch correctly.

3. Map-literal temp shadowing. A user parameter named _m (or any reserved-looking name) could be shadowed by the AOT compiler’s internal map-literal builder, silently corrupting the function’s local state. Reserved temp names are now namespaced.

None of these have a workaround in v0.0.8, they all required changes in the transpiler. Upgrade if you’re hitting any kind of multi-package AOT weirdness.

std/db: createAndReturn, updateAndReturn, type-aware .auto()

Section titled “std/db: createAndReturn, updateAndReturn, type-aware .auto()”

Three quality-of-life improvements to the repository API:

createAndReturn — insert + read in one round-trip

Section titled “createAndReturn — insert + read in one round-trip”
// Before, two round-trips
var result: any = await userRepo.create({"email": "alice@x.com", "name": "Alice"})
var user: any = await userRepo.findById(result["lastInsertId"])
// After, one round-trip, RETURNING-based
var user: User = await userRepo.createAndReturn({"email": "alice@x.com", "name": "Alice"})
println(user.id) // 1
println(user.createdAt) // populated by DB default

On Postgres, this uses INSERT ... RETURNING *. On SQLite/MySQL/MSSQL, it uses the equivalent native primitive (RETURNING on SQLite ≥3.35, LAST_INSERT_ID() round-trip on MySQL, OUTPUT INSERTED.* on SQL Server).

var updated: User = await userRepo
.where("id", 1)
.updateAndReturn({"name": "Alice Smith"})
println(updated.name) // "Alice Smith"

Returns the updated row(s). On dialects that don’t expose RETURNING for UPDATE, the repository transparently issues an update + select pair inside a single transaction.

schema.pk("id").auto() historically forced INTEGER. v0.0.9 lets .auto() inherit the column’s declared type:

const UserSchema = db.define<User>("users", (schema) => {
schema.pk("id").auto() // → BIGSERIAL on Postgres, INTEGER AUTOINCREMENT on SQLite
})
const OrderSchema = db.define<Order>("orders", (schema) => {
schema.bigint("id").primaryKey().auto() // explicit BIGINT auto-increment
})

This matters when an int (32-bit) auto-increment is genuinely too small for your domain .bigint(...) + .auto() now does the right thing instead of being silently downcast.

A few drift points between the bytecode VM and the AOT runtime have been smoothed out.

time.toISO now emits UTC ISO 8601 (YYYY-MM-DDTHH:MM:SSZ) in both modes. Previously the VM used time.RFC3339, which embeds the local timezone offset, so the same code produced different output on different hosts. Audit logs, JWT iat/exp claims, and DB timestamp columns now agree across machines:

println(time.toISO(time.now()))
// 2026-05-08T02:51:32Z ← always UTC, always Z-suffixed

crypto.sha256Raw and crypto.hmacRaw (AOT) now return Buffer IDs and accept Buffer inputs, matching the VM contract:

import { Buffer } from "std/buffer"
import { crypto } from "std/crypto"
var key = Buffer.fromString("secret")
var hash: Buffer = crypto.hmacRaw(key, Buffer.fromString("message"))
println(hash.length()) // 32 works identically on VM and AOT

If you’ve been using these in mixed-mode pipelines (e.g. develop on the VM, deploy with AOT), the round-trip now actually round-trips.

  • Accurate source positions for runtime panics. When an AOT binary panics, the stack trace now points at the original .chuks source line. Massively reduces the cognitive load of debugging production crashes.
  • Typechecker catches more class-shape mistakes using a class name where an instance is expected, or accessing an instance method as if it were unbound, now produce typed errors instead of cryptic runtime failures.
  • Graceful shutdown uses a cancellable sleep, and the AOT launcher forwards SIGINT/SIGTERM to the child process. Mid-request shutdown is now clean: in-flight requests complete, new requests are refused with a clean error, and the process exits with code 0.
  • os.exec in AOT runs commands via the shell, captures stderr separately, and returns the real exit code, bringing it in line with the VM’s behavior.

Everything you need from v0.0.7 / v0.0.8 still applies

Section titled “Everything you need from v0.0.7 / v0.0.8 still applies”

No breaking changes. Drop-in upgrade.

Terminal window
curl -fsSL https://raw.githubusercontent.com/chuks-programming-language/releases/main/install.sh | bash
chuks --version # 0.0.9

All 298 golden tests pass on both VM and AOT.

v0.1.0 is where Chuks starts treating its public surface area as semi-stable. The themes:

  • Phase 4 IR kind-tagged values, cross-module type tables, constant folding
  • Public-beta package registry at registry.chuks.org
  • Signed releases & reproducible builds every binary verifiable against a published manifest
  • AST-level permission scanner in the registry, replacing the regex layer
  • Capability diffs between versions, surfaced to package consumers at install time

The 0.0.x line was about getting the language to feel right. 0.1.x is about making the supply chain trustworthy enough to recommend Chuks to people you don’t know.

Thanks for following along. Next stop: 0.1.0.