Advanced Usage

Explore advanced patterns for custom distributions, validation, serialization, and extending the Persona SDK for complex use cases.

Custom Distributions

Create your own distribution classes by extending BaseDistribution:

custom-distribution.ts
1import { BaseDistribution } from '@jamesaphoenix/persona-sdk';
2
3// Custom triangular distribution
4export class TriangularDistribution extends BaseDistribution<number> {
5 constructor(
6 private readonly min: number,
7 private readonly max: number,
8 private readonly mode: number,
9 seed?: number
10 ) {
11 super(seed);
12 if (min >= max || mode < min || mode > max) {
13 throw new Error('Invalid triangular distribution parameters');
14 }
15 }
16
17 sample(): number {
18 const u = this.random.real(0, 1, false);
19 const F = (this.mode - this.min) / (this.max - this.min);
20
21 if (u < F) {
22 return this.min + Math.sqrt(u * (this.max - this.min) * (this.mode - this.min));
23 } else {
24 return this.max - Math.sqrt((1 - u) * (this.max - this.min) * (this.max - this.mode));
25 }
26 }
27
28 mean(): number {
29 return (this.min + this.max + this.mode) / 3;
30 }
31
32 variance(): number {
33 const a = this.min, b = this.max, c = this.mode;
34 return (a*a + b*b + c*c - a*b - a*c - b*c) / 18;
35 }
36
37 toString(): string {
38 return `Triangular(${this.min}, ${this.max}, ${this.mode})`;
39 }
40}
41
42// Use in PersonaBuilder
43const persona = PersonaBuilder.create()
44 .withAge(new TriangularDistribution(22, 65, 35)) // Most people around 35
45 .withName('Professional')
46 .build();

Validation & Constraints

Add validation rules and constraints to ensure data quality:

validation.ts
1import { PersonaBuilder, ValidationRule } from '@jamesaphoenix/persona-sdk';
2
3// Custom validation rules
4const ageValidation: ValidationRule = {
5 attribute: 'age',
6 validate: (value: any) => {
7 if (typeof value !== 'number' || value < 0 || value > 120) {
8 throw new Error('Age must be between 0 and 120');
9 }
10 return true;
11 }
12};
13
14const emailValidation: ValidationRule = {
15 attribute: 'email',
16 validate: (value: any) => {
17 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
18 if (typeof value !== 'string' || !emailRegex.test(value)) {
19 throw new Error('Invalid email format');
20 }
21 return true;
22 }
23};
24
25// Builder with validation
26const persona = PersonaBuilder.create()
27 .withValidation([ageValidation, emailValidation])
28 .withAge(28)
29 .withAttribute('email', 'alice@example.com')
30 .withAttribute('salary', 75000)
31 .addConstraint('salary', (value) => value > 0 && value < 1000000)
32 .build();
33
34// Validation runs automatically during build()
35console.log('Persona created with valid data:', persona.toObject());

Serialization & Persistence

Save and load personas with complete state preservation:

serialization.ts
1import { PersonaGroup, PersonaSerializer } from '@jamesaphoenix/persona-sdk';
2
3// Create and populate group
4const group = new PersonaGroup('Engineering Team');
5group.add(PersonaBuilder.create()
6 .withName('Alice')
7 .withAge(28)
8 .withOccupation('Developer')
9 .build()
10);
11
12// Serialize to JSON
13const serialized = PersonaSerializer.serialize(group);
14console.log('Serialized:', JSON.stringify(serialized, null, 2));
15
16// Save to file
17await PersonaSerializer.saveToFile(group, 'team.json');
18
19// Load from file
20const loadedGroup = await PersonaSerializer.loadFromFile('team.json');
21console.log('Loaded group:', loadedGroup.name, loadedGroup.size);
22
23// Serialize with distributions (preserves random state)
24const builderWithDistribution = PersonaBuilder.create()
25 .withAge(new NormalDistribution(35, 5, 12345)) // with seed
26 .withName('Random Employee');
27
28const serializedBuilder = PersonaSerializer.serializeBuilder(builderWithDistribution);
29const restoredBuilder = PersonaSerializer.deserializeBuilder(serializedBuilder);
30
31// Both builders will generate identical personas
32const persona1 = builderWithDistribution.build();
33const persona2 = restoredBuilder.build();
34console.log('Ages match:', persona1.age === persona2.age);

