@aptre/common

中文文档
0.38.2发布于 2026/11/08MITTypeScriptESM + CJS构建来源已验证近 30 天下载 2.4万

Common project configuration files and dependencies.

原始使用文档
文档目录23 节
  1. What is this?
  2. Key Features
  3. Supported Languages
  4. Stable Type IDs
  5. Rust Support via WASI
  6. Example Output
  7. Installation
  8. Quick Start
  9. CLI Commands
  10. TLA+ Specs
  11. How It Works
  12. Architecture
  13. C++ Support
  14. Configuration
  15. package.json Configuration
  16. aptre.languages
  17. aptre.rust
  18. aptre.tsTypedDecode
  19. aptre.tsTypedEncode
  20. aptre.tsImportBoundaries
  21. Related Projects
  22. Support
  23. License

common

GoDoc Widget npm Widget

Unified protobuf code generation for Go, TypeScript, C++, and Rust with WASM.

What is this?

This repository provides the aptre CLI — a single tool that generates protobuf code for four languages without requiring you to install protoc, protoc plugins, or language-specific toolchains. Everything runs via embedded WebAssembly modules using wazero.

Key Features

  • Zero native dependencies — protoc and plugins run as WASM, no installation required
  • Multi-language output — generates Go, TypeScript, C++, and Rust from a single command
  • Smart caching — only regenerates when source files actually change
  • Go-style imports — use familiar import paths in your .proto files

Supported Languages

LanguageMessage TypesRPC ServicesPlugin
Go*.pb.go*_srpc.pb.goprotobuf-go-lite
TypeScript*.pb.ts*_srpc.pb.tsprotobuf-es-lite
C++*.pb.cc/h*_srpc.pb.hpp/cppBuilt-in protoc + starpc
Rust*.pb.rs*_srpc.pb.rsprost (WASI) + starpc

Stable Type IDs

A message can declare a stable type identifier with the (typeid.id) option:

import "github.com/aperturerobotics/common/typeid/typeid.proto";

message CreateFile {
  option (typeid.id) = "fs/create-file";

  string path = 1;
}

For each proto file with such messages, aptre generate writes *_typeid.pb.go holding a CreateFileTypeID constant, a TypeID() method, and the file's []typeid.Type list (OpsTypes for ops.proto) pairing each identifier with a constructor. The generator runs inside aptre, so it needs no plugin binary. The generator rejects two messages in one file with the same identifier; an application that builds a registry from several lists checks the rest.

C++ builds that import the option also compile typeid/typeid.pb.cc; other languages ignore it.

Rust Support via WASI

The Rust code generation uses an embedded WASI build of protoc-gen-prost, meaning you don't need Cargo, rustc, or any Rust toolchain installed to generate .pb.rs files. The prost plugin runs entirely within the wazero WebAssembly runtime alongside protoc itself.

This is powered by go-protoc-gen-prost, which embeds a ~600KB WASM binary that implements the full prost protobuf code generator.

Example Output

Given a simple proto file:

syntax = "proto3";
package echo;

message EchoMsg {
  string body = 1;
}

The generated echo.pb.rs:

// @generated
// This file is @generated by prost-build.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct EchoMsg {
    #[prost(string, tag="1")]
    pub body: ::prost::alloc::string::String,
}

See starpc for a complete example.

Installation

# Run directly (no install needed)
go run github.com/aperturerobotics/common/cmd/aptre@latest generate

# Or install globally
go install github.com/aperturerobotics/common/cmd/aptre@latest

Quick Start

  1. Set up your project with Go and optionally TypeScript:
# Initialize Go module
go mod init github.com/yourorg/yourproject
  1. Create a proto file using Go-style imports:
syntax = "proto3";
package example;

// Import .proto files using Go-style import paths
import "github.com/aperturerobotics/controllerbus/controller/controller.proto";

message GetBusInfoResponse {
  repeated controller.Info running_controllers = 1;
}
  1. Generate code:
# Stage files for git (required for discovery)
git add -A

# Generate all languages
go run github.com/aperturerobotics/common/cmd/aptre@latest generate

# Generate only Go output
go run github.com/aperturerobotics/common/cmd/aptre@latest generate --language go

# Or with verbose output
go run github.com/aperturerobotics/common/cmd/aptre@latest generate --verbose

