Troubleshooting
Common issues and solutions when working with Luq. Find quick fixes for the most frequent problems.
TypeScript Errors
Plugin methods not available / TypeScript compilation errors
 Symptom: TypeScript complains that validation methods like .email() or .min() don't exist.
 Cause: The plugin providing the method hasn't been imported and registered with .use().
Solution:
// ❌ This will cause TypeScript errors
const validator = Builder()
  .for<User>()
  .v('email', b => b.string.required().email()) // Error: 'email' method not available
  .build();
// ✅ Import and use the plugin first
import { stringEmailPlugin } from '@maroonedog/luq/plugin';
const validator = Builder()
  .use(stringEmailPlugin) // Add plugin before using
  .for<User>()
  .v('email', b => b.string.required().email()) // Now works
  .build();  Field not found / Path errors
Symptom: TypeScript errors about fields not existing on your type.
Cause: Incorrect field paths or forgetting to use dot notation for nested properties.
Solution:
// ❌ Wrong - trying to validate non-existent field
type User = {
  profile: {
    name: string;
  };
};
const validator = Builder()
  .for<User>()
  .v('name', b => b.string.required()) // Error: 'name' doesn't exist on User
  .build();
// ✅ Correct - use dot notation for nested fields
const validator = Builder()
  .for<User>()
  .v('profile.name', b => b.string.required()) // Correct path
  .build();  Type inference issues
Symptom: TypeScript can't determine field types or shows generic errors.
 Cause: Missing .for<YourType>() call in the builder chain.
Solution:
// ❌ Problem - TypeScript can't infer the type
const validator = Builder()
  .use(requiredPlugin)
  // Missing .for<Type>() specification
  .v('email', b => b.string.required()) // Type error
  .build();
// ✅ Solution - Always specify the type
const validator = Builder()
  .use(requiredPlugin)
  .for<User>() // Specify type here
  .v('email', b => b.string.required()) // Now TypeScript knows the structure
  .build();  Array Validation Issues
How to validate arrays and array items
 Key concept: Luq uses [*] notation to validate array items. When you write items[*].name, it automatically validates the name field of ALL items in the array.
 Common mistake: Trying to use specific index notation like items[0].name or trying to split into multiple validators - neither approach is correct in Luq.
The correct Luq pattern - everything in ONE validator:
// ❌ Common mistake - trying to validate array items with index notation
type UserList = {
  users: Array<{
    name: string;
    email: string;
  }>;
  tags: string[];  // Primitive array
  scores: number[]; // Another primitive array
};
const wrongValidator = Builder()
  .use(arrayMinLengthPlugin)
  .use(requiredPlugin)
  .for<UserList>()
  .v('users[0].name', b => b.string.required()) // Wrong! Specific index doesn't work
  .v('tags[0]', b => b.string.required())       // Wrong! Can't validate specific index
  .build();
// ✅ Correct - Luq uses [*] notation to validate ALL items in an array
const validator = Builder()
  .use(arrayMinLengthPlugin)
  .use(requiredPlugin)
  .use(stringMinPlugin)
  .use(stringEmailPlugin)
  .use(numberMinPlugin)
  .use(numberMaxPlugin)
  .for<UserList>()
  // Array of objects - validate the array and its object properties
  .v('users', b => b.array.required().minLength(1))     // Validate array itself
  .v('users[*].name', b => b.string.required().min(2))  // Validates name of ALL users
  .v('users[*].email', b => b.string.required().email()) // Validates email of ALL users
  
  // Primitive array (string[]) - validate array and its elements
  .v('tags', b => b.array.required().minLength(1).maxLength(10)) // Array validation
  .v('tags[*]', b => b.string.required().min(2).max(20))         // Each string element
  
  // Primitive array (number[]) - validate array and its elements  
  .v('scores', b => b.array.required())               // Array validation
  .v('scores[*]', b => b.number.required().min(0).max(100)) // Each number element
  .build();
// The [*] notation works for both object arrays and primitive arrays
// For primitive arrays: use 'fieldName[*]' to validate each element  Primitive arrays (string[], number[], boolean[])
Common need: Validating arrays of primitive types like tags, scores, IDs, flags, etc.
 Key pattern: Use fieldName[*] to validate each primitive element in the array. Validate both the array itself (length, uniqueness) AND each element (min/max, pattern, etc.).
