Multi-File Schemas
Multi-File Schemas
Section titled “Multi-File Schemas”As your LUMOS project grows, you’ll want to organize schemas across multiple files for better maintainability. LUMOS supports two complementary systems:
- JavaScript-style imports - Simple, explicit type importing
- Rust-style modules - Hierarchical module organization
Quick Start
Section titled “Quick Start”JavaScript-Style Imports
Section titled “JavaScript-Style Imports”// types.lumos - Define shared typestype UserId = PublicKey;type Lamports = u64;
#[solana]enum AccountStatus { Active, Frozen, Closed,}// accounts.lumos - Import and use typesimport { UserId, Lamports, AccountStatus } from "./types.lumos";
#[solana]#[account]struct UserAccount { owner: UserId, // Resolves to PublicKey balance: Lamports, // Resolves to u64 status: AccountStatus, // Imported enum}Generate Multi-File Schema
Section titled “Generate Multi-File Schema”# LUMOS automatically resolves importslumos generate accounts.lumos
# Output includes all types from all imported filesJavaScript-Style Imports
Section titled “JavaScript-Style Imports”The import system uses familiar JavaScript/TypeScript syntax:
import { Type1, Type2, Type3 } from "./relative/path.lumos";Import Syntax
Section titled “Import Syntax”// Single importimport { UserId } from "./types.lumos";
// Multiple imports (comma-separated)import { UserId, Lamports, AccountStatus } from "./types.lumos";
// Multi-line importsimport { UserId, WalletAddress, AccountId, UnixTimestamp, Lamports, TokenAmount,} from "./types.lumos";What Can Be Imported?
Section titled “What Can Be Imported?”| Item Type | Example | Importable? |
|---|---|---|
| Type Aliases | type UserId = PublicKey | ✅ Yes |
| Structs | struct User { ... } | ✅ Yes |
| Enums | enum Status { ... } | ✅ Yes |
Relative Paths
Section titled “Relative Paths”Imports use relative paths from the current file:
project/├── schema/│ ├── types.lumos # Shared types│ ├── accounts.lumos # import from "./types.lumos"│ └── instructions/│ └── token.lumos # import from "../types.lumos"import { UserId } from "./types.lumos";
// schema/instructions/token.lumosimport { UserId } from "../types.lumos";Type Aliases
Section titled “Type Aliases”Type aliases create semantic names for primitive types, making schemas self-documenting:
Defining Type Aliases
Section titled “Defining Type Aliases”// Identity typestype UserId = PublicKey;type WalletAddress = PublicKey;type AccountId = PublicKey;
// Temporal typestype UnixTimestamp = i64;type Slot = u64;type Epoch = u64;
// Financial typestype Lamports = u64;type TokenAmount = u64;type BasisPoints = u16; // 1 = 0.01%, 10000 = 100%
// Collection typestype UserList = [PublicKey];type AmountList = [u64];
// Optional typestype OptionalOwner = Option<PublicKey>;type NullableTimestamp = Option<i64>;Using Type Aliases
Section titled “Using Type Aliases”import { UserId, Lamports, UnixTimestamp, AccountStatus } from "./types.lumos";
#[solana]#[account]struct StakeAccount { owner: UserId, // Self-documenting: this is a user ID staked_amount: Lamports, // Clear: this is a lamport amount staked_at: UnixTimestamp, // Obvious: this is a timestamp status: AccountStatus, // Imported enum}Generated Code
Section titled “Generated Code”Type aliases are preserved in generated code:
Rust Output:
use solana_program::pubkey::Pubkey;
pub type UserId = Pubkey;pub type Lamports = u64;pub type UnixTimestamp = i64;
#[derive(BorshSerialize, BorshDeserialize)]pub struct StakeAccount { pub owner: UserId, pub staked_amount: Lamports, pub staked_at: UnixTimestamp, pub status: AccountStatus,}TypeScript Output:
import { PublicKey } from '@solana/web3.js';
export type UserId = PublicKey;export type Lamports = number;export type UnixTimestamp = number;
export interface StakeAccount { owner: UserId; stakedAmount: Lamports; stakedAt: UnixTimestamp; status: AccountStatus;}Rust-Style Modules
Section titled “Rust-Style Modules”For larger projects, use Rust-style modules with mod and use statements:
Module Declaration
Section titled “Module Declaration”mod types; // Loads ./types.lumos or ./types/mod.lumosmod accounts;mod instructions;
#[solana]#[account]struct AppState { initialized: bool, admin: PublicKey,}Module Resolution
Section titled “Module Resolution”LUMOS looks for modules in two locations:
- Sibling file:
./module_name.lumos - Directory module:
./module_name/mod.lumos
project/├── main.lumos # mod types; mod models;├── types.lumos # Found as sibling file└── models/ ├── mod.lumos # Found as directory module ├── user.lumos └── account.lumosNested Modules
Section titled “Nested Modules”Create hierarchical organization:
mod models;
// models/mod.lumos (or models.lumos)mod user;mod account;
type Timestamp = i64;
// models/user.lumos#[solana]#[account]struct User { wallet: PublicKey, username: String,}
// models/account.lumos#[solana]#[account]struct Account { owner: PublicKey, balance: u64,}Use Statements
Section titled “Use Statements”Import types from other modules with use:
mod types;
use crate::types::UserId;use crate::types::AccountStatus;
#[solana]#[account]struct Config { admin: UserId, status: AccountStatus,}Path Keywords:
crate::- Start from the root modulesuper::- Go up one levelself::- Current module
use crate::types::UserId; // From rootuse super::Timestamp; // From parent (models)use self::helpers::validate; // From current moduleVisibility
Section titled “Visibility”Control type visibility with pub:
// Private - only accessible within this modulestruct InternalState { secret: [u8],}
// public.lumos// Public - accessible from other modulespub struct PublicConfig { name: String, enabled: bool,}Complete Example: DeFi Project
Section titled “Complete Example: DeFi Project”Here’s a real-world multi-file project structure:
defi-protocol/├── main.lumos # Entry point├── types.lumos # Shared type aliases├── accounts/│ ├── mod.lumos # Account module│ ├── user.lumos # User accounts│ ├── pool.lumos # Liquidity pool accounts│ └── vault.lumos # Vault accounts└── instructions/ ├── mod.lumos # Instruction module ├── stake.lumos # Staking instructions └── swap.lumos # Swap instructionstypes.lumos
Section titled “types.lumos”// Shared type aliases for the entire project
// Identitytype UserId = PublicKey;type PoolId = PublicKey;type VaultId = PublicKey;
// Financialtype Lamports = u64;type TokenAmount = u64;type ShareAmount = u64;type BasisPoints = u16;
// Temporaltype UnixTimestamp = i64;
// Optionaltype OptionalOwner = Option<PublicKey>;
// Enums#[solana]enum AccountStatus { Uninitialized, Active, Frozen, Closed,}
#[solana]enum PoolType { ConstantProduct, StableSwap, Concentrated,}accounts/user.lumos
Section titled “accounts/user.lumos”import { UserId, Lamports, TokenAmount, UnixTimestamp, AccountStatus,} from "../types.lumos";
#[solana]#[account]struct UserAccount { // Identity user_id: UserId,
// Balances sol_balance: Lamports, token_balance: TokenAmount,
// Staking staked_amount: TokenAmount, rewards_earned: TokenAmount,
// Metadata created_at: UnixTimestamp, last_activity: UnixTimestamp, status: AccountStatus,}
#[solana]#[account]struct UserPosition { owner: UserId, pool_id: PublicKey, liquidity_shares: TokenAmount, entry_price: u64, opened_at: UnixTimestamp,}accounts/pool.lumos
Section titled “accounts/pool.lumos”import { PoolId, UserId, TokenAmount, BasisPoints, UnixTimestamp, AccountStatus, PoolType,} from "../types.lumos";
#[solana]#[account]struct LiquidityPool { // Identity pool_id: PoolId, authority: UserId,
// Token configuration token_a_mint: PublicKey, token_b_mint: PublicKey, token_a_reserve: PublicKey, token_b_reserve: PublicKey,
// Pool state pool_type: PoolType, total_liquidity: TokenAmount,
// Fees swap_fee: BasisPoints, protocol_fee: BasisPoints,
// Metadata created_at: UnixTimestamp, status: AccountStatus,}instructions/stake.lumos
Section titled “instructions/stake.lumos”import { UserId, TokenAmount, UnixTimestamp,} from "../types.lumos";
#[solana]enum StakingInstruction { // Initialize staking pool Initialize { authority: UserId, reward_rate: u64, },
// Stake tokens Stake { user: UserId, amount: TokenAmount, lock_duration: UnixTimestamp, },
// Unstake tokens Unstake { user: UserId, amount: TokenAmount, },
// Claim rewards ClaimRewards { user: UserId, },}Generate the Project
Section titled “Generate the Project”# Generate from entry point - all imports resolved automaticallylumos generate main.lumos --rust --typescript
# Or generate specific fileslumos generate accounts/user.lumosValidation & Error Handling
Section titled “Validation & Error Handling”Circular Dependency Detection
Section titled “Circular Dependency Detection”LUMOS detects and prevents circular imports:
import { TypeB } from "./b.lumos";
// b.lumosimport { TypeA } from "./a.lumos"; // ERROR: Circular dependencyError Message:
error: Circular dependency detected --> a.lumos:1:1 | = note: a.lumos -> b.lumos -> a.lumosSolution: Extract shared types to a common file:
type SharedType = u64;
// a.lumosimport { SharedType } from "./common.lumos";
// b.lumosimport { SharedType } from "./common.lumos";Missing Import Errors
Section titled “Missing Import Errors”import { NonExistent } from "./types.lumos";Error:
error: Type 'NonExistent' not found in './types.lumos' --> accounts.lumos:1:10 | 1 | import { NonExistent } from "./types.lumos"; | ^^^^^^^^^^^ | = help: Available types: UserId, Lamports, AccountStatusFile Not Found
Section titled “File Not Found”import { Type } from "./missing.lumos";Error:
error: File not found: ./missing.lumos --> accounts.lumos:1:25 | 1 | import { Type } from "./missing.lumos"; | ^^^^^^^^^^^^^^^^^Best Practices
Section titled “Best Practices”1. Organize by Domain
Section titled “1. Organize by Domain”project/├── types/ # Shared types├── accounts/ # Account definitions├── instructions/ # Program instructions└── events/ # Event definitions2. Use Type Aliases Liberally
Section titled “2. Use Type Aliases Liberally”// ✅ Good - Self-documentingstruct Transfer { from: WalletAddress, to: WalletAddress, amount: Lamports,}
// ❌ Avoid - Requires contextstruct Transfer { from: PublicKey, to: PublicKey, amount: u64,}3. Keep Files Focused
Section titled “3. Keep Files Focused”Each file should have a single responsibility:
types.lumos- Type aliases onlyuser.lumos- User-related structspool.lumos- Pool-related structs
4. Avoid Deep Nesting
Section titled “4. Avoid Deep Nesting”// ✅ Good - 2-3 levels maxproject/├── types.lumos├── accounts/│ ├── user.lumos│ └── pool.lumos└── instructions.lumos
// ❌ Avoid - Too deepproject/├── domain/│ └── models/│ └── accounts/│ └── user/│ └── profile.lumos5. Single Source of Truth
Section titled “5. Single Source of Truth”Define each type in exactly one place:
// types.lumos - Define heretype UserId = PublicKey;
// accounts.lumos - Import, don't redefineimport { UserId } from "./types.lumos";// NOT: type UserId = PublicKey; // Duplication!CLI Commands
Section titled “CLI Commands”Generate with Imports
Section titled “Generate with Imports”# Automatically resolves all importslumos generate schema.lumos
# Specify output directorylumos generate schema.lumos --output ./generated
# Generate specific languageslumos generate schema.lumos --lang rust,typescript,pythonValidate Multi-File Schema
Section titled “Validate Multi-File Schema”# Validates imports and type referenceslumos validate schema.lumos
# Verbose output shows resolved importslumos validate schema.lumos --verboseWatch Mode
Section titled “Watch Mode”# Watches all imported files for changeslumos generate schema.lumos --watchMigration from Single File
Section titled “Migration from Single File”Before (Single File)
Section titled “Before (Single File)”// schema.lumos (500+ lines)type UserId = PublicKey;type Lamports = u64;
#[solana]enum Status { ... }
#[solana]struct User { ... }
#[solana]struct Pool { ... }
// ... many more typesAfter (Multi-File)
Section titled “After (Multi-File)”type UserId = PublicKey;type Lamports = u64;
#[solana]enum Status { ... }
// accounts/user.lumosimport { UserId, Status } from "../types.lumos";
#[solana]struct User { ... }
// accounts/pool.lumosimport { Lamports } from "../types.lumos";
#[solana]struct Pool { ... }Migration Steps
Section titled “Migration Steps”- Identify shared types - Find types used across multiple structs
- Extract to types.lumos - Move type aliases and common enums
- Split by domain - Group related structs into files
- Add imports - Update each file with necessary imports
- Validate - Run
lumos validateto catch missing imports - Generate - Verify output matches original