npm package: @pipup/atproto
Description: AT Protocol utilities for blog platforms (PiPup, Whitewind, Leaflet, etc.)
Author: PiPup
Latest version: 0.1.7

# @pipup/atproto

ATProto utilities for blog platforms including import functionality.

## Overview

This package provides common functionality for working with ATProto-based blog platforms. Its main purpose is to translate not only from other lexicons that use Markdown but also from other formats like blocks.

## Features

- **Leaflet Import**: Convert Leaflet documents to PiPup format with support for:
  - Rich text facets (bold, italic, code, links)
  - Nested unordered lists
  - Images, blockquotes, headers
  - Code blocks, math equations
  - Special blocks (Bluesky posts, websites, iframes)

- **Whitewind Import**: Convert Whitewind blog entries to PiPup format

- **Lexicon-based Validation**: ATProto lexicon-generated types and validation

## Installation

```bash
pnpm add @pipup/atproto
```

## Usage

### Whitewind Import

```typescript
import {
  transformWhitewindToPipup,
  validateWhitewindRecord,
  type WhitewindBlogRecord,
  type PipupBlogRecord
} from '@pipup/atproto'

// Validate using lexicon-generated function
const result = validateWhitewindRecord(record)
if (result.success) {
  // Transform to PiPup format
  const entry: PipupBlogRecord = transformWhitewindToPipup(record)
  console.log(entry.content) // Markdown with title inserted
}
```

### Leaflet Import

```typescript
import {
  transformLeafletToPipup,
  validateLeafletRecord,
  isLeafletRecord,
  type LeafletDocumentRecord,
  type PipupBlogRecord
} from '@pipup/atproto'

// Real-time ingestion (jetstream/firehose) - use type guard only
if (isLeafletRecord(doc)) {
  const entry: PipupBlogRecord = transformLeafletToPipup(doc, authorDid)
  // Process entry...
}

// Backfill (ATProto API) - use full validation
const result = validateLeafletRecord(doc)
if (result.success) {
  const entry = transformLeafletToPipup(doc, authorDid)
  // Process entry...
} else {
  console.error('Validation error:', result.error)
}
```

### Advanced: Block-level Transformation

```typescript
import {
  blocksToMarkdown,
  applyFacets,
  buildByteToCharMap
} from '@pipup/atproto'

// Transform Leaflet blocks to Markdown
const markdown = blocksToMarkdown(blocks, authorDid)

// Apply rich text facets to plaintext
const formatted = applyFacets('hello world', facets)

// Handle UTF-8 byte offsets
const byteMap = buildByteToCharMap(text)
```

## API Reference

### Type Exports

All types are re-exported from lexicon-generated code:

- `WhitewindBlogRecord` - Whitewind blog entry record
- `LeafletDocumentRecord` - Leaflet document record
- `PipupBlogRecord` - PiPup blog entry record (output format)

### Validation Functions

**Type Guards** (for real-time ingestion from jetstream/firehose):
- `isWhitewindRecord(record: any): boolean` - Checks `$type` field only
- `isLeafletRecord(record: any): boolean` - Checks `$type` field only
- `isPipupRecord(record: any): boolean` - Checks `$type` field only

**Full Validation** (for backfill via ATProto API):
- `validateWhitewindRecord(record: any): ValidationResult` - Complete lexicon schema validation
- `validateLeafletRecord(record: any): ValidationResult` - Complete lexicon schema validation
- `validatePipupRecord(record: any): ValidationResult` - Complete lexicon schema validation

**Important**: Use type guards for real-time ingestion (jetstream/firehose) because records are in raw IPLD format. Only use full validation for backfill where records are fetched through the ATProto API and have been processed.

### Transformation Functions

**Whitewind:**
```typescript
transformWhitewindToPipup(
  entry: WhitewindBlogRecord
): PipupBlogRecord
```

**Leaflet:**
```typescript
transformLeafletToPipup(
  doc: LeafletDocumentRecord,
  authorDid: string
): PipupBlogRecord

blocksToMarkdown(
  blocks: Array<any>,
  authorDid: string
): string

applyFacets(
  plaintext: string,
  facets?: Array<any>
): string

buildByteToCharMap(
  input: string
): number[]
```

## Development

```bash
# Install dependencies
pnpm install

# Build the package
pnpm build

# Run tests
pnpm test

# Watch mode for development
pnpm dev

# Run tests once (CI mode)
pnpm test:run
```

## Architecture

This package follows a thin wrapper approach:

1. **Lexicon Schemas** (`lexicons/`) - ATProto record definitions in JSON
2. **Generated Types** (`src/lexicon/`) - TypeScript types generated via `@atproto/lex-cli`
3. **Transformation Logic** (`src/*/transform.ts`) - Business logic for format conversion
4. **Re-exports** (`src/*/index.ts`) - Clean API surface

No custom wrapper types are created - all types come directly from lexicon generation.

## Build Process

1. `tsc` - Compile TypeScript with `@` path aliases
2. `tsc-alias` - Resolve `@` imports to relative paths
3. `fix-imports.js` - Add `.js` extensions to lexicon imports

## License

MIT