// Validating primitive arrays (string[], number[], boolean[], etc.)
type AppData = {
  keywords: string[];
  ratings: number[];
  categories: string[];
  flags: boolean[];
};
// ✅ Correct pattern for primitive arrays
const validator = Builder()
  .use(requiredPlugin)
  .use(arrayMinLengthPlugin)
  .use(arrayMaxLengthPlugin)
  .use(arrayUniquePlugin)
  .use(stringMinPlugin)
  .use(stringMaxPlugin)
  .use(stringPatternPlugin)
  .use(numberMinPlugin)
  .use(numberMaxPlugin)
  .use(booleanTruthyPlugin)
  .for<AppData>()
  
  // String array - validate both array and each string element
  .v('keywords', b => b.array.required().minLength(1).maxLength(10).unique()) // Array properties
  .v('keywords[*]', b => b.string.required().min(3).max(30).pattern(/^[a-z]+$/)) // Each string
  
  // Number array - validate both array and each number element
  .v('ratings', b => b.array.required().minLength(1))    // Array properties
  .v('ratings[*]', b => b.number.required().min(1).max(5)) // Each number must be 1-5
  
  // Another string array with different rules
  .v('categories', b => b.array.required().unique())      // No duplicates
  .v('categories[*]', b => b.string.required().min(2))    // Each category name
  
  // Boolean array
  .v('flags', b => b.array.required())                    // Array validation
  .v('flags[*]', b => b.boolean.required().truthy())      // Each flag must be true
  .build();
// Example usage:
const result = validator.validate({
  keywords: ['react', 'typescript', 'nodejs'],  // ✅ Valid
  ratings: [5, 4, 3, 5, 4],                     // ✅ Valid
  categories: ['frontend', 'backend'],          // ✅ Valid
  flags: [true, true, false]                    // ❌ Invalid - one is false
});  Remember:
- • 
.v('tags', ...)validates the array itself (length, uniqueness) - • 
.v('tags[*]', ...)validates EACH element in the array - • Both validations work together in ONE validator
 
Complex nested structures (arrays and objects)
Real-world scenario: Validating complex data with arrays of objects, nested objects, and arrays within objects.
 Luq's powerful pattern: Combine [*] notation for arrays with dot notation for objects. Everything stays in ONE validator!
// Most common case: Array of objects with nested structures
type Order = {
  orderId: string;
  items: Array<{
    productId: string;
    name: string;
    quantity: number;
    price: number;
    attributes: {
      color?: string;
      size?: string;
    };
  }>;
  customer: {
    name: string;
    addresses: Array<{
      type: 'billing' | 'shipping';
      street: string;
      city: string;
    }>;
  };
};
// ✅ Luq's elegant approach - use [*] notation for array items, dot notation for nested objects
const orderValidator = Builder()
  .use(requiredPlugin)
  .use(arrayMinLengthPlugin)
  .use(stringMinPlugin)
  .use(numberMinPlugin)
  .use(stringPatternPlugin)
  .use(optionalPlugin)
  .use(oneOfPlugin)
  .for<Order>()
  // Validate root fields
  .v('orderId', b => b.string.required().pattern(/^ORD-d{6}$/))
  
  // Validate array and its items
  .v('items', b => b.array.required().minLength(1))
  .v('items[*].productId', b => b.string.required().pattern(/^PROD-d{4}$/))
  .v('items[*].name', b => b.string.required().min(1))
  .v('items[*].quantity', b => b.number.required().min(1))
  .v('items[*].price', b => b.number.required().min(0))
  
  // Validate nested objects within array items
  .v('items[*].attributes.color', b => 
    b.string.optional().oneOf(['blue', 'red', 'green', 'black', 'white']))
  .v('items[*].attributes.size', b => 
    b.string.optional().oneOf(['S', 'M', 'L', 'XL']))
  
  // Validate nested object
  .v('customer.name', b => b.string.required().min(2))
  
  // Validate nested array within object
  .v('customer.addresses', b => b.array.required().minLength(1))
  .v('customer.addresses[*].type', b => b.string.required().oneOf(['billing', 'shipping']))
  .v('customer.addresses[*].street', b => b.string.required().min(5))
  .v('customer.addresses[*].city', b => b.string.required().min(2))
  .build();
