npm package: @lyleunderwood/filereader-polyfill
Description: W3C File API specification compliant FileReader polyfill for Node.js environments
Author: Lyle Underwood
Latest version: 1.0.0

# FileReader Polyfill

A comprehensive FileReader API polyfill for Node.js environments that provides **100% W3C File API specification compliance** with full compatibility with the Web FileReader API, including support for both File and Blob objects.

## Features

- ✅ **W3C File API Specification Compliant** - Passes comprehensive spec compliance tests
- ✅ Complete FileReader API implementation
- ✅ Support for all read methods: `readAsText`, `readAsDataURL`, `readAsArrayBuffer`, `readAsBinaryString`
- ✅ Full event support: `loadstart`, `progress`, `load`, `error`, `abort`, `loadend`
- ✅ Proper error handling with DOMException types (`InvalidStateError`, `NotReadableError`, `AbortError`)
- ✅ Works with both File and Blob objects
- ✅ Stream-based reading with progress events
- ✅ Proper abort functionality
- ✅ File path support for Node.js environments
- ✅ TypeScript support with full type definitions
- ✅ Event-driven architecture with EventTarget inheritance

## Requirements

- Node.js 16.7.0 or higher (required for native `Blob` and `EventTarget` support)

## Installation

```bash
npm install @lyleunderwood/filereader-polyfill
```

## Usage

### Basic Usage

```javascript
import { FileReader, File } from '@lyleunderwood/filereader-polyfill';

// Reading from a Blob
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const reader = new FileReader();

reader.onload = (event) => {
  console.log('Result:', reader.result);
};

reader.readAsText(blob);
```

### Reading from File Paths (Node.js specific)

```javascript
import { FileReader, File } from '@lyleunderwood/filereader-polyfill';

// Create File from file path
const file = new File('/path/to/your/file.txt');
const reader = new FileReader();

reader.onload = () => {
  console.log('File content:', reader.result);
};

reader.readAsText(file);
```

### Global Polyfill

The polyfill automatically installs itself globally if `FileReader` or `File` don't exist:

```javascript
import '@lyleunderwood/filereader-polyfill';

// Now you can use FileReader and File globally
const reader = new FileReader();
const file = new File(['data'], 'example.txt');
```

### Event Handling

```javascript
const reader = new FileReader();

reader.onloadstart = () => console.log('Started reading');
reader.onprogress = (event) => {
  if (event.lengthComputable) {
    const progress = (event.loaded / event.total) * 100;
    console.log(`Progress: ${progress}%`);
  }
};
reader.onload = () => console.log('Reading completed');
reader.onerror = () => console.error('Error occurred:', reader.error);
reader.onabort = () => console.log('Reading aborted');
reader.onloadend = () => console.log('Reading finished');

// You can also use addEventListener
reader.addEventListener('load', (event) => {
  console.log('Load event:', event);
});
```

### Reading Different Formats

```javascript
const reader = new FileReader();
const blob = new Blob(['Hello, World!']);

// Read as text
reader.readAsText(blob);

// Read as ArrayBuffer
reader.readAsArrayBuffer(blob);

// Read as Data URL
reader.readAsDataURL(blob);

// Read as binary string
reader.readAsBinaryString(blob);
```

### Abort Reading

```javascript
const reader = new FileReader();
const blob = new Blob(['Large file content...']);

reader.readAsText(blob);

// Abort the reading operation
setTimeout(() => {
  reader.abort();
}, 100);
```

### Error Handling

The polyfill provides W3C-compliant error handling with proper DOMException types:

```javascript
const reader = new FileReader();

// Handle different error scenarios
reader.onerror = (event) => {
  console.log('Error name:', reader.error.name);
  console.log('Error message:', reader.error.message);
};

reader.onabort = (event) => {
  console.log('Operation was aborted');
  // reader.error will be AbortError
};

// Reading from non-existent file (Node.js path constructor)
try {
  const file = new File('/path/to/nonexistent/file.txt');
  reader.readAsText(file); // Will trigger error event with NotReadableError
} catch (error) {
  // File constructor errors are deferred to read operations
}

// Attempting to read while already reading
try {
  reader.readAsText(blob);
  reader.readAsText(blob); // Throws InvalidStateError
} catch (error) {
  console.log(error.name); // "InvalidStateError"
}
```

## API Reference

### FileReader Class

Extends `EventTarget` and implements the complete Web FileReader API.

#### Properties

- `readyState`: Current state (`EMPTY`, `LOADING`, `DONE`)
- `result`: The file's contents (available after successful read)
- `error`: Error information if reading failed

