npm package: @havena/esm-core-api
Description: Core API library for ESM
Latest version: 1.0.1

# @havena/esm-core-api

Core ESM API library providing authentication, HTTP client utilities, React hooks, and type definitions for the Haven ESM platform.

## Installation

```bash
npm install @havena/esm-core-api
# or
yarn add @havena/esm-core-api
```

## Overview

This package provides a comprehensive set of APIs and utilities for building applications on the Haven ESM platform:

- **Authentication**: Better Auth integration with organization, admin, and multi-session support
- **HTTP Client**: Axios-based API client with SWR integration
- **React Hooks**: Custom hooks for session management, permissions, and data fetching
- **Storage**: Local storage utilities
- **Utilities**: URL construction, object manipulation, and helper functions
- **Types**: TypeScript type definitions for the platform

## Core Features

### Authentication API

The authentication API is built on top of Better Auth and provides a comprehensive authentication system with organization support, admin roles, and multi-session capabilities.

#### Auth Client

```typescript
import { Auth } from "@havena/esm-core-api";

// Access the auth client
const { data: session } = Auth.client.useSession();

// Sign out with confirmation
Auth.confirmedSignOut();

// Delete account with confirmation
Auth.confirmedDeleteAccount();
```

**Features:**

- Organization management with teams
- Admin role-based access control
- Username-based authentication
- Anonymous user support
- API key authentication
- Multi-session support
- JWT token support

#### Auth ACL (Access Control Lists)

The package includes predefined ACL configurations for organization and admin plugins, providing role-based permission management.

### Hive API

The Hive API provides HTTP client functionality, data fetching hooks, and API utilities.

#### API Configuration Provider

Wrap your application with `ApiConfigProvider` to configure SWR with the API fetcher:

```typescript
import { ApiConfigProvider } from "@havena/esm-core-api";

function App() {
  return <ApiConfigProvider>{/* Your app components */}</ApiConfigProvider>;
}
```

#### API Fetch

Make HTTP requests using the `apiFetch` function:

```typescript
import { apiFetch } from "@havena/esm-core-api";

// GET request
const response = await apiFetch("/users", {
  method: "GET",
});

// POST request
const response = await apiFetch("/users", {
  method: "POST",
  data: { name: "John Doe", email: "john@example.com" },
});

// With custom headers
const response = await apiFetch("/protected", {
  method: "GET",
  headers: {
    Authorization: "Bearer token",
  },
});
```

#### Use API Hook

A wrapper around SWR for data fetching:

```typescript
import { useApi } from "@havena/esm-core-api";

function UserList() {
  const { data, error, isLoading } = useApi("/users");

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {data?.data?.results?.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
```

#### URL Construction

Build URLs with query parameters:

```typescript
import { constructUrl } from "@havena/esm-core-api";

// Basic usage
const url = constructUrl("/api/users", { id: 123, active: true });
// → '/api/users?id=123&active=true'

// Array values
const url = constructUrl("/api/search", {
  tags: ["javascript", "typescript"],
  limit: 10,
});
// → '/api/search?tags=javascript&tags=typescript&limit=10'

// With merge strategy
const url = constructUrl(
  "/api/data?sort=name",
  { sort: "date", limit: 5 },
  { mergeStrategy: "preserve" }
);
// → '/api/data?sort=name&limit=5' (preserves existing 'sort')
```

**UrlBuilder Class:**

```typescript
import { UrlBuilder } from "@havena/esm-core-api";

const url = new UrlBuilder("/api/users")
  .param("page", 1)
  .param("limit", 10)
  .paramIf(includeInactive, "includeInactive", true)
  .build();
```

#### File Upload

Upload files with progress tracking:

```typescript
import { uploadFiles } from "@havena/esm-core-api";

const result = await uploadFiles({
  files: {
    avatar: [file1, file2],
    documents: [file3],
  },
  onProgress: (progress) => {
    console.log(`Upload progress: ${progress}%`);
  },
  relatedModelName: "User",
  relatedModelId: "123",
  purpose: "profile",
  tags: ["avatar", "profile"],
});

// Get file URL
import { getHiveFileUrl } from "@havena/esm-core-api";
const fileUrl = getHiveFileUrl(result.files[0].blob.storagePath);
```

#### HTTP Client

Direct access to the configured Axios instance:

```typescript
import { httpClient } from "@havena/esm-core-api";

const response = await httpClient.get("/users");
const response = await httpClient.post("/users", { name: "John" });
```

### React Hooks

#### Session Management

```typescript
import { useSession, SessionProvider } from "@havena/esm-core-api";

// Wrap your app with SessionProvider
function App() {
  return (
    <SessionProvider value={{ user, session }}>
      <YourComponents />
    </SessionProvider>
  );
}

// Use session in components
function Profile() {
  const { user, session } = useSession();

  return <div>Welcome, {user?.name}</div>;
}
```

