Skip to content

§8 Compilation model

This chapter defines how Bynk sources are organised into a project, how a unit’s name relates to its file, and the pipeline that turns sources into type-correct TypeScript. Emission itself — what each construct compiles to — is §7.

A bynk.toml file at a directory’s root marks it as a project and configures its layout. Its keys:

TableKeyControls
[project]name, versionthe project’s name and version
[paths]srcthe directory holding source units
teststhe directory holding test units
outthe default output directory
[fmt]indent, max_line_widthformatter settings (consumed by bynkc fmt)
[lsp]diagnostics_modelanguage-server settings

Bynk compiles in one of two modes.

Project mode — a bynk.toml is present. Source units live under [paths].src and test units under [paths].tests. This is the mode that supports a src/tests split and bynkc test.

Legacy mode — no manifest. A single .bynk file compiles as one standalone unit. The project-only features — the src/tests split and bynkc test — require project mode.

In project mode a unit’s file path MUST mirror its qualified name. A context commerce.orders MUST live at src/commerce/orders.bynk, and a test commerce.orders at tests/commerce/orders.bynk. A path that does not match the declared name is rejected (bynk.project.inconsistent_commons_name, bynk.project.inconsistent_test_path), as is a name declared as both a commons and a context (bynk.project.kind_conflict). The source tree therefore mirrors the program’s architecture.

§8.4 Build pipeline & conformance to TypeScript

Section titled “§8.4 Build pipeline & conformance to TypeScript”

A build runs the pipeline lex → parse → resolve → check → emit. The first four stages establish well-formedness (§3§5); only a well-formed program reaches emission (§7).

Emission writes, into the output directory, the per-context and per-test modules, the composition root, the runtime library (§7.4), and a generated tsconfig.json. Every emitted module imports the runtime as ./runtime.js (or ../runtime.js by directory depth). The tsconfig.json enables strict and targets ES2022 with NodeNext module resolution.

First-party commons (v0.20b). Alongside the injected first-party adapters (the bynk surface and platform adapters, §7.3.6), the toolchain ships first-party library units inside the reserved bynk.* prefix: bynk.list and bynk.map, ordinary commons written in Bynk over the collection kernel (§5.10). A project that uses bynk.list (or bynk.map, which itself uses bynk.list) has the unit injected as a synthetic source file ahead of grouping; it then flows through the ordinary commons pipeline — tables, uses resolution, type-checking, and emission to bynk/list.ts / bynk/map.ts beside the other modules. Unconsumed, nothing is injected and the output is unchanged.

v0.22a adds bynk.string on the same path (uses bynk.stringbynk/string.ts): derived helpers written in Bynk over the string kernel (§5.2) — currently join(parts: List[String], sep: String) -> String. The kernel operations themselves are compiler built-ins, not commons functions.

A successful Bynk build emits TypeScript that is type-correct end to end: it compiles under tsc --strict with no errors. This is the final gate of the compilation model — a Bynk program’s well-formedness is realised, not merely asserted, in a type-checked TypeScript program. bynkc test continues past this gate, running the compiled, aggregated test runner on Node.

The deploy platform (--platform, values cloudflare — the default — and node) is a selection axis distinct from the emit topology (--target {bundle,workers}, §7.2): the target chooses how the output is laid out, the platform chooses which host the ambient surface binds to. The platform selects the first-party bynk binding module linked into the output (bynk-cloudflare.ts / bynk-node.ts, §7.3.6). Because the bynk contract names canonical provider symbols, changing platform changes only that one imported module; porting Bynk to a new host means supplying this one binding.

As of v0.19 the axis also carries the platform lock: a deployment unit whose closure reaches a platform-native capability MUST be built with the matching --platform (§5.8).

An adapter’s binding "<module>" path is resolved relative to the adapter’s source file; the module is copied verbatim into the output tree beside the adapter’s emitted module, so the composition root’s import resolves and the tsc --strict gate of §8.4 checks the binding’s implements contract. The union of all adapters’ pinned requires entries (§5.8) is emitted as the dependencies map of a generated package.json; a project with no requires emits none.