# Fortify Schema - Conditional Validation Operators Reference

This document provides a comprehensive reference for all conditional validation operators available in Fortify Schema.

## Overview

Fortify Schema supports multiple conditional validation syntaxes:

1. **Syntax** (Recommended): `"when condition *? thenSchema : elseSchema"`
2. **Parentheses Syntax**: `"when(condition) then(thenSchema) else(elseSchema)"`
3. **When.field() API**: Programmatic conditional validation
4. **Legacy Syntax**: `"when:condition:thenSchema:elseSchema"` (backward compatibility)

## Supported Operators

### Comparison Operators

| Operator | Description           | Example                                        | IDE Support                            |
| -------- | --------------------- | ---------------------------------------------- | -------------------------------------- |
| `=`      | Equals                | `"when role=admin *? string[] : string[]?"`    | ✅ Full TypeScript inference           |
| `!=`     | Not equals            | `"when accountType!=free *? string : string?"` | ⚠️ **Works at runtime, no IDE errors** |
| `>`      | Greater than          | `"when age>18 *? string : string?"`            | ✅ Full TypeScript inference           |
| `<`      | Less than             | `"when age<65 *? string : string?"`            | ✅ Full TypeScript inference           |
| `>=`     | Greater than or equal | `"when age>=18 *? string : string?"`           | ✅ Full TypeScript inference           |
| `<=`     | Less than or equal    | `"when age<=65 *? string : string?"`           | ✅ Full TypeScript inference           |

### Pattern Operators

| Operator | Description          | Example                                       | IDE Support                  |
| -------- | -------------------- | --------------------------------------------- | ---------------------------- |
| `~`      | Regex match          | `"when email~^admin *? string[] : string[]?"` | ✅ Full TypeScript inference |
| `!~`     | Negative regex match | `"when email!~@temp *? string : string?"`     | ✅ Full TypeScript inference |

### Existence Operators

| Operator   | Description                       | Example                                            | IDE Support                  |
| ---------- | --------------------------------- | -------------------------------------------------- | ---------------------------- |
| `.exists`  | Field exists (not null/undefined) | `"when paymentMethod.exists *? string : string?"`  | ✅ Full TypeScript inference |
| `.!exists` | Field does not exist              | `"when paymentMethod.!exists *? string? : string"` | ✅ Full TypeScript inference |

### State Operators

| Operator  | Description                          | Example                                          | IDE Support                  |
| --------- | ------------------------------------ | ------------------------------------------------ | ---------------------------- |
| `.empty`  | Field is empty (string/array/object) | `"when description.empty *? =required : string"` | ✅ Full TypeScript inference |
| `.!empty` | Field is not empty                   | `"when tags.!empty *? string[] : string[]?"`     | ✅ Full TypeScript inference |
| `.null`   | Field is exactly null                | `"when optionalField.null *? string? : string"`  | ✅ Full TypeScript inference |
| `.!null`  | Field is not null                    | `"when requiredField.!null *? string : string?"` | ✅ Full TypeScript inference |

### Array Operators

| Operator | Description        | Example                                                 | IDE Support                  |
| -------- | ------------------ | ------------------------------------------------------- | ---------------------------- |
| `.in()`  | Value in array     | `"when role.in(admin,teacher) *? string[] : string[]?"` | ✅ Full TypeScript inference |
| `.!in()` | Value not in array | `"when role.!in(guest,user) *? string[] : string[]?"`   | ✅ Full TypeScript inference |

### String Operators

| Operator        | Description                   | Example                                                 | IDE Support                  |
| --------------- | ----------------------------- | ------------------------------------------------------- | ---------------------------- |
| `.startsWith()` | String starts with value      | `"when filename.startsWith(temp_) *? =delete : string"` | ✅ Full TypeScript inference |
| `.endsWith()`   | String ends with value        | `"when filename.endsWith(.tmp) *? =delete : string"`    | ✅ Full TypeScript inference |
| `.contains()`   | String contains value         | `"when path.contains(/temp/) *? =cleanup : string"`     | ✅ Full TypeScript inference |
| `.!contains()`  | String does not contain value | `"when path.!contains(/secure/) *? =public : =private"` | ✅ Full TypeScript inference |

## Syntax Examples

### 1. Clean Syntax (Recommended)

