npm package: @robertotaylor0/mediform-client-sdk
Description: Client SDK for integrating MediForm forms into EHR applications
Author: MediForm
Latest version: 1.1.2

# @robertotaylor0/mediform-client-sdk

Official JavaScript/TypeScript SDK for integrating MediForm forms into EHR applications.

## Features

- 🚀 **Easy Integration** - Simple API for fetching and rendering forms
- 🔒 **Secure** - API key authentication with rate limiting
- ⚡ **Fast** - Built-in caching and optimized API calls
- 🎨 **Customizable** - Theme support and custom components
- ✅ **Validation** - Comprehensive client-side validation
- 🤖 **AI Auto-Population** - Extract values from images and text using AI
- 📱 **Responsive** - Mobile-friendly form rendering
- ♿ **Accessible** - WCAG 2.1 AA compliant
- 🔧 **TypeScript** - Full type definitions included

## Installation

```bash
npm install @robertotaylor0/mediform-client-sdk
# or
yarn add @robertotaylor0/mediform-client-sdk
# or
pnpm add @robertotaylor0/mediform-client-sdk
```

## Quick Start

### 1. Initialize the Client

```typescript
import { MediFormClient } from '@robertotaylor0/mediform-client-sdk';

const client = new MediFormClient({
  apiKey: 'mk_your_api_key_here',
  baseUrl: 'https://your-mediform-instance.com',
  cache: true, // Enable caching (default: true)
  defaultEnvironment: 'prod', // Optional, defaults to 'prod'
});
```