#### Methods

- `readAsText(blob, encoding?)`: Read as text with optional encoding
- `readAsDataURL(blob)`: Read as Data URL
- `readAsArrayBuffer(blob)`: Read as ArrayBuffer
- `readAsBinaryString(blob)`: Read as binary string
- `abort()`: Abort the current reading operation

#### Events

- `loadstart`: Reading started
- `progress`: Reading progress (includes `loaded` and `total` properties)
- `load`: Reading completed successfully
- `error`: Error occurred during reading
- `abort`: Reading was aborted
- `loadend`: Reading finished (success, error, or abort)

### File Class

Extends native `Blob` and supports both Web API and Node.js file path constructors.

#### Constructors

```javascript
// Web API style
new File(fileBits, fileName, options)

// Node.js file path style
new File(filePath)
```

#### Properties

- `name`: File name
- `lastModified`: Last modification timestamp
- `size`: File size in bytes
- `type`: MIME type
- `path`: File path (Node.js specific, when created from path)

## TypeScript Support

Full W3C specification-compliant TypeScript definitions are included:

```typescript
import { 
  FileReader, 
  File,
  type IFileReader,
  type IFile,
  type NodeFile,
  type EventHandler,
  type ProgressEvent,
  type FileReaderState,
  type BlobPart,
  type FilePropertyBag
} from '@lyleunderwood/filereader-polyfill';

// W3C compliant usage
const reader: FileReader = new FileReader();
const file: File = new File(['content'], 'example.txt');

// Type-safe event handlers
reader.onload = (event: Event) => {
  console.log(reader.result);
};

reader.onprogress = (event: Event) => {
  const progressEvent = event as ProgressEvent;
  if (progressEvent.lengthComputable) {
    const progress = progressEvent.loaded / progressEvent.total;
    console.log(`Progress: ${Math.round(progress * 100)}%`);
  }
};

// File constructor with proper types
const fileFromBits: File = new File(
  ['Hello, World!'] as BlobPart[],
  'hello.txt',
  { type: 'text/plain', lastModified: Date.now() } as FilePropertyBag
);

// Node.js specific file path constructor
const fileFromPath: NodeFile = new File('/path/to/file.txt');
console.log(fileFromPath.path); // string | undefined
```

### Available Types

- **`IFileReader`**: W3C specification compliant FileReader interface
- **`IFile`**: W3C specification compliant File interface  
- **`NodeFile`**: Extended File interface with Node.js specific `path` property
- **`EventHandler`**: W3C compliant event handler function type
- **`ProgressEvent`**: Progress event interface with `lengthComputable`, `loaded`, and `total`
- **`FileReaderState`**: Enum for FileReader states (EMPTY, LOADING, DONE)
- **`BlobPart`**: Union type for valid Blob constructor parts
- **`FilePropertyBag`**: Options interface for File constructor

## W3C Specification Compliance

This polyfill implements the [W3C File API specification](https://www.w3.org/TR/FileAPI/) with 100% compliance for Node.js environments. It includes comprehensive test coverage that verifies:

- ✅ All FileReader interface requirements (constants, attributes, methods, events)
- ✅ All File interface requirements (constructor, properties, Blob inheritance)
- ✅ Proper error handling with correct DOMException types
- ✅ Event firing order and timing per specification
- ✅ State management and concurrent operation prevention
- ✅ Progress events with proper `lengthComputable`, `loaded`, and `total` properties

### Known Limitations

Due to the Node.js environment, some aspects cannot be implemented with 100% browser fidelity:

- **File system access**: Cannot detect file changes after File creation (deferred to read operations)
- **Event objects**: Uses generic Event instead of native ProgressEvent (properties are preserved)
- **Microtask timing**: Event firing may differ slightly from browser microtask scheduling
- **Memory management**: Cannot replicate exact browser garbage collection behavior
- **Security restrictions**: Cannot enforce same-origin policies or file access permissions

These limitations do not affect functional compatibility with the W3C specification.

## Browser Compatibility

This polyfill is designed for Node.js environments. For browser usage, use the native FileReader API which is widely supported.

## Testing

Run the comprehensive test suite including W3C specification compliance tests:

```bash
npm test
```

The test suite includes:
- Unit tests for all functionality
- W3C File API specification compliance tests
- Error handling and edge case coverage
- Integration tests between File and FileReader

## Contributing

Contributions are welcome! Please ensure all tests pass and maintain W3C specification compliance when making changes.

## License

MIT License - see LICENSE file for details.