npm package: @flusys/ng-core
Description: Core utilities and services for FLUSYS Angular packages
Latest version: 4.1.1

# @flusys/ng-core

> Foundation library for the FLUSYS Angular platform — configuration injection, base HTTP services, API loaders, and HTTP interceptors.

[![npm version](https://img.shields.io/npm/v/@flusys/ng-core.svg)](https://www.npmjs.com/package/@flusys/ng-core)
[![Angular](https://img.shields.io/badge/Angular-21-red.svg)](https://angular.io)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## Table of Contents

- [Overview](#overview)
- [Features](#features)
- [Compatibility](#compatibility)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration Reference](#configuration-reference)
  - [IAppConfig](#iappconfig)
  - [APP_CONFIG Token](#app_config-token)
- [Base Services](#base-services)
  - [BaseApiService](#baseapiservice)
  - [ApiLoaderService](#apiloaderservice)
- [HTTP Interceptors](#http-interceptors)
- [Helper Functions](#helper-functions)
- [Exported Tokens](#exported-tokens)
- [Troubleshooting](#troubleshooting)
- [License](#license)

---

## Overview

`@flusys/ng-core` is the zero-dependency foundation of the FLUSYS Angular package hierarchy. Every other FLUSYS Angular package depends on it. It provides:

- The **`APP_CONFIG`** injection token — single source of truth for all application configuration
- **`BaseApiService`** — typed POST-only RPC HTTP client
- **`ApiLoaderService`** — manages global loading state across concurrent requests
- **HTTP interceptors** — base URL injection and error normalization
- Utility helper functions for feature detection

This package has **no FLUSYS dependencies** — it only depends on Angular core.

---

## Features

- ✅ Typed `IAppConfig` with full IntelliSense
- ✅ POST-only RPC HTTP client (`BaseApiService`)
- ✅ Global loading state management (`ApiLoaderService`)
- ✅ `APP_CONFIG` injection token for DI-based configuration
- ✅ Feature detection helpers (`isAuthEnabled()`, `isIamEnabled()`, `isSignUpEnabled()`, etc.)
- ✅ HTTP interceptors for base URL and error handling
- ✅ Zoneless-compatible (no Zone.js dependency)
- ✅ Signal-friendly architecture

---

## Compatibility

| Package | Version |
|---------|---------|
| Angular | 21+ |
| TypeScript | 5.x |
| RxJS | 7.x |

---

## Installation

```bash
npm install @flusys/ng-core
```

---

## Quick Start

### 1. Define App Config

```typescript
// environments/environment.ts
import { IAppConfig } from '@flusys/ng-core';

export const environment: IAppConfig = {
  appName: 'My App',
  apiBaseUrl: 'http://localhost:2002',
  production: false,
  services: {
    auth: {
      enabled: true,
      features: {
        signUp: true,
        emailVerification: true,
      },
    },
    iam: { enabled: true },
    storage: { enabled: true },
    email: { enabled: false },
    formBuilder: { enabled: false },
    eventManager: { enabled: false },
    notification: { enabled: false },
    localization: { enabled: false },
  },
  multiTenant: false,
  permissionMode: 'FULL',
};
```

### 2. Provide Config in `app.config.ts`

```typescript
import { ApplicationConfig } from '@angular/core';
import { APP_CONFIG } from '@flusys/ng-core';
import { environment } from './environments/environment';

export const appConfig: ApplicationConfig = {
  providers: [
    { provide: APP_CONFIG, useValue: environment },
    // ... other providers
  ],
};
```

### 3. Inject Config Anywhere

```typescript
import { inject } from '@angular/core';
import { APP_CONFIG, IAppConfig } from '@flusys/ng-core';

@Injectable({ providedIn: 'root' })
export class MyService {
  private config = inject<IAppConfig>(APP_CONFIG);

  getApiUrl(): string {
    return this.config.apiBaseUrl;
  }
}
```

---

## Configuration Reference

### IAppConfig

Complete configuration interface for the FLUSYS platform:

```typescript
interface IAppConfig {
  /** Application display name */
  appName: string;

  /** Backend API base URL */
  apiBaseUrl: string;

  /** Production mode flag */
  production: boolean;

  /** Multi-tenant mode — isolates data per company */
  multiTenant?: boolean;

  /** IAM permission evaluation mode */
  permissionMode?: 'RBAC' | 'DIRECT' | 'FULL';

  services: {
    auth?: {
      enabled: boolean;
      features?: {
        /** Allow self-registration */
        signUp?: boolean;
        /** Email verification on register */
        emailVerification?: boolean;
        /** Require company selection after login */
        companySelection?: boolean;
      };
    };
    iam?: { enabled: boolean };
    storage?: { enabled: boolean };
    email?: { enabled: boolean };
    formBuilder?: { enabled: boolean };
    eventManager?: { enabled: boolean };
    notification?: { enabled: boolean };
    localization?: { enabled: boolean };
  };
}
```

### APP_CONFIG Token

```typescript
import { APP_CONFIG } from '@flusys/ng-core';

// In component/service
const config = inject<IAppConfig>(APP_CONFIG);
```

---

## Base Services

### BaseApiService

Typed HTTP client configured for POST-only RPC endpoints. All FLUSYS API services extend this class.

```typescript
import { BaseApiService, ISingleResponse, IListResponse } from '@flusys/ng-core';

@Injectable({ providedIn: 'root' })
export class ProductApiService extends BaseApiService {
  protected resource = 'product';

  getAll(params: IListParams): Observable<IListResponse<Product>> {
    return this.post(`${this.resource}/get-all`, params);
  }

  getById(id: string): Observable<ISingleResponse<Product>> {
    return this.post(`${this.resource}/get/${id}`, {});
  }

  insert(data: CreateProductDto): Observable<ISingleResponse<Product>> {
    return this.post(`${this.resource}/insert`, data);
  }

  update(data: UpdateProductDto): Observable<ISingleResponse<Product>> {
    return this.post(`${this.resource}/update`, data);
  }

  delete(id: string): Observable<IMessageResponse> {
    return this.post(`${this.resource}/delete`, { id });
  }
}
```

**Key Methods:**

| Method | Signature | Description |
|--------|-----------|-------------|
| `post<T>` | `(path, body, options?)` | POST request with automatic base URL |
| `get<T>` | `(path, options?)` | GET request (rarely used in FLUSYS) |
| `setLoader` | `(show: boolean)` | Manually toggle loading indicator |

**BaseApiService** automatically:
- Prepends `apiBaseUrl` from `APP_CONFIG`
- Sets `Content-Type: application/json`
- Triggers `ApiLoaderService` for loading state
- Normalizes errors through `GlobalErrorHandler`

### ApiLoaderService

Tracks concurrent HTTP requests to show/hide global loading indicators.

```typescript
import { ApiLoaderService } from '@flusys/ng-core';

@Component({ ... })
export class AppComponent {
  private loader = inject(ApiLoaderService);

  // Reactive loading state (signal)
  isLoading = this.loader.isLoading;
}
```

```html
<!-- Use in template -->
@if (isLoading()) {
  <p-progressBar mode="indeterminate" />
}
```

**ApiLoaderService API:**

| Member | Type | Description |
|--------|------|-------------|
| `isLoading` | `Signal<boolean>` | True when any HTTP request is pending |
| `increment()` | `void` | Manually increment request counter |
| `decrement()` | `void` | Manually decrement request counter |

---

## HTTP Interceptors

`@flusys/ng-core` provides base HTTP interceptors registered via `provideHttpClient`:

### Base URL Interceptor

Automatically prepends `APP_CONFIG.apiBaseUrl` to all relative HTTP requests.

```typescript
// Absolute URLs are passed through unchanged
// Relative URLs like '/auth/login' become 'http://localhost:2002/auth/login'
```

### Error Normalizer Interceptor

Normalizes all HTTP error responses into a consistent `IErrorResponse` shape before they reach your services.

### Registering Interceptors

```typescript
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { baseUrlInterceptor, errorNormalizerInterceptor } from '@flusys/ng-core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withInterceptors([
        baseUrlInterceptor,
        errorNormalizerInterceptor,
      ])
    ),
  ],
};
```

---

## Helper Functions

Feature detection helpers that read from `APP_CONFIG`:

```typescript
import {
  isAuthEnabled,
  isIamEnabled,
  isStorageEnabled,
  isSignUpEnabled,
  isEmailVerificationEnabled,
  isCompanySelectionEnabled,
  isLocalizationEnabled,
  isNotificationEnabled,
} from '@flusys/ng-core';

// Usage in a service or guard
const authOn = isAuthEnabled(config);       // boolean
const iamOn = isIamEnabled(config);         // boolean
const storageOn = isStorageEnabled(config); // boolean
const signUpOn = isSignUpEnabled(config);   // boolean
```

---

## Exported Tokens

| Token | Type | Description |
|-------|------|-------------|
| `APP_CONFIG` | `InjectionToken<IAppConfig>` | Global app configuration |
| `TRANSLATE_ADAPTER` | `InjectionToken<ITranslateAdapter>` | Localization adapter interface |

---

## Troubleshooting

**`NullInjectorError: No provider for APP_CONFIG`**

You forgot to provide `APP_CONFIG` in `app.config.ts`:

```typescript
providers: [
  { provide: APP_CONFIG, useValue: environment },
]
```

**`Cannot read properties of undefined (reading 'apiBaseUrl')`**

`APP_CONFIG` was provided but the environment object is missing the required field. Check that your environment file implements the full `IAppConfig` interface.

**`HTTP 404 on API requests`**

Check that `apiBaseUrl` does not have a trailing slash:

```typescript
// ❌ Wrong
apiBaseUrl: 'http://localhost:2002/'

// ✅ Correct
apiBaseUrl: 'http://localhost:2002'
```

---

## License

MIT © FLUSYS