# @memberstack/dom

The official Memberstack JavaScript SDK for authentication and membership management. Works with **any JavaScript framework** - React, Vue, Svelte, Next.js, Nuxt, SvelteKit, or vanilla JS.

## Installation

```bash
npm install @memberstack/dom
```

## Quick Start

```typescript
import MemberstackDOM from '@memberstack/dom';

// Initialize with your public key (find it in your Memberstack dashboard)
const memberstack = MemberstackDOM.init({
  publicKey: 'pk_sb_...' // Use pk_sb_ for sandbox, pk_ for live
});

// Check if a user is logged in
const { data: member } = await memberstack.getCurrentMember();

if (member) {
  console.log('Logged in as:', member.auth.email);
} else {
  console.log('Not logged in');
}
```

## Core Concepts

### Authentication State

Listen for authentication changes reactively:

```typescript
const { unsubscribe } = memberstack.onAuthChange((member) => {
  if (member) {
    // User is logged in
    console.log('Welcome,', member.auth.email);
    console.log('Plans:', member.planConnections);
  } else {
    // User is logged out
    console.log('Not authenticated');
  }
});

// Clean up when done (e.g., component unmount)
unsubscribe();
```

### Prebuilt UI Modals

Launch beautiful, pre-styled authentication modals with a single function call:

```typescript
// Open login modal
await memberstack.openModal('LOGIN');

// Open signup modal
await memberstack.openModal('SIGNUP');

// Open profile modal (for logged-in users)
await memberstack.openModal('PROFILE');

// Open forgot password modal
await memberstack.openModal('FORGOT_PASSWORD');
```

Modals are fully customizable through your Memberstack dashboard and support translations.

### Programmatic Authentication

For custom UI, use the authentication methods directly:

```typescript
// Sign up a new member
const { data } = await memberstack.signupMemberEmailPassword({
  email: 'user@example.com',
  password: 'securePassword123',
  customFields: {
    firstName: 'Jane',
    lastName: 'Doe'
  }
});

// Log in an existing member
const { data } = await memberstack.loginMemberEmailPassword({
  email: 'user@example.com',
  password: 'securePassword123'
});

// Log out
await memberstack.logout();
```

## API Reference

### Initialization

#### `init(config)`

Initialize the Memberstack SDK. Call this once when your app loads.

```typescript
const memberstack = MemberstackDOM.init({
  publicKey: 'pk_...', // Required: Your public API key
  useCookies: true,    // Optional: Enable cookie-based sessions
  domain: 'https://...' // Optional: Custom API domain
});
```

### Authentication Methods

| Method | Description |
|--------|-------------|
| `loginMemberEmailPassword({ email, password })` | Log in with email and password |
| `signupMemberEmailPassword({ email, password, customFields?, plans? })` | Create a new member account |
| `logout()` | Sign out the current member |
| `getCurrentMember()` | Get the currently authenticated member |
| `onAuthChange(callback)` | Subscribe to authentication state changes |

### Passwordless Authentication

| Method | Description |
|--------|-------------|
| `sendMemberLoginPasswordlessEmail({ email })` | Send a login code to an existing member |
| `sendMemberSignupPasswordlessEmail({ email })` | Send a signup code to a new member |
| `loginMemberPasswordless({ email, passwordlessToken })` | Complete passwordless login |
| `signupMemberPasswordless({ email, passwordlessToken, customFields? })` | Complete passwordless signup |

### OAuth / Social Login

| Method | Description |
|--------|-------------|
| `loginWithProvider({ provider })` | Log in via OAuth (e.g., Google, Facebook) |
| `signupWithProvider({ provider, customFields?, plans? })` | Sign up via OAuth |
| `connectProvider({ provider })` | Connect an OAuth provider to existing account |
| `disconnectProvider({ provider })` | Disconnect an OAuth provider |

### Member Management

| Method | Description |
|--------|-------------|
| `updateMember({ customFields })` | Update member's custom fields |
| `updateMemberAuth({ email?, oldPassword?, newPassword? })` | Update email or password |
| `updateMemberProfileImage({ profileImage })` | Upload a new profile image |
| `deleteMember()` | Delete the current member's account |
| `getMemberJSON()` | Get member's JSON data store |
| `updateMemberJSON({ json })` | Update member's JSON data store |

### Password Reset

| Method | Description |
|--------|-------------|
| `sendMemberResetPasswordEmail({ email })` | Send password reset email |
| `resetMemberPassword({ token, newPassword })` | Complete password reset |
| `setPassword({ password })` | Set password (for passwordless members) |

### Plans & Payments

| Method | Description |
|--------|-------------|
| `getPlans()` | Get all available plans |
| `getPlan({ planId })` | Get a specific plan |
| `addPlan({ planId })` | Add a free plan to member |
| `removePlan({ planId })` | Remove a plan from member |
| `purchasePlansWithCheckout({ priceId, successUrl?, cancelUrl? })` | Start Stripe checkout |
| `launchStripeCustomerPortal({ returnUrl? })` | Open Stripe billing portal |