#### User Search

```typescript
import { useSearchUser } from "@havena/esm-core-api";

function UserSearch() {
  const { users, isLoading, setFilters, filters } = useSearchUser();

  return (
    <div>
      <input
        onChange={(e) => setFilters({ search: e.target.value })}
        placeholder="Search users..."
      />
      {isLoading && <div>Loading...</div>}
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}
```

#### System Access Control

Check system-level permissions (server-side validation):

```typescript
import { useUserHasSystemAccess } from "@havena/esm-core-api";

function AdminPanel() {
  const { hasAccess, isLoading } = useUserHasSystemAccess({
    user: ["impersonate", "delete"],
    category: ["create"],
  });

  if (isLoading) return <Loader />;

  return (
    <>
      {hasAccess && <ImpersonateUserButton />}
      {hasAccess && <DeleteUserButton />}
    </>
  );
}
```

Client-side permission check (for UI optimization only):

```typescript
import { useUserHasSystemAccessSync } from "@havena/esm-core-api";

function Menu() {
  const { hasAccess, isLoading } = useUserHasSystemAccessSync({
    user: ["impersonate"],
  });

  // This is for UI only - backend must validate!
  return <Menu>{hasAccess && <ImpersonateMenuItem />}</Menu>;
}
```

#### Organization Access Control

Check organization-level permissions:

```typescript
import { useUserHasOrganizationAccess } from "@havena/esm-core-api";

function PropertyManagement() {
  const { hasAccess, isLoading } = useUserHasOrganizationAccess({
    property: ["create", "update"],
    file: ["upload"],
  });

  if (isLoading) return <Loader />;

  return (
    <>
      {hasAccess && <CreatePropertyButton />}
      {hasAccess && <UploadFileButton />}
    </>
  );
}
```

#### Search Parameters

Manage URL search parameters (React Router v5 compatible):

```typescript
import { useSearchParams } from "@havena/esm-core-api";

function FilterComponent() {
  const [searchParams, setSearchParams] = useSearchParams();

  const page = searchParams.get("page") || "1";
  const sort = searchParams.get("sort") || "name";

  const handleSort = (newSort: string) => {
    setSearchParams({ ...Object.fromEntries(searchParams), sort: newSort });
  };

  return (
    <select value={sort} onChange={(e) => handleSort(e.target.value)}>
      <option value="name">Name</option>
      <option value="date">Date</option>
    </select>
  );
}
```

Extended version with utility methods:

```typescript
import { useSearchParamsExtended } from "@havena/esm-core-api";

function AdvancedFilters() {
  const {
    searchParams,
    getParam,
    setParam,
    deleteParam,
    updateParams,
    clearParams,
    getParamsObject,
  } = useSearchParamsExtended();

  return (
    <div>
      <button onClick={() => setParam("filter", "active")}>Set Filter</button>
      <button onClick={() => deleteParam("filter")}>Remove Filter</button>
      <button onClick={() => updateParams({ page: "1", sort: "name" })}>
        Update Multiple
      </button>
      <button onClick={clearParams}>Clear All</button>
    </div>
  );
}
```

Typed search parameters:

```typescript
import { useTypedSearchParams, searchParamUtils } from "@havena/esm-core-api";

function TypedFilters() {
  const [params, setParams, clearParams] = useTypedSearchParams({
    page: {
      parse: searchParamUtils.parseNumber,
      serialize: String,
      defaultValue: 1,
    },
    active: {
      parse: searchParamUtils.parseBoolean,
      serialize: String,
      defaultValue: false,
    },
    tags: {
      parse: searchParamUtils.parseArray,
      serialize: searchParamUtils.serializeArray,
      defaultValue: [],
    },
  });

  return (
    <div>
      <p>Page: {params.page}</p>
      <p>Active: {params.active ? "Yes" : "No"}</p>
      <p>Tags: {params.tags.join(", ")}</p>
      <button onClick={() => setParams({ page: params.page + 1 })}>
        Next Page
      </button>
    </div>
  );
}
```

### Storage

#### Local Storage Hook

```typescript
import { useLocalStorage } from "@havena/esm-core-api";

function Preferences() {
  const [theme, setTheme] = useLocalStorage<string>("theme");

  return (
    <select value={theme || "light"} onChange={(e) => setTheme(e.target.value)}>
      <option value="light">Light</option>
      <option value="dark">Dark</option>
    </select>
  );
}
```

### Utilities

#### Object Manipulation

```typescript
import {
  flattenObject,
  unflattenArray,
  flattenObjectToPairs,
  unflattenPairsToObject,
} from "@havena/esm-core-api";

// Flatten nested objects
const nested = { user: { name: "John", age: 30 } };
const flattened = flattenObject(nested);
// → ['user.name', '"John"', 'user.age', '30']

// Unflatten back
const restored = unflattenArray(flattened);
// → { user: { name: 'John', age: 30 } }

// Flatten to key-value pairs
const pairs = flattenObjectToPairs(nested);
// → [['user.name', 'John'], ['user.age', 30]]
```