// Everything in ONE validator using [*] notation for arrays and dot notation for objects
// No need to split validators or iterate manually!  Nested arrays (Array of arrays)
Multi-dimensional arrays: Validating matrices, 2D/3D arrays, or other nested array structures.
 Luq's solution: Chain [*] notation for each level of nesting. data[*][*] validates all elements in a 2D array.
// Complex case: Array of arrays
type Matrix = {
  name: string;
  data: number[][];
  metadata: {
    rows: number;
    cols: number;
  };
};
// ❌ Wrong approach - specific index notation doesn't work
const badValidator = Builder()
  .for<Matrix>()
  .v('data[0][0]', b => b.number.required()) // Wrong! Specific indices don't work
  .build();
// ✅ Correct approach - Luq's [*] notation for nested arrays
const matrixValidator = Builder()
  .use(requiredPlugin)
  .use(arrayMinLengthPlugin)
  .use(numberMinPlugin)
  .for<Matrix>()
  .v('name', b => b.string.required())
  .v('data', b => b.array.required().minLength(1)) // Validate outer array
  .v('data[*]', b => b.array.required()) // Each item must be an array
  .v('data[*][*]', b => b.number.required().min(0)) // Each number in nested arrays
  .v('metadata.rows', b => b.number.required().min(1))
  .v('metadata.cols', b => b.number.required().min(1))
  .build();
// The [*] notation can be chained for nested arrays: data[*][*]
// This validates ALL elements at each level of nesting  Luq Array Validation Patterns:
- • 
tags[*]- Validates each string in a string[] array - • 
scores[*]- Validates each number in a number[] array - • 
items[*].name- Validates the name field of ALL items in an object array - • 
items[*].subitems[*].value- Nested arrays within objects - • 
matrix[*][*]- 2D arrays (matrix) - • 
cube[*][*][*]- 3D arrays - • 
items[*].tags[*]- Primitive array within object array - • 
items[*].address.city- Combine [*] with dot notation for nested objects - • Everything in ONE validator - no splitting or manual iteration!
 
Bundle Size Issues
Bundle size larger than expected
Symptom: Your bundle includes more Luq code than you expected.
Causes: Importing from barrel exports or using wildcard imports.
Solution:
// ✅ Good - specific imports
import { requiredPlugin } from '@maroonedog/luq/plugin';
import { stringMinPlugin } from '@maroonedog/luq/plugin';
// ❌ Avoid - importing everything
import * as plugins from 'luq/plugins';  Bundle Analysis Tips:
- • Use 
webpack-bundle-analyzerto visualize what's included - • Check your bundler's tree-shaking configuration
 - • Ensure you're using ES modules (import/export)
 - • Avoid importing entire plugin collections
 
Performance Problems
Slow validation performance
Common causes: Creating validators repeatedly, heavy transformations, or inefficient data structures.
Debugging validation performance:
// Debug slow validation
const start = performance.now();
const result = validator.validate(largeData);
const end = performance.now();
console.log(`Validation took ${end - start} milliseconds`);
// Check for heavy transformations
const validator = Builder()
  .for<Data>()
  .v('content', b => 
    b.string
      .required()
      .transform(content => {
        console.time('transform'); // Debug transform time
        const result = heavyProcessing(content);
        console.timeEnd('transform');
        return result;
      })
  )
  .build();  Performance Tips:
- • Create validators once and reuse them
 - • Move heavy processing outside of transforms
 - • Use simpler data structures when possible
 - • Consider using abort-early validation for large objects
 
Memory leaks in long-running applications
Symptom: Memory usage grows over time in server applications.
Cause: Creating new validators instead of reusing them.
Solution:
// ❌ Memory leak - creating validators repeatedly
function validateData(data: User) {
  const validator = Builder() // New validator every call!
    .use(requiredPlugin)
    .for<User>()
    .v('email', b => b.string.required().email())
    .build();
    
  return validator.validate(data);
}
// ✅ Fix - Create validator once
const USER_VALIDATOR = Builder()
  .use(requiredPlugin)
  .for<User>()
  .v('email', b => b.string.required().email())
  .build();
