Skip to main content

TypeScript Build Setup

TypeScript erases type information at runtime. Aster's aster-gen scanner reads your source at build time via the TypeScript compiler API, discovers all @Service, @WireType, @Rpc, @ServerStream, @ClientStream, and @BidiStream decorated classes, and emits aster-rpc.generated.ts with the metadata the runtime needs — field shapes, wire type mappings, method signatures, and contract identity hashes.

This replaces the need to pass { request, response } to every decorator. You write clean decorators; the scanner extracts types from the AST.

Install

npm install @aster-rpc/aster

aster-gen ships as the aster-gen bin inside @aster-rpc/aster — no extra package needed. TypeScript is a peer dependency (bring your own version).

Run the scanner

# defaults: reads ./tsconfig.json, writes ./aster-rpc.generated.ts
npx aster-gen

# custom paths
npx aster-gen -p tsconfig.app.json -o build/aster-rpc.generated.ts

Or add it to your package.json:

{
"scripts": {
"build": "aster-gen && tsc"
}
}

Re-run after adding or changing wire types, RPC methods, or decorator options. The generated file is deterministic — same input always produces the same output.

Use in your app

AsterServer.start() auto-imports aster-rpc.generated.js from the working directory — no manual import or wiring needed:

import { AsterServer } from '@aster-rpc/aster';
import { MyService } from './services.js';

const server = new AsterServer({
services: [new MyService()],
});
await server.start();
await server.serve();

If the generated file is missing, the runtime falls back to reflection and logs a warning.

What gets generated

The output aster-rpc.generated.ts contains two exports:

  • WIRE_TYPES — ordered array of wire type descriptors (tag, fields, nested type refs, field name sets) in dependency order.
  • SERVICES — array of service descriptors with method signatures, RPC patterns, pre-derived manifest fields, and BLAKE3 type hashes for cross-language contract identity.

The file is // @ts-nocheck and auto-formatted. Commit it or gitignore it — either works. Committing makes the example runnable without a build step; gitignoring keeps diffs clean.

Bundler plugins

For projects using a bundler, plugins run aster-gen automatically on build and during hot reload. When using a bundler plugin, pass the generated option explicitly since dynamic import won't resolve at bundle time:

Vite

// vite.config.ts
import { defineConfig } from 'vite';
import { asterGen } from '@aster-rpc/aster/vite-plugin';

export default defineConfig({
plugins: [
asterGen({
project: 'tsconfig.json',
out: 'aster-rpc.generated.ts',
}),
],
});

The plugin runs on buildStart and watches for changes to decorated source files during dev.

Webpack

// webpack.config.ts
import { AsterGenPlugin } from '@aster-rpc/aster/webpack-plugin';

export default {
plugins: [
new AsterGenPlugin({
project: 'tsconfig.json',
out: 'aster-rpc.generated.ts',
}),
],
};

The plugin hooks into beforeCompile and re-scans when source files change.

Type mapping

The scanner maps TypeScript types to wire types per the Aster spec:

TypeScriptWire type
stringstring
booleanbool
numberfloat64
bigintint64
Datetimestamp
Uint8Arraybinary
T[] / Array<T>list<T>
Set<T>set<T>
Map<K, V>map<K, V>
T | null / T | undefinednullable<T>
Class with @WireTyperef (by tag)

Branded numeric types

Plain number maps to float64. For narrower types (int32, uint16, etc.), use branded types:

import { i32, u64, f32 } from '@aster-rpc/aster';

@WireType("myapp/Metrics")
class Metrics {
count: i32 = 0; // → int32
total_bytes: u64 = 0n; // → uint64
avg_latency: f32 = 0; // → float32
}

The scanner detects branded types via their phantom property and maps them to the correct wire primitive.

Runtime fallback

If aster-rpc.generated.js is not found (e.g. during rapid prototyping), the runtime falls back to reflection: instantiating each wire type class and inspecting Object.keys(). This works for simple cases but has limitations:

  • Empty arrays don't reveal their element type.
  • Optional/nullable nested types aren't recursed.
  • Non-default-constructible classes can't be introspected.
  • Contract identity hashes are zeroed (cross-language calls won't match).

A console warning fires once per class on the fallback path. For production use, always run aster-gen.