### Prebuilt UI

| Method | Description |
|--------|-------------|
| `openModal(type, params?)` | Open a prebuilt modal |
| `hideModal()` | Close the current modal |

Modal types: `'LOGIN'`, `'SIGNUP'`, `'PROFILE'`, `'FORGOT_PASSWORD'`, `'RESET_PASSWORD'`

### Data Tables

| Method | Description |
|--------|-------------|
| `getDataTables()` | List all data tables |
| `getDataTable({ table })` | Get a data table schema |
| `getDataRecords({ table, limit?, after? })` | List records from a table |
| `getDataRecord({ table, recordId })` | Get a single record |
| `createDataRecord({ table, data })` | Create a new record |
| `updateDataRecord({ recordId, data })` | Update a record |
| `deleteDataRecord({ recordId })` | Delete a record |
| `queryDataRecords({ table, query })` | Advanced query with filters |

### Content & App Info

| Method | Description |
|--------|-------------|
| `getApp()` | Get app configuration |
| `getSecureContent({ contentId })` | Fetch gated content |
| `getRestrictedUrlGroups()` | Get content access groups |
| `sendMemberVerificationEmail()` | Send email verification |

## Common Patterns

### Protected Routes

```typescript
// Check access before rendering protected content
const { data: member } = await memberstack.getCurrentMember();

if (!member) {
  window.location.href = '/login';
  return;
}

// Check for specific plan
const hasPro = member.planConnections.some(
  pc => pc.planId === 'pln_pro123' && pc.active
);

if (!hasPro) {
  window.location.href = '/upgrade';
  return;
}
```

### Reactive Auth State (React Example)

```typescript
import { useEffect, useState } from 'react';
import MemberstackDOM from '@memberstack/dom';

const memberstack = MemberstackDOM.init({ publicKey: 'pk_...' });

function useAuth() {
  const [member, setMember] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const { unsubscribe } = memberstack.onAuthChange((m) => {
      setMember(m);
      setLoading(false);
    });
    return () => unsubscribe();
  }, []);

  return { member, loading, memberstack };
}
```

### Sign Up with Plan

```typescript
// Sign up and assign a plan in one step
const { data } = await memberstack.signupMemberEmailPassword({
  email: 'user@example.com',
  password: 'securePassword123',
  plans: [{ planId: 'pln_free_tier' }]
});

// Or redirect to Stripe checkout for paid plans
await memberstack.purchasePlansWithCheckout({
  priceId: 'prc_monthly_pro',
  successUrl: '/welcome',
  cancelUrl: '/pricing'
});
```

### Custom Fields

```typescript
// Set custom fields during signup
await memberstack.signupMemberEmailPassword({
  email: 'user@example.com',
  password: 'password123',
  customFields: {
    firstName: 'Jane',
    company: 'Acme Inc',
    role: 'developer'
  }
});

// Update custom fields later
await memberstack.updateMember({
  customFields: {
    company: 'New Company'
  }
});
```

## TypeScript Support

This package includes full TypeScript definitions. Import types directly:

```typescript
import MemberstackDOM from '@memberstack/dom';
import type {
  Member,
  Plan,
  LoginMemberEmailPasswordPayload,
  SignupMemberEmailPasswordPayload
} from '@memberstack/dom';
```

## Error Handling

All methods return promises that may reject on error:

```typescript
try {
  const { data } = await memberstack.loginMemberEmailPassword({
    email: 'user@example.com',
    password: 'wrongpassword'
  });
} catch (error) {
  // Handle authentication error
  console.error('Login failed:', error.message);
}
```

## Environment Setup

Store your public key in environment variables:

```bash
# .env
NEXT_PUBLIC_MEMBERSTACK_PUBLIC_KEY=pk_sb_xxx  # Next.js
VITE_MEMBERSTACK_PUBLIC_KEY=pk_sb_xxx         # Vite
REACT_APP_MEMBERSTACK_PUBLIC_KEY=pk_sb_xxx    # Create React App
```

```typescript
const memberstack = MemberstackDOM.init({
  publicKey: process.env.NEXT_PUBLIC_MEMBERSTACK_PUBLIC_KEY
});
```

## Sandbox vs Live

- **Sandbox keys** (`pk_sb_...`): Use for development and testing. Data is separate from production.
- **Live keys** (`pk_...`): Use for production. Real member data and Stripe transactions.

## Resources

- [Full Documentation](https://docs.memberstack.com)
- [Dashboard](https://app.memberstack.com)
- [API Reference](https://developers.memberstack.com)
- [Data Tables Guide](https://docs.memberstack.com/data-tables)

## Support

- [Help Center](https://help.memberstack.com)
- [Community](https://community.memberstack.com)
- [Contact Support](https://memberstack.com/support)

## License

MIT
