Peak is a transpiler that brings generic programming back to Salesforce Apex. Write reusable generic classes once, and Peak generates type-safe concrete Apex classes ready for deployment.
// Write once
public class Queue<T> {
private List<T> items;
public void enqueue(T item) { items.add(item); }
public T dequeue() { return items.remove(0); }
}
// Use everywhere
Queue<Integer> numbers = new Queue<Integer>();
Queue<Account> accounts = new Queue<Account>();- Write once, use everywhere - Create a generic like
Queue<T>and use it with any type - Type safety - Generated classes are strongly typed; no casting, no runtime errors
- Zero runtime cost - All generics resolve at compile time to concrete classes
- Future-proof - Minimal syntax transformation ensures compatibility with Apex updates
- Nested generics - Support for complex types like
Queue<List<Integer>>
# Install from source (requires Go 1.20+)
git clone https://github.com/ipavlic/peak.git
cd peak
go build -o peak ./cmd/peak
# Or install directly
go install github.com/ipavlic/peak/cmd/peak@latestpeak examples/ # Transpile directory
peak --watch examples/ # Auto-recompile on changes
peak --out-dir build/ src/ # Custom output directory
peak --root-dir . --out-dir build/ # Preserve structure from root
peak --api-version 64.0 src/ # Set API version for meta filesCreate a .peak file with generic type parameters:
// Queue.peak - A generic queue that works with any type
public class Queue<T> {
private List<T> items;
public Queue() {
this.items = new List<T>();
}
public void enqueue(T item) {
items.add(item);
}
public T dequeue() {
return items.remove(0);
}
}Reference your generic class with concrete types:
// QueueExample.peak - Uses Queue with specific types
public class QueueExample {
private Queue<Integer> intQueue;
private Queue<String> stringQueue;
public QueueExample() {
this.intQueue = new Queue<Integer>();
this.stringQueue = new Queue<String>();
}
}Run Peak to generate concrete Apex classes:
peak examples/Peak generates:
-
Transpiled usage files -
QueueExample.clswith generic references replaced:public class QueueExample { private QueueInteger intQueue; // Queue<Integer> → QueueInteger private QueueString stringQueue; // Queue<String> → QueueString // ... }
-
Concrete class files - Type-specific classes from templates:
QueueInteger.cls- allTreplaced withIntegerQueueString.cls- allTreplaced withString
-
Templates skipped -
Queue.peakis not compiled (it's a template)
All .cls files are ready to deploy to Salesforce!
--help, -h Display help message
--watch, -w Watch for changes and auto-recompile
--out-dir, -o <dir> Output directory (overrides config)
--root-dir, -r <dir> Root directory for preserving structure
--api-version, -a <version> Salesforce API version for .cls-meta.xml (default: 65.0)
Create peakconfig.json in your source directory:
{
"compilerOptions": {
"outDir": "build/classes",
"rootDir": ".",
"apiVersion": "65.0",
"verbose": false,
"instantiate": {
"classes": {
"Queue": ["Integer", "String", "Boolean"],
"Optional": ["Double", "Decimal"]
},
"methods": {
"Repository.get": ["Account", "Contact", "String"],
"Repository.put": ["Account", "Contact"]
}
}
}
}Config Options:
outDir- Output directory for generated files (default: co-located with source)rootDir- Root directory to preserve relative paths when usingoutDir. When set withoutDir, preserves directory structure relative to this root instead of the source directory.apiVersion- Salesforce API version for .cls-meta.xml files (default: "65.0")verbose- Enable detailed logging (default: false)instantiate.classes- Force generation of specific class instantiationsinstantiate.methods- Force generation of specific method instantiations (format:"ClassName.methodName": ["Type1", "Type2"])
Priority: CLI flags > Config file > Defaults
Example - Directory Structure Preservation:
# Without rootDir: src/utils/Queue.peak → build/classes/utils/Queue.cls
peak --out-dir build/classes src/
# With rootDir: src/utils/Queue.peak → build/classes/src/utils/Queue.cls
peak --root-dir . --out-dir build/classes src/Type parameters must be single uppercase letters (T, K, V, etc.):
✓ class Queue<T> // Good - single letter
✓ class Dict<K, V> // Good - multiple single letters
✗ class Queue<Type> // Error - multi-letter not allowed
✗ class Dict<T, T> // Error - duplicate parametersApex's native List<T>, Set<T>, and Map<K,V> remain unchanged. Only custom generic classes are transformed.
Define classes with multiple type parameters:
public class Dict<K, V> {
private List<K> keys;
private List<V> values;
public void put(K key, V value) {
keys.add(key);
values.add(value);
}
public V get(K key) {
Integer index = keys.indexOf(key);
return index >= 0 ? values.get(index) : null;
}
}
// Use with any key-value combination
Dict<String, Integer> scores = new Dict<String, Integer>();
Dict<Integer, Account> accountMap = new Dict<Integer, Account>();Generic types can be nested to any depth:
Queue<List<Integer>> batchQueue = new Queue<List<Integer>>();
Dict<String, Queue<Account>> accountQueues = new Dict<String, Queue<Account>>();Generates concrete classes like QueueListInteger.cls and DictStringQueueAccount.cls.
Define generic methods that work with any type:
public class Repository {
public <T> T get(String key) { ... }
public <T> void put(String key, T value) { ... }
}Configure concrete method generation in peakconfig.json:
{
"compilerOptions": {
"instantiate": {
"methods": {
"Repository.get": ["Account", "Contact", "String"],
"Repository.put": ["Account", "Contact"]
}
}
}
}Generates concrete methods:
public Account getAccount(String key) { ... }
public Contact getContact(String key) { ... }
public void putAccount(String key, Account value) { ... }Naming: methodName + type (e.g., getString, putAccount)
Peak provides clear error messages with line/column info. Files with errors are reported but don't block other files from compiling.
Queue.peak:5:14: error: type parameter must be a single letter, got: Type
See examples/ directory:
Templates:
Queue.peak- Generic queue<T>Dict.peak- Generic dictionary<K, V>Repository.peak- Generic methods
Usage:
QueueExample.peak- Basic usageNestedGenericsExample.peak- Nested types (Queue<List<Integer>>)ComplexExample.peak- Advanced patterns
Run peak examples/ to try it out!
go build -o peak ./cmd/peak # Build
go test ./... # TestStructure:
cmd/peak/- CLI applicationpkg/parser/- Generic syntax parserpkg/transpiler/- Template instantiationexamples/- Example files
Not yet supported:
- Type constraints:
class Queue<T extends SObject> - Variance annotations:
class Queue<out T>
Note: Generated class names use simple concatenation (Queue<List<Integer>> → QueueListInteger), which can create long names for deeply nested generics.
Contributions are welcome! Please feel free to submit issues or pull requests.
MIT License - see LICENSE file for details.
Questions or Issues? Open an issue or check the examples/ directory for working code.