Execution
Source:
packages/core/src/core/execution/runner.ts,packages/core/src/core/execution/engine.ts,packages/core/src/core/state/context.ts,packages/core/src/core/execution/pool.ts,packages/core/src/core/execution/worker.ts
generate
async function generate(journey: Journey | Journey[], options: RunOptions): Promise<void>;Main entry point for synthetic data generation. Accepts a single journey or an array. Orchestrates dataset hydration, user creation, and event output through the configured adapter.
RunOptions
interface RunOptions {
users: number; // Total users to simulate (0-10M)
persona?: PersonaDefinition; // Persona for user attribute generation
datasets?: DatasetDefinition[]; // Definitions to hydrate before execution
preloadedDatasets?: Dataset[]; // Pre-populated datasets to inject
lanes?: number; // Concurrent async lanes (default: 1)
adapter?: OutputAdapter; // Output destination (default: ConsoleAdapter)
debug?: boolean; // Enable telemetry (default: false)
telemetryPath?: string; // Telemetry output path (default: './telemetry-report.json')
startDate?: Date; // Start of date range (requires endDate)
endDate?: Date; // End of date range (requires startDate)
eventSchema?: EventSchemaConfig; // Zod schema validation for events
workerModule?: string; // Module path for worker thread parallelism
workers?: number; // Worker thread count (default: CPU cores)
}Execution modes:
| Configuration | Mode | Details |
|---|---|---|
| Default | Sequential | Single-threaded, one user at a time |
lanes > 1 | Parallel lanes | Promise.all concurrency in the main thread |
workerModule set | Worker threads | True multi-core parallelism via worker_threads |
import { generate, InMemoryAdapter } from '@synode/core';
const adapter = new InMemoryAdapter();
await generate(journey, {
users: 1000,
lanes: 4,
persona: shoppers,
datasets: [productsDef],
adapter,
});Engine
Internal class that executes a single journey against a context, yielding events as an async generator. Documented for advanced use cases (custom runners, testing).
class Engine {
constructor(journey: Journey);
async *run(context?: SynodeContext): AsyncGenerator<Event>;
}The engine handles:
- Prerequisite checks (
journey.requires) - Journey-level and adventure-level bounce evaluation
- Sequential adventure and action execution
- Scope cleanup (
action,adventure,journey) after each phase - Suppression period delays
- Wrapping handler errors in
SynodeErrorwith codeHANDLER_ERROR - Validating handler return type (must be
Event[])
import { Engine, SynodeContext } from '@synode/core';
const engine = new Engine(journey);
const ctx = new SynodeContext();
for await (const event of engine.run(ctx)) {
console.log(event.name, event.timestamp);
}SynodeContext
Implementation of the Context interface. Manages per-user state, timing, datasets, and identity.
class SynodeContext implements Context {
constructor(startTime?: Date, idGenerator?: IdGenerator, locale?: string);
// Context interface methods
get userId(): string;
get sessionId(): string;
get faker(): Faker;
readonly locale: string;
get<T>(key: string): T | undefined;
set<T>(key: string, value: T, options?: ContextSetOptions): void;
now(): Date;
generateId(prefix?: string): string;
hasCompletedJourney(journeyId: string): boolean;
markJourneyComplete(journeyId: string): void;
dataset(id: string): DatasetHandle;
typedDataset<TRow extends DatasetRow>(id: string): DatasetHandle<TRow>;
// Internal methods (used by Engine)
registerDataset(dataset: Dataset): void;
advanceTime(ms: number): void;
rotateSession(): void;
clearScope(scope: ContextScope): void;
}Key behaviors:
set()with a scope means the field is auto-cleared when that scope endsdataset()/typedDataset()throwSynodeError(DATASET_NOT_FOUND) with "Did you mean..." suggestionsnow()returns a copy of the internal clock (safe to mutate)rotateSession()generates a new session ID (called per journey)
WorkerPool
Internal class that manages worker threads for true multi-core parallelism. Used automatically when workerModule is set in RunOptions.
class WorkerPool {
constructor(options: WorkerPoolOptions);
async run(): Promise<void>;
}Worker modules must export { journeys: Journey[] } and optionally persona, datasets, and preloadedDatasets. Datasets are serialized via structured clone and rehydrated in each worker.