function validateData(data: User) {
  return USER_VALIDATOR.validate(data); // Reuse validator
}  Runtime Issues
Unexpected validation results
Debugging steps:
- 1. Check that all required plugins are imported and registered
 - 2. Verify field paths match your data structure exactly
 - 3. Test with simplified data to isolate the issue
 - 4. Check for type mismatches between expected and actual data
 
Debug validation step by step:
// Add logging to understand what's happening
const result = validator.validate(data);
console.log('Validation result:', result.isValid());
if (!result.isValid()) {
  console.log('Errors:', result.getErrors());
  console.log('Data being validated:', JSON.stringify(data, null, 2));
}  Error messages not user-friendly
Problem: Default error messages are too technical for end users.
Solution - Customize error messages:
// Default error messages might not be user-friendly
const result = validator.validate({ email: 'invalid-email' });
// Error: "stringEmail validation failed"
// ✅ Customize error messages for better UX
const validator = Builder()
  .use(stringEmailPlugin)
  .for<User>()
  .v('email', b => 
    b.string
      .required({ message: 'Email is required' })
      .email({ message: 'Please enter a valid email address' })
  )
  .build();  Advanced Issues
Async validation (database checks, API calls)
Luq's approach: Async validation uses a separate layer design - resolve all async data BEFORE validation, then pass it to the synchronous validator.
The correct async pattern with type safety:
// Luq's async validation pattern - separate async layer approach
import { createAsyncContext, addAsyncSupport } from '@maroonedog/luq/async.experimental';
// Define async context type
interface ValidationContext {
  emailExists: boolean;
  domainValid: boolean;
  quotaAvailable: { canCreate: boolean; limit: number };
}
// ✅ Correct pattern - Luq's async layer separation
// Step 1: Create normal synchronous validator
const syncValidator = Builder()
  .use(requiredPlugin)
  .use(stringEmailPlugin)
  .use(fromContextPlugin) // Plugin that can access async context
  .for<User>()
  .v('email', b => b.string.required().email())
  .build();
// Step 2: Add async support to the validator
const validator = addAsyncSupport(syncValidator);
// Step 3: Create and resolve async context BEFORE validation
const asyncContext = await createAsyncContext<ValidationContext>()
  .set('emailExists', checkEmailExists(data.email))        // Parallel
  .set('domainValid', validateDomain(data.email))          // Parallel
  .set('quotaAvailable', checkUserQuota(data.userId))      // Parallel
  .build(); // All async operations run in parallel
// Step 4: Validate with pre-resolved async context
const result = await validator
  .withAsyncContext<ValidationContext>(asyncContext)
  .validate(data);
// The async data is available in plugins via getAsyncContext
const emailExistsPlugin = {
  name: 'emailExists',
  validate: (value: string, context: any) => {
    // Get pre-resolved async data (no await needed here!)
    const asyncData = getAsyncContext<ValidationContext>(context);
    
    if (asyncData?.emailExists) {
      return { valid: false, message: 'Email already exists' };
    }
    
    return { valid: true };
  }
};
// Key benefits:
// 1. All async operations run in parallel (not sequential)
// 2. Async is resolved ONCE before validation
// 3. Validation itself remains synchronous and fast
// 4. Full type safety with TypeScript generics  Luq's Async Architecture:
- 1. Create async context with all async operations running in PARALLEL
 - 2. Resolve all async data ONCE before validation
 - 3. Pass pre-resolved context to validator using 
withAsyncContext - 4. Validation remains synchronous and fast
 - 5. Plugins access async data via 
getAsyncContext(no await needed) - 6. Full type safety with TypeScript generics
 
Performance Benefits:
- • All async operations run in parallel (200ms instead of 600ms sequential)
 - • Only ONE await during the entire validation process
 - • Synchronous validation performance is unaffected
 - • Zero overhead when not using async features
 
Getting Help
Still having issues?
Check the documentation:
Review the core concepts and examples to ensure you're following best practices.
Create a minimal reproduction:
Strip down your code to the smallest possible example that demonstrates the issue.
Check GitHub Issues:
Search for similar issues in the GitHub repository.
Ask for help:
Open a new issue with your minimal reproduction and detailed description.