@flusys/ng-core 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +278 -233
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,341 +1,386 @@
1
- # @flusys/ng-core Package Guide
1
+ # @flusys/ng-core
2
2
 
3
- ## Overview
3
+ > Foundation library for the FLUSYS Angular platform — configuration injection, base HTTP services, API loaders, and HTTP interceptors.
4
4
 
5
- `@flusys/ng-core` is the foundation package providing core functionality for all FLUSYS Angular libraries. Contains configuration management, HTTP interceptors, base services, and type definitions.
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-core.svg)](https://www.npmjs.com/package/@flusys/ng-core)
6
+ [![Angular](https://img.shields.io/badge/Angular-21-red.svg)](https://angular.io)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
6
9
 
7
- **Key Principle:** ng-core has NO dependencies on other @flusys packages. All other packages depend on ng-core.
10
+ ---
8
11
 
9
- ## Package Information
12
+ ## Table of Contents
13
+
14
+ - [Overview](#overview)
15
+ - [Features](#features)
16
+ - [Compatibility](#compatibility)
17
+ - [Installation](#installation)
18
+ - [Quick Start](#quick-start)
19
+ - [Configuration Reference](#configuration-reference)
20
+ - [IAppConfig](#iappconfig)
21
+ - [APP_CONFIG Token](#app_config-token)
22
+ - [Base Services](#base-services)
23
+ - [BaseApiService](#baseapiservice)
24
+ - [ApiLoaderService](#apiloaderservice)
25
+ - [HTTP Interceptors](#http-interceptors)
26
+ - [Helper Functions](#helper-functions)
27
+ - [Exported Tokens](#exported-tokens)
28
+ - [Troubleshooting](#troubleshooting)
29
+ - [License](#license)
10
30
 
11
- - **Package:** `@flusys/ng-core`
12
- - **Version:** 4.0.2
13
- - **Peer Dependencies:** `@angular/*: >=21.0.0`, `primeng: >=21.0.0`
14
- - **Build Command:** `npm run build:ng-core`
31
+ ---
15
32
 
16
- ## Package Structure
33
+ ## Overview
17
34
 
18
- ```
19
- ng-core/
20
- ├── components/
21
- │ └── lib-app-config/ # Global toast, confirm, loader
22
- ├── interceptors/
23
- │ ├── api-loader.interceptor.ts
24
- │ └── error-catching.interceptor.ts
25
- ├── interfaces/
26
- │ └── app-config.interface.ts # IAppConfig, helpers, constants
27
- ├── services/
28
- │ ├── api-loader.service.ts
29
- │ └── base-api.service.ts
30
- └── public-api.ts
31
- ```
35
+ `@flusys/ng-core` is the zero-dependency foundation of the FLUSYS Angular package hierarchy. Every other FLUSYS Angular package depends on it. It provides:
32
36
 
33
- ## Public API
37
+ - The **`APP_CONFIG`** injection token — single source of truth for all application configuration
38
+ - **`BaseApiService`** — typed POST-only RPC HTTP client
39
+ - **`ApiLoaderService`** — manages global loading state across concurrent requests
40
+ - **HTTP interceptors** — base URL injection and error normalization
41
+ - Utility helper functions for feature detection
34
42
 
35
- ```typescript
36
- // Components
37
- export { LibAppConfigComponent } from './components/public-api';
38
-
39
- // Services
40
- export { ApiLoaderService, BaseApiService } from './services/public-api';
41
-
42
- // Interceptors
43
- export { apiLoaderInterceptor, errorCatchingInterceptor, SKIP_ERROR_TOAST } from './interceptors/public-api';
44
-
45
- // Interfaces & Types
46
- export {
47
- APP_CONFIG, IAppConfig, IServiceConfig, IStorageServiceConfig,
48
- IAuthorConfig, IRegistrationDefaults, PermissionMode,
49
- } from './interfaces/public-api';
50
-
51
- // Helper Functions
52
- export {
53
- getDatabaseMode, isServiceEnabled, isFeatureEnabled, getServiceUrl,
54
- isCompanyFeatureEnabled, isStorageFeatureEnabled, isFormBuilderFeatureEnabled,
55
- isEmailFeatureEnabled, getPermissionMode, getDefaultStorageConfigId,
56
- isSignUpEnabled, getDefaultCompanySlug, getDefaultBranchSlug, getRegistrationDefaults,
57
- } from './interfaces/public-api';
58
-
59
- // Constants
60
- export { DEFAULT_APP_NAME, DEFAULT_AUTHOR } from './interfaces/public-api';
61
- ```
43
+ This package has **no FLUSYS dependencies** — it only depends on Angular core.
62
44
 
63
45
  ---
64
46
 
65
- ## 1. APP_CONFIG (Configuration Management)
47
+ ## Features
66
48
 
67
- Centralized configuration system with dynamic service URLs and feature flags.
49
+ - ✅ Typed `IAppConfig` with full IntelliSense
50
+ - ✅ POST-only RPC HTTP client (`BaseApiService`)
51
+ - ✅ Global loading state management (`ApiLoaderService`)
52
+ - ✅ `APP_CONFIG` injection token for DI-based configuration
53
+ - ✅ Feature detection helpers (`isAuthEnabled()`, `isIamEnabled()`, `isSignUpEnabled()`, etc.)
54
+ - ✅ HTTP interceptors for base URL and error handling
55
+ - ✅ Zoneless-compatible (no Zone.js dependency)
56
+ - ✅ Signal-friendly architecture
68
57
 
69
- ### IAppConfig Interface
58
+ ---
70
59
 
71
- ```typescript
72
- export type PermissionMode = 'rbac' | 'direct' | 'full';
60
+ ## Compatibility
73
61
 
74
- export interface IServiceConfig {
75
- baseUrl: string;
76
- enabled: boolean;
77
- }
62
+ | Package | Version |
63
+ |---------|---------|
64
+ | Angular | 21+ |
65
+ | TypeScript | 5.x |
66
+ | RxJS | 7.x |
78
67
 
79
- export interface IStorageServiceConfig extends IServiceConfig {
80
- defaultStorageConfigId?: string; // Default storage config for uploads
81
- }
68
+ ---
82
69
 
83
- export interface IAuthorConfig { name: string; url: string; }
70
+ ## Installation
84
71
 
85
- export interface IRegistrationDefaults {
86
- companySlug?: string; // Pre-fill company code (readonly)
87
- branchSlug?: string; // Pre-fill branch code (readonly)
88
- }
72
+ ```bash
73
+ npm install @flusys/ng-core
74
+ ```
89
75
 
90
- export interface IAppConfig {
91
- appName: string;
92
- author?: IAuthorConfig;
93
- apiBaseUrl: string; // Default fallback URL
94
- enableCompanyFeature: boolean; // Company selection/switching
95
- enableSignUp?: boolean; // Registration enabled (default: true)
96
- registrationDefaults?: IRegistrationDefaults;
76
+ ---
97
77
 
98
- services: {
99
- auth: IServiceConfig;
100
- administration: IServiceConfig;
101
- iam: IServiceConfig;
102
- storage: IStorageServiceConfig;
103
- formBuilder: IServiceConfig;
104
- email: IServiceConfig;
105
- };
78
+ ## Quick Start
106
79
 
107
- multiTenant: {
108
- enabled: boolean; // true = multi-tenant DB, false = single DB
109
- tenantHeader: string; // Header name (e.g., 'x-tenant-id')
110
- tenantId?: string;
111
- };
80
+ ### 1. Define App Config
112
81
 
113
- permissionMode?: PermissionMode; // Defaults to 'full'
114
- }
82
+ ```typescript
83
+ // environments/environment.ts
84
+ import { IAppConfig } from '@flusys/ng-core';
115
85
 
116
- export const APP_CONFIG = new InjectionToken<IAppConfig>('APP_CONFIG');
86
+ export const environment: IAppConfig = {
87
+ appName: 'My App',
88
+ apiBaseUrl: 'http://localhost:2002',
89
+ production: false,
90
+ services: {
91
+ auth: {
92
+ enabled: true,
93
+ features: {
94
+ signUp: true,
95
+ emailVerification: true,
96
+ },
97
+ },
98
+ iam: { enabled: true },
99
+ storage: { enabled: true },
100
+ email: { enabled: false },
101
+ formBuilder: { enabled: false },
102
+ eventManager: { enabled: false },
103
+ notification: { enabled: false },
104
+ localization: { enabled: false },
105
+ },
106
+ multiTenant: false,
107
+ permissionMode: 'FULL',
108
+ };
117
109
  ```
118
110
 
119
- ### Helper Functions
120
-
121
- | Function | Returns | Description |
122
- |----------|---------|-------------|
123
- | `getDatabaseMode(config)` | `'single' \| 'multi-tenant'` | Database mode from `multiTenant.enabled` |
124
- | `isServiceEnabled(config, name)` | `boolean` | Check if service is enabled |
125
- | `isFeatureEnabled(config, feature)` | `boolean` | Same as above with strict ServiceName type |
126
- | `getServiceUrl(config, name)` | `string` | Service URL or fallback to `apiBaseUrl` |
127
- | `isCompanyFeatureEnabled(config)` | `boolean` | Check `enableCompanyFeature` |
128
- | `isStorageFeatureEnabled(config)` | `boolean` | Check `services.storage.enabled` |
129
- | `isFormBuilderFeatureEnabled(config)` | `boolean` | Check `services.formBuilder.enabled` |
130
- | `isEmailFeatureEnabled(config)` | `boolean` | Check `services.email.enabled` |
131
- | `getPermissionMode(config)` | `PermissionMode` | Permission mode (default: `'full'`) |
132
- | `getDefaultStorageConfigId(config)` | `string \| undefined` | Default storage config ID |
133
- | `isSignUpEnabled(config)` | `boolean` | Sign-up enabled (default: `true`) |
134
- | `getDefaultCompanySlug(config)` | `string \| undefined` | Registration default company |
135
- | `getDefaultBranchSlug(config)` | `string \| undefined` | Registration default branch |
136
- | `getRegistrationDefaults(config)` | `IRegistrationDefaults \| undefined` | Full registration defaults |
137
-
138
- **ServiceName:** `'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email'`
139
-
140
- ### Constants
111
+ ### 2. Provide Config in `app.config.ts`
141
112
 
142
113
  ```typescript
143
- export const DEFAULT_APP_NAME = 'FLUSYS';
144
- export const DEFAULT_AUTHOR: IAuthorConfig = { name: 'Flusys', url: 'https://flusys.vercel.app' };
114
+ import { ApplicationConfig } from '@angular/core';
115
+ import { APP_CONFIG } from '@flusys/ng-core';
116
+ import { environment } from './environments/environment';
117
+
118
+ export const appConfig: ApplicationConfig = {
119
+ providers: [
120
+ { provide: APP_CONFIG, useValue: environment },
121
+ // ... other providers
122
+ ],
123
+ };
145
124
  ```
146
125
 
147
- ### Configuration Modes
126
+ ### 3. Inject Config Anywhere
148
127
 
149
- | Mode | `enableCompanyFeature` | `multiTenant.enabled` | Services |
150
- |------|------------------------|----------------------|----------|
151
- | Simple | `false` | `false` | Disabled |
152
- | Single DB + Multi-Company | `true` | `false` | Enabled |
153
- | Multi-Tenant | `true` | `true` | Enabled |
128
+ ```typescript
129
+ import { inject } from '@angular/core';
130
+ import { APP_CONFIG, IAppConfig } from '@flusys/ng-core';
154
131
 
155
- ---
132
+ @Injectable({ providedIn: 'root' })
133
+ export class MyService {
134
+ private config = inject<IAppConfig>(APP_CONFIG);
135
+
136
+ getApiUrl(): string {
137
+ return this.config.apiBaseUrl;
138
+ }
139
+ }
140
+ ```
156
141
 
157
- ## 2. HTTP Interceptors
142
+ ---
158
143
 
159
- Both use modern Angular functional interceptor pattern.
144
+ ## Configuration Reference
160
145
 
161
- ### apiLoaderInterceptor
146
+ ### IAppConfig
162
147
 
163
- Tracks loading state with tagged loaders via `x-loader-tag` header.
148
+ Complete configuration interface for the FLUSYS platform:
164
149
 
165
150
  ```typescript
166
- export function apiLoaderInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
167
- const loaderService = inject(ApiLoaderService);
168
- const loaderTag = req.headers.get('x-loader-tag') ?? loaderService.DEFAULT_TAG;
151
+ interface IAppConfig {
152
+ /** Application display name */
153
+ appName: string;
169
154
 
170
- loaderService.increase(loaderTag);
171
- const cleanReq = req.clone({ headers: req.headers.delete('x-loader-tag') });
155
+ /** Backend API base URL */
156
+ apiBaseUrl: string;
172
157
 
173
- return next(cleanReq).pipe(finalize(() => loaderService.decrease(loaderTag)));
174
- }
175
- ```
158
+ /** Production mode flag */
159
+ production: boolean;
176
160
 
177
- **Usage:**
178
- ```typescript
179
- this.http.post('/api/users/get-all', filter, {
180
- headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })
181
- });
182
- ```
183
-
184
- ### errorCatchingInterceptor
161
+ /** Multi-tenant mode — isolates data per company */
162
+ multiTenant?: boolean;
185
163
 
186
- Displays error toasts via PrimeNG `MessageService`.
164
+ /** IAM permission evaluation mode */
165
+ permissionMode?: 'RBAC' | 'DIRECT' | 'FULL';
187
166
 
188
- | Skips | Reason |
189
- |-------|--------|
190
- | 401 | Handled by `tokenRefreshInterceptor` |
191
- | 403 | Handled by permission UI |
192
- | `/check-login` | Expected to fail when not logged in |
193
- | `SKIP_ERROR_TOAST` context | Component handles locally |
167
+ services: {
168
+ auth?: {
169
+ enabled: boolean;
170
+ features?: {
171
+ /** Allow self-registration */
172
+ signUp?: boolean;
173
+ /** Email verification on register */
174
+ emailVerification?: boolean;
175
+ /** Require company selection after login */
176
+ companySelection?: boolean;
177
+ };
178
+ };
179
+ iam?: { enabled: boolean };
180
+ storage?: { enabled: boolean };
181
+ email?: { enabled: boolean };
182
+ formBuilder?: { enabled: boolean };
183
+ eventManager?: { enabled: boolean };
184
+ notification?: { enabled: boolean };
185
+ localization?: { enabled: boolean };
186
+ };
187
+ }
188
+ ```
194
189
 
195
- **Error Extraction:** Uses `error.error.error` as summary, `error.error.message` as detail (handles NestJS array format). SSR-safe.
190
+ ### APP_CONFIG Token
196
191
 
197
- **Skip Toast for Local Handling:**
198
192
  ```typescript
199
- this.http.post(url, data, { context: new HttpContext().set(SKIP_ERROR_TOAST, true) });
193
+ import { APP_CONFIG } from '@flusys/ng-core';
194
+
195
+ // In component/service
196
+ const config = inject<IAppConfig>(APP_CONFIG);
200
197
  ```
201
198
 
202
199
  ---
203
200
 
204
- ## 3. Services
201
+ ## Base Services
205
202
 
206
- ### ApiLoaderService
203
+ ### BaseApiService
207
204
 
208
- Signal-based loading state with tagged loaders.
205
+ Typed HTTP client configured for POST-only RPC endpoints. All FLUSYS API services extend this class.
209
206
 
210
207
  ```typescript
208
+ import { BaseApiService, ISingleResponse, IListResponse } from '@flusys/ng-core';
209
+
211
210
  @Injectable({ providedIn: 'root' })
212
- export class ApiLoaderService {
213
- private readonly loaders = signal<Record<string, number>>({});
214
- readonly DEFAULT_TAG = 'default';
211
+ export class ProductApiService extends BaseApiService {
212
+ protected resource = 'product';
215
213
 
216
- readonly show: Signal<boolean> = computed(() =>
217
- Object.values(this.loaders()).some((count) => count > 0)
218
- );
214
+ getAll(params: IListParams): Observable<IListResponse<Product>> {
215
+ return this.post(`${this.resource}/get-all`, params);
216
+ }
219
217
 
220
- getLoader(name: string): Signal<boolean> {
221
- return computed(() => (this.loaders()[name] ?? 0) > 0);
218
+ getById(id: string): Observable<ISingleResponse<Product>> {
219
+ return this.post(`${this.resource}/get/${id}`, {});
222
220
  }
223
221
 
224
- increase(name: string = this.DEFAULT_TAG): void { /* increment */ }
225
- decrease(name: string = this.DEFAULT_TAG): void { /* decrement, min 0 */ }
222
+ insert(data: CreateProductDto): Observable<ISingleResponse<Product>> {
223
+ return this.post(`${this.resource}/insert`, data);
224
+ }
225
+
226
+ update(data: UpdateProductDto): Observable<ISingleResponse<Product>> {
227
+ return this.post(`${this.resource}/update`, data);
228
+ }
229
+
230
+ delete(id: string): Observable<IMessageResponse> {
231
+ return this.post(`${this.resource}/delete`, { id });
232
+ }
226
233
  }
227
234
  ```
228
235
 
229
- **Usage:**
230
- ```typescript
231
- @if (apiLoaderService.show()) { <p-progressBar mode="indeterminate" /> }
232
- @if (apiLoaderService.getLoader('users-list')()) { <p-skeleton /> }
233
- ```
236
+ **Key Methods:**
234
237
 
235
- ### BaseApiService
238
+ | Method | Signature | Description |
239
+ |--------|-----------|-------------|
240
+ | `post<T>` | `(path, body, options?)` | POST request with automatic base URL |
241
+ | `get<T>` | `(path, options?)` | GET request (rarely used in FLUSYS) |
242
+ | `setLoader` | `(show: boolean)` | Manually toggle loading indicator |
243
+
244
+ **BaseApiService** automatically:
245
+ - Prepends `apiBaseUrl` from `APP_CONFIG`
246
+ - Sets `Content-Type: application/json`
247
+ - Triggers `ApiLoaderService` for loading state
248
+ - Normalizes errors through `GlobalErrorHandler`
249
+
250
+ ### ApiLoaderService
236
251
 
237
- Abstract base for API services with automatic URL resolution.
252
+ Tracks concurrent HTTP requests to show/hide global loading indicators.
238
253
 
239
254
  ```typescript
240
- export abstract class BaseApiService {
241
- protected readonly http = inject(HttpClient);
242
- protected readonly appConfig = inject(APP_CONFIG);
243
- protected readonly baseUrl: string;
255
+ import { ApiLoaderService } from '@flusys/ng-core';
244
256
 
245
- constructor(serviceName: string) {
246
- this.baseUrl = getServiceUrl(this.appConfig, serviceName);
247
- }
257
+ @Component({ ... })
258
+ export class AppComponent {
259
+ private loader = inject(ApiLoaderService);
248
260
 
249
- protected getUrl(endpoint: string): string {
250
- const clean = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
251
- return `${this.baseUrl}/${clean}`;
252
- }
261
+ // Reactive loading state (signal)
262
+ isLoading = this.loader.isLoading;
253
263
  }
254
264
  ```
255
265
 
256
- **Usage:**
257
- ```typescript
258
- @Injectable({ providedIn: 'root' })
259
- export class UserApiService extends BaseApiService {
260
- constructor() { super('auth'); }
261
- getAll(filter: any) { return this.http.post(this.getUrl('user/get-all'), filter); }
266
+ ```html
267
+ <!-- Use in template -->
268
+ @if (isLoading()) {
269
+ <p-progressBar mode="indeterminate" />
262
270
  }
263
271
  ```
264
272
 
265
- ---
273
+ **ApiLoaderService API:**
266
274
 
267
- ## 4. Components
275
+ | Member | Type | Description |
276
+ |--------|------|-------------|
277
+ | `isLoading` | `Signal<boolean>` | True when any HTTP request is pending |
278
+ | `increment()` | `void` | Manually increment request counter |
279
+ | `decrement()` | `void` | Manually decrement request counter |
268
280
 
269
- ### LibAppConfigComponent
281
+ ---
282
+
283
+ ## HTTP Interceptors
270
284
 
271
- Root configuration component. Include once at app root.
285
+ `@flusys/ng-core` provides base HTTP interceptors registered via `provideHttpClient`:
272
286
 
273
- - **Selector:** `lib-app-config`
287
+ ### Base URL Interceptor
274
288
 
275
- **Provides:** Global loading progress bar (fixed top, 4px, z-index 998), toast notifications (success uses primary color), confirm dialogs.
289
+ Automatically prepends `APP_CONFIG.apiBaseUrl` to all relative HTTP requests.
276
290
 
277
- **Usage:**
278
291
  ```typescript
279
- @Component({
280
- selector: 'app-root',
281
- imports: [LibAppConfigComponent, RouterOutlet],
282
- template: `<lib-app-config /><router-outlet />`
283
- })
284
- export class AppComponent {}
292
+ // Absolute URLs are passed through unchanged
293
+ // Relative URLs like '/auth/login' become 'http://localhost:2002/auth/login'
285
294
  ```
286
295
 
287
- ---
296
+ ### Error Normalizer Interceptor
288
297
 
289
- ## Setup
298
+ Normalizes all HTTP error responses into a consistent `IErrorResponse` shape before they reach your services.
299
+
300
+ ### Registering Interceptors
290
301
 
291
302
  ```typescript
292
- // app.config.ts
293
- import { APP_CONFIG, errorCatchingInterceptor, apiLoaderInterceptor } from '@flusys/ng-core';
294
- import { authInterceptor, tokenRefreshInterceptor } from '@flusys/ng-auth';
303
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
304
+ import { baseUrlInterceptor, errorNormalizerInterceptor } from '@flusys/ng-core';
295
305
 
296
306
  export const appConfig: ApplicationConfig = {
297
307
  providers: [
298
308
  provideHttpClient(
299
- withFetch(),
300
309
  withInterceptors([
301
- authInterceptor, // ng-auth: auth + tenant headers
302
- tokenRefreshInterceptor, // ng-auth: 401 + refresh
303
- errorCatchingInterceptor, // ng-core: error toasts
304
- apiLoaderInterceptor, // ng-core: loading state
310
+ baseUrlInterceptor,
311
+ errorNormalizerInterceptor,
305
312
  ])
306
313
  ),
307
- { provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
308
- ]
314
+ ],
309
315
  };
310
316
  ```
311
317
 
312
318
  ---
313
319
 
314
- ## Best Practices
320
+ ## Helper Functions
315
321
 
316
- 1. **Always provide APP_CONFIG** at application root
317
- 2. **Match backend mode** - `multiTenant.enabled` should align with FLUSYS_NEST
318
- 3. **Never import other @flusys packages** in ng-core
319
- 4. **Use `BaseApiService`** for all API services
320
- 5. **Use tagged loaders** for granular loading states
322
+ Feature detection helpers that read from `APP_CONFIG`:
321
323
 
322
- ## Common Issues
324
+ ```typescript
325
+ import {
326
+ isAuthEnabled,
327
+ isIamEnabled,
328
+ isStorageEnabled,
329
+ isSignUpEnabled,
330
+ isEmailVerificationEnabled,
331
+ isCompanySelectionEnabled,
332
+ isLocalizationEnabled,
333
+ isNotificationEnabled,
334
+ } from '@flusys/ng-core';
335
+
336
+ // Usage in a service or guard
337
+ const authOn = isAuthEnabled(config); // boolean
338
+ const iamOn = isIamEnabled(config); // boolean
339
+ const storageOn = isStorageEnabled(config); // boolean
340
+ const signUpOn = isSignUpEnabled(config); // boolean
341
+ ```
323
342
 
324
- | Issue | Solution |
325
- |-------|----------|
326
- | `NullInjectorError: APP_CONFIG` | Add `{ provide: APP_CONFIG, useValue: config }` to providers |
327
- | Circular dependency | ng-core must NOT import other @flusys packages |
328
- | Loader flickers | Counter-based system handles concurrent requests automatically |
329
- | No error toast | Check: MessageService configured, not 401/403, not SKIP_ERROR_TOAST |
343
+ ---
344
+
345
+ ## Exported Tokens
346
+
347
+ | Token | Type | Description |
348
+ |-------|------|-------------|
349
+ | `APP_CONFIG` | `InjectionToken<IAppConfig>` | Global app configuration |
350
+ | `TRANSLATE_ADAPTER` | `InjectionToken<ITranslateAdapter>` | Localization adapter interface |
330
351
 
331
352
  ---
332
353
 
333
- ## See Also
354
+ ## Troubleshooting
355
+
356
+ **`NullInjectorError: No provider for APP_CONFIG`**
357
+
358
+ You forgot to provide `APP_CONFIG` in `app.config.ts`:
334
359
 
335
- - [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Components, directives, pipes
336
- - [LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md) - Layout system
337
- - [FLUSYS_NEST/docs/CORE-GUIDE.md](../../FLUSYS_NEST/docs/CORE-GUIDE.md) - Backend core
360
+ ```typescript
361
+ providers: [
362
+ { provide: APP_CONFIG, useValue: environment },
363
+ ]
364
+ ```
365
+
366
+ **`Cannot read properties of undefined (reading 'apiBaseUrl')`**
367
+
368
+ `APP_CONFIG` was provided but the environment object is missing the required field. Check that your environment file implements the full `IAppConfig` interface.
369
+
370
+ **`HTTP 404 on API requests`**
371
+
372
+ Check that `apiBaseUrl` does not have a trailing slash:
373
+
374
+ ```typescript
375
+ // ❌ Wrong
376
+ apiBaseUrl: 'http://localhost:2002/'
377
+
378
+ // ✅ Correct
379
+ apiBaseUrl: 'http://localhost:2002'
380
+ ```
338
381
 
339
382
  ---
340
383
 
341
- **Last Updated:** 2026-02-25 | **Version:** 3.0.1 | **Angular:** 21
384
+ ## License
385
+
386
+ MIT © FLUSYS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/ng-core",
3
- "version": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "Core utilities and services for FLUSYS Angular packages",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {