npm package: @powerit/cms-client
Description: JavaScript client library for PowerIT CMS public API
Author: PowerIT
Latest version: 1.1.0

# @powerit/cms-client

A pure JavaScript client library for the PowerIT CMS public API. Works in browsers, Node.js, and any JavaScript runtime.

## Installation

```bash
npm install @powerit/cms-client
```

## Quick Start

```javascript
import { createClient } from '@powerit/cms-client';

const cms = createClient({
  baseUrl: 'https://your-cms.example.com',
  apiKey: 'your-api-key',
  locale: 'en' // Optional default locale
});

// Get all published content
const { data: contents } = await cms.getContents();

// Get a specific page by slug
const page = await cms.getContentBySlug('home');
console.log(page.translation.title);
```

## Configuration

```javascript
const cms = createClient({
  // Required
  baseUrl: 'https://your-cms.example.com',
  apiKey: 'your-api-key',

  // Optional
  locale: 'en',              // Default locale for content
  timeout: 30000,            // Request timeout in ms
  headers: {                 // Additional headers
    'X-Custom-Header': 'value'
  },
  fetch: customFetch         // Custom fetch implementation
});
```

### Node.js Usage

For Node.js versions without native `fetch`, provide a fetch implementation:

```javascript
import { createClient } from '@powerit/cms-client';
import fetch from 'node-fetch';

const cms = createClient({
  baseUrl: 'https://your-cms.example.com',
  apiKey: 'your-api-key',
  fetch: fetch
});
```

## API Reference

### Content

```javascript
// List published contents
const { data, meta } = await cms.getContents({
  type: 'page',        // Filter by content type
  locale: 'en',        // Filter by locale
  page: 1,             // Pagination
  per_page: 10
});

// Get content by ID or slug
const content = await cms.getContent('home');
const content = await cms.getContent(123);

// Get content by slug (explicit)
const page = await cms.getContentBySlug('about-us', { locale: 'en' });

// Get contents by type
const { data: blogs } = await cms.getContentsByType('blog');

// Get available content types
const { types } = await cms.getContentTypes();
// ['page', 'blog', 'news']

// Get available locales
const { locales } = await cms.getContentLocales();
// ['en', 'ro', 'fr']
```

### Menus

Requires `read_menus` permission on API key.

```javascript
// List all menus
const { menus } = await cms.getMenus();

// Get menu by ID or location
const menu = await cms.getMenu('header');
const menu = await cms.getMenu(1);

// Get menu by location name
const footer = await cms.getMenuByLocation('footer');
```

### Redirects

Requires `read_redirects` permission on API key.

```javascript
// List all redirects
const { redirects } = await cms.getRedirects();

// Check if a path has a redirect
const redirect = await cms.checkRedirect('/old-page');
if (redirect) {
  console.log(`Redirect to ${redirect.target_path} (${redirect.status_code})`);
}

// Resolve a path through redirect chains
const resolved = await cms.resolvePath('/old-page');
if (resolved) {
  console.log(`Final destination: ${resolved.finalPath}`);
  console.log(`Hops taken: ${resolved.hops}`);
}
```

### Languages

```javascript
// Get active languages
const { languages } = await cms.getLanguages();

// Get language codes only
const codes = await cms.getLanguageCodes();
// ['en', 'ro', 'fr']

// Check if a language is supported
const supported = await cms.isLanguageSupported('de');
```

### Search

```javascript
// Search content
const { data: results, meta } = await cms.search('keyword', {
  page: 1,
  per_page: 20,
  type: 'blog',        // Filter by content type
  locale: 'en'         // Filter by locale
});

// Get search suggestions
const { suggestions } = await cms.getSuggestions('key');

// Track click analytics
await cms.trackClick({
  query: 'search term',
  content_id: 123,
  position: 1
});
```

### Locale Management

```javascript
// Set default locale
cms.setLocale('ro');

// Get current locale
const locale = cms.getLocale();

// Override locale per request
const content = await cms.getContent('home', { locale: 'fr' });
```

## Content Structure

Content responses include the following structure:

```typescript
interface Content {
  id: number;
  type: string;            // 'page', 'blog', etc.
  slug: string;
  status: 'draft' | 'published' | 'archived';
  published_at: string;
  created_at: string;
  updated_at: string;
  author?: {
    id: number;
    name: string;
  };
  translation: {
    locale: string;
    title: string;
    body: ContentBlock[];  // Array of content blocks
    excerpt?: string;
    seo?: {
      meta_description?: string;
      meta_keywords?: string;
    };
  };
  available_locales: string[];
}
```

### Content Block Types

The `body` field contains an array of typed content blocks:

- **hero**: Hero sections with title, subtitle, CTA buttons
- **text**: Rich text content (HTML, Markdown, or plain text)
- **gallery**: Image galleries with various layouts
- **faq**: FAQ sections with expandable items
- **collections**: Dynamic collection data with entities

## Error Handling

The client throws `CMSError` for API errors:

```javascript
import { createClient, CMSError } from '@powerit/cms-client';

try {
  const content = await cms.getContent('non-existent');
} catch (error) {
  if (error instanceof CMSError) {
    console.log(error.status);   // HTTP status code
    console.log(error.message);  // Error message
    console.log(error.data);     // Additional error data
  }
}
```

## TypeScript Support

The package includes TypeScript definitions:

```typescript
import { createClient, Content, Menu, CMSClientConfig } from '@powerit/cms-client';

const config: CMSClientConfig = {
  baseUrl: 'https://cms.example.com',
  apiKey: 'your-api-key'
};

const cms = createClient(config);

const content: Content = await cms.getContent('home');
```

## Examples

### Static Site Generation

```javascript
import { createClient } from '@powerit/cms-client';

const cms = createClient({
  baseUrl: process.env.CMS_URL,
  apiKey: process.env.CMS_API_KEY
});

// Generate pages for all content
async function generatePages() {
  const { types } = await cms.getContentTypes();

  for (const type of types) {
    const { data: contents } = await cms.getContentsByType(type);

    for (const content of contents) {
      await generatePage(content);
    }
  }
}
```

### Multi-language Site

```javascript
const cms = createClient({
  baseUrl: 'https://cms.example.com',
  apiKey: 'your-api-key'
});

// Get content in user's language with fallback
async function getLocalizedContent(slug, preferredLocale) {
  const locales = await cms.getLanguageCodes();

  // Try preferred locale first
  if (locales.includes(preferredLocale)) {
    try {
      return await cms.getContentBySlug(slug, { locale: preferredLocale });
    } catch (e) {
      // Fall through to default
    }
  }

  // Fallback to default (no locale = API default)
  return cms.getContentBySlug(slug);
}
```

### Middleware for Redirects

```javascript
// Express.js example
import { createClient } from '@powerit/cms-client';

const cms = createClient({
  baseUrl: process.env.CMS_URL,
  apiKey: process.env.CMS_API_KEY
});

app.use(async (req, res, next) => {
  const redirect = await cms.checkRedirect(req.path);

  if (redirect) {
    return res.redirect(redirect.status_code, redirect.target_path);
  }

  next();
});
```

## License

MIT