#### String Utilities

```typescript
import {
  getNameInitials,
  getNameInitialsFromEmail,
  parseDate,
} from "@havena/esm-core-api";

const initials = getNameInitials("John Doe"); // → 'JD'
const emailInitials = getNameInitialsFromEmail("john.doe@example.com"); // → 'JD'
const date = parseDate("2024-01-01"); // → Date object
```

#### Path Utilities

```typescript
import { extractIdFromPath } from "@havena/esm-core-api";

// Extract ID from path
const id = extractIdFromPath("/dashboard/users/123", "/dashboard/users");
// → '123'
```

#### Type Utilities

```typescript
import { Path, FieldPath } from "@havena/esm-core-api";

type User = {
  name: string;
  address: {
    city: string;
    zip: number;
  };
};

type UserPaths = Path<User>;
// → 'name' | 'address' | 'address.city' | 'address.zip'
```

### Types

The package exports comprehensive TypeScript types:

```typescript
import type {
  User,
  Session,
  UserSession,
  Organization,
  OrganizationRole,
  Member,
  Team,
  TeamMember,
  Invitation,
  APIFetchInit,
  APIFetchResponse,
  APIFetchError,
  PaginationControls,
  APIListResponse,
  FileUploadPayload,
  FileUploadResultsPayload,
  FileBlob,
  FileMetadata,
} from "@havena/esm-core-api";
```

## API Reference

### Authentication

- `Auth.client` - Better Auth client instance
- `Auth.confirmedSignOut()` - Sign out with confirmation modal
- `Auth.confirmedDeleteAccount()` - Delete account with confirmation modal
- `Auth.pluginOptions` - Plugin configuration options

### HTTP Client

- `apiFetch<T, K>(url, options?)` - Make HTTP requests
- `httpClient` - Axios instance with base URL configured
- `ApiConfigProvider` - React provider for SWR configuration
- `useApi<T>(resource, fetcher?, options?)` - SWR hook wrapper
- `mutate` - SWR mutate function
- `handleApiErrors` - Error handling utility

### URL Utilities

- `constructUrl(path, params?, options?)` - Build URLs with query parameters
- `UrlBuilder` - Fluent URL builder class

### File Operations

- `uploadFiles(options, requestParams?)` - Upload files with progress tracking
- `cleanFiles(paths)` - Delete files by paths
- `getHiveFileUrl(path)` - Get streaming URL for media files

### Hooks

- `useSession()` - Access user session context
- `useSearchUser()` - Search users with debouncing
- `useUserHasSystemAccess(permissions)` - Server-side system permission check
- `useUserHasSystemAccessSync(permissions)` - Client-side system permission check
- `useUserHasOrganizationAccess(permissions)` - Server-side organization permission check
- `useSearchParams()` - Manage URL search parameters
- `useSearchParamsExtended()` - Extended search params with utilities
- `useTypedSearchParams(schema)` - Typed search parameters

### Storage

- `useLocalStorage<T>(key)` - Local storage hook

### Utilities

- `flattenObject(obj, parentKey?)` - Flatten nested objects to array
- `unflattenArray(arr)` - Restore object from flattened array
- `flattenObjectToPairs(obj, parentKey?)` - Flatten to key-value pairs
- `unflattenPairsToObject(pairs)` - Restore object from pairs
- `getNameInitials(name)` - Get initials from name
- `getNameInitialsFromEmail(email)` - Get initials from email
- `parseDate(date?, defaultNow?)` - Parse date string
- `extractIdFromPath(pathname, basePath?)` - Extract ID from URL path
- `sleep(milliseconds)` - Promise-based delay
- `Path<T>` - Type utility for object paths
- `FieldPath<T>` - Type utility for form field paths

## Peer Dependencies

This package requires the following peer dependencies:

- `react` ^18.3.1
- `react-dom` ^18.3.1
- `react-router` ^5.3.4
- `react-router-dom` ^5.3.4
- `@mantine/core` ^8.3.4
- `@mantine/hooks` ^8.3.4
- `@mantine/notifications` ^8.3.4
- `@mantine/modals` ^8.3.4
- `swr` ^2.3.6
- `zustand` ^5.0.8

## Security Notes

⚠️ **Important Security Considerations:**

1. **Permission Checks**: Always use server-side permission checks (`useUserHasSystemAccess`, `useUserHasOrganizationAccess`) for security-critical operations. Client-side checks (`useUserHasSystemAccessSync`) are for UI optimization only and can be bypassed.

2. **API Validation**: The backend MUST validate all permissions and access control before executing any operations. Never rely solely on client-side checks.

3. **Authentication**: The auth client handles authentication tokens automatically. Ensure your backend properly validates these tokens.

## License