CLI Commands

CommandDescription
generateGenerate protobuf code (Go, TypeScript, C++, Rust)
generate --forceRegenerate all files, ignoring cache
cleanRemove generated files and cache
depsEnsure all dependencies are installed
lintRun golangci-lint
fixRun golangci-lint with --fix
testRun go test
test --browserRun tests in browser with WebAssembly
formatFormat Go code with gofumpt
outdatedShow outdated dependencies

TLA+ Specs

aptre-tla checks TLA+ specs with tla-ts, in Node or Bun with no Java. A spec Foo.tla is checked once per model config beside it: Foo.cfg and each Foo-<variant>.cfg. A .tla with no config is a helper module. Module names resolve from the spec's directory, then from the shared modules in tla/:

  • Surface.tla: rules every screen keeps for every person who can reach it. EveryShownActionWorks holds when every action a person sees succeeds for them; NobodyIsStuck holds when every person has something to do.
# Check every spec under the current directory
bunx aptre-tla
# Check one spec or one directory
bunx aptre-tla pkg/foo/Foo.tla

A failure prints the broken rule and the shortest path to it, one state per line, and the command exits 1. Projects add it to their check script as "check:tla": "aptre-tla". The @aptre/common/tla export offers findModels and checkModel for tools such as commit hooks.

How It Works

