npm package: @form-guardian/dom
Description: Universal DOM-based form autosave library. Works with any form library (React, Vue, Angular, Svelte) or vanilla HTML forms. Provides attachFormAutosave function with analytics events, batching, and automatic draft restoration.
Author: Tishkin Yuri
Latest version: 1.0.5

# @form-guardian/dom

Universal DOM-based form autosave library. Works with any form library (React, Vue, Angular, Svelte) or vanilla HTML forms. Provides `attachFormAutosave` function with analytics events, batching, and automatic draft restoration.

## Overview

`@form-guardian/dom` is the universal package for form autosave. It works by attaching event listeners to DOM elements, making it compatible with any JavaScript framework or vanilla HTML. It automatically saves form values as users type and can restore them when the form is loaded again.

## Installation

```bash
npm install @form-guardian/dom
```

## Features

- ✅ **Universal Compatibility** - Works with React, Vue, Angular, Svelte, or vanilla HTML
- ✅ **Automatic Saving** - Saves form values as users type (debounced)
- ✅ **Automatic Restoration** - Optionally restores drafts on form load
- ✅ **Analytics Events** - Callbacks for `onBeforeSave`, `onAfterSave`, `onBeforeRestore`, `onAfterRestore`, `onDraftExpired`
- ✅ **Batching** - Save changes in batches to reduce storage operations
- ✅ **Field Filtering** - Blacklist/whitelist specific fields
- ✅ **Type Safety** - Full TypeScript support with generic types

## Quick Start

### Vanilla JavaScript

```typescript
import { attachFormAutosave } from '@form-guardian/dom';

const form = document.querySelector('#my-form');

const autosave = attachFormAutosave({
  formId: 'contact-form',
  root: form,
  autoRestore: true,
  debounceMs: 500,
  onAfterSave: (values) => {
    console.log('Draft saved:', values);
  }
});

// Clear draft on successful submit
form.addEventListener('submit', async () => {
  await autosave.clear();
});
```

### React

```typescript
import { useEffect, useRef } from 'react';
import { attachFormAutosave } from '@form-guardian/dom';

function MyForm() {
  const formRef = useRef<HTMLFormElement>(null);

  useEffect(() => {
    if (!formRef.current) return;

    const autosave = attachFormAutosave({
      formId: 'my-form',
      root: formRef.current,
      autoRestore: true,
      onAfterSave: (values) => {
        console.log('Saved:', values);
      }
    });

    return () => {
      autosave.destroy();
    };
  }, []);

  return <form ref={formRef}>...</form>;
}
```

## API

### `attachFormAutosave<T>(options)`

Attach autosave functionality to a form element.

#### Options

- **`formId`** (required) - Unique identifier for the form
- **`root`** (required) - Form or container element
- **`autoRestore`** (optional) - Automatically restore draft on mount (default: `false`)
- **`debounceMs`** (optional) - Debounce delay in milliseconds (default: `500`)
- **`ttl`** (optional) - Time to live for drafts
- **`blacklist`** (optional) - Array of CSS selectors for fields to exclude
- **`whitelist`** (optional) - Array of CSS selectors for fields to explicitly include
- **`batchSaveInterval`** (optional) - Save changes in batches (milliseconds)
- **`onBeforeSave`** (optional) - Callback before saving
- **`onAfterSave`** (optional) - Callback after saving
- **`onBeforeRestore`** (optional) - Callback before restoring
- **`onAfterRestore`** (optional) - Callback after restoring
- **`onDraftExpired`** (optional) - Callback when draft expires

#### Returns

A `FormAutosaveHandle` object with methods:
- **`restore()`** - Manually restore the draft
- **`clear()`** - Clear the draft
- **`destroy()`** - Remove event listeners and cleanup
- **`getCurrentValues()`** - Get current form values
- **`hasDraft()`** - Check if draft exists
- **`getDraftMeta()`** - Get draft metadata

## Examples

### With Analytics

```typescript
const autosave = attachFormAutosave({
  formId: 'checkout-form',
  root: form,
  onBeforeSave: async (values) => {
    // Track save attempt
    analytics.track('draft_save_started', { formId: 'checkout-form' });
  },
  onAfterSave: async (values) => {
    // Track successful save
    analytics.track('draft_saved', { formId: 'checkout-form', fieldCount: Object.keys(values).length });
  },
  onDraftExpired: async (draftId) => {
    // Track expiration
    analytics.track('draft_expired', { draftId });
  }
});
```

### With Batching

```typescript
const autosave = attachFormAutosave({
  formId: 'large-form',
  root: form,
  batchSaveInterval: 5000, // Save every 5 seconds
  onAfterSave: (values) => {
    console.log('Batch saved:', values);
  }
});
```

### Field Filtering

```typescript
const autosave = attachFormAutosave({
  formId: 'secure-form',
  root: form,
  // Exclude password fields and sensitive data
  blacklist: [
    '[data-no-save]',
    'input[name="password"]',
    'input[name="ssn"]'
  ],
  // But allow specific safe fields
  whitelist: [
    'input[data-safe]',
    'textarea[data-safe]'
  ]
});
```

### TypeScript with Custom Types

```typescript
interface ContactFormData {
  name: string;
  email: string;
  phone: string;
  message: string;
}

const autosave = attachFormAutosave<ContactFormData>({
  formId: 'contact-form',
  root: form,
  onAfterSave: (values) => {
    // values is typed as ContactFormData
    console.log(values.name); // TypeScript knows this exists
  }
});
```

## Field Selection

By default, the library tracks:
- `<input>` elements (except `type="submit"`, `type="button"`, `type="reset"`, `type="password"`)
- `<textarea>` elements
- Elements with `contenteditable="true"`

Fields are identified by:
1. `name` attribute (preferred)
2. `id` attribute
3. `data-fg-key` attribute

## When to Use

Use `@form-guardian/dom` if you:
- Want universal compatibility with any framework
- Need fine-grained control over autosave behavior
- Are building a custom form solution
- Want to use analytics events and batching

For React applications, consider using `@form-guardian/react` for a more React-idiomatic API with hooks.