Performance Optimization

Optimize for large-scale persona generation:

performance.ts
1import { PersonaGroup, BatchGenerator } from '@jamesaphoenix/persona-sdk';
2
3// Batch generation for performance
4const batchGenerator = new BatchGenerator({
5 batchSize: 1000,
6 parallelWorkers: 4,
7 memoryLimit: '512MB'
8});
9
10// Generate 100,000 personas efficiently
11const largeGroup = await batchGenerator.generate(100000, {
12 template: PersonaBuilder.create()
13 .withAge(new NormalDistribution(35, 10))
14 .withOccupation(new CategoricalDistribution([
15 { value: 'Engineer', probability: 0.4 },
16 { value: 'Designer', probability: 0.3 },
17 { value: 'Manager', probability: 0.3 }
18 ])),
19 nameTemplate: 'Employee',
20 correlations: [
21 { attribute1: 'age', attribute2: 'salary', correlation: 0.6 }
22 ]
23});
24
25console.log(`Generated ${largeGroup.size} personas in batches`);
26
27// Stream processing for memory efficiency
28const stream = batchGenerator.createStream(PersonaBuilder.create()
29 .withAge(new UniformDistribution(25, 65))
30 .withName('Streamed Persona')
31);
32
33let count = 0;
34for await (const persona of stream) {
35 // Process each persona without storing all in memory
36 console.log(`Processing persona ${++count}: ${persona.name}`);
37
38 if (count >= 10000) break; // Process 10k personas
39}

Plugin System

Extend functionality with custom plugins:

plugins.ts
1import { PersonaPlugin, PersonaBuilder } from '@jamesaphoenix/persona-sdk';
2
3// Create a plugin for generating realistic names
4class RealisticNamesPlugin implements PersonaPlugin {
5 name = 'realistic-names';
6
7 async install(builder: PersonaBuilder) {
8 // Add methods to builder
9 builder.withRealisticName = (demographics: {
10 ethnicity?: string;
11 gender?: string;
12 region?: string;
13 }) => {
14 const name = this.generateRealisticName(demographics);
15 return builder.withName(name);
16 };
17 }
18
19 private generateRealisticName(demographics: any): string {
20 // Implementation would use name databases
21 // This is a simplified example
22 const firstNames = {
23 'male': ['James', 'John', 'Robert', 'Michael'],
24 'female': ['Mary', 'Patricia', 'Jennifer', 'Linda']
25 };
26
27 const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown'];
28
29 const first = firstNames[demographics.gender]?.[0] || 'Alex';
30 const last = lastNames[0];
31
32 return `${first} ${last}`;
33 }
34}
35
36// Register and use plugin
37PersonaBuilder.use(new RealisticNamesPlugin());
38
39const persona = PersonaBuilder.create()
40 .withRealisticName({ gender: 'female', ethnicity: 'hispanic' })
41 .withAge(30)
42 .build();
43
44console.log('Generated realistic persona:', persona.name);

⚡ Performance Tips

  • • Use seeded distributions for reproducible results
  • • Batch generate for large datasets (>1000 personas)
  • • Stream processing for memory-constrained environments
  • • Cache compiled distributions for repeated use
  • • Use Web Workers for client-side parallel generation

🔒 Security & Privacy

  • • Never include real PII in synthetic personas
  • • Use differential privacy for sensitive correlations
  • • Validate all inputs in custom distributions
  • • Sanitize generated data before persistence
  • • Audit persona data for potential privacy leaks