**New in v2.0:** The SDK now supports environment-based form publishing. See the [Environments](#environments) section below.

### 2. Fetch a Form

```typescript
// Get a specific form
const form = await client.getForm('form-id');

// List available forms
const { items, pagination } = await client.listForms({
  category: 'intake',
  page: 1,
  limit: 20,
});

// Get a specific version
const formV2 = await client.getFormVersion('template-id', 2);
```

### 3. Render the Form (React)

```tsx
import { FormRenderer } from '@robertotaylor0/mediform-client-sdk';
import '@robertotaylor0/mediform-client-sdk/dist/index.css';

function PatientForm() {
  const [form, setForm] = useState(null);

  useEffect(() => {
    client.getForm('patient-intake')
      .then(setForm)
      .catch(console.error);
  }, []);

  const handleSubmit = async (data) => {
    console.log('Form data:', data);
    // Process form data in your EHR
    await saveToEHR(data);
  };

  if (!form) return <div>Loading...</div>;

  return (
    <FormRenderer
      formDefinition={form.formDefinition}
      customCss={form.customCss}
      onSubmit={handleSubmit}
      onChange={(data) => console.log('Changed:', data)}
      theme={{
        colors: {
          primary: '#007bff',
          error: '#dc3545',
        },
      }}
    />
  );
}
```

## Environments

The MediForm platform supports environment-based publishing, allowing forms to be published to different environments (Production, Development, Test, etc.) with independent versioning.

### Configuration

Set a default environment when initializing the client:

```typescript
const client = new MediFormClient({
  apiKey: 'mk_your_api_key',
  baseUrl: 'https://your-instance.com',
  defaultEnvironment: 'prod', // Defaults to 'prod' if not specified
});
```

### Fetching from Specific Environments

```typescript
// List forms from default environment (prod)
const forms = await client.listForms();

// List forms from specific environment
const devForms = await client.listForms({ environment: 'dev' });
const testForms = await client.listForms({ environment: 'test' });

// Get form by template ID + environment (returns latest version in that environment)
const prodForm = await client.getForm('template-id', 'prod');
const devForm = await client.getForm('template-id', 'dev');

// The form object includes environment information
console.log(prodForm.environment); // 'prod'
console.log(prodForm.version); // Version number in that environment
```

### Listing Available Environments

```typescript
const { environments } = await client.listEnvironments();

environments.forEach(env => {
  console.log(`${env.name} (${env.slug})`);
  console.log(`  Default: ${env.isDefault}`);
  if (env.description) {
    console.log(`  Description: ${env.description}`);
  }
});

// Example output:
// Production (prod)
//   Default: true
//   Description: Production environment for live forms
// Development (dev)
//   Default: false
//   Description: Development environment for testing
// Test (test)
//   Default: false
//   Description: QA and validation environment
```

### Environment Use Cases

**1. Development/Production Separation:**
```typescript
// Fetch development forms for testing
const devForm = await client.getForm('intake-form', 'dev');

// Fetch production forms for live use
const prodForm = await client.getForm('intake-form', 'prod');
```

**2. Dynamic Environment Selection:**
```typescript
const environment = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';
const form = await client.getForm('intake-form', environment);
```

**3. Multi-Environment Support:**
```typescript
// Application supports multiple customer environments
const clientEnv = customerConfig.environment; // 'client-a-prod', 'client-b-prod', etc.
const forms = await client.listForms({ environment: clientEnv });
```

### Environment-Aware Caching

The SDK automatically includes environment in cache keys, ensuring forms from different environments don't collide in the cache.

```typescript
// These are cached separately:
await client.getForm('form-1', 'prod'); // Cache key: 'form:form-1:prod'
await client.getForm('form-1', 'dev');  // Cache key: 'form:form-1:dev'
```

## API Reference

### MediFormClient

#### Constructor

```typescript
new MediFormClient(config: MediFormClientConfig)
```

**Config Options:**
- `apiKey` (required): Your MediForm API key
- `baseUrl` (required): URL of your MediForm instance
- `cache` (optional): Enable response caching (default: `true`)
- `cacheTimeout` (optional): Cache timeout in milliseconds (default: `300000` = 5 min)

#### Methods

##### `listForms(filter?: FormListFilter): Promise<PaginatedResponse<FormListItem>>`

List all available forms with optional filtering.

```typescript
const result = await client.listForms({
  category: 'intake',
  tags: ['diabetes', 'cardiology'],
  search: 'patient',
  page: 1,
  limit: 20,
});

console.log(result.items); // Array of forms
console.log(result.pagination); // Pagination info

// ⚠️ IMPORTANT: Access form properties via metadata object
result.items.forEach(form => {
  // ✅ Correct way - metadata is nested:
  console.log(form.metadata.name);        // Form name
  console.log(form.metadata.description); // Form description
  console.log(form.metadata.category);    // Form category
  console.log(form.metadata.tags);        // Form tags array
  
  // These are at root level:
  console.log(form.id);                   // Form ID
  console.log(form.version);              // Version number
});
```

**FormListItem Structure:**

```typescript
interface FormListItem {
  id: string;              // Published form ID
  templateId: string;      // Template ID
  version: number;         // Version number
  metadata: {              // ⚠️ Metadata is NESTED
    name: string;          // Form name
    description?: string;  // Form description
    tags?: string[];       // Form tags
    category?: string;     // Form category
  };
  publishedAt: string;     // ISO timestamp
}
```

##### `getForm(formId: string): Promise<FormDefinition>`

Fetch a complete form definition by ID.

```typescript
const form = await client.getForm('published-form-id');

// Full structure:
console.log(form.id);                    // Published form ID
console.log(form.templateId);            // Template ID  
console.log(form.version);               // Version number
console.log(form.formDefinition);        // FormElement (the actual form)
console.log(form.customCss);             // CSS string
console.log(form.publishedAt);           // ISO timestamp

// ⚠️ Metadata is nested:
console.log(form.metadata.name);         // ✅ Form name
console.log(form.metadata.description);  // ✅ Description
console.log(form.metadata.category);     // ✅ Category
console.log(form.metadata.tags);         // ✅ Tags array

// ❌ These don't exist at root:
// console.log(form.name);               // undefined!
// console.log(form.description);        // undefined!
```

##### `getFormVersion(formId: string, version: number): Promise<FormDefinition>`

Fetch a specific version of a form.

```typescript
const formV1 = await client.getFormVersion('template-id', 1);
const formV2 = await client.getFormVersion('template-id', 2);
```

#### Working with Form List Items

When displaying forms from `listForms()`, remember that form details are nested in the `metadata` object:

```tsx
import { MediFormClient } from '@robertotaylor0/mediform-client-sdk';
import { useState, useEffect } from 'react';

function FormList() {
  const [forms, setForms] = useState([]);

  useEffect(() => {
    client.listForms().then(response => {
      setForms(response.items);
    });
  }, []);

  return (
    <div>
      {forms.map(form => (
        <div key={form.id}>
          {/* ✅ CORRECT: Access via metadata object */}
          <h2>{form.metadata.name}</h2>
          <p>{form.metadata.description}</p>
          <span>Category: {form.metadata.category}</span>
          
          {/* ❌ INCORRECT: These will be undefined */}
          {/* <h2>{form.name}</h2> */}
          {/* <p>{form.description}</p> */}
          
          {/* ✅ CORRECT: Version is at root level */}
          <span>Version: {form.version}</span>
          
          {form.metadata.tags && (
            <div>
              {form.metadata.tags.map(tag => (
                <span key={tag} className="tag">{tag}</span>
              ))}
            </div>
          )}
        </div>
      ))}
    </div>
  );
}
```

##### `submitForm(formId: string, data: FormData): Promise<SubmissionResult>`

Submit form data (if submission endpoint is enabled).

```typescript
const result = await client.submitForm('form-id', {
  patientName: 'John Doe',
  age: 45,
  symptoms: ['fever', 'cough'],
});

console.log(result.id); // Submission ID
```

##### `clearCache(): void`

Clear all cached responses.

```typescript
client.clearCache();
```

##### `extractFormValues(formId: string, input: ExtractionInput): Promise<ExtractionResult>`

Extract form values from an image or text using AI (requires AI extraction to be enabled).

```typescript
// Extract from an image (data URI)
const result = await client.extractFormValues('form-id', {
  completedFormImageUri: 'data:image/png;base64,iVBORw0KG...',
});

// Extract from text
const result = await client.extractFormValues('form-id', {
  completedFormText: 'Patient name: John Doe, Age: 45, Symptoms: fever, cough',
});

// Process extracted values
Object.entries(result.extractions).forEach(([fieldId, extraction]) => {
  console.log(`${fieldId}:`, extraction.value);
  console.log(`Confidence: ${extraction.confidence * 100}%`);
  if (extraction.originalText) {
    console.log(`Original text: ${extraction.originalText}`);
  }
});
```

##### `checkHealth(): Promise<HealthStatus>`

Check API health status.

```typescript
const health = await client.checkHealth();
console.log(health.status); // 'healthy'
```

### FormRenderer

React component for rendering forms.

#### Props

```typescript
interface FormRendererProps {
  formDefinition: FormElement;
  onSubmit?: (data: FormData) => void | Promise<void>;
  onChange?: (data: Partial<FormData>) => void;
  initialValues?: Partial<FormData>;
  theme?: ThemeConfig;
  customComponents?: ComponentMap;
  customCss?: string;
  showValidation?: boolean;
  disabled?: boolean;
}
```

**Props Details:**

- `formDefinition` (required): The form structure from API
- `onSubmit` (optional): Called when form is submitted with valid data
- `onChange` (optional): Called whenever form data changes
- `initialValues` (optional): Pre-populate form fields
- `theme` (optional): Custom theme configuration
- `customComponents` (optional): Override default field components
- `customCss` (optional): Additional CSS styles
- `showValidation` (optional): Show validation errors (default: `true`)
- `disabled` (optional): Disable all form fields

#### Example with All Props

```tsx
<FormRenderer
  formDefinition={form.formDefinition}
  onSubmit={handleSubmit}
  onChange={(data) => console.log('Data:', data)}
  initialValues={{ patientName: 'John Doe', age: 45 }}
  theme={{
    colors: {
      primary: '#007bff',
      secondary: '#6c757d',
      error: '#dc3545',
      success: '#28a745',
      background: '#ffffff',
      border: '#dee2e6',
    },
    fonts: {
      family: 'Arial, sans-serif',
      size: '16px',
    },
    spacing: {
      field: '1rem',
      section: '2rem',
    },
  }}
  customComponents={{
    text: MyCustomTextField,
    number: MyCustomNumberField,
  }}
  customCss=".my-form { padding: 20px; }"
  showValidation={true}
  disabled={false}
/>
```

### Validation

The SDK includes a comprehensive validation engine.

#### Validate Entire Form

```typescript
import { validateForm } from '@robertotaylor0/mediform-client-sdk';

const result = validateForm(formDefinition, formData);

if (!result.valid) {
  console.log('Errors:', result.errors);
  // result.errors = { fieldId: ['error message 1', 'error message 2'] }
}
```

#### Validate Single Field

```typescript
import { validateField } from '@robertotaylor0/mediform-client-sdk';

const errors = validateField(formDefinition, 'patientName', 'John Doe');

if (errors.length > 0) {
  console.log('Field errors:', errors);
}
```

#### Supported Validation Rules

- ✅ Required fields
- ✅ Min/max length (strings)
- ✅ Min/max value (numbers)
- ✅ Pattern matching (regex)
- ✅ Decimal places (numbers)
- ✅ Custom validation rules
- ✅ Input masking (phone, SSN, date, etc.)

## Form Auto-Population (AI Extraction)

The SDK includes AI-powered form auto-population that can extract values from images of completed forms or from text containing form responses.

### Quick Example

```typescript
import { MediFormClient, convertExtractionsToFormData } from '@robertotaylor0/mediform-client-sdk';

const client = new MediFormClient({
  apiKey: 'mk_your_api_key',
  baseUrl: 'https://your-instance.com',
});

// Get form definition
const form = await client.getForm('patient-intake');

// Extract values from an image
const extractionResult = await client.extractFormValues('patient-intake', {
  completedFormImageUri: imageDataUri, // data:image/png;base64,...
});

// Convert to form data format
const formData = convertExtractionsToFormData(
  extractionResult.extractions,
  form.formDefinition
);

// Render with pre-populated data
<FormRenderer
  formDefinition={form.formDefinition}
  initialValues={formData}
  onSubmit={handleSubmit}
/>
```

### Extract from Image

Perfect for scanning paper forms or photos of completed forms:

```typescript
// Read image file as data URI
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

const reader = new FileReader();
reader.onload = async (e) => {
  const dataUri = e.target.result as string;

  const result = await client.extractFormValues('form-id', {
    completedFormImageUri: dataUri,
  });

  // Filter by confidence (only include high-confidence extractions)
  const filtered = filterByConfidence(result.extractions, 0.7);

  // Apply to form
  const formData = convertExtractionsToFormData(filtered, form.formDefinition);
};

reader.readAsDataURL(file);
```

### Extract from Text

Perfect for clinical notes, dictation, or typed responses:

```typescript
const clinicalNote = `
  Patient: John Doe
  Age: 45 years old
  Chief complaint: Fever and cough for 3 days
  Temperature: 101.2°F
  Blood pressure: 120/80
  Medications: None
`;

const result = await client.extractFormValues('visit-note', {
  completedFormText: clinicalNote,
});

// Convert and apply
const formData = convertExtractionsToFormData(
  result.extractions,
  form.formDefinition
);
```

### Extraction Utilities

The SDK provides helper functions for working with extracted data:

#### `convertExtractionsToFormData()`

Converts extraction results to the format expected by FormRenderer:

```typescript
import { convertExtractionsToFormData } from '@robertotaylor0/mediform-client-sdk';

const formData = convertExtractionsToFormData(
  extractionResult.extractions,
  form.formDefinition
);

// Handles type conversions:
// - Checkboxes → arrays
// - Yes/No → booleans
// - Numbers → numeric types
// - Dates → ISO strings
```

#### `filterByConfidence()`

Filter extractions by confidence threshold:

```typescript
import { filterByConfidence } from '@robertotaylor0/mediform-client-sdk';

// Only keep extractions with 70%+ confidence
const highConfidence = filterByConfidence(result.extractions, 0.7);

// Only keep extractions with 90%+ confidence
const veryHighConfidence = filterByConfidence(result.extractions, 0.9);
```

#### `getLowConfidenceFields()`

Get list of fields that need manual review:

```typescript
import { getLowConfidenceFields } from '@robertotaylor0/mediform-client-sdk';

// Get fields with confidence < 70%
const needsReview = getLowConfidenceFields(result.extractions, 0.7);

console.log('Please review these fields:', needsReview);
// ['field1', 'field3', 'field7']
```

#### `validateExtractions()`

Validate extracted values against form rules:

```typescript
import { validateExtractions } from '@robertotaylor0/mediform-client-sdk';

const errors = validateExtractions(
  result.extractions,
  form.formDefinition
);

Object.entries(errors).forEach(([fieldId, errorMsg]) => {
  if (errorMsg) {
    console.log(`${fieldId}: ${errorMsg}`);
  }
});
```

#### `extractFieldLabels()`

Get human-readable labels for all fields:

```typescript
import { extractFieldLabels } from '@robertotaylor0/mediform-client-sdk';

const labels = extractFieldLabels(form.formDefinition);

// Use for display in review UI
Object.entries(result.extractions).forEach(([fieldId, extraction]) => {
  console.log(`${labels[fieldId]}: ${extraction.value}`);
});
```

### Complete Extraction Example

Full example with confidence filtering and validation:

```tsx
import {
  MediFormClient,
  FormRenderer,
  convertExtractionsToFormData,
  filterByConfidence,
  getLowConfidenceFields,
  validateExtractions,
  extractFieldLabels,
} from '@robertotaylor0/mediform-client-sdk';

function AutoPopulateForm() {
  const [form, setForm] = useState(null);
  const [formData, setFormData] = useState({});
  const [lowConfidenceFields, setLowConfidenceFields] = useState([]);

  const handleImageUpload = async (file: File) => {
    // Convert to data URI
    const dataUri = await fileToDataUri(file);

    // Extract values
    const result = await client.extractFormValues(form.id, {
      completedFormImageUri: dataUri,
    });

    // Only use high-confidence extractions (70%+)
    const filtered = filterByConfidence(result.extractions, 0.7);

    // Identify fields that need review
    const needsReview = getLowConfidenceFields(result.extractions, 0.7);
    setLowConfidenceFields(needsReview);

    // Validate extracted values
    const errors = validateExtractions(filtered, form.formDefinition);

    // Convert to form data format
    const data = convertExtractionsToFormData(filtered, form.formDefinition);
    setFormData(data);

    // Show summary
    const labels = extractFieldLabels(form.formDefinition);
    const summary = Object.entries(result.extractions).map(([id, ext]) => ({
      label: labels[id],
      value: ext.value,
      confidence: ext.confidence,
      error: errors[id],
    }));

    console.log('Extraction summary:', summary);
  };

  return (
    <div>
      <input
        type="file"
        accept="image/*,application/pdf"
        onChange={(e) => handleImageUpload(e.target.files[0])}
      />

      {lowConfidenceFields.length > 0 && (
        <div className="warning">
          Please review these fields: {lowConfidenceFields.join(', ')}
        </div>
      )}

      {form && (
        <FormRenderer
          formDefinition={form.formDefinition}
          initialValues={formData}
          onSubmit={handleSubmit}
        />
      )}
    </div>
  );
}
```

### Extraction Response Format

The extraction result includes confidence scores and metadata:

```typescript
interface ExtractionResult {
  extractions: {
    [fieldId: string]: {
      value: any;                    // Extracted value
      confidence: number;             // 0.0 to 1.0
      originalText?: string | null;  // Original text from image/document
      alternatives?: any[];          // Alternative interpretations
    };
  };
}
```

**Example response:**

```json
{
  "extractions": {
    "patientName": {
      "value": "John Doe",
      "confidence": 0.95,
      "originalText": "John Doe"
    },
    "age": {
      "value": 45,
      "confidence": 0.92,
      "originalText": "45"
    },
    "symptoms": {
      "value": ["fever", "cough"],
      "confidence": 0.88,
      "originalText": "fever, cough"
    }
  }
}
```

### Best Practices

1. **Always filter by confidence** - Only auto-populate fields with high confidence (0.7+)
2. **Flag low-confidence fields** - Highlight fields that need manual review
3. **Validate extracted data** - Check against form validation rules before submitting
4. **Show original text** - Display `originalText` for user verification
5. **Allow manual override** - Let users correct any mistakes
6. **Test with real data** - Handwriting and image quality vary greatly

### Supported Input Types

- **Images**: PNG, JPEG, GIF, WebP (as data URIs)
- **PDFs**: Single or multi-page PDFs (converted to images internally)
- **Text**: Plain text, clinical notes, dictation, Q&A format

## Theming

Customize the appearance of forms to match your EHR's design.

```tsx
const theme = {
  colors: {
    primary: '#007bff',      // Primary buttons, focus states
    secondary: '#6c757d',    // Secondary elements
    error: '#dc3545',        // Error messages, validation
    success: '#28a745',      // Success states
    background: '#ffffff',   // Form background
    border: '#dee2e6',       // Input borders
  },
  fonts: {
    family: 'Inter, system-ui, sans-serif',
    size: '16px',
  },
  spacing: {
    field: '1rem',    // Space between fields
    section: '2rem',  // Space between sections
  },
};

<FormRenderer formDefinition={form} theme={theme} />
```

## Custom Components

Replace default field components with your own.

```tsx
import { FieldProps } from '@robertotaylor0/mediform-client-sdk';

function MyCustomTextField({ element, value, onChange, error }: FieldProps) {
  return (
    <div>
      <label>{element.properties?.label}</label>
      <input
        type="text"
        value={value || ''}
        onChange={(e) => onChange(e.target.value)}
        className={error ? 'error' : ''}
      />
      {error && <span className="error-message">{error}</span>}
    </div>
  );
}

<FormRenderer
  formDefinition={form}
  customComponents={{
    text: MyCustomTextField,
  }}
/>
```

## Error Handling

The SDK throws `MediFormError` for API errors.

```typescript
import { MediFormClient, MediFormError } from '@robertotaylor0/mediform-client-sdk';

try {
  const form = await client.getForm('invalid-id');
} catch (error) {
  if (error instanceof MediFormError) {
    console.log('Error code:', error.code);
    console.log('Status:', error.statusCode);
    console.log('Message:', error.message);
    console.log('Details:', error.details);
  }
}
```

**Common Error Codes:**
- `MISSING_API_KEY` - API key not provided
- `INVALID_API_KEY` - API key is invalid or expired
- `RATE_LIMIT_EXCEEDED` - Too many requests
- `NOT_FOUND` - Form not found
- `FORBIDDEN` - Access denied to form
- `CORS_FORBIDDEN` - Origin not allowed
- `INTERNAL_ERROR` - Server error

## TypeScript Support

The SDK is written in TypeScript and includes full type definitions.

```typescript
import type {
  FormDefinition,
  FormListItem,
  FormElement,
  FormData,
  ValidationResult,
  ThemeConfig,
  ExtractedField,
  ExtractionResult,
  ExtractionInput,
  PaginatedResponse,
} from '@robertotaylor0/mediform-client-sdk';

// All types are exported and available
```

### Key Type Definitions

#### FormListItem

Returned by `listForms()`:

```typescript
interface FormListItem {
  id: string;
  templateId: string;
  version: number;
  metadata: {
    name: string;
    description?: string;
    tags?: string[];
    category?: string;
  };
  publishedAt: string;
}
```

#### FormDefinition

Returned by `getForm()` and `getFormVersion()`:

```typescript
interface FormDefinition {
  id: string;
  templateId: string;
  version: number;
  metadata: {
    name: string;
    description?: string;
    tags?: string[];
    category?: string;
  };
  formDefinition: FormElement;  // The actual form structure
  customCss: string;
  publishedAt: string;
}
```

#### PaginatedResponse

Returned by `listForms()`:

```typescript
interface PaginatedResponse<T> {
  items: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };
}
```

#### ExtractedField

Part of extraction results:

```typescript
interface ExtractedField {
  value: any;
  confidence: number;      // 0.0 to 1.0
  originalText?: string | null;
  alternatives?: any[];
}
```

## React Integration Example

Complete example with loading states and error handling:

```tsx
import React, { useState, useEffect } from 'react';
import { MediFormClient, FormRenderer, MediFormError } from '@robertotaylor0/mediform-client-sdk';
import '@robertotaylor0/mediform-client-sdk/dist/index.css';

const client = new MediFormClient({
  apiKey: process.env.REACT_APP_MEDIFORM_API_KEY!,
  baseUrl: process.env.REACT_APP_MEDIFORM_URL!,
});

function FormPage({ formId }: { formId: string }) {
  const [form, setForm] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    client.getForm(formId)
      .then(setForm)
      .catch((err) => {
        if (err instanceof MediFormError) {
          setError(err.message);
        } else {
          setError('Failed to load form');
        }
      })
      .finally(() => setLoading(false));
  }, [formId]);

  const handleSubmit = async (data) => {
    try {
      await saveToEHR(data);
      alert('Form submitted successfully!');
    } catch (err) {
      alert('Failed to submit form');
    }
  };

  if (loading) return <div>Loading form...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!form) return <div>Form not found</div>;

  return (
    <div className="form-container">
      <h1>{form.metadata.name}</h1>
      <FormRenderer
        formDefinition={form.formDefinition}
        customCss={form.customCss}
        onSubmit={handleSubmit}
      />
    </div>
  );
}
```

## Browser Support

- Chrome/Edge: Latest 2 versions
- Firefox: Latest 2 versions
- Safari: Latest 2 versions
- Mobile browsers: iOS Safari 13+, Chrome Android 80+

## Performance Tips

1. **Enable Caching** - Reduces API calls (enabled by default)
2. **Use Form Versions** - Pin to specific versions for stability
3. **Lazy Load** - Only load forms when needed
4. **Bundle Size** - The SDK is tree-shakeable (use named imports)
5. **CDN for Styles** - Load CSS from CDN for faster initial load

## Security Best Practices

1. **Never expose API keys** in client-side code
   - Use environment variables
   - Proxy requests through your backend if needed

2. **Validate on server** - Always validate form submissions on your server
   - Client-side validation can be bypassed

3. **Sanitize data** - Clean user input before storing in your EHR
   - Use a library like DOMPurify for HTML content

4. **Use HTTPS** - Always use HTTPS in production

## Troubleshooting

### "Invalid API Key" Error

- Check that your API key is correct
- Verify the key hasn't been revoked
- Check if the key has expired

### "CORS Error"

- Ensure your domain is whitelisted in the API key settings
- Check that you're using HTTPS in production

### "Rate Limit Exceeded"

- Reduce request frequency
- Implement request throttling
- Contact support to increase limits

### Forms Not Loading

- Check network connectivity
- Verify the form ID is correct
- Check if the form is published and API-enabled
- Look at browser console for errors

## Common Mistakes

### 1. Accessing Form Properties Incorrectly

❌ **INCORRECT:**
```typescript
const forms = await client.listForms();
console.log(forms.items[0].name); // undefined!
console.log(forms.items[0].description); // undefined!
```

✅ **CORRECT:**
```typescript
const forms = await client.listForms();
console.log(forms.items[0].metadata.name); // Works!
console.log(forms.items[0].metadata.description); // Works!
```

**Why:** Form properties like `name`, `description`, `category`, and `tags` are nested inside the `metadata` object. Only `id`, `templateId`, `version`, and `publishedAt` are at the root level.

### 2. Wrong CSS Import Path

❌ **INCORRECT:**
```typescript
import '@robertotaylor0/mediform-client-sdk/dist/styles.css';
```

✅ **CORRECT:**
```typescript
import '@robertotaylor0/mediform-client-sdk/dist/index.css';
```

**Why:** The published package contains `index.css`, not `styles.css`.

### 3. Assuming Latest Version

Always specify the version explicitly in production:

```json
{
  "dependencies": {
    "@robertotaylor0/mediform-client-sdk": "1.0.0"
  }
}
```

**Why:** Pinning versions prevents unexpected breaking changes in production.

## License

MIT

## Support

- Documentation: [https://docs.mediform.com](https://docs.mediform.com)
- GitHub Issues: [https://github.com/your-org/mediform-sdk/issues](https://github.com/your-org/mediform-sdk/issues)
- Email: support@mediform.com

## Changelog

### v1.0.0 (Current)
- ✅ Form fetching and listing with pagination
- ✅ React FormRenderer component with full theming support
- ✅ AI-powered form auto-population from images and text
- ✅ Extraction utility functions (convertExtractionsToFormData, filterByConfidence, getLowConfidenceFields, etc.)
- ✅ Comprehensive validation engine with field-level and form-level validation
- ✅ Response caching for improved performance
- ✅ Full TypeScript definitions including FormListItem, FormDefinition, ExtractedField, ExtractionInput, ExtractionResult, and PaginatedResponse types
- ✅ Conditional logic evaluation
- ✅ Custom component support
- ✅ WCAG 2.1 AA accessibility compliance
- ✅ Comprehensive error handling with MediFormError class

---

**Made with ❤️ by the MediForm Team**