The aptre tool orchestrates code generation using embedded WebAssembly:

  1. Discovery — Finds .proto files matching your targets (default: ./*.proto)
  2. Caching — Checks .protoc-manifest.json to skip unchanged files
  3. Protoc (WASM) — Runs go-protoc-wasi to parse protos and invoke plugins
  4. Plugins — Native plugins for Go/TS, WASM plugin for Rust (prost)
  5. Post-processing — Fixes imports and formats output

Architecture

┌─────────────────────────────────────────────────────────────┐
│                         aptre CLI                           │
├─────────────────────────────────────────────────────────────┤
│                     wazero runtime                          │
├─────────────┬─────────────────────────┬─────────────────────┤
│ protoc.wasm │  protoc-gen-prost.wasm  │   Native Plugins    │
│  (parsing)  │     (Rust output)       │  (Go, TS, C++)      │
└─────────────┴─────────────────────────┴─────────────────────┘

C++ Support

C++ protobuf files (.pb.cc and .pb.h) are generated alongside other outputs. Add vendor/ to your include path:

# CMakeLists.txt
include_directories(${PROJECT_SOURCE_DIR}/vendor)
#include "github.com/yourorg/yourproject/example/example.pb.h"

For StarPC C++ services, the *_srpc.pb.hpp files provide client/server stubs.

Configuration

The generator uses sensible defaults but can be customized:

  • Targets: Proto file patterns (default: ./*.proto)
  • Exclude: Patterns to skip (e.g., vendor/**)
  • ToolsDir: Plugin binary location (default: .tools)
  • Cache: Manifest file (default: .protoc-manifest.json)

package.json Configuration

When a repo has a package.json, aptre generate also reads an optional top-level aptre config object from it.

Example:

{
  "name": "spacewave",
  "private": true,
  "aptre": {
    "languages": ["go", "ts", "cpp", "rust"],
    "tsImportBoundaries": ["auth", "bldr", "db", "forge", "identity", "net"]
  }
}

aptre.languages

languages selects which output languages to generate. Supported values are go, ts, cpp, rust, csharp, and python. C# and Python are opt-in. Omit languages to preserve the existing default: generate Go, C++, and Rust when the project has a go.mod, plus TypeScript when it has a package.json.

The CLI flag takes precedence over package.json:

aptre generate --language go

aptre.rust

Setting aptre.rust makes aptre generate compile the whole schema graph in a single run and write one Rust file per schema, beside the schema, even when a protobuf package spans several directories. Every other selected language still uses the per-directory generator.

{
  "aptre": {
    "module": "example.com/app",
    "languages": ["rust"],
    "rust": {
      "prostOptions": ["enable_type_names", "btree_map=."],
      "exclude": [],
      "descriptorSet": "wire/descriptors.bin",
      "moduleFile": "wire/messages.rs",
      "inventory": ".protoc-rust-files.txt"
    }
  }
}
  • module is the import path that prefixes the project's schemas. It defaults to the go.mod module, so a project without go.mod must set it. Schemas under vendor/ keep the import path they have inside that directory.
  • prostOptions are passed to the prost plugin in order. Use them for type names, deterministic maps, boxed fields and extern_path mappings.
  • exclude lists schema patterns to leave out of compilation.
  • descriptorSet receives the serialized descriptors of the whole graph, including imports and source information, for runtime reflection.
  • moduleFile receives include! declarations that nest each generated file under its protobuf package, naming each segment as Prost does (snake case, r# for keywords).
  • inventory lists every generated path. The next run removes files that the inventory names but the schemas no longer produce.

Select schemas with --targets. Outputs are built in memory first and written only when their bytes change. aptre generate --check --language rust compares all outputs without writing, prints each outdated: path and exits non-zero. The check never prepares dependencies: when rpc includes starpc and protoc-gen-starpc-rust is not built, it fails and names the missing plugin; run aptre deps first.

tests/rust-graph/check.bash generates a project with every kind of type path, checks it, and runs the Rust tests over exactly that output.

aptre.tsTypedDecode

tsTypedDecode makes protoc-gen-es-lite generate a typed decoder for every message, a switch on the field number that reads straight into the message, in place of the shared field walker:

{
  "aptre": {
    "tsTypedDecode": true
  }
}

Turn it on for code that runs in a JavaScript interpreter such as QuickJS or that an ahead-of-time compiler builds, where the typed decoder is faster. It makes the generated files larger, and under a JIT the walker is as fast, so it is off by default.

aptre.tsTypedEncode

tsTypedEncode does the same for encoding: protoc-gen-es-lite generates an encoder for every message that writes its fields in number order, with the same bytes as the shared field walker. It needs @aptre/protobuf-es-lite 1.2.4 or later. The same reasoning applies: turn it on for interpreters and ahead-of-time compilers, and leave it off under a JIT.

aptre.tsImportBoundaries

tsImportBoundaries configures how generated TypeScript protobuf imports are rewritten inside a monorepo.

By default, same-module generated imports stay relative. That is usually what you want in a single-package repo.

For monorepos that generate protobuf TypeScript into multiple top-level members, relative imports can break when a build system mirrors sources into another tree or when each member needs a stable repo-root import path. In that case, define module-relative boundary prefixes in aptre.tsImportBoundaries.

When a generated *.pb.ts file imports another generated protobuf file:

  • If the import stays within the same configured boundary, it remains relative.
  • If the import crosses from one configured boundary to another, aptre rewrites it to an @go/... import.
  • Imports that resolve outside the current module are still rewritten to @go/... from vendor/ as before.

Given:

{
  "aptre": {
    "tsImportBoundaries": ["bldr", "db"]
  }
}

an import like:

import { VolumeInfo } from '../../db/volume/volume.pb.js'

in bldr/plugin/plugin.pb.ts becomes:

import { VolumeInfo } from '@go/github.com/yourorg/yourrepo/db/volume/volume.pb.js'

Boundary entries are matched against paths relative to the Go module root, so they should name repo directories such as bldr, db, or net, not full module paths.

Support

Please open a GitHub issue with any questions or issues.

... or reach out on Matrix Chat or Discord.

License

MIT

版本与发布记录

@aptre/common 共发布 125 个版本,首次发布于 2024/04/24;过去 12 个月发布了 59 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.38.2latest2026/11/08今天
0.38.12026/11/016 天前
0.38.02026/11/016 天前
0.37.02026/10/317 天前
0.36.52026/10/2414 天前
0.36.42026/10/2117 天前

关于 @aptre/common 的常见问题

@aptre/common 是什么?

Common project configuration files and dependencies.

如何安装 @aptre/common?

在项目目录运行 npm install @aptre/common,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 aptre-tla,可以用 npx aptre-tla 直接运行。

@aptre/common 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aptre/common 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@aptre/common 可以商用吗?

@aptre/common 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@aptre/common 的下载量有多少?

根据 npm 官方下载统计,@aptre/common 在 2026/10/07 – 2026/11/05 这 30 天内被下载 23,801 次,日均约 793 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aptre/common 还在维护吗?

最新版本 0.38.2 发布于 2026/11/08(今天);至今共发布 125 个版本,其中过去 12 个月发布了 59 个;首次发布于 2024/04/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。