The cleanest and most intuitive syntax:

```typescript
const UserSchema = Interface({
  role: "admin|user|guest",
  accountType: "free|premium|enterprise",
  age: "int(13,120)",
  userType: "student|teacher|admin",
  email: "email",
  description: "string?",
  tags: "string[]?",

  // Basic equality
  permissions: "when role=admin *? string[] : string[]?",

  // Inequality (works at runtime, no IDE errors)
  paymentMethod: "when accountType!=free *? string : string?",

  // Numeric comparisons
  accessLevel: "when age>=18 *? string : string?",
  discountRate: "when age<25 *? number(0,0.5) : number(0,0.1)",

  // Pattern matching
  adminAccess: "when email~^admin *? =true : =false",
  publicEmail: "when email!~@temp *? =public : =private",

  // Existence checks
  billingAddress: "when paymentMethod.exists *? string : string?",
  guestFeatures: "when paymentMethod.!exists *? string[] : string[]?",

  // State checks
  descriptionRequired: "when description.empty *? =required : string",
  tagValidation: "when tags.!empty *? string[] : string[]?",
  optionalCheck: "when description.null *? string? : string",
  requiredCheck: "when email.!null *? string : string?",

  // Array operations
  maxProjects: "when userType.in(admin,teacher) *? int(1,) : int(1,10)",
  restrictions: "when role.!in(guest) *? string[] : string[]?",

  // String operations
  tempFileCleanup: "when filename.startsWith(temp_) *? =delete : =keep",
  backupRequired: "when filename.endsWith(.important) *? =true : =false",
  securityLevel: "when path.contains(/secure/) *? =high : =normal",
  publicAccess: "when path.!contains(/private/) *? =true : =false",
});
```

### 2. Parentheses Syntax

More explicit syntax with proper nesting support:

```typescript
const OrderSchema = Interface({
  orderType: "pickup|delivery",
  customerType: "regular|premium|vip",

  // Basic conditions
  deliveryFee: "when(orderType=delivery) then(number(5,50)) else(number?)",

  // Inequality
  rushFee: "when(customerType!=vip) then(number(10,25)) else(number?)",

  // Numeric comparisons
  discount: "when(orderValue>=100) then(number(0,0.2)) else(number(0,0.05))",

  // Special operators
  insurance: "when(deliveryAddress.exists) then(boolean) else(boolean?)",
  priority:
    "when(customerType.in(premium,vip)) then(high|urgent) else(normal|low)",
});
```

### 3. When.field() API (Programmatic)

For complex logic and better IDE support:

```typescript
const EventSchema = Interface({
  eventType: "conference|workshop|webinar",
  duration: "int(30,480)", // minutes
  attendeeCount: "int(1,1000)",

  // Programmatic conditional validation
  venue: When.field("eventType")
    .is("webinar")
    .then("string?")
    .isNot("webinar")
    .then("string")
    .default("string"),

  catering: When.field("attendeeCount")
    .when((count) => count >= 50)
    .then("boolean")
    .default("boolean?"),

  equipment: When.field("eventType")
    .in(["conference", "workshop"])
    .then("string[]")
    .default("string[]?"),
});
```

## IDE Support Analysis

### ✅ **Working Operators (Full TypeScript Support)**

These operators work perfectly with TypeScript IDE inference:

**Comparison Operators:**

- `=` (equals)
- `>` (greater than)
- `<` (less than)
- `>=` (greater than or equal)
- `<=` (less than or equal)

**Pattern Operators:**

- `~` (regex match)
- `!~` (negative regex match)

**Existence Operators:**

- `.exists` (field exists)
- `.!exists` (field does not exist)

**State Operators:**

- `.empty` (field is empty)
- `.!empty` (field is not empty)
- `.null` (field is exactly null)
- `.!null` (field is not null)

**Array Operators:**

- `.in()` (value in array)
- `.!in()` (value not in array)

**String Operators:**

- `.startsWith()` (string starts with value)
- `.endsWith()` (string ends with value)
- `.contains()` (string contains value)
- `.!contains()` (string does not contain value)

### ⚠️ **Runtime-Only Operators**

These operators work at runtime but don't show IDE errors:

- `!=` (not equals) - **This is the issue you discovered!**

## The `!=` Operator Issue

