Skip to main content

Documentation Index

Fetch the complete documentation index at: https://intunedhq.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Recipe

This recipe shows how to validate data structures using Zod (TypeScript) or Pydantic (Python), including synchronous and asynchronous validation patterns.
TypeScript: Zod version 3.22+ is supported, which provides the zod/v3 export path required by libraries like json-schema-to-zod. Python: Examples target Pydantic v2 (pydantic>=2.0).

Code example

Synchronous validation

TypeScript
import { z } from "zod";

// Define the Contract schema
const ContractSchema = z.object({
  contractId: z.string().uuid("Invalid contract ID format"),
  clientName: z.string().min(1, "Client name is required"),
  contractValue: z.number().min(0, "Contract value must be non-negative"),
  status: z.union([
    z.literal("active"),
    z.literal("completed"),
    z.literal("terminated"),
  ]),
  startDate: z.string().datetime("Invalid start date format"),
  endDate: z.string().datetime("Invalid end date format").optional(),
});

// Infer TypeScript type from schema
type Contract = z.infer<typeof ContractSchema>;

// Example data
const validContract = {
  contractId: "550e8400-e29b-41d4-a716-446655440000",
  clientName: "Client X",
  contractValue: 50000,
  status: "active" as const,
  startDate: "2024-01-01T00:00:00Z",
  endDate: "2024-12-31T23:59:59Z",
};

const invalidContract = {
  contractId: "invalid-uuid",
  clientName: "",
  contractValue: -1000, // invalid: negative value
  status: "active" as const,
  startDate: "2024-01-01T00:00:00Z",
};

// Synchronous validation with safeParse()

const result1 = ContractSchema.safeParse(validContract);
if (!result1.success) {
  console.error("Validation errors:", result1.error.format());
} else {
  console.log("✓ Valid contract:", result1.data);
}

// Handling validation errors
const result2 = ContractSchema.safeParse(invalidContract);
if (!result2.success) {
  console.error("Validation errors:", result2.error.format());
} else {
  console.log("Valid contract:", result2.data);
}

// Alternative: parse() method (throws on error)
try {
  const validated = ContractSchema.parse(validContract);
  console.log("\n✓ Parsed successfully:", validated);
} catch (error) {
  if (error instanceof z.ZodError) {
    console.error("Validation failed:", error.issues);
  }
}

Asynchronous validation

TypeScript
import { z } from "zod";

const asyncContract = {
  contractId: "550e8400-e29b-41d4-a716-446655440000",
  clientName: "Client Y",
  contractValue: 75000,
  status: "active" as const,
  startDate: "2024-06-01T00:00:00Z",
};

// Asynchronous validation with refinements

const AsyncContractSchema = ContractSchema.extend({
  clientName: z.string().refine(
    async (val) => {
      // Simulate database check for duplicate client
      await new Promise((resolve) => setTimeout(resolve, 100));
      return val !== "Client X"; // Fail if client already has active contract
    },
    { message: "Client already has an active contract" }
  ),
});

AsyncContractSchema.safeParseAsync(asyncContract)
  .then((result) => {
    if (!result.success) {
      console.error("✗ Async validation errors:", result.error.format());
    } else {
      console.log("✓ Valid contract (async):", result.data);
    }
  })
  .catch((err) => console.error("Unexpected error:", err));
Pydantic validators run synchronously. For async checks (like database lookups), validate the data structure first with model_validate(), then run async logic afterward—as shown above.

Comparison: TypeScript vs Python

PatternTypeScript (Zod)Python (Pydantic)
Safe validation (no throw)safeParse(){ success, data, error }try/except ValidationError
Strict validation (throws)parse() → throws ZodErrormodel_validate() → raises ValidationError
Async validation.safeParseAsync() / .refine(async fn)Validate sync first, then run async checks
Skip validationN/Amodel_construct()
Access errorserror.format() / error.issuese.errors() → list of dicts

Common pitfalls

Date parsing — Pydantic automatically parses ISO 8601 strings into datetime objects. Watch out for timezone-aware vs. naive datetimes: "2024-01-01T00:00:00Z" becomes timezone-aware, while "2024-01-01T00:00:00" is naive. Mixing them raises an error. Optional fields — Mark optional fields with Optional[X] = None (or X | None = None in Python 3.10+). A field typed as Optional[X] without a default is still required on input. Custom validators — Use @field_validator for single-field checks and @model_validator(mode='after') for cross-field checks:
from pydantic import BaseModel, field_validator, model_validator


class Contract(BaseModel):
    client_name: str
    start_date: datetime
    end_date: Optional[datetime] = None

    @field_validator("client_name")
    @classmethod
    def name_must_not_be_blank(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("Client name cannot be blank")
        return v.strip()

    @model_validator(mode="after")
    def end_after_start(self) -> "Contract":
        if self.end_date and self.end_date <= self.start_date:
            raise ValueError("end_date must be after start_date")
        return self

TypeScript SDK

Learn about the Intuned Browser SDK for TypeScript.

Python SDK

Learn about the Intuned Browser SDK for Python.