@synode/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1142 @@
1
+ import { Faker } from "@faker-js/faker";
2
+ import { z } from "zod";
3
+
4
+ //#region src/types.d.ts
5
+
6
+ /**
7
+ * Represents a generated event.
8
+ * @see {@link Action} for how events are generated
9
+ */
10
+ interface Event {
11
+ id: string;
12
+ userId: string;
13
+ sessionId: string;
14
+ name: string;
15
+ timestamp: Date;
16
+ payload: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * Lifecycle scope for context fields.
20
+ * Fields with a scope will be automatically cleared when that scope ends.
21
+ */
22
+ type ContextScope = 'action' | 'adventure' | 'journey';
23
+ /**
24
+ * Options for setting a context field with TTL/scope.
25
+ */
26
+ interface ContextSetOptions {
27
+ /**
28
+ * Lifecycle scope for this field. When the scope ends, the field is automatically cleared.
29
+ * - 'action': Field lives only for the duration of the current action
30
+ * - 'adventure': Field lives for the duration of the current adventure
31
+ * - 'journey': Field lives for the duration of the current journey
32
+ */
33
+ scope?: ContextScope;
34
+ }
35
+ /**
36
+ * Represents the execution context for a user's journey.
37
+ * @see {@link SynodeContext} for the implementation
38
+ */
39
+ interface Context {
40
+ readonly userId: string;
41
+ readonly sessionId: string;
42
+ readonly locale: string;
43
+ readonly faker: Faker;
44
+ get<T>(key: string): T | undefined;
45
+ set<T>(key: string, value: T, options?: ContextSetOptions): void;
46
+ now(): Date;
47
+ generateId(prefix?: string): string;
48
+ hasCompletedJourney(journeyId: string): boolean;
49
+ markJourneyComplete(journeyId: string): void;
50
+ dataset(id: string): DatasetHandle;
51
+ /**
52
+ * Get a typed dataset handle. Use this with InferDatasetRow for type safety.
53
+ * @example
54
+ * ```typescript
55
+ * const productsDef = defineDataset({ ... });
56
+ * type Product = InferDatasetRow<typeof productsDef>;
57
+ * const products = ctx.typedDataset<Product>('products');
58
+ * const product = products.randomRow(); // typed as Product
59
+ * ```
60
+ */
61
+ typedDataset<TRow extends DatasetRow>(id: string): DatasetHandle<TRow>;
62
+ }
63
+ /**
64
+ * Handle for querying a dataset from context.
65
+ */
66
+ interface DatasetHandle<TRow = DatasetRow> {
67
+ randomRow(): TRow;
68
+ getRowById(id: string | number): TRow | undefined;
69
+ getRowByIndex(index: number): TRow | undefined;
70
+ getAllRows(): TRow[];
71
+ size(): number;
72
+ }
73
+ /**
74
+ * Represents a single row in a dataset.
75
+ */
76
+ type DatasetRow = Record<string, unknown>;
77
+ /**
78
+ * Represents a generated dataset with metadata and rows.
79
+ */
80
+ interface Dataset<TRow = DatasetRow> {
81
+ id: string;
82
+ name: string;
83
+ rows: TRow[];
84
+ }
85
+ /**
86
+ * Field generator with access to row metadata.
87
+ */
88
+ type DatasetFieldGenerator<T = unknown> = T | ((context: Context, row: {
89
+ index: number;
90
+ data: DatasetRow;
91
+ }) => T | Promise<T>);
92
+ /**
93
+ * Configuration for defining a dataset.
94
+ * @see {@link InferDatasetRow} for type inference
95
+ * @see {@link defineDataset} for the builder function
96
+ */
97
+ interface DatasetDefinition<TFields$1 extends Record<string, unknown> = Record<string, unknown>> {
98
+ id: string;
99
+ name: string;
100
+ count: number;
101
+ fields: { [K in keyof TFields$1]: TFields$1[K] | ((context: Context, row: {
102
+ index: number;
103
+ data: DatasetRow;
104
+ }) => TFields$1[K] | Promise<TFields$1[K]>) };
105
+ }
106
+ /**
107
+ * Infer the row type from a dataset definition.
108
+ * This provides type safety similar to Zod's z.infer.
109
+ * @see {@link DatasetDefinition} for defining datasets
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const productsDef = defineDataset({
114
+ * id: 'products',
115
+ * name: 'Products',
116
+ * count: 100,
117
+ * fields: {
118
+ * id: (ctx, row) => `prod-${row.index}`,
119
+ * name: (ctx) => ctx.faker.commerce.productName(),
120
+ * price: (ctx) => ctx.faker.number.float({ min: 10, max: 500 }),
121
+ * category: oneOf(['electronics', 'clothing', 'home']),
122
+ * },
123
+ * });
124
+ *
125
+ * // Infer the type
126
+ * type Product = InferDatasetRow<typeof productsDef>;
127
+ * // Result: { id: string; name: string; price: number; category: string; }
128
+ * ```
129
+ */
130
+ /**
131
+ * Infers the row type from a dataset definition, unwrapping generator functions and promises.
132
+ * For each field, if it is a function, uses its return type (unwrapping Promise if needed); otherwise, uses the type as-is.
133
+ */
134
+ type InferDatasetRow<T> = T extends DatasetDefinition<infer TFields> ? { [K in keyof TFields]: TFields[K] extends ((...args: any[]) => infer R) ? R extends Promise<infer U> ? U : R : TFields[K] } : never;
135
+ /**
136
+ * Time span configuration for delays.
137
+ */
138
+ interface TimeSpan {
139
+ min: number;
140
+ max: number;
141
+ distribution?: 'uniform' | 'gaussian' | 'exponential';
142
+ }
143
+ /**
144
+ * Suppression period after journey completion or bounce.
145
+ */
146
+ interface SuppressionPeriod {
147
+ min: number;
148
+ max: number;
149
+ }
150
+ /**
151
+ * Represents the configuration for a synthetic data generation journey.
152
+ * @see {@link Adventure} for session sequences
153
+ * @see {@link defineJourney} for the builder function
154
+ */
155
+ interface Journey {
156
+ id: string;
157
+ name: string;
158
+ /**
159
+ * Journey IDs that must be completed before this journey can start.
160
+ */
161
+ requires?: string[];
162
+ adventures: Adventure[];
163
+ /**
164
+ * Chance (0-1) that the journey is never started or completed.
165
+ */
166
+ bounceChance?: number;
167
+ /**
168
+ * Suppression period after journey completes or bounces.
169
+ */
170
+ suppressionPeriod?: SuppressionPeriod;
171
+ }
172
+ /**
173
+ * Represents a sequence of actions within a journey.
174
+ * @see {@link Action} for individual behaviors
175
+ * @see {@link defineAdventure} for the builder function
176
+ */
177
+ interface Adventure {
178
+ id: string;
179
+ name: string;
180
+ actions: Action[];
181
+ /**
182
+ * Time span between actions in this adventure.
183
+ */
184
+ timeSpan?: TimeSpan;
185
+ /**
186
+ * Chance (0-1) to abandon this adventure.
187
+ */
188
+ bounceChance?: number;
189
+ /**
190
+ * What to do on bounce: 'stop' ends the journey, 'skip' continues to next adventure.
191
+ */
192
+ onBounce?: 'stop' | 'skip';
193
+ }
194
+ /**
195
+ * Represents a single step or event generator in an adventure.
196
+ * @see {@link defineAction} for the builder function
197
+ */
198
+ interface Action {
199
+ id: string;
200
+ name: string;
201
+ /**
202
+ * Handler must return an array of Events.
203
+ */
204
+ handler: (context: Context) => Event[] | Promise<Event[]>;
205
+ /**
206
+ * Time span between individual events generated by this action.
207
+ */
208
+ timeSpan?: TimeSpan;
209
+ /**
210
+ * Chance (0-1) to abandon during this action.
211
+ */
212
+ bounceChance?: number;
213
+ }
214
+ /**
215
+ * Type definition for a field generator.
216
+ * Can be a static value or a function that returns a value.
217
+ */
218
+ type FieldGenerator<T = unknown> = T | ((context: Context, payload: Record<string, unknown>) => T | Promise<T>);
219
+ /**
220
+ * Configuration object for defining an action.
221
+ * @see {@link Action} for the resolved type
222
+ */
223
+ interface ActionDefinition {
224
+ id: string;
225
+ name: string;
226
+ /**
227
+ * Map of field names to their generators.
228
+ */
229
+ fields?: Record<string, FieldGenerator>;
230
+ /**
231
+ * Custom handler function. If provided, 'fields' is ignored.
232
+ * Must return an array of Events.
233
+ */
234
+ handler?: (context: Context) => Event[] | Promise<Event[]>;
235
+ /**
236
+ * Time span between individual events generated by this action.
237
+ */
238
+ timeSpan?: TimeSpan;
239
+ /**
240
+ * Chance (0-1) to abandon during this action.
241
+ */
242
+ bounceChance?: number;
243
+ }
244
+ /**
245
+ * Validation behavior when an event fails schema validation.
246
+ * - 'strict': throw on first invalid event
247
+ * - 'warn': keep the event but record the failure in the summary
248
+ * - 'skip': drop the event and record the failure in the summary
249
+ */
250
+ type EventValidationMode = 'strict' | 'warn' | 'skip';
251
+ /**
252
+ * Configuration for event schema validation.
253
+ *
254
+ * @param schema - A single Zod schema applied to all events, or a record mapping
255
+ * event names to their individual Zod schemas
256
+ * @param mode - Validation behavior on failure (default: 'strict')
257
+ */
258
+ interface EventSchemaConfig {
259
+ schema: z.ZodType | Record<string, z.ZodType>;
260
+ mode?: EventValidationMode;
261
+ }
262
+ /**
263
+ * Interface for output adapters that handle generated events.
264
+ *
265
+ * Adapters receive events one at a time during generation and are responsible
266
+ * for routing them to their final destination (console, file, HTTP, etc.).
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * const adapter: OutputAdapter = {
271
+ * write(event) { console.log(event.name); },
272
+ * close() { console.log('done'); },
273
+ * };
274
+ * ```
275
+ */
276
+ interface OutputAdapter {
277
+ /**
278
+ * Writes a single event to the output destination.
279
+ *
280
+ * @param event - The generated event to write
281
+ * @returns void or a Promise that resolves when the write completes
282
+ */
283
+ write(event: Event): Promise<void> | void;
284
+ /**
285
+ * Optional cleanup hook called once after all events have been written.
286
+ * Use for flushing buffers, closing file handles, or finalizing connections.
287
+ *
288
+ * @returns void or a Promise that resolves when cleanup completes
289
+ */
290
+ close?(): Promise<void> | void;
291
+ }
292
+ //#endregion
293
+ //#region src/errors.d.ts
294
+ /**
295
+ * Error codes for all Synode validation and runtime errors.
296
+ */
297
+ type ErrorCode = 'INVALID_BOUNCE_CHANCE' | 'INVALID_TIME_SPAN' | 'INVALID_SUPPRESSION_PERIOD' | 'UNKNOWN_JOURNEY_REF' | 'CIRCULAR_DEPENDENCY' | 'DUPLICATE_ID' | 'DATASET_NOT_FOUND' | 'DATASET_EMPTY' | 'HANDLER_ERROR' | 'ADAPTER_WRITE_ERROR' | 'INVALID_HANDLER_RETURN' | 'TYPO_DETECTED' | 'INVALID_DATASET_COUNT';
298
+ /**
299
+ * Options for constructing a SynodeError.
300
+ *
301
+ * @param code - The structured error code identifying the error type
302
+ * @param message - The human-readable error message
303
+ * @param path - Hierarchical path segments (journey, adventure, action, step)
304
+ * @param suggestion - Optional suggestion for fixing the error
305
+ * @param expected - Optional description of the expected value
306
+ * @param received - Optional description of the actual value received
307
+ * @param cause - Optional underlying error that caused this one
308
+ */
309
+ interface SynodeErrorOptions {
310
+ code: ErrorCode;
311
+ message: string;
312
+ path: string[];
313
+ suggestion?: string;
314
+ expected?: string;
315
+ received?: string;
316
+ cause?: unknown;
317
+ }
318
+ /**
319
+ * Formats an error message with a hierarchical path prefix and optional suggestion.
320
+ *
321
+ * @param message - The base error message
322
+ * @param path - Hierarchical path segments labeled Journey/Adventure/Action/Step
323
+ * @param suggestion - Optional suggestion appended on a new line
324
+ * @returns The formatted message string
325
+ */
326
+ declare function formatErrorMessage(message: string, path: string[], suggestion?: string): string;
327
+ /**
328
+ * Structured error class for all Synode validation and runtime errors.
329
+ *
330
+ * @example
331
+ * ```typescript
332
+ * throw new SynodeError({
333
+ * code: 'INVALID_BOUNCE_CHANCE',
334
+ * message: 'Bounce chance must be between 0 and 1',
335
+ * path: ['Purchase Flow', 'Checkout', 'Submit Order'],
336
+ * expected: 'number between 0 and 1',
337
+ * received: '1.5',
338
+ * });
339
+ * ```
340
+ */
341
+ declare class SynodeError extends Error {
342
+ /** Structured error code identifying the error type. */
343
+ readonly code: ErrorCode;
344
+ /** Hierarchical path where the error occurred. */
345
+ readonly path: string[];
346
+ /** Optional suggestion for fixing the error. */
347
+ readonly suggestion: string | undefined;
348
+ /** The original unformatted error message. */
349
+ readonly rawMessage: string;
350
+ /** Optional description of the expected value. */
351
+ readonly expected: string | undefined;
352
+ /** Optional description of the actual value received. */
353
+ readonly received: string | undefined;
354
+ constructor(options: SynodeErrorOptions);
355
+ /**
356
+ * Produces a structured multi-line representation of the error for human-readable output.
357
+ *
358
+ * Format:
359
+ * ```
360
+ * [ERROR_CODE] rawMessage
361
+ * Path: Journey 'x' > Adventure 'y' > Action 'z'
362
+ * Expected: what was expected
363
+ * Received: what was provided
364
+ * Fix: suggestion
365
+ * ```
366
+ *
367
+ * Path is omitted when path is empty. Expected/Received are omitted when not provided.
368
+ * Fix is omitted when there is no suggestion.
369
+ *
370
+ * @returns A formatted string representation of the error.
371
+ */
372
+ format(): string;
373
+ }
374
+ /**
375
+ * Computes the Levenshtein edit distance between two strings using a space-efficient
376
+ * two-row approach.
377
+ *
378
+ * @param a - First string
379
+ * @param b - Second string
380
+ * @returns The minimum number of single-character edits (insertions, deletions, substitutions)
381
+ */
382
+ declare function levenshtein(a: string, b: string): number;
383
+ /**
384
+ * Finds the closest matching string from a list of candidates using Levenshtein distance.
385
+ * Returns undefined if no candidate is within the dynamic threshold.
386
+ *
387
+ * The threshold is `max(3, floor(maxLen * 0.4))` where maxLen is the longer of the
388
+ * input and candidate strings.
389
+ *
390
+ * @param input - The string to match against candidates
391
+ * @param candidates - List of valid strings to compare against
392
+ * @returns The closest matching candidate, or undefined if none is close enough
393
+ */
394
+ declare function suggestClosest(input: string, candidates: string[]): string | undefined;
395
+ /**
396
+ * Builds a human-readable suggestion string for a not-found error, including
397
+ * the closest match (if any) and the full list of available options.
398
+ *
399
+ * @param kind - The kind of entity (e.g., "dataset", "journey")
400
+ * @param requested - The name that was requested but not found
401
+ * @param available - List of valid names
402
+ * @returns A suggestion string with "Did you mean..." and/or available options
403
+ */
404
+ declare function buildNotFoundSuggestion(kind: string, requested: string, available: string[]): string;
405
+ //#endregion
406
+ //#region src/state/ids.d.ts
407
+ /**
408
+ * Interface for ID generation strategies.
409
+ */
410
+ interface IdGenerator {
411
+ generate(prefix?: string): string;
412
+ }
413
+ //#endregion
414
+ //#region src/state/context.d.ts
415
+ declare class SynodeContext implements Context {
416
+ private idGenerator;
417
+ readonly locale: string;
418
+ private state;
419
+ private fieldMetadata;
420
+ private currentTime;
421
+ private _userId;
422
+ private _sessionId;
423
+ private _faker;
424
+ private completedJourneys;
425
+ private datasets;
426
+ constructor(startTime?: Date, idGenerator?: IdGenerator, locale?: string);
427
+ get faker(): Faker;
428
+ get userId(): string;
429
+ get sessionId(): string;
430
+ get<T>(key: string): T | undefined;
431
+ /**
432
+ * Set a context field value with optional scope for automatic cleanup.
433
+ *
434
+ * @param key - The field name
435
+ * @param value - The field value
436
+ * @param options - Optional settings including lifecycle scope
437
+ *
438
+ * @remarks
439
+ * When calling `set()` multiple times on the same field with different scopes,
440
+ * the most recent scope setting will be used. For example, if a field is first
441
+ * set with `scope: 'adventure'` and later set again with `scope: 'action'`,
442
+ * it will be cleared at the end of the action rather than the adventure.
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * ctx.set('cartItems', [], { scope: 'adventure' });
447
+ * // Later in the same adventure...
448
+ * ctx.set('cartItems', ['item1'], { scope: 'action' }); // Now scoped to action
449
+ * ```
450
+ */
451
+ set<T>(key: string, value: T, options?: ContextSetOptions): void;
452
+ now(): Date;
453
+ generateId(prefix?: string): string;
454
+ hasCompletedJourney(journeyId: string): boolean;
455
+ markJourneyComplete(journeyId: string): void;
456
+ dataset(id: string): DatasetHandle;
457
+ typedDataset<TRow extends DatasetRow>(id: string): DatasetHandle<TRow>;
458
+ /**
459
+ * Internal: Register a dataset for use in this context.
460
+ */
461
+ registerDataset(dataset: Dataset): void;
462
+ /**
463
+ * Internal: Advance the simulation time.
464
+ */
465
+ advanceTime(ms: number): void;
466
+ /**
467
+ * Internal: Rotate the session ID.
468
+ */
469
+ rotateSession(): void;
470
+ /**
471
+ * Internal: Clear all fields with the specified scope.
472
+ * Called by the engine when a scope ends.
473
+ */
474
+ clearScope(scope: ContextScope): void;
475
+ }
476
+ //#endregion
477
+ //#region src/generators/builder.d.ts
478
+ /**
479
+ * Defines a new journey.
480
+ * @param config The journey configuration.
481
+ * @returns The configured journey object.
482
+ * @see {@link Journey}
483
+ */
484
+ declare function defineJourney(config: Journey): Journey;
485
+ /**
486
+ * Defines a new adventure.
487
+ * @param config The adventure configuration.
488
+ * @returns The configured adventure object.
489
+ * @see {@link Adventure}
490
+ */
491
+ declare function defineAdventure(config: Adventure): Adventure;
492
+ /**
493
+ * Defines a new action.
494
+ * @param config The action configuration.
495
+ * @returns The configured action object.
496
+ * @see {@link Action}
497
+ * @see {@link ActionDefinition}
498
+ */
499
+ declare function defineAction(config: ActionDefinition): Action;
500
+ //#endregion
501
+ //#region src/generators/fields.d.ts
502
+ /**
503
+ * Returns a value generated by Faker.js using the context's locale.
504
+ * @param generator Function that takes a Faker instance and returns a value.
505
+ */
506
+ declare function fake<T>(generator: (faker: Faker) => T): FieldGenerator<T>;
507
+ /**
508
+ * Returns one of the provided options randomly.
509
+ */
510
+ declare function oneOf<T>(options: T[]): FieldGenerator<T>;
511
+ /**
512
+ * Returns true with the given probability (0-1).
513
+ */
514
+ declare function chance(probability: number): FieldGenerator<boolean>;
515
+ /**
516
+ * Returns a value based on weighted probabilities.
517
+ * @param options Map of value to weight (weights should sum to 1, but will be normalized if not)
518
+ */
519
+ declare function weighted<T extends string>(options: Record<T, number>): FieldGenerator<T>;
520
+ //#endregion
521
+ //#region src/generators/persona.d.ts
522
+ /**
523
+ * Configuration for a user persona.
524
+ */
525
+ interface PersonaDefinition {
526
+ id: string;
527
+ name: string;
528
+ /**
529
+ * Attributes to initialize in the user's context.
530
+ * Can include 'locale' to set the faker locale.
531
+ */
532
+ attributes: Record<string, FieldGenerator>;
533
+ }
534
+ /**
535
+ * Represents an instantiated persona.
536
+ */
537
+ interface Persona {
538
+ id: string;
539
+ name: string;
540
+ attributes: Record<string, unknown>;
541
+ }
542
+ /**
543
+ * Defines a new persona.
544
+ */
545
+ declare function definePersona(config: PersonaDefinition): PersonaDefinition;
546
+ /**
547
+ * Generates a concrete persona instance from a definition.
548
+ * This resolves all dynamic fields and weighted distributions.
549
+ */
550
+ declare function generatePersona(definition: PersonaDefinition, baseContext: Context): Promise<Persona>;
551
+ //#endregion
552
+ //#region src/generators/dataset.d.ts
553
+ /**
554
+ * Defines a new dataset with type inference support.
555
+ *
556
+ * @example
557
+ * ```typescript
558
+ * const products = defineDataset({
559
+ * id: 'products',
560
+ * name: 'Products',
561
+ * count: 100,
562
+ * fields: {
563
+ * id: (ctx, row) => `prod-${row.index}`,
564
+ * name: (ctx) => ctx.faker.commerce.productName(),
565
+ * price: (ctx) => ctx.faker.number.float({ min: 10, max: 500 }),
566
+ * },
567
+ * });
568
+ *
569
+ * // Type inference works automatically
570
+ * type Product = InferDatasetRow<typeof products>;
571
+ * ```
572
+ */
573
+ declare function defineDataset<TFields$1 extends Record<string, unknown>>(config: DatasetDefinition<TFields$1>): DatasetDefinition<TFields$1>;
574
+ /**
575
+ * Generates a concrete dataset from a definition.
576
+ * @param definition The dataset definition.
577
+ * @param context Context providing access to faker, other datasets, and user data.
578
+ */
579
+ declare function generateDataset<TFields$1 extends Record<string, unknown>>(definition: DatasetDefinition<TFields$1>, context: Context): Promise<Dataset>;
580
+ //#endregion
581
+ //#region src/execution/engine.d.ts
582
+ declare class Engine {
583
+ private journey;
584
+ constructor(journey: Journey);
585
+ /**
586
+ * Executes the journey and yields events.
587
+ * @param context Optional context to use. If not provided, a new one is created.
588
+ */
589
+ run(context?: SynodeContext): AsyncGenerator<Event>;
590
+ }
591
+ //#endregion
592
+ //#region src/execution/runner.d.ts
593
+ interface RunOptions {
594
+ /**
595
+ * Total number of users to simulate.
596
+ */
597
+ users: number;
598
+ /**
599
+ * Persona definition to use for generating users.
600
+ * If provided, context will be initialized with persona attributes.
601
+ */
602
+ persona?: PersonaDefinition;
603
+ /**
604
+ * Optional dataset definitions to pre-generate before journey execution.
605
+ * Datasets can be referenced within journey actions using ctx.dataset('id').
606
+ */
607
+ datasets?: DatasetDefinition[];
608
+ /**
609
+ * Number of parallel lanes to use for generation.
610
+ * Lanes process users concurrently using async execution.
611
+ * @default 1
612
+ */
613
+ lanes?: number;
614
+ /**
615
+ * Output adapter to write events to.
616
+ * @default ConsoleAdapter
617
+ */
618
+ adapter?: OutputAdapter;
619
+ /**
620
+ * Enable debug mode with telemetry collection.
621
+ * When enabled, collects detailed metrics about the generation run.
622
+ * @default false
623
+ */
624
+ debug?: boolean;
625
+ /**
626
+ * File path to save telemetry data when debug mode is enabled.
627
+ * @default './telemetry-report.json'
628
+ */
629
+ telemetryPath?: string;
630
+ /**
631
+ * Pre-loaded datasets to register with each user's context.
632
+ * Unlike `datasets` (which generates from definitions), these are already populated.
633
+ * Useful for datasets imported from external sources like BigQuery.
634
+ */
635
+ preloadedDatasets?: Dataset[];
636
+ /**
637
+ * Start of the date range for user start times.
638
+ * Must be provided together with endDate.
639
+ */
640
+ startDate?: Date;
641
+ /**
642
+ * End of the date range for user start times.
643
+ * Must be provided together with startDate.
644
+ */
645
+ endDate?: Date;
646
+ /**
647
+ * Event schema validation configuration.
648
+ * When provided, each event is validated against the schema before adapter.write().
649
+ */
650
+ eventSchema?: EventSchemaConfig;
651
+ /**
652
+ * Path to a module that exports journey definitions for worker thread parallelism.
653
+ * When provided, generation runs in worker threads instead of Promise.all lanes.
654
+ * The module must export: { journeys: Journey[], persona?, datasets?, preloadedDatasets? }
655
+ */
656
+ workerModule?: string;
657
+ /**
658
+ * Number of worker threads to spawn. Default: number of CPU cores.
659
+ * Only used when workerModule is set.
660
+ */
661
+ workers?: number;
662
+ }
663
+ /**
664
+ * Generates synthetic data based on the provided journey configuration.
665
+ */
666
+ declare function generate(journey: Journey | Journey[], options: RunOptions): Promise<void>;
667
+ //#endregion
668
+ //#region src/execution/worker-types.d.ts
669
+ /**
670
+ * Initialization payload sent to each worker thread via `workerData`.
671
+ * Contains the module path, user range assignment, and any pre-serialized datasets.
672
+ */
673
+ interface WorkerInit {
674
+ /** Absolute path to the user's journey module (TS or JS). */
675
+ workerModule: string;
676
+ /** First user index (inclusive) this worker should process. */
677
+ userStart: number;
678
+ /** Last user index (exclusive) this worker should process. */
679
+ userEnd: number;
680
+ /** ISO 8601 start date for randomizing user start times. */
681
+ startDate?: string;
682
+ /** ISO 8601 end date for randomizing user start times. */
683
+ endDate?: string;
684
+ /** Pre-generated datasets serialized for structured clone transfer. */
685
+ serializedDatasets?: SerializedDataset[];
686
+ }
687
+ /**
688
+ * A dataset serialized for transfer across the structured clone boundary.
689
+ * Mirrors {@link Dataset} but uses plain objects instead of class instances.
690
+ */
691
+ interface SerializedDataset {
692
+ /** Unique dataset identifier. */
693
+ id: string;
694
+ /** Human-readable dataset name. */
695
+ name: string;
696
+ /** The dataset rows as plain objects. */
697
+ rows: Record<string, unknown>[];
698
+ }
699
+ /**
700
+ * Message sent from a worker when it has generated a batch of events for one user.
701
+ */
702
+ interface WorkerEventBatch {
703
+ type: 'events';
704
+ events: Event[];
705
+ }
706
+ /**
707
+ * Message sent from a worker when it begins processing a new user.
708
+ */
709
+ interface WorkerUserStarted {
710
+ type: 'user-started';
711
+ }
712
+ /**
713
+ * Message sent from a worker when it finishes processing a user.
714
+ */
715
+ interface WorkerUserCompleted {
716
+ type: 'user-completed';
717
+ /** Number of events generated for this user. */
718
+ eventCount: number;
719
+ }
720
+ /**
721
+ * Message sent from a worker when an unrecoverable error occurs.
722
+ */
723
+ interface WorkerErrorMessage {
724
+ type: 'error';
725
+ /** Human-readable error message. */
726
+ message: string;
727
+ /** Optional stack trace from the original error. */
728
+ stack?: string;
729
+ }
730
+ /**
731
+ * Final message sent from a worker when all assigned users have been processed.
732
+ */
733
+ interface WorkerDone {
734
+ type: 'done';
735
+ /** Total events generated across all users in this worker. */
736
+ totalEvents: number;
737
+ /** Total users processed by this worker. */
738
+ totalUsers: number;
739
+ }
740
+ /**
741
+ * Discriminated union of all messages a worker thread can send to the parent.
742
+ * Use the `type` field to narrow the message in a switch/if block.
743
+ */
744
+ type WorkerMessage = WorkerEventBatch | WorkerUserStarted | WorkerUserCompleted | WorkerErrorMessage | WorkerDone;
745
+ //#endregion
746
+ //#region src/monitoring/validation.d.ts
747
+ declare const ActionSchema: z.ZodObject<{
748
+ id: z.ZodString;
749
+ name: z.ZodString;
750
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
751
+ }, z.core.$strip>;
752
+ declare const AdventureSchema: z.ZodObject<{
753
+ id: z.ZodString;
754
+ name: z.ZodString;
755
+ actions: z.ZodArray<z.ZodObject<{
756
+ id: z.ZodString;
757
+ name: z.ZodString;
758
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
759
+ }, z.core.$strip>>;
760
+ }, z.core.$strip>;
761
+ declare const JourneySchema: z.ZodObject<{
762
+ id: z.ZodString;
763
+ name: z.ZodString;
764
+ adventures: z.ZodArray<z.ZodObject<{
765
+ id: z.ZodString;
766
+ name: z.ZodString;
767
+ actions: z.ZodArray<z.ZodObject<{
768
+ id: z.ZodString;
769
+ name: z.ZodString;
770
+ handler: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
771
+ }, z.core.$strip>>;
772
+ }, z.core.$strip>>;
773
+ }, z.core.$strip>;
774
+ /**
775
+ * Validates that a bounce chance value is between 0 and 1 (inclusive).
776
+ *
777
+ * @param value - The bounce chance value to validate
778
+ * @param path - Hierarchical path for error reporting
779
+ * @throws SynodeError with INVALID_BOUNCE_CHANCE code if out of range
780
+ */
781
+ declare function validateBounceChance(value: number | undefined, path: string[]): void;
782
+ /**
783
+ * Validates that a time span has min <= max.
784
+ *
785
+ * @param timeSpan - The time span to validate
786
+ * @param path - Hierarchical path for error reporting
787
+ * @throws SynodeError with INVALID_TIME_SPAN code if min > max
788
+ */
789
+ declare function validateTimeSpan(timeSpan: TimeSpan | undefined, path: string[]): void;
790
+ /**
791
+ * Validates the journey configuration including structural checks for bounce chances,
792
+ * time spans, suppression periods, duplicate IDs, unknown references, and circular dependencies.
793
+ *
794
+ * @param config - The journey configuration to validate
795
+ * @param allJourneys - Optional list of all journeys for cross-journey validation
796
+ * @throws ZodError if basic schema validation fails
797
+ * @throws SynodeError for structural validation failures
798
+ */
799
+ declare function validateConfig(config: Journey, allJourneys?: Journey[]): void;
800
+ /**
801
+ * Performs a dry run of the journey for a specified number of users.
802
+ * Returns all generated events in memory.
803
+ */
804
+ declare function dryRun(journey: Journey, userCount?: number): Promise<Event[]>;
805
+ //#endregion
806
+ //#region src/monitoring/event-validation.d.ts
807
+ /**
808
+ * A single validation issue from a failed schema check.
809
+ */
810
+ interface ValidationIssue {
811
+ path: (string | number)[];
812
+ message: string;
813
+ code: string;
814
+ }
815
+ /**
816
+ * Options for constructing a SynodeValidationError.
817
+ */
818
+ interface SynodeValidationErrorOptions {
819
+ message: string;
820
+ event: Event;
821
+ issues: ValidationIssue[];
822
+ }
823
+ /**
824
+ * Error thrown when an event fails schema validation in strict mode.
825
+ */
826
+ declare class SynodeValidationError extends Error {
827
+ readonly event: Event;
828
+ readonly issues: ValidationIssue[];
829
+ constructor(options: SynodeValidationErrorOptions);
830
+ }
831
+ /**
832
+ * Aggregate summary of event validation results.
833
+ */
834
+ interface ValidationSummary {
835
+ eventsValidated: number;
836
+ eventsValid: number;
837
+ eventsInvalid: number;
838
+ validationErrors: {
839
+ eventName: string;
840
+ path: string;
841
+ message: string;
842
+ }[];
843
+ }
844
+ /**
845
+ * Wraps `z.object()` for defining event payload schemas.
846
+ *
847
+ * @param shape - Zod raw shape describing the expected payload fields
848
+ * @returns A ZodObject schema
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * const pageViewSchema = defineEventSchema({
853
+ * url: z.string().url(),
854
+ * referrer: z.string().optional(),
855
+ * });
856
+ * ```
857
+ */
858
+ declare function defineEventSchema<T extends z.ZodRawShape>(shape: T): z.ZodObject<T>;
859
+ /**
860
+ * Creates a fresh zeroed validation summary.
861
+ *
862
+ * @returns An empty ValidationSummary ready for accumulation
863
+ */
864
+ declare function createValidationSummary(): ValidationSummary;
865
+ /**
866
+ * Validates an event against its configured schema.
867
+ *
868
+ * Behavior depends on the configured mode:
869
+ * - `strict` (default): throws {@link SynodeValidationError} on first failure
870
+ * - `warn`: returns the event but records failure in the summary
871
+ * - `skip`: returns `undefined` (event is dropped) and records failure in the summary
872
+ *
873
+ * Events with no matching schema in a per-name map are passed through without validation.
874
+ *
875
+ * @param event - The event to validate
876
+ * @param config - Schema and mode configuration
877
+ * @param summary - Mutable summary accumulating validation statistics
878
+ * @returns The event if it passes or is kept (warn mode), or undefined if skipped
879
+ * @throws SynodeValidationError in strict mode when validation fails
880
+ */
881
+ declare function validateEvent(event: Event, config: EventSchemaConfig, summary: ValidationSummary): Event | undefined;
882
+ //#endregion
883
+ //#region src/monitoring/telemetry.d.ts
884
+ /**
885
+ * Telemetry data point captured every second during generation.
886
+ */
887
+ interface TelemetrySnapshot {
888
+ /**
889
+ * Timestamp of this snapshot in ISO format.
890
+ */
891
+ timestamp: string;
892
+ /**
893
+ * Elapsed time since generation started in milliseconds.
894
+ */
895
+ elapsedMs: number;
896
+ /**
897
+ * Number of events generated in this second.
898
+ */
899
+ eventsPerSecond: number;
900
+ /**
901
+ * Total events generated so far.
902
+ */
903
+ totalEvents: number;
904
+ /**
905
+ * Number of users currently being processed across all lanes.
906
+ */
907
+ activeUsers: number;
908
+ /**
909
+ * Number of users completed so far.
910
+ */
911
+ completedUsers: number;
912
+ /**
913
+ * Number of parallel lanes in use.
914
+ */
915
+ lanes: number;
916
+ }
917
+ /**
918
+ * A single validation error captured during event generation.
919
+ */
920
+ interface TelemetryValidationError {
921
+ eventName: string;
922
+ path: string;
923
+ message: string;
924
+ }
925
+ /**
926
+ * Complete telemetry report for a generation run.
927
+ */
928
+ interface TelemetryReport {
929
+ /**
930
+ * When the generation started.
931
+ */
932
+ startTime: string;
933
+ /**
934
+ * When the generation ended.
935
+ */
936
+ endTime: string;
937
+ /**
938
+ * Total duration in milliseconds.
939
+ */
940
+ durationMs: number;
941
+ /**
942
+ * Total number of users processed.
943
+ */
944
+ totalUsers: number;
945
+ /**
946
+ * Total number of events generated.
947
+ */
948
+ totalEvents: number;
949
+ /**
950
+ * Number of parallel lanes used.
951
+ */
952
+ lanes: number;
953
+ /**
954
+ * Average events per second across the entire run.
955
+ */
956
+ averageEventsPerSecond: number;
957
+ /**
958
+ * Number of users currently active (at report time).
959
+ */
960
+ activeUsers: number;
961
+ /**
962
+ * Number of users completed (at report time).
963
+ */
964
+ completedUsers: number;
965
+ /**
966
+ * Total number of events that were validated.
967
+ */
968
+ eventsValidated: number;
969
+ /**
970
+ * Total number of events that passed validation.
971
+ */
972
+ eventsValid: number;
973
+ /**
974
+ * Total number of events that failed validation.
975
+ */
976
+ eventsInvalid: number;
977
+ /**
978
+ * Validation errors collected during the run (capped at 50).
979
+ */
980
+ validationErrors: TelemetryValidationError[];
981
+ /**
982
+ * Per-second telemetry snapshots.
983
+ */
984
+ snapshots: TelemetrySnapshot[];
985
+ }
986
+ /**
987
+ * Collects telemetry data during generation runs.
988
+ */
989
+ declare class TelemetryCollector {
990
+ private startTime;
991
+ private snapshots;
992
+ private intervalHandle;
993
+ private currentSecondEvents;
994
+ private totalEvents;
995
+ private activeUsers;
996
+ private completedUsers;
997
+ private lanes;
998
+ private eventsValidated;
999
+ private eventsValid;
1000
+ private eventsInvalid;
1001
+ private validationErrors;
1002
+ constructor(lanes: number);
1003
+ /**
1004
+ * Start collecting telemetry data every second.
1005
+ */
1006
+ start(): void;
1007
+ /**
1008
+ * Stop collecting telemetry data.
1009
+ */
1010
+ stop(): void;
1011
+ /**
1012
+ * Record that an event was generated.
1013
+ */
1014
+ recordEvent(): void;
1015
+ /**
1016
+ * Record that a user started processing.
1017
+ */
1018
+ recordUserStarted(): void;
1019
+ /**
1020
+ * Record that a user completed processing.
1021
+ */
1022
+ recordUserCompleted(): void;
1023
+ /**
1024
+ * Merge a validation summary from a lane or journey into the collector totals.
1025
+ *
1026
+ * @param summary - Aggregated validation counts and errors to record.
1027
+ */
1028
+ recordValidationSummary(summary: {
1029
+ eventsValidated: number;
1030
+ eventsValid: number;
1031
+ eventsInvalid: number;
1032
+ validationErrors: {
1033
+ eventName: string;
1034
+ path: string;
1035
+ message: string;
1036
+ }[];
1037
+ }): void;
1038
+ /**
1039
+ * Generate the final telemetry report.
1040
+ */
1041
+ getReport(): TelemetryReport;
1042
+ /**
1043
+ * Save the telemetry report to a JSON file.
1044
+ */
1045
+ saveReport(filePath: string): Promise<void>;
1046
+ private captureSnapshot;
1047
+ }
1048
+ //#endregion
1049
+ //#region src/monitoring/timing.d.ts
1050
+ /**
1051
+ * Generates a delay in milliseconds based on a time span configuration.
1052
+ */
1053
+ declare function generateDelay(timeSpan: TimeSpan): number;
1054
+ /**
1055
+ * Checks if a bounce should occur based on probability.
1056
+ */
1057
+ declare function shouldBounce(bounceChance?: number): boolean;
1058
+ //#endregion
1059
+ //#region src/adapters/console.d.ts
1060
+ /**
1061
+ * Adapter that writes events to the console as pretty-printed JSON.
1062
+ *
1063
+ * @example
1064
+ * ```ts
1065
+ * await generate(journey, { users: 10, adapter: new ConsoleAdapter() });
1066
+ * ```
1067
+ */
1068
+ declare class ConsoleAdapter implements OutputAdapter {
1069
+ /** @inheritdoc */
1070
+ write(event: Event): void;
1071
+ }
1072
+ //#endregion
1073
+ //#region src/adapters/memory.d.ts
1074
+ /**
1075
+ * Adapter that stores events in memory.
1076
+ * Useful for testing and dry runs.
1077
+ *
1078
+ * @example
1079
+ * ```ts
1080
+ * const adapter = new InMemoryAdapter();
1081
+ * await generate(journey, { users: 10, adapter });
1082
+ * console.log(adapter.events.length);
1083
+ * ```
1084
+ */
1085
+ declare class InMemoryAdapter implements OutputAdapter {
1086
+ readonly events: Event[];
1087
+ /** @inheritdoc */
1088
+ write(event: Event): void;
1089
+ /**
1090
+ * Clears all stored events.
1091
+ */
1092
+ clear(): void;
1093
+ }
1094
+ //#endregion
1095
+ //#region src/adapters/callback.d.ts
1096
+ /**
1097
+ * Adapter that forwards events to a user-supplied callback function.
1098
+ * Supports both synchronous and asynchronous callbacks.
1099
+ *
1100
+ * @example
1101
+ * ```ts
1102
+ * const events: Event[] = [];
1103
+ * const adapter = new CallbackAdapter((event) => events.push(event));
1104
+ * await generate(journey, { users: 10, adapter });
1105
+ * ```
1106
+ */
1107
+ declare class CallbackAdapter implements OutputAdapter {
1108
+ private callback;
1109
+ constructor(callback: (event: Event) => void | Promise<void>);
1110
+ /** @inheritdoc */
1111
+ write(event: Event): Promise<void>;
1112
+ }
1113
+ //#endregion
1114
+ //#region src/dataset-io.d.ts
1115
+ /**
1116
+ * Export format options.
1117
+ */
1118
+ type ExportFormat = 'csv' | 'json' | 'jsonl';
1119
+ /**
1120
+ * Exports a dataset to a string in the specified format.
1121
+ */
1122
+ declare function exportDatasetToString(dataset: Dataset, format: ExportFormat): string;
1123
+ /**
1124
+ * Imports a dataset from a string in the specified format.
1125
+ */
1126
+ declare function importDatasetFromString(content: string, format: ExportFormat, id?: string, name?: string): Dataset;
1127
+ /**
1128
+ * Validates that a file path does not escape the given base directory.
1129
+ * @throws Error if path traversal is detected.
1130
+ */
1131
+ declare function validateFilePath(filePath: string, basePath?: string): string;
1132
+ /**
1133
+ * Exports a dataset to a file in the specified format.
1134
+ */
1135
+ declare function exportDataset(dataset: Dataset, filePath: string, format: ExportFormat): Promise<void>;
1136
+ /**
1137
+ * Imports a dataset from a file in the specified format.
1138
+ */
1139
+ declare function importDataset(id: string, name: string, filePath: string, format: ExportFormat): Promise<Dataset>;
1140
+ //#endregion
1141
+ export { Action, ActionDefinition, ActionSchema, Adventure, AdventureSchema, CallbackAdapter, ConsoleAdapter, Context, ContextScope, ContextSetOptions, Dataset, DatasetDefinition, DatasetFieldGenerator, DatasetHandle, DatasetRow, Engine, ErrorCode, Event, EventSchemaConfig, EventValidationMode, ExportFormat, FieldGenerator, InMemoryAdapter, InferDatasetRow, Journey, JourneySchema, OutputAdapter, Persona, PersonaDefinition, RunOptions, SerializedDataset, SuppressionPeriod, SynodeContext, SynodeError, SynodeErrorOptions, SynodeValidationError, SynodeValidationErrorOptions, TelemetryCollector, TelemetryReport, TelemetrySnapshot, TelemetryValidationError, TimeSpan, ValidationIssue, ValidationSummary, WorkerDone, WorkerErrorMessage, WorkerEventBatch, WorkerInit, WorkerMessage, WorkerUserCompleted, WorkerUserStarted, buildNotFoundSuggestion, chance, createValidationSummary, defineAction, defineAdventure, defineDataset, defineEventSchema, defineJourney, definePersona, dryRun, exportDataset, exportDatasetToString, fake, formatErrorMessage, generate, generateDataset, generateDelay, generatePersona, importDataset, importDatasetFromString, levenshtein, oneOf, shouldBounce, suggestClosest, validateBounceChance, validateConfig, validateEvent, validateFilePath, validateTimeSpan, weighted };
1142
+ //# sourceMappingURL=index.d.cts.map