You correctly identified that `!=` doesn't throw IDE errors but works at runtime:

```typescript
// This compiles without errors but validates correctly at runtime
permissions: "when role!=admin *? string[]? : string[]";
//                    ^^
//                    No IDE error, but runtime validation works!
```

**Why this happens:**

- TypeScript's conditional type inference doesn't recognize `!=` as a valid type guard
- The runtime parser correctly handles `!=` operator
- Other operators like `=`, `>=`, etc. work with TypeScript's type system

## Workarounds for `!=` IDE Support

### Option 1: Use `.!in()` for single values

```typescript
// Instead of: "when role!=admin *? ..."
permissions: "when role.!in(admin) *? string[]? : string[]";
```

### Option 2: Use When.field() API

```typescript
permissions: When.field("role")
  .isNot("admin")
  .then("string[]?")
  .else("string[]");
```

### Option 3: Use positive logic

```typescript
// Instead of: "when accountType!=free *? ..."
paymentMethod: "when accountType.in(premium,enterprise) *? string : string?";
```

## Complete Working Example

```typescript
import { Interface, When } from "fortify-schema";

const UserSchema = Interface({
  // Basic fields
  role: "admin|user|guest",
  accountType: "free|premium|enterprise",
  age: "int(13,120)",
  userType: "student|teacher|admin",

  // ✅ WORKING: Equality (full IDE support)
  permissions: "when role=admin *? string[] : string[]?",

  // ⚠️ RUNTIME ONLY: Inequality (no IDE errors, but validates correctly)
  paymentMethod: "when accountType!=free *? string : string?",

  // ✅ WORKING: Numeric comparisons (full IDE support)
  accessLevel: "when age>=18 *? string : string?",
  discountRate: "when userType=student *? number(0,0.5) : number(0,0.1)",

  // ✅ WORKING: Special operators (full IDE support)
  maxProjects: "when userType.in(admin,teacher) *? int(1,) : int(1,10)",
  billingAddress: "when paymentMethod.exists *? string : string?",

  // ✅ WORKING: Programmatic API (full IDE support)
  notifications: When.field("accountType")
    .isNot("free")
    .then("string[]")
    .else("string[]?"),
});

// Test the schema
const result = UserSchema.safeParse({
  role: "=user",
  accountType: "free",
  age: 25,
  userType: "student",
  // paymentMethod: undefined, // This will pass validation correctly
});

console.log(result.success); // true
console.log(result.data); // Fully typed result
```

## Operator Precedence

When parsing conditions, operators are checked in this order (highest to lowest precedence):

**Existence Operators (Highest Precedence):**

1. `.!exists` (field does not exist)
2. `.exists` (field exists)

**State Operators:** 3. `.!empty` (field is not empty) 4. `.empty` (field is empty) 5. `.!null` (field is not null) 6. `.null` (field is exactly null)

**Array Operators:** 7. `.!in()` (value not in array) 8. `.in()` (value in array)

**Pattern Operators:** 9. `!~` (negative regex match) 10. `~` (regex match)

**String Operators:** 11. `.!contains()` (string does not contain value) 12. `.contains()` (string contains value) 13. `.endsWith()` (string ends with value) 14. `.startsWith()` (string starts with value)

**Comparison Operators (Lowest Precedence):** 15. `!=` (not equals) 16. `>=` (greater than or equal) 17. `<=` (less than or equal) 18. `>` (greater than) 19. `<` (less than) 20. `=` (equals)

## Best Practices

1. **Use `=` instead of `!=` when possible** for better IDE support
2. **Use `.!in()` for multiple exclusions** instead of multiple `!=` conditions
3. **Use When.field() API for complex logic** that needs full TypeScript support
4. **Test conditional validation thoroughly** since some operators work at runtime only
5. **Prefer positive logic over negative logic** for better readability

## Migration Guide

If you're using `!=` and want full IDE support:

```typescript
// Before (runtime only)
permissions: "when role!=admin *? string[]? : string[]";

// After (full IDE support)
permissions: When.field("role")
  .isNot("admin")
  .then("string[]?")
  .else("string[]");

// Or use positive logic
permissions: "when role.in(user,guest) *? string[]? : string[]";
```

This comprehensive reference should help you understand why `!=` works at runtime but doesn't show IDE errors, and provides alternatives for full TypeScript support.
