Rystem.RepositoryFramework.TypescriptGenerator
10.0.3-preview.4
See the version list below for details.
dotnet tool install --global Rystem.RepositoryFramework.TypescriptGenerator --version 10.0.3-preview.4
dotnet new tool-manifest
dotnet tool install --local Rystem.RepositoryFramework.TypescriptGenerator --version 10.0.3-preview.4
#tool dotnet:?package=Rystem.RepositoryFramework.TypescriptGenerator&version=10.0.3-preview.4&prerelease
nuke :add-package Rystem.RepositoryFramework.TypescriptGenerator --version 10.0.3-preview.4
Rystem TypeScript Generator
CLI tool to generate TypeScript types and services from C# Rystem Repository/CQRS models.
Installation
Global Installation (recommended)
dotnet tool install -g Rystem.RepositoryFramework.TypescriptGenerator
Local Installation (per-project)
dotnet new tool-manifest # if you don't have one already
dotnet tool install Rystem.RepositoryFramework.TypescriptGenerator
Update
# Global
dotnet tool update -g Rystem.RepositoryFramework.TypescriptGenerator
# Local
dotnet tool update Rystem.RepositoryFramework.TypescriptGenerator
Uninstall
# Global
dotnet tool uninstall -g Rystem.RepositoryFramework.TypescriptGenerator
# Local
dotnet tool uninstall Rystem.RepositoryFramework.TypescriptGenerator
Usage
Basic Syntax
rystem-ts generate --dest <destination> --models <model-definitions> [options]
Options
| Option | Alias | Required | Description |
|---|---|---|---|
--dest |
-d |
✅ | Destination folder for generated TypeScript files |
--models |
-m |
✅ | Repository definitions (see format below) |
--project |
-p |
❌ | Path to C# project (.csproj) or assembly (.dll) |
--overwrite |
❌ | Overwrite existing files (default: true) |
Model Definition Format
The --models option accepts repository definitions in the following format:
"{Model,Key,Type,Factory},{Model2,Key2,Type2,Factory2}"
Fields:
- ModelName: Name of the C# entity class. Can be simple (
Calendar) or fully qualified (Fantacalcio.Domain.Calendar) - KeyName: Name of the key type. Can be simple (
LeagueKey) or fully qualified (Fantacalcio.Domain.LeagueKey) - RepositoryType: One of
Repository,Query, orCommand - FactoryName: Name for the generated service factory (optional, defaults to ModelName)
Note: If multiple types with the same name exist in different namespaces, you must use the fully qualified name (with namespace) to avoid ambiguity.
Examples
Single Repository
rystem-ts generate \
--dest ./src/api \
--models "{User,Guid,Repository,users}"
Multiple Repositories
rystem-ts generate \
--dest ./src/api \
--models "{Calendar,LeagueKey,Repository,serieA},{Team,Guid,Query,teams},{Match,int,Command,matches}"
With Fully Qualified Names (Namespaces)
Use fully qualified names when you have types with the same name in different namespaces:
rystem-ts generate \
--dest ./src/api \
--models "{Fantacalcio.Domain.Calendar,Fantacalcio.Domain.LeagueKey,Repository,serieA}"
With Specific Project
rystem-ts generate \
--dest ./src/api \
--models "{Product,int,Repository,products}" \
--project ./src/MyApp.Core/MyApp.Core.csproj
Disable Overwrite
rystem-ts generate \
--dest ./src/api \
--models "{Order,Guid,Repository,orders}" \
--overwrite false
Generated Output
The tool generates the following structure:
📁 <destination>/
├── 📁 types/
│ ├── Calendar.ts # Raw + Clean interfaces + Mapper
│ ├── LeagueKey.ts
│ ├── Team.ts
│ └── DayOfWeek.ts # Enums
├── 📁 services/
│ ├── common.ts # Entity, State, BatchOperation, Page, QueryOptions
│ └── serieA.service.ts # Service class with API methods
├── 📁 bootstrap/
│ └── repositorySetup.ts # RepositoryServices configuration with auth handlers
└── index.ts # Services registry with lazy singleton pattern
Generated Types
Types (types/*.ts)
For each C# model, the tool generates:
// Raw interface (matches JSON from API)
export interface CalendarRaw {
league_id: string; // Uses JsonPropertyName if present
start_date: string; // Dates as strings
teams: TeamRaw[];
}
// Clean interface (TypeScript-friendly)
export interface Calendar {
leagueId: string; // camelCase names
startDate: Date; // Proper Date type
teams: Team[];
}
// Mapper function
export function mapCalendar(raw: CalendarRaw): Calendar {
return {
leagueId: raw.league_id,
startDate: new Date(raw.start_date),
teams: raw.teams.map(mapTeam),
};
}
Common Types (services/common.ts)
export interface Entity<T, TKey> {
key: TKey;
value: T | null;
}
export interface State<T> {
isOk: boolean;
message?: string;
value?: T;
}
export interface Page<T, TKey> {
items: Entity<T, TKey>[];
totalCount: number;
pageSize: number;
pageIndex: number;
totalPages: number;
}
export interface BatchOperation<T, TKey> {
command: 'insert' | 'update' | 'delete';
key: TKey;
value?: T;
}
Services (services/*.service.ts)
export class SerieAService {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
// Query methods
async get(key: LeagueKey): Promise<Calendar | null> { ... }
async query(options?: QueryOptions): Promise<Entity<Calendar, LeagueKey>[]> { ... }
async page(pageIndex: number, pageSize: number): Promise<Page<Calendar, LeagueKey>> { ... }
async exist(key: LeagueKey): Promise<State<boolean>> { ... }
async count(): Promise<number> { ... }
// Command methods (Repository/Command only)
async insert(key: LeagueKey, value: Calendar): Promise<State<Calendar>> { ... }
async update(key: LeagueKey, value: Calendar): Promise<State<Calendar>> { ... }
async delete(key: LeagueKey): Promise<State<boolean>> { ... }
async batch(operations: BatchOperation<Calendar, LeagueKey>[]): Promise<BatchResult[]> { ... }
}
Services Registry (index.ts)
export interface ServiceConfig {
baseUrl: string;
}
export class Services {
private static config: ServiceConfig | null = null;
private static _serieA: SerieAService | null = null;
static configure(config: ServiceConfig): void {
this.config = config;
}
static get serieA(): SerieAService {
this.ensureConfigured();
if (!this._serieA) {
this._serieA = new SerieAService(this.config!.baseUrl);
}
return this._serieA;
}
}
// Usage:
Services.configure({ baseUrl: 'https://api.example.com' });
const calendar = await Services.serieA.get({ leagueId: 'serie-a' });
Bootstrap Setup (bootstrap/repositorySetup.ts)
The bootstrap file integrates with the rystem.repository.client npm package for centralized repository configuration with authentication:
import { RepositoryServices, RepositorySettings, RepositoryEndpoint } from 'rystem.repository.client';
import { CalendarRaw } from '../types/Calendar';
import { LeagueKeyRaw } from '../types/LeagueKey';
export interface RepositoryConfig {
baseUrl: string;
headersEnricher?: (
endpoint: RepositoryEndpoint,
uri: string,
method: string,
headers: HeadersInit,
body: any
) => Promise<HeadersInit>;
errorHandler?: (
endpoint: RepositoryEndpoint,
uri: string,
method: string,
headers: HeadersInit,
body: any,
err: any
) => Promise<boolean>;
}
export const setupRepositoryServices = (config: RepositoryConfig): void => {
const services = RepositoryServices.Create(config.baseUrl);
// Calendar repository
services.addRepository<CalendarRaw, LeagueKeyRaw>(x => {
x.name = 'calendar';
x.path = 'calendar';
x.complexKey = true;
if (config.headersEnricher) {
x.addHeadersEnricher(config.headersEnricher);
}
if (config.errorHandler) {
x.addErrorHandler(config.errorHandler);
}
});
};
Usage with Authentication:
import { setupRepositoryServices } from './bootstrap/repositorySetup';
import { tokenService } from './services/tokenService';
// Setup with auth headers and error handling
setupRepositoryServices({
baseUrl: 'https://api.example.com/api/',
// Add Bearer token to all requests
headersEnricher: async (endpoint, uri, method, headers, body) => {
const token = await tokenService.getAccessToken();
return token
? { ...headers, Authorization: `Bearer ${token}` }
: headers;
},
// Handle 401 errors with token refresh
errorHandler: async (endpoint, uri, method, headers, body, err) => {
if (err?.status === 401) {
const refreshed = await tokenService.refreshToken();
return refreshed; // Return true to retry request
}
return false; // Don't retry
}
});
Repository Types
| Type | Query Methods | Command Methods |
|---|---|---|
Repository |
✅ get, query, page, exist, count | ✅ insert, update, delete, batch |
Query |
✅ get, query, page, exist, count | ❌ |
Command |
❌ | ✅ insert, update, delete, batch |
C# Model Requirements
Basic Model
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
}
With JsonPropertyName (for Raw types)
public class User
{
[JsonPropertyName("user_id")]
public Guid Id { get; set; }
[JsonPropertyName("full_name")]
public string Name { get; set; }
[JsonPropertyName("created_at")]
public DateTime CreatedAt { get; set; }
}
Custom Key Types
public record LeagueKey(string LeagueId, int Season);
Enums
public enum OrderStatus
{
Pending = 0,
Processing = 1,
Completed = 2,
Cancelled = 3
}
Integration with Rystem Repository API
This tool is designed to work with Rystem.RepositoryFramework.Api.Server.
Server Setup
// Program.cs
builder.Services.AddRepository<Calendar, LeagueKey>(settings =>
{
settings.WithEntityFramework<AppDbContext>();
});
// Expose as REST API
app.UseEndpointsForRepositoryPattern();
Client Usage
// Setup (once at app startup)
Services.configure({
baseUrl: 'https://localhost:5001/api'
});
// Use anywhere in your app
const calendars = await Services.serieA.query();
const calendar = await Services.serieA.get({ leagueId: 'serie-a', season: 2024 });
// Create/Update
await Services.serieA.insert(
{ leagueId: 'serie-a', season: 2025 },
{ startDate: new Date(), teams: [] }
);
Troubleshooting
"Project file not found"
Make sure the --project path is correct, or run the command from a directory containing a .csproj file.
"Multiple .csproj files found"
Specify which project to use with --project:
rystem-ts generate --dest ./src/api --models "{User,Guid,Repository,users}" --project ./src/MyApp.Core.csproj
"Model not found in assembly"
Ensure:
- The model class is
public - The project has been built (
dotnet build) - The model name matches exactly (case-sensitive)
"Multiple types found with name 'X'"
This error occurs when you have multiple classes with the same name in different namespaces:
Multiple types found with name 'Calendar'. Please use the fully qualified name:
- Fantacalcio.Domain.Calendar
- OtherProject.Models.Calendar
Solution: Use the fully qualified name (with namespace) in your --models argument:
# Instead of:
--models "{Calendar,LeagueKey,Repository,serieA}"
# Use:
--models "{Fantacalcio.Domain.Calendar,Fantacalcio.Domain.LeagueKey,Repository,serieA}"
Contributing
This tool is part of the Rystem framework.
License
MIT License - see the main repository for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.9 | 95 | 5/13/2026 |
| 10.0.8 | 120 | 3/26/2026 |
| 10.0.7 | 106 | 3/3/2026 |
| 10.0.6 | 106 | 2/22/2026 |
| 10.0.5 | 109 | 2/9/2026 |
| 10.0.4 | 110 | 2/9/2026 |
| 10.0.3 | 109 | 2/9/2026 |
| 10.0.3-preview.15 | 61 | 2/8/2026 |
| 10.0.3-preview.14 | 65 | 2/7/2026 |
| 10.0.3-preview.13 | 63 | 2/7/2026 |
| 10.0.3-preview.12 | 65 | 2/7/2026 |
| 10.0.3-preview.10 | 57 | 2/7/2026 |
| 10.0.3-preview.9 | 71 | 2/7/2026 |
| 10.0.3-preview.8 | 82 | 2/7/2026 |
| 10.0.3-preview.7 | 65 | 2/1/2026 |
| 10.0.3-preview.6 | 69 | 2/1/2026 |
| 10.0.3-preview.4 | 65 | 2/1/2026 |
| 10.0.3-preview.3 | 59 | 2/1/2026 |
| 10.0.3-preview.2 | 63 | 2/1/2026 |
| 10.0.3-preview.1 | 64 | 2/1/2026 |