kimu-core 0.4.1 → 0.5.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.
- package/.editorconfig +116 -30
- package/.gitattributes +81 -11
- package/.github/FUNDING.yml +8 -8
- package/.github/kimu-copilot-instructions.md +3779 -3779
- package/.github/workflows/deploy-demo.yml +39 -39
- package/.nvmrc +1 -0
- package/.prettierignore +44 -0
- package/.prettierrc +16 -0
- package/FUNDING.md +31 -31
- package/icon.svg +10 -10
- package/kimu-core-0.5.0.tgz +0 -0
- package/package.json +10 -3
- package/scripts/minify-css-assets.js +82 -82
- package/src/core/index.ts +47 -47
- package/src/core/kimu-global-styles.ts +136 -136
- package/src/core/kimu-reactive.ts +196 -196
- package/src/extensions/{kimu-home → app-root}/component.ts +5 -5
- package/src/extensions/extensions-manifest.json +4 -4
- package/src/main.ts +3 -3
- package/src/modules-repository/api-axios/CHANGELOG.md +48 -48
- package/src/modules-repository/api-axios/QUICK-REFERENCE.md +178 -178
- package/src/modules-repository/api-axios/README.md +304 -304
- package/src/modules-repository/api-axios/api-axios-service.ts +355 -355
- package/src/modules-repository/api-axios/examples.ts +293 -293
- package/src/modules-repository/api-axios/index.ts +19 -19
- package/src/modules-repository/api-axios/interfaces.ts +71 -71
- package/src/modules-repository/api-axios/module.ts +41 -41
- package/src/modules-repository/api-core/CHANGELOG.md +42 -42
- package/src/modules-repository/api-core/QUICK-REFERENCE.md +192 -192
- package/src/modules-repository/api-core/README.md +435 -435
- package/src/modules-repository/api-core/api-core-service.ts +289 -289
- package/src/modules-repository/api-core/examples.ts +432 -432
- package/src/modules-repository/api-core/index.ts +8 -8
- package/src/modules-repository/api-core/interfaces.ts +83 -83
- package/src/modules-repository/api-core/module.ts +30 -30
- package/src/modules-repository/event-bus/README.md +273 -273
- package/src/modules-repository/event-bus/event-bus-service.ts +176 -176
- package/src/modules-repository/event-bus/module.ts +30 -30
- package/src/modules-repository/notification/README.md +423 -423
- package/src/modules-repository/notification/module.ts +30 -30
- package/src/modules-repository/notification/notification-service.ts +436 -436
- package/src/modules-repository/router/README.it.md +61 -10
- package/src/modules-repository/router/README.md +61 -10
- package/src/modules-repository/router/router-config.ts.example +61 -0
- package/src/modules-repository/router/router.ts +18 -0
- package/src/modules-repository/state/README.md +409 -409
- package/src/modules-repository/state/module.ts +30 -30
- package/src/modules-repository/state/state-service.ts +296 -296
- package/src/modules-repository/theme/README.md +311 -267
- package/src/modules-repository/theme/module.ts +30 -30
- package/src/modules-repository/theme/pre-build.js +40 -40
- package/src/modules-repository/theme/theme-service.ts +411 -389
- package/src/modules-repository/theme/themes/theme-cherry-blossom.css +78 -78
- package/src/modules-repository/theme/themes/theme-cozy.css +111 -111
- package/src/modules-repository/theme/themes/theme-cyberpunk.css +150 -150
- package/src/modules-repository/theme/themes/theme-dark.css +79 -79
- package/src/modules-repository/theme/themes/theme-forest.css +171 -171
- package/src/modules-repository/theme/themes/theme-gold.css +100 -100
- package/src/modules-repository/theme/themes/theme-high-contrast.css +126 -126
- package/src/modules-repository/theme/themes/theme-lava.css +101 -101
- package/src/modules-repository/theme/themes/theme-lavender.css +90 -90
- package/src/modules-repository/theme/themes/theme-light.css +79 -79
- package/src/modules-repository/theme/themes/theme-matrix.css +103 -103
- package/src/modules-repository/theme/themes/theme-midnight.css +81 -81
- package/src/modules-repository/theme/themes/theme-nord.css +94 -94
- package/src/modules-repository/theme/themes/theme-ocean.css +84 -84
- package/src/modules-repository/theme/themes/theme-retro80s.css +343 -343
- package/src/modules-repository/theme/themes/theme-sunset.css +62 -62
- package/src/modules-repository/theme/themes-config-default.json +19 -0
- package/src/modules-repository/theme/themes-config.d.ts +27 -27
- package/src/modules-repository/theme/{themes-config.json → themes-config.json.example} +223 -213
- /package/src/extensions/{kimu-home → app-root}/lang/en.json +0 -0
- /package/src/extensions/{kimu-home → app-root}/lang/it.json +0 -0
- /package/src/extensions/{kimu-home → app-root}/style.css +0 -0
- /package/src/extensions/{kimu-home → app-root}/view.html +0 -0
|
@@ -1,435 +1,435 @@
|
|
|
1
|
-
# KIMU API Core Module
|
|
2
|
-
|
|
3
|
-
Modulo minimale per la gestione di richieste HTTP verso API esterne, basato su `fetch` nativo.
|
|
4
|
-
|
|
5
|
-
## Caratteristiche
|
|
6
|
-
|
|
7
|
-
- ✅ Zero dipendenze esterne
|
|
8
|
-
- ✅ Basato su fetch API nativa
|
|
9
|
-
- ✅ Promise-based (async/await)
|
|
10
|
-
- ✅ TypeScript-first con type safety completo
|
|
11
|
-
- ✅ Supporto per tutti i metodi HTTP (GET, POST, PUT, PATCH, DELETE)
|
|
12
|
-
- ✅ Gestione automatica di query parameters, headers, body
|
|
13
|
-
- ✅ Interceptor per request/response/error
|
|
14
|
-
- ✅ Timeout configurabile
|
|
15
|
-
- ✅ Gestione errori strutturata
|
|
16
|
-
- ✅ Supporto per diversi tipi di risposta (JSON, text, blob, arraybuffer)
|
|
17
|
-
- ✅ BaseURL globale configurabile
|
|
18
|
-
- ✅ Lightweight (~2KB minified)
|
|
19
|
-
|
|
20
|
-
## Installazione
|
|
21
|
-
|
|
22
|
-
Il modulo è incluso nel repository `kimu-modules`. Per installarlo:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Usando kimu-cli (quando disponibile)
|
|
26
|
-
kimu install module api-core
|
|
27
|
-
|
|
28
|
-
# Manualmente
|
|
29
|
-
# Copia la cartella src/modules/api-core nel tuo progetto
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Utilizzo Base
|
|
33
|
-
|
|
34
|
-
### Inizializzazione
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { KimuModuleManager } from '@kimu/core';
|
|
38
|
-
import ApiCoreModule from './modules/api-core/module';
|
|
39
|
-
|
|
40
|
-
// Inizializza il modulo
|
|
41
|
-
const moduleManager = KimuModuleManager.getInstance();
|
|
42
|
-
const apiModule = new ApiCoreModule();
|
|
43
|
-
await moduleManager.loadModule(apiModule);
|
|
44
|
-
|
|
45
|
-
// Ottieni il servizio
|
|
46
|
-
const api = moduleManager.getService('api-core');
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Configurazione Globale
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
import { apiCoreService } from './modules/api-core/api-core-service';
|
|
53
|
-
|
|
54
|
-
// Configura baseURL e headers di default
|
|
55
|
-
apiCoreService.configure({
|
|
56
|
-
baseURL: 'https://api.example.com',
|
|
57
|
-
timeout: 10000, // 10 secondi
|
|
58
|
-
headers: {
|
|
59
|
-
'Authorization': 'Bearer YOUR_TOKEN',
|
|
60
|
-
'X-Custom-Header': 'value'
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Richieste GET
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
// GET semplice
|
|
69
|
-
const response = await api.get('/users');
|
|
70
|
-
console.log(response.data); // Array di utenti
|
|
71
|
-
|
|
72
|
-
// GET con query parameters
|
|
73
|
-
const response = await api.get('/users', {
|
|
74
|
-
params: {
|
|
75
|
-
page: 1,
|
|
76
|
-
limit: 10,
|
|
77
|
-
sort: 'name'
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// GET con headers custom
|
|
82
|
-
const response = await api.get('/users', {
|
|
83
|
-
headers: {
|
|
84
|
-
'Accept-Language': 'it-IT'
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Richieste POST
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
// POST con JSON body
|
|
93
|
-
const response = await api.post('/users', {
|
|
94
|
-
name: 'Mario Rossi',
|
|
95
|
-
email: 'mario@example.com',
|
|
96
|
-
age: 30
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// POST con FormData
|
|
100
|
-
const formData = new FormData();
|
|
101
|
-
formData.append('file', fileInput.files[0]);
|
|
102
|
-
formData.append('title', 'My File');
|
|
103
|
-
|
|
104
|
-
const response = await api.post('/upload', formData);
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Altri Metodi HTTP
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
// PUT - aggiorna risorsa
|
|
111
|
-
const response = await api.put('/users/123', {
|
|
112
|
-
name: 'Mario Rossi Updated'
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// PATCH - aggiorna parzialmente
|
|
116
|
-
const response = await api.patch('/users/123', {
|
|
117
|
-
age: 31
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// DELETE - elimina risorsa
|
|
121
|
-
const response = await api.delete('/users/123');
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Richiesta Generica
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
const response = await api.request('/custom-endpoint', {
|
|
128
|
-
method: 'POST',
|
|
129
|
-
body: { key: 'value' },
|
|
130
|
-
headers: { 'X-Custom': 'header' },
|
|
131
|
-
params: { filter: 'active' },
|
|
132
|
-
timeout: 5000,
|
|
133
|
-
responseType: 'json'
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Gestione Errori
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
try {
|
|
141
|
-
const response = await api.get('/users');
|
|
142
|
-
console.log(response.data);
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error('Error:', error.message);
|
|
145
|
-
console.error('Status:', error.status);
|
|
146
|
-
console.error('Status Text:', error.statusText);
|
|
147
|
-
console.error('Response Data:', error.data);
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## Interceptor
|
|
152
|
-
|
|
153
|
-
### Request Interceptor
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
apiCoreService.configure({
|
|
157
|
-
requestInterceptor: (config) => {
|
|
158
|
-
// Aggiungi token dinamicamente
|
|
159
|
-
const token = localStorage.getItem('token');
|
|
160
|
-
if (token) {
|
|
161
|
-
config.headers = {
|
|
162
|
-
...config.headers,
|
|
163
|
-
'Authorization': `Bearer ${token}`
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Log della richiesta
|
|
168
|
-
console.log('Making request:', config);
|
|
169
|
-
|
|
170
|
-
return config;
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Response Interceptor
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
apiCoreService.configure({
|
|
179
|
-
responseInterceptor: (response) => {
|
|
180
|
-
// Log della risposta
|
|
181
|
-
console.log('Response received:', response.status);
|
|
182
|
-
|
|
183
|
-
// Trasforma i dati
|
|
184
|
-
response.data = {
|
|
185
|
-
...response.data,
|
|
186
|
-
receivedAt: new Date().toISOString()
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
return response;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Error Interceptor
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
apiCoreService.configure({
|
|
198
|
-
errorInterceptor: async (error) => {
|
|
199
|
-
// Gestione errore 401: refresh token
|
|
200
|
-
if (error.status === 401) {
|
|
201
|
-
const newToken = await refreshToken();
|
|
202
|
-
localStorage.setItem('token', newToken);
|
|
203
|
-
|
|
204
|
-
// Puoi anche rilanciare una richiesta o modificare l'errore
|
|
205
|
-
error.message = 'Session expired, please login again';
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Log errori
|
|
209
|
-
console.error('API Error:', error);
|
|
210
|
-
|
|
211
|
-
return error;
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
## Timeout e Cancellazione
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
// Timeout automatico
|
|
220
|
-
const response = await api.get('/slow-endpoint', {
|
|
221
|
-
timeout: 3000 // 3 secondi
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// Cancellazione manuale con AbortController
|
|
225
|
-
const controller = new AbortController();
|
|
226
|
-
|
|
227
|
-
const request = api.get('/data', {
|
|
228
|
-
signal: controller.signal
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// Cancella dopo 2 secondi
|
|
232
|
-
setTimeout(() => controller.abort(), 2000);
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const response = await request;
|
|
236
|
-
} catch (error) {
|
|
237
|
-
if (error.message.includes('timeout')) {
|
|
238
|
-
console.log('Request was cancelled');
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Tipi di Risposta
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
// JSON (default)
|
|
247
|
-
const response = await api.get('/users', {
|
|
248
|
-
responseType: 'json'
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// Testo
|
|
252
|
-
const response = await api.get('/readme.txt', {
|
|
253
|
-
responseType: 'text'
|
|
254
|
-
});
|
|
255
|
-
console.log(response.data); // stringa
|
|
256
|
-
|
|
257
|
-
// Blob (per file)
|
|
258
|
-
const response = await api.get('/image.png', {
|
|
259
|
-
responseType: 'blob'
|
|
260
|
-
});
|
|
261
|
-
const imageUrl = URL.createObjectURL(response.data);
|
|
262
|
-
|
|
263
|
-
// ArrayBuffer (per dati binari)
|
|
264
|
-
const response = await api.get('/binary-data', {
|
|
265
|
-
responseType: 'arraybuffer'
|
|
266
|
-
});
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## Uso in Componenti KIMU
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
import { KimuComponentElement } from '@kimu/core';
|
|
273
|
-
import { KimuModuleManager } from '@kimu/core';
|
|
274
|
-
|
|
275
|
-
export class UserListComponent extends KimuComponentElement {
|
|
276
|
-
private api: any;
|
|
277
|
-
private users: any[] = [];
|
|
278
|
-
|
|
279
|
-
async onInit() {
|
|
280
|
-
// Ottieni il servizio API
|
|
281
|
-
this.api = KimuModuleManager.getInstance().getService('api-core');
|
|
282
|
-
|
|
283
|
-
// Carica i dati
|
|
284
|
-
await this.loadUsers();
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async loadUsers() {
|
|
288
|
-
try {
|
|
289
|
-
const response = await this.api.get('/users');
|
|
290
|
-
this.users = response.data;
|
|
291
|
-
this.onRender();
|
|
292
|
-
} catch (error) {
|
|
293
|
-
console.error('Failed to load users:', error);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async createUser(userData: any) {
|
|
298
|
-
try {
|
|
299
|
-
const response = await this.api.post('/users', userData);
|
|
300
|
-
this.users.push(response.data);
|
|
301
|
-
this.onRender();
|
|
302
|
-
} catch (error) {
|
|
303
|
-
console.error('Failed to create user:', error);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
## TypeScript Support
|
|
310
|
-
|
|
311
|
-
Il modulo è completamente tipizzato:
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
interface User {
|
|
315
|
-
id: number;
|
|
316
|
-
name: string;
|
|
317
|
-
email: string;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Type-safe request
|
|
321
|
-
const response = await api.get<User[]>('/users');
|
|
322
|
-
response.data.forEach(user => {
|
|
323
|
-
console.log(user.name); // Type-safe access
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
// Type-safe POST
|
|
327
|
-
const newUser: Omit<User, 'id'> = {
|
|
328
|
-
name: 'Mario',
|
|
329
|
-
email: 'mario@example.com'
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
const response = await api.post<User>('/users', newUser);
|
|
333
|
-
console.log(response.data.id); // Type-safe
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
## Best Practices
|
|
337
|
-
|
|
338
|
-
### 1. Configura baseURL globalmente
|
|
339
|
-
```typescript
|
|
340
|
-
apiCoreService.configure({
|
|
341
|
-
baseURL: process.env.API_URL || 'https://api.example.com'
|
|
342
|
-
});
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### 2. Usa interceptor per autenticazione
|
|
346
|
-
```typescript
|
|
347
|
-
apiCoreService.configure({
|
|
348
|
-
requestInterceptor: (config) => {
|
|
349
|
-
const token = getAuthToken();
|
|
350
|
-
if (token) {
|
|
351
|
-
config.headers = {
|
|
352
|
-
...config.headers,
|
|
353
|
-
'Authorization': `Bearer ${token}`
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
return config;
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### 3. Gestisci errori globalmente
|
|
362
|
-
```typescript
|
|
363
|
-
apiCoreService.configure({
|
|
364
|
-
errorInterceptor: (error) => {
|
|
365
|
-
if (error.status === 401) {
|
|
366
|
-
// Redirect to login
|
|
367
|
-
window.location.href = '/login';
|
|
368
|
-
} else if (error.status >= 500) {
|
|
369
|
-
// Show error notification
|
|
370
|
-
showNotification('Server error, please try again later');
|
|
371
|
-
}
|
|
372
|
-
return error;
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### 4. Crea wrapper service per API specifiche
|
|
378
|
-
```typescript
|
|
379
|
-
class UserService {
|
|
380
|
-
private api = apiCoreService;
|
|
381
|
-
|
|
382
|
-
async getUsers() {
|
|
383
|
-
return this.api.get<User[]>('/users');
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async getUser(id: number) {
|
|
387
|
-
return this.api.get<User>(`/users/${id}`);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
async createUser(user: Omit<User, 'id'>) {
|
|
391
|
-
return this.api.post<User>('/users', user);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async updateUser(id: number, user: Partial<User>) {
|
|
395
|
-
return this.api.patch<User>(`/users/${id}`, user);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async deleteUser(id: number) {
|
|
399
|
-
return this.api.delete(`/users/${id}`);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
## Confronto con Altri Framework
|
|
405
|
-
|
|
406
|
-
| Feature | KIMU api-core | Angular HttpClient | Axios | Fetch nativo |
|
|
407
|
-
|---------|---------------|-------------------|-------|--------------|
|
|
408
|
-
| Bundle Size | ~2KB | ~40KB | ~13KB | 0KB (nativo) |
|
|
409
|
-
| Dependencies | 0 | RxJS required | 0 | 0 |
|
|
410
|
-
| TypeScript | ✅ Native | ✅ Native | ✅ Native | ⚠️ Basic |
|
|
411
|
-
| Interceptors | ✅ | ✅ | ✅ | ❌ |
|
|
412
|
-
| Timeout | ✅ | ✅ | ✅ | ⚠️ Manual |
|
|
413
|
-
| Query Params | ✅ Auto | ✅ Auto | ✅ Auto | ❌ Manual |
|
|
414
|
-
| Error Handling | ✅ Structured | ✅ Structured | ✅ Structured | ⚠️ Basic |
|
|
415
|
-
| Promise/Observable | Promise | Observable | Promise | Promise |
|
|
416
|
-
|
|
417
|
-
## Roadmap
|
|
418
|
-
|
|
419
|
-
- [ ] Support per retry automatico
|
|
420
|
-
- [ ] Caching integrato delle richieste
|
|
421
|
-
- [ ] Request deduplication
|
|
422
|
-
- [ ] Upload progress tracking
|
|
423
|
-
- [ ] Download progress tracking
|
|
424
|
-
- [ ] Request queue con priorità
|
|
425
|
-
- [ ] Mock/test utilities
|
|
426
|
-
|
|
427
|
-
## License
|
|
428
|
-
|
|
429
|
-
Questo modulo fa parte di KIMU-Core ed è distribuito sotto licenza MPL-2.0.
|
|
430
|
-
|
|
431
|
-
## Supporto
|
|
432
|
-
|
|
433
|
-
Per issue, feature request o domande:
|
|
434
|
-
- GitHub Issues: [kimu-core/issues](https://github.com/UnicoVerso/kimu-core/issues)
|
|
435
|
-
- Documentazione: [kimu-docs](https://github.com/UnicoVerso/kimu-docs)
|
|
1
|
+
# KIMU API Core Module
|
|
2
|
+
|
|
3
|
+
Modulo minimale per la gestione di richieste HTTP verso API esterne, basato su `fetch` nativo.
|
|
4
|
+
|
|
5
|
+
## Caratteristiche
|
|
6
|
+
|
|
7
|
+
- ✅ Zero dipendenze esterne
|
|
8
|
+
- ✅ Basato su fetch API nativa
|
|
9
|
+
- ✅ Promise-based (async/await)
|
|
10
|
+
- ✅ TypeScript-first con type safety completo
|
|
11
|
+
- ✅ Supporto per tutti i metodi HTTP (GET, POST, PUT, PATCH, DELETE)
|
|
12
|
+
- ✅ Gestione automatica di query parameters, headers, body
|
|
13
|
+
- ✅ Interceptor per request/response/error
|
|
14
|
+
- ✅ Timeout configurabile
|
|
15
|
+
- ✅ Gestione errori strutturata
|
|
16
|
+
- ✅ Supporto per diversi tipi di risposta (JSON, text, blob, arraybuffer)
|
|
17
|
+
- ✅ BaseURL globale configurabile
|
|
18
|
+
- ✅ Lightweight (~2KB minified)
|
|
19
|
+
|
|
20
|
+
## Installazione
|
|
21
|
+
|
|
22
|
+
Il modulo è incluso nel repository `kimu-modules`. Per installarlo:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Usando kimu-cli (quando disponibile)
|
|
26
|
+
kimu install module api-core
|
|
27
|
+
|
|
28
|
+
# Manualmente
|
|
29
|
+
# Copia la cartella src/modules/api-core nel tuo progetto
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Utilizzo Base
|
|
33
|
+
|
|
34
|
+
### Inizializzazione
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { KimuModuleManager } from '@kimu/core';
|
|
38
|
+
import ApiCoreModule from './modules/api-core/module';
|
|
39
|
+
|
|
40
|
+
// Inizializza il modulo
|
|
41
|
+
const moduleManager = KimuModuleManager.getInstance();
|
|
42
|
+
const apiModule = new ApiCoreModule();
|
|
43
|
+
await moduleManager.loadModule(apiModule);
|
|
44
|
+
|
|
45
|
+
// Ottieni il servizio
|
|
46
|
+
const api = moduleManager.getService('api-core');
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Configurazione Globale
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { apiCoreService } from './modules/api-core/api-core-service';
|
|
53
|
+
|
|
54
|
+
// Configura baseURL e headers di default
|
|
55
|
+
apiCoreService.configure({
|
|
56
|
+
baseURL: 'https://api.example.com',
|
|
57
|
+
timeout: 10000, // 10 secondi
|
|
58
|
+
headers: {
|
|
59
|
+
'Authorization': 'Bearer YOUR_TOKEN',
|
|
60
|
+
'X-Custom-Header': 'value'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Richieste GET
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// GET semplice
|
|
69
|
+
const response = await api.get('/users');
|
|
70
|
+
console.log(response.data); // Array di utenti
|
|
71
|
+
|
|
72
|
+
// GET con query parameters
|
|
73
|
+
const response = await api.get('/users', {
|
|
74
|
+
params: {
|
|
75
|
+
page: 1,
|
|
76
|
+
limit: 10,
|
|
77
|
+
sort: 'name'
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// GET con headers custom
|
|
82
|
+
const response = await api.get('/users', {
|
|
83
|
+
headers: {
|
|
84
|
+
'Accept-Language': 'it-IT'
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Richieste POST
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// POST con JSON body
|
|
93
|
+
const response = await api.post('/users', {
|
|
94
|
+
name: 'Mario Rossi',
|
|
95
|
+
email: 'mario@example.com',
|
|
96
|
+
age: 30
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// POST con FormData
|
|
100
|
+
const formData = new FormData();
|
|
101
|
+
formData.append('file', fileInput.files[0]);
|
|
102
|
+
formData.append('title', 'My File');
|
|
103
|
+
|
|
104
|
+
const response = await api.post('/upload', formData);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Altri Metodi HTTP
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// PUT - aggiorna risorsa
|
|
111
|
+
const response = await api.put('/users/123', {
|
|
112
|
+
name: 'Mario Rossi Updated'
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// PATCH - aggiorna parzialmente
|
|
116
|
+
const response = await api.patch('/users/123', {
|
|
117
|
+
age: 31
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// DELETE - elimina risorsa
|
|
121
|
+
const response = await api.delete('/users/123');
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Richiesta Generica
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const response = await api.request('/custom-endpoint', {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
body: { key: 'value' },
|
|
130
|
+
headers: { 'X-Custom': 'header' },
|
|
131
|
+
params: { filter: 'active' },
|
|
132
|
+
timeout: 5000,
|
|
133
|
+
responseType: 'json'
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Gestione Errori
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
try {
|
|
141
|
+
const response = await api.get('/users');
|
|
142
|
+
console.log(response.data);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('Error:', error.message);
|
|
145
|
+
console.error('Status:', error.status);
|
|
146
|
+
console.error('Status Text:', error.statusText);
|
|
147
|
+
console.error('Response Data:', error.data);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Interceptor
|
|
152
|
+
|
|
153
|
+
### Request Interceptor
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
apiCoreService.configure({
|
|
157
|
+
requestInterceptor: (config) => {
|
|
158
|
+
// Aggiungi token dinamicamente
|
|
159
|
+
const token = localStorage.getItem('token');
|
|
160
|
+
if (token) {
|
|
161
|
+
config.headers = {
|
|
162
|
+
...config.headers,
|
|
163
|
+
'Authorization': `Bearer ${token}`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Log della richiesta
|
|
168
|
+
console.log('Making request:', config);
|
|
169
|
+
|
|
170
|
+
return config;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Response Interceptor
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
apiCoreService.configure({
|
|
179
|
+
responseInterceptor: (response) => {
|
|
180
|
+
// Log della risposta
|
|
181
|
+
console.log('Response received:', response.status);
|
|
182
|
+
|
|
183
|
+
// Trasforma i dati
|
|
184
|
+
response.data = {
|
|
185
|
+
...response.data,
|
|
186
|
+
receivedAt: new Date().toISOString()
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return response;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Error Interceptor
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
apiCoreService.configure({
|
|
198
|
+
errorInterceptor: async (error) => {
|
|
199
|
+
// Gestione errore 401: refresh token
|
|
200
|
+
if (error.status === 401) {
|
|
201
|
+
const newToken = await refreshToken();
|
|
202
|
+
localStorage.setItem('token', newToken);
|
|
203
|
+
|
|
204
|
+
// Puoi anche rilanciare una richiesta o modificare l'errore
|
|
205
|
+
error.message = 'Session expired, please login again';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Log errori
|
|
209
|
+
console.error('API Error:', error);
|
|
210
|
+
|
|
211
|
+
return error;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Timeout e Cancellazione
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Timeout automatico
|
|
220
|
+
const response = await api.get('/slow-endpoint', {
|
|
221
|
+
timeout: 3000 // 3 secondi
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Cancellazione manuale con AbortController
|
|
225
|
+
const controller = new AbortController();
|
|
226
|
+
|
|
227
|
+
const request = api.get('/data', {
|
|
228
|
+
signal: controller.signal
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Cancella dopo 2 secondi
|
|
232
|
+
setTimeout(() => controller.abort(), 2000);
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const response = await request;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (error.message.includes('timeout')) {
|
|
238
|
+
console.log('Request was cancelled');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Tipi di Risposta
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// JSON (default)
|
|
247
|
+
const response = await api.get('/users', {
|
|
248
|
+
responseType: 'json'
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Testo
|
|
252
|
+
const response = await api.get('/readme.txt', {
|
|
253
|
+
responseType: 'text'
|
|
254
|
+
});
|
|
255
|
+
console.log(response.data); // stringa
|
|
256
|
+
|
|
257
|
+
// Blob (per file)
|
|
258
|
+
const response = await api.get('/image.png', {
|
|
259
|
+
responseType: 'blob'
|
|
260
|
+
});
|
|
261
|
+
const imageUrl = URL.createObjectURL(response.data);
|
|
262
|
+
|
|
263
|
+
// ArrayBuffer (per dati binari)
|
|
264
|
+
const response = await api.get('/binary-data', {
|
|
265
|
+
responseType: 'arraybuffer'
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Uso in Componenti KIMU
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { KimuComponentElement } from '@kimu/core';
|
|
273
|
+
import { KimuModuleManager } from '@kimu/core';
|
|
274
|
+
|
|
275
|
+
export class UserListComponent extends KimuComponentElement {
|
|
276
|
+
private api: any;
|
|
277
|
+
private users: any[] = [];
|
|
278
|
+
|
|
279
|
+
async onInit() {
|
|
280
|
+
// Ottieni il servizio API
|
|
281
|
+
this.api = KimuModuleManager.getInstance().getService('api-core');
|
|
282
|
+
|
|
283
|
+
// Carica i dati
|
|
284
|
+
await this.loadUsers();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async loadUsers() {
|
|
288
|
+
try {
|
|
289
|
+
const response = await this.api.get('/users');
|
|
290
|
+
this.users = response.data;
|
|
291
|
+
this.onRender();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error('Failed to load users:', error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async createUser(userData: any) {
|
|
298
|
+
try {
|
|
299
|
+
const response = await this.api.post('/users', userData);
|
|
300
|
+
this.users.push(response.data);
|
|
301
|
+
this.onRender();
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error('Failed to create user:', error);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## TypeScript Support
|
|
310
|
+
|
|
311
|
+
Il modulo è completamente tipizzato:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
interface User {
|
|
315
|
+
id: number;
|
|
316
|
+
name: string;
|
|
317
|
+
email: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Type-safe request
|
|
321
|
+
const response = await api.get<User[]>('/users');
|
|
322
|
+
response.data.forEach(user => {
|
|
323
|
+
console.log(user.name); // Type-safe access
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Type-safe POST
|
|
327
|
+
const newUser: Omit<User, 'id'> = {
|
|
328
|
+
name: 'Mario',
|
|
329
|
+
email: 'mario@example.com'
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const response = await api.post<User>('/users', newUser);
|
|
333
|
+
console.log(response.data.id); // Type-safe
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Best Practices
|
|
337
|
+
|
|
338
|
+
### 1. Configura baseURL globalmente
|
|
339
|
+
```typescript
|
|
340
|
+
apiCoreService.configure({
|
|
341
|
+
baseURL: process.env.API_URL || 'https://api.example.com'
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### 2. Usa interceptor per autenticazione
|
|
346
|
+
```typescript
|
|
347
|
+
apiCoreService.configure({
|
|
348
|
+
requestInterceptor: (config) => {
|
|
349
|
+
const token = getAuthToken();
|
|
350
|
+
if (token) {
|
|
351
|
+
config.headers = {
|
|
352
|
+
...config.headers,
|
|
353
|
+
'Authorization': `Bearer ${token}`
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return config;
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### 3. Gestisci errori globalmente
|
|
362
|
+
```typescript
|
|
363
|
+
apiCoreService.configure({
|
|
364
|
+
errorInterceptor: (error) => {
|
|
365
|
+
if (error.status === 401) {
|
|
366
|
+
// Redirect to login
|
|
367
|
+
window.location.href = '/login';
|
|
368
|
+
} else if (error.status >= 500) {
|
|
369
|
+
// Show error notification
|
|
370
|
+
showNotification('Server error, please try again later');
|
|
371
|
+
}
|
|
372
|
+
return error;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### 4. Crea wrapper service per API specifiche
|
|
378
|
+
```typescript
|
|
379
|
+
class UserService {
|
|
380
|
+
private api = apiCoreService;
|
|
381
|
+
|
|
382
|
+
async getUsers() {
|
|
383
|
+
return this.api.get<User[]>('/users');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async getUser(id: number) {
|
|
387
|
+
return this.api.get<User>(`/users/${id}`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async createUser(user: Omit<User, 'id'>) {
|
|
391
|
+
return this.api.post<User>('/users', user);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async updateUser(id: number, user: Partial<User>) {
|
|
395
|
+
return this.api.patch<User>(`/users/${id}`, user);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async deleteUser(id: number) {
|
|
399
|
+
return this.api.delete(`/users/${id}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Confronto con Altri Framework
|
|
405
|
+
|
|
406
|
+
| Feature | KIMU api-core | Angular HttpClient | Axios | Fetch nativo |
|
|
407
|
+
|---------|---------------|-------------------|-------|--------------|
|
|
408
|
+
| Bundle Size | ~2KB | ~40KB | ~13KB | 0KB (nativo) |
|
|
409
|
+
| Dependencies | 0 | RxJS required | 0 | 0 |
|
|
410
|
+
| TypeScript | ✅ Native | ✅ Native | ✅ Native | ⚠️ Basic |
|
|
411
|
+
| Interceptors | ✅ | ✅ | ✅ | ❌ |
|
|
412
|
+
| Timeout | ✅ | ✅ | ✅ | ⚠️ Manual |
|
|
413
|
+
| Query Params | ✅ Auto | ✅ Auto | ✅ Auto | ❌ Manual |
|
|
414
|
+
| Error Handling | ✅ Structured | ✅ Structured | ✅ Structured | ⚠️ Basic |
|
|
415
|
+
| Promise/Observable | Promise | Observable | Promise | Promise |
|
|
416
|
+
|
|
417
|
+
## Roadmap
|
|
418
|
+
|
|
419
|
+
- [ ] Support per retry automatico
|
|
420
|
+
- [ ] Caching integrato delle richieste
|
|
421
|
+
- [ ] Request deduplication
|
|
422
|
+
- [ ] Upload progress tracking
|
|
423
|
+
- [ ] Download progress tracking
|
|
424
|
+
- [ ] Request queue con priorità
|
|
425
|
+
- [ ] Mock/test utilities
|
|
426
|
+
|
|
427
|
+
## License
|
|
428
|
+
|
|
429
|
+
Questo modulo fa parte di KIMU-Core ed è distribuito sotto licenza MPL-2.0.
|
|
430
|
+
|
|
431
|
+
## Supporto
|
|
432
|
+
|
|
433
|
+
Per issue, feature request o domande:
|
|
434
|
+
- GitHub Issues: [kimu-core/issues](https://github.com/UnicoVerso/kimu-core/issues)
|
|
435
|
+
- Documentazione: [kimu-docs](https://github.com/UnicoVerso/kimu-docs)
|