npm package: @seedts/jsx-runtime
Description: JSX runtime for SeedTS seed definitions
Author: SeedTS Contributors
Latest version: 0.1.1

# @seedts/jsx-runtime

> JSX runtime for declarative seed definitions in SeedTS

The JSX runtime package enables you to write database seeds using familiar React-like JSX syntax. This provides a clean, declarative way to define seeds with full TypeScript support.

## Installation

```bash
pnpm add @seedts/jsx-runtime
```

> **Note:** This package is automatically included when you install the main `seedts` package.

## Setup

### TypeScript Configuration

Add JSX support to your `tsconfig.json`:

```json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "seedts"
  }
}
```

### File Extension

Use `.tsx` extension for seed files:
- `users.seed.tsx` ✅
- `users.seed.ts` ❌ (won't work with JSX)

## Quick Start

### Basic Example

```tsx
import { Seed, Action, Entity, Attribute } from 'seedts/jsx-runtime';
import { PostgreSQLAdapter } from '@seedts/adapter-postgresql';

const adapter = new PostgreSQLAdapter(pool);

export const UsersSeed = (
  <Seed name="users" adapter={adapter}>
    <Action count={10}>
      <Entity>
        <Attribute
          name="email"
          factory={(ctx) => `user${ctx.index}@example.com`}
        />
        <Attribute
          name="name"
          factory={(ctx) => `User ${ctx.index}`}
        />
        <Attribute
          name="role"
          factory={() => 'user'}
        />
      </Entity>
    </Action>
  </Seed>
);
```

## Components

### `<Seed>` - Seed Container

The root component for a seed definition.

```tsx
<Seed
  name="users"              // Seed name (required)
  adapter={adapter}         // Database adapter (required)
  dependsOn="other-seed"   // Single dependency
  dependsOn={["a", "b"]}   // Multiple dependencies
  beforeInsert={fn}        // Hook: before insert
  afterInsert={fn}         // Hook: after insert
  onSuccess={fn}           // Hook: on success
  onError={fn}             // Hook: on error
>
  {/* Actions go here */}
</Seed>
```

**Props:**
- `name: string` - Unique seed identifier
- `adapter: SeedAdapter` - Database adapter instance
- `dependsOn?: string | string[]` - Seed dependencies
- `beforeInsert?: (data) => Promise<T[]> | T[]` - Pre-insert hook
- `afterInsert?: (data, inserted) => Promise<void> | void` - Post-insert hook
- `onSuccess?: (data, metadata) => Promise<void> | void` - Success hook
- `onError?: (error) => Promise<void> | void` - Error hook

### `<Action>` - Data Generation Action

Defines a batch of data to generate and insert.

```tsx
<Action
  count={100}                    // Number of records
  run={condition}                // Conditional execution
  condition={asyncFn}            // Async condition check
  beforeInsert={transformFn}     // Transform before insert
  onConflict="skip"              // Conflict strategy
>
  <Entity />
</Action>
```

**Props:**
- `count: number` - Number of records to generate
- `run?: boolean` - Conditional execution (default: true)
- `condition?: (ctx) => Promise<boolean> | boolean` - Async condition
- `beforeInsert?: (data) => Promise<T[]> | T[]` - Transform data
- `onConflict?: 'skip' | 'update' | 'error'` - Conflict resolution

### `<Entity>` - Record Definition

Defines the structure of a single record.

```tsx
<Entity
  data={{name: 'Static', value: 123}}  // Static data
  factory={factoryFn}                   // Dynamic generation
>
  <Attribute name="field1" factory={fn1} />
  <Attribute name="field2" factory={fn2} />
</Entity>
```

**Props:**
- `data?: Partial<T>` - Static data for all records
- `factory?: (ctx) => Partial<T>` - Dynamic data generation

### `<Attribute>` - Field Definition

Defines a single field/column in a record.

```tsx
<Attribute
  name="email"                    // Field name
  factory={factoryFn}             // Value generator
  transform={transformFn}         // Value transformer
  required={true}                 // Validation
  unique={true}                   // Uniqueness check
/>
```

**Props:**
- `name: string` - Field/column name
- `factory: (ctx) => any` - Value generation function
- `transform?: (value) => any` - Value transformation
- `required?: boolean` - Field is required
- `unique?: boolean` - Value must be unique

## Examples

### With Dependencies

```tsx
// users.seed.tsx
export const UsersSeed = (
  <Seed name="users" adapter={adapter}>
    <Action count={50}>
      <Entity>
        <Attribute name="email" factory={faker.email()} />
        <Attribute name="name" factory={faker.fullName()} />
      </Entity>
    </Action>
  </Seed>
);

// posts.seed.tsx
export const PostsSeed = (
  <Seed name="posts" dependsOn="users" adapter={adapter}>
    <Action count={100}>
      <Entity factory={async (ctx) => {
        const users = await ctx.getSeed('users');
        return {
          userId: users[ctx.index % users.length].id,
        };
      }}>
        <Attribute name="title" factory={faker.sentence()} />
        <Attribute name="content" factory={faker.paragraph()} />
      </Entity>
    </Action>
  </Seed>
);
```

### Conditional Seeding

```tsx
<Seed name="demo-users" adapter={adapter}>
  <Action
    count={50}
    run={process.env.NODE_ENV === 'development'}
  >
    <Entity>
      <Attribute name="email" factory={faker.email()} />
    </Entity>
  </Action>

  <Action
    count={1}
    condition={async (ctx) => {
      const users = await ctx.getSeed('users');
      return users.length === 0;
    }}
  >
    <Entity data={{ email: 'admin@example.com', role: 'admin' }} />
  </Action>
</Seed>
```

### Data Transformers

```tsx
<Seed name="users" adapter={adapter}>
  <Action count={10}>
    <Entity>
      <Attribute
        name="email"
        factory={faker.email()}
        transform={(email) => email.toLowerCase()}
      />
      <Attribute
        name="password"
        factory={() => 'password123'}
        transform={hashPassword}
      />
      <Attribute
        name="createdAt"
        factory={() => new Date()}
      />
    </Entity>
  </Action>
</Seed>
```

### Lifecycle Hooks

```tsx
<Seed
  name="users"
  adapter={adapter}
  beforeInsert={(data) => {
    console.log(`Inserting ${data.length} users`);
    return data.map(user => ({
      ...user,
      createdAt: new Date(),
    }));
  }}
  afterInsert={(data, inserted) => {
    console.log(`Inserted ${inserted.length} users`);
  }}
  onSuccess={(data, metadata) => {
    console.log(`Success! Duration: ${metadata.duration}ms`);
  }}
  onError={(error) => {
    console.error('Seeding failed:', error);
  }}
>
  <Action count={100}>
    <Entity>
      <Attribute name="email" factory={faker.email()} />
    </Entity>
  </Action>
</Seed>
```

### Multiple Actions

```tsx
<Seed name="users" adapter={adapter}>
  {/* Admin users */}
  <Action count={5}>
    <Entity>
      <Attribute name="email" factory={faker.email()} />
      <Attribute name="role" factory={() => 'admin'} />
    </Entity>
  </Action>

  {/* Regular users */}
  <Action count={100}>
    <Entity>
      <Attribute name="email" factory={faker.email()} />
      <Attribute name="role" factory={() => 'user'} />
    </Entity>
  </Action>

  {/* Premium users */}
  <Action count={20}>
    <Entity>
      <Attribute name="email" factory={faker.email()} />
      <Attribute name="role" factory={() => 'premium'} />
      <Attribute name="subscriptionEnd" factory={() => {
        const date = new Date();
        date.setFullYear(date.getFullYear() + 1);
        return date;
      }} />
    </Entity>
  </Action>
</Seed>
```

### Static Data

```tsx
<Seed name="categories" adapter={adapter}>
  <Action count={1}>
    <Entity data={{
      name: 'Technology',
      slug: 'technology',
      order: 1,
    }} />
  </Action>
  <Action count={1}>
    <Entity data={{
      name: 'Science',
      slug: 'science',
      order: 2,
    }} />
  </Action>
</Seed>
```

### With Faker Integration

```tsx
import { faker } from '@seedts/faker';

<Seed name="users" adapter={adapter}>
  <Action count={100}>
    <Entity>
      <Attribute name="email" factory={faker.email()} />
      <Attribute name="firstName" factory={faker.firstName()} />
      <Attribute name="lastName" factory={faker.lastName()} />
      <Attribute name="age" factory={faker.int({ min: 18, max: 80 })} />
      <Attribute name="bio" factory={faker.paragraph()} />
      <Attribute name="avatar" factory={faker.imageUrl()} />
      <Attribute name="website" factory={faker.url()} />
    </Entity>
  </Action>
</Seed>
```

## Execution Context

Access execution context in factory functions:

```tsx
<Attribute
  name="email"
  factory={(ctx) => {
    // Current index (0-based)
    const index = ctx.index;

    // Total count
    const total = ctx.count;

    // Seed name
    const seedName = ctx.seedName;

    // Get data from other seeds
    const otherData = await ctx.getSeed('other-seed');

    return `user${index}@example.com`;
  }}
/>
```

## Type Safety

Full TypeScript support with type inference:

```tsx
type User = {
  id?: number;
  email: string;
  name: string;
  role: 'admin' | 'user';
};

export const UsersSeed = (
  <Seed<User> name="users" adapter={adapter}>
    <Action count={10}>
      <Entity>
        {/* TypeScript will enforce correct types */}
        <Attribute<User, 'email'>
          name="email"
          factory={(ctx) => `user${ctx.index}@example.com`}
        />
        <Attribute<User, 'role'>
          name="role"
          factory={() => 'user'} // Type-checked!
        />
      </Entity>
    </Action>
  </Seed>
);
```

## Running JSX Seeds

```typescript
import { Executor } from '@seedts/core';
import { UsersSeed } from './seeds/users.seed';
import { PostsSeed } from './seeds/posts.seed';

const executor = new Executor([UsersSeed, PostsSeed]);
await executor.execute();
```

## API Reference

For detailed API documentation, visit:
- [Full Documentation](https://seedts.dev/docs/api/jsx-runtime)
- [JSX Guide](https://seedts.dev/docs/guides/jsx)
- [Component Reference](https://seedts.dev/docs/api/components)

## Related Packages

- **[@seedts/core](https://www.npmjs.com/package/@seedts/core)** - Core execution engine
- **[@seedts/types](https://www.npmjs.com/package/@seedts/types)** - Type definitions
- **[@seedts/faker](https://www.npmjs.com/package/@seedts/faker)** - Faker integration
- **[seedts](https://www.npmjs.com/package/seedts)** - Main convenience package

## Contributing

Contributions are welcome! Please see our [Contributing Guide](https://github.com/DarioBR91/seedts/blob/main/CONTRIBUTING.md).

## License

MIT © [SeedTS Contributors](../../LICENSE)