@zerosls/clm-sdk 1.0.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/.docs/publicacion-npm.md +111 -0
- package/.env.example +14 -0
- package/.gitlab-ci.yml +23 -0
- package/README.md +202 -0
- package/dist/config/config.d.ts +3 -0
- package/dist/config/config.js +21 -0
- package/dist/core/api-client.d.ts +27 -0
- package/dist/core/api-client.js +183 -0
- package/dist/core/api-error.d.ts +15 -0
- package/dist/core/api-error.js +46 -0
- package/dist/core/event-emitter.d.ts +11 -0
- package/dist/core/event-emitter.js +32 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +59 -0
- package/dist/modules/legacy/areas/areas-api.d.ts +34 -0
- package/dist/modules/legacy/areas/areas-api.js +44 -0
- package/dist/modules/legacy/areas/types.d.ts +37 -0
- package/dist/modules/legacy/areas/types.js +1 -0
- package/dist/modules/legacy/classificationtypes/classificationtypes-api.d.ts +34 -0
- package/dist/modules/legacy/classificationtypes/classificationtypes-api.js +46 -0
- package/dist/modules/legacy/classificationtypes/types.d.ts +41 -0
- package/dist/modules/legacy/classificationtypes/types.js +1 -0
- package/dist/modules/v1/auth/auth-api.d.ts +17 -0
- package/dist/modules/v1/auth/auth-api.js +63 -0
- package/dist/modules/v1/auth/types.d.ts +18 -0
- package/dist/modules/v1/auth/types.js +1 -0
- package/dist/modules/v1/main/main-api.d.ts +11 -0
- package/dist/modules/v1/main/main-api.js +14 -0
- package/dist/modules/v1/main/types.d.ts +3 -0
- package/dist/modules/v1/main/types.js +1 -0
- package/dist/modules/v1/notifications/notification-api.d.ts +16 -0
- package/dist/modules/v1/notifications/notification-api.js +26 -0
- package/dist/modules/v1/notifications/types.d.ts +53 -0
- package/dist/modules/v1/notifications/types.js +1 -0
- package/dist/modules/v1/users/types.d.ts +64 -0
- package/dist/modules/v1/users/types.js +1 -0
- package/dist/modules/v1/users/users-api.d.ts +81 -0
- package/dist/modules/v1/users/users-api.js +113 -0
- package/dist/types/common.d.ts +18 -0
- package/dist/types/common.js +1 -0
- package/dist/types/sdk.d.ts +42 -0
- package/dist/types/sdk.js +11 -0
- package/dist/utils/cache.d.ts +10 -0
- package/dist/utils/cache.js +43 -0
- package/dist/utils/http.d.ts +5 -0
- package/dist/utils/http.js +56 -0
- package/package.json +38 -0
- package/src/config/config.ts +24 -0
- package/src/core/api-client.ts +272 -0
- package/src/core/api-error.ts +54 -0
- package/src/core/event-emitter.ts +43 -0
- package/src/index.ts +89 -0
- package/src/modules/legacy/areas/areas-api.ts +73 -0
- package/src/modules/legacy/areas/types.ts +49 -0
- package/src/modules/legacy/classificationtypes/classificationtypes-api.ts +80 -0
- package/src/modules/legacy/classificationtypes/types.ts +52 -0
- package/src/modules/v1/auth/auth-api.ts +75 -0
- package/src/modules/v1/auth/types.ts +20 -0
- package/src/modules/v1/main/main-api.ts +20 -0
- package/src/modules/v1/main/types.ts +3 -0
- package/src/modules/v1/notifications/notification-api.ts +55 -0
- package/src/modules/v1/notifications/types.ts +58 -0
- package/src/modules/v1/users/types.ts +83 -0
- package/src/modules/v1/users/users-api.ts +148 -0
- package/src/types/common.ts +22 -0
- package/src/types/sdk.ts +38 -0
- package/src/utils/cache.ts +58 -0
- package/src/utils/http.ts +77 -0
- package/tests/integration/legacy/auth-areas.test.ts +115 -0
- package/tests/integration/legacy/auth-classification-types.test.ts +80 -0
- package/tests/integration/v1/auth-logs.test.ts +145 -0
- package/tests/integration/v1/auth-users.test.ts +189 -0
- package/tests/modules/legacy/areas/areas-api.test.ts +232 -0
- package/tests/modules/legacy/classification-types/classification-types-api.test.ts +100 -0
- package/tests/modules/v1/auth/auth-api.test.ts +134 -0
- package/tests/modules/v1/users/users-api.test.ts +176 -0
- package/tests/setup.ts +12 -0
- package/tests/utils/test-utils.ts +453 -0
- package/tsconfig.json +16 -0
- package/tsconfig.test.json +13 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Guía de Publicación del SDK
|
|
2
|
+
|
|
3
|
+
Este documento describe el proceso para publicar nuevas versiones del SDK en npm usando GitLab CI.
|
|
4
|
+
|
|
5
|
+
## Requisitos previos
|
|
6
|
+
|
|
7
|
+
- Token NPM configurado en GitLab CI/CD Variables como `NPM_TOKEN`
|
|
8
|
+
- Archivo `.gitlab-ci.yml` correctamente configurado
|
|
9
|
+
|
|
10
|
+
## Proceso de publicación
|
|
11
|
+
|
|
12
|
+
### 1. Desarrollo y pruebas
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Instalar dependencias
|
|
16
|
+
npm install
|
|
17
|
+
|
|
18
|
+
# Ejecutar pruebas
|
|
19
|
+
npm test
|
|
20
|
+
|
|
21
|
+
# Desarrollo con enlace local
|
|
22
|
+
npm link # En el SDK
|
|
23
|
+
npm link @zero/clm-sdk # En el proyecto frontend
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Actualizar versión
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Incremento de versión patch (1.0.0 -> 1.0.1)
|
|
30
|
+
npm version patch
|
|
31
|
+
|
|
32
|
+
# Incremento de versión minor (1.0.0 -> 1.1.0)
|
|
33
|
+
npm version minor
|
|
34
|
+
|
|
35
|
+
# Incremento de versión major (1.0.0 -> 2.0.0)
|
|
36
|
+
npm version major
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Estos comandos:
|
|
40
|
+
- Actualizan la versión en `package.json`
|
|
41
|
+
- Crean un commit con el cambio
|
|
42
|
+
- Crean un tag con formato `v1.0.0`
|
|
43
|
+
|
|
44
|
+
### 3. Publicar a GitLab y npm
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Enviar cambios y tags a GitLab
|
|
48
|
+
git push --follow-tags
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
El pipeline de GitLab CI se activará automáticamente cuando detecte un tag con formato `v*`, compilando y publicando el paquete en npm.
|
|
52
|
+
|
|
53
|
+
### 4. Verificar publicación
|
|
54
|
+
|
|
55
|
+
Verificar que la nueva versión aparece en npm:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm view @zero/clm-sdk versions
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Gestión de tags
|
|
62
|
+
|
|
63
|
+
### Crear tag manualmente
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Crear tag
|
|
67
|
+
git tag v1.0.1
|
|
68
|
+
|
|
69
|
+
# Enviar tag a GitLab
|
|
70
|
+
git push origin v1.0.1
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Eliminar tag (si es necesario)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Eliminar tag local
|
|
77
|
+
git tag -d v1.0.1
|
|
78
|
+
|
|
79
|
+
# Eliminar tag en GitLab
|
|
80
|
+
git push origin :refs/tags/v1.0.1
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Desarrollo con npm link
|
|
84
|
+
|
|
85
|
+
Para desarrollar el SDK mientras se prueba en un proyecto frontend:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# En el directorio del SDK
|
|
89
|
+
npm link
|
|
90
|
+
npm run dev
|
|
91
|
+
|
|
92
|
+
# En el directorio del frontend
|
|
93
|
+
npm link @zero/clm-sdk
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Para desconectar:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# En el directorio del frontend
|
|
100
|
+
npm unlink --no-save @zero/clm-sdk
|
|
101
|
+
npm install
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Solución de problemas
|
|
105
|
+
|
|
106
|
+
### Fallo en la publicación
|
|
107
|
+
|
|
108
|
+
1. Verificar logs en GitLab CI
|
|
109
|
+
2. Comprobar que el token NPM es válido y está configurado correctamente
|
|
110
|
+
3. Asegurar que la versión no existe ya en npm
|
|
111
|
+
```
|
package/.env.example
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Base URL configuration
|
|
2
|
+
SDK_BASE_URL=https://dev-api.zeroclm.io/zeroclm-api
|
|
3
|
+
SDK_DEFAULT_ORGANIZATION=default-org
|
|
4
|
+
|
|
5
|
+
# Cache settings
|
|
6
|
+
SDK_CACHE_TTL=60000
|
|
7
|
+
|
|
8
|
+
# Version information
|
|
9
|
+
SDK_VERSION=1.0.0
|
|
10
|
+
|
|
11
|
+
# API endpoints
|
|
12
|
+
SDK_AUTH_ENDPOINT=/auth
|
|
13
|
+
SDK_USERS_ENDPOINT=/users
|
|
14
|
+
SDK_SETTINGS_ENDPOINT=/settings
|
package/.gitlab-ci.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
stages:
|
|
2
|
+
- build
|
|
3
|
+
- publish
|
|
4
|
+
|
|
5
|
+
build:
|
|
6
|
+
stage: build
|
|
7
|
+
image: node:latest
|
|
8
|
+
script:
|
|
9
|
+
- npm ci
|
|
10
|
+
- npm run build
|
|
11
|
+
artifacts:
|
|
12
|
+
paths:
|
|
13
|
+
- dist/
|
|
14
|
+
|
|
15
|
+
publish:
|
|
16
|
+
stage: publish
|
|
17
|
+
image: node:latest
|
|
18
|
+
script:
|
|
19
|
+
- npm ci
|
|
20
|
+
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
|
|
21
|
+
- npm publish --access public
|
|
22
|
+
rules:
|
|
23
|
+
- if: $CI_COMMIT_BRANCH == "main"
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# CLM SDK
|
|
2
|
+
|
|
3
|
+
Cliente TypeScript para consumir la API de ZeroCLM.
|
|
4
|
+
|
|
5
|
+
## Instalación
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @zero/clm-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Uso básico
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ClmSdk } from '@zero/clm-sdk';
|
|
15
|
+
|
|
16
|
+
// Crear instancia del SDK
|
|
17
|
+
const sdk = new ClmSdk({
|
|
18
|
+
baseUrl: 'https://api.clm-app.com/api', // URL base de la API
|
|
19
|
+
organization: 'mi-organizacion', // ID de organización
|
|
20
|
+
token: 'jwt-token-opcional' // Token JWT (opcional)
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Login
|
|
24
|
+
async function login() {
|
|
25
|
+
try {
|
|
26
|
+
const response = await sdk.auth.login({
|
|
27
|
+
email: 'usuario@ejemplo.com',
|
|
28
|
+
password: 'contraseña'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
console.log('Usuario logueado:', response.user);
|
|
32
|
+
// El token se establece automáticamente para futuras peticiones
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Error de login:', error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Obtener configuraciones
|
|
39
|
+
async function getSettings() {
|
|
40
|
+
try {
|
|
41
|
+
const settings = await sdk.settings.getAll({
|
|
42
|
+
group: 'general',
|
|
43
|
+
enabled: true
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log('Configuraciones:', settings);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Error al obtener configuraciones:', error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Módulos disponibles
|
|
54
|
+
|
|
55
|
+
### Auth
|
|
56
|
+
|
|
57
|
+
Gestión de autenticación y tokens:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// Login
|
|
61
|
+
const loginResponse = await sdk.auth.login({ email, password });
|
|
62
|
+
|
|
63
|
+
// Refresh token
|
|
64
|
+
const refreshResponse = await sdk.auth.refreshToken({ refreshToken });
|
|
65
|
+
|
|
66
|
+
// Logout
|
|
67
|
+
await sdk.auth.logout();
|
|
68
|
+
|
|
69
|
+
// Verificar si está autenticado
|
|
70
|
+
const isAuthenticated = sdk.auth.isAuthenticated();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Settings
|
|
74
|
+
|
|
75
|
+
Gestión de configuraciones:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Obtener todas las configuraciones
|
|
79
|
+
const settings = await sdk.settings.getAll();
|
|
80
|
+
|
|
81
|
+
// Obtener configuración por ID
|
|
82
|
+
const setting = await sdk.settings.getById('config-id');
|
|
83
|
+
|
|
84
|
+
// Obtener por grupo y nombre
|
|
85
|
+
const appName = await sdk.settings.getByName('general', 'app_name');
|
|
86
|
+
|
|
87
|
+
// Crear configuración
|
|
88
|
+
const newSetting = await sdk.settings.create({
|
|
89
|
+
group_name: 'app',
|
|
90
|
+
name: 'theme',
|
|
91
|
+
value: 'dark',
|
|
92
|
+
data_type: 'string',
|
|
93
|
+
description: 'Tema de la aplicación'
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Actualizar configuración
|
|
97
|
+
await sdk.settings.update('config-id', { value: 'nuevo-valor' });
|
|
98
|
+
|
|
99
|
+
// Activar/desactivar configuración
|
|
100
|
+
await sdk.settings.toggleEnabled('config-id', true);
|
|
101
|
+
|
|
102
|
+
// Eliminar configuración
|
|
103
|
+
await sdk.settings.delete('config-id');
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Manejo de errores
|
|
107
|
+
|
|
108
|
+
El SDK incluye un sistema de manejo de errores personalizado:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { ApiError, ErrorType } from '@zero/clm-sdk';
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
await sdk.settings.getById('id-inexistente');
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof ApiError) {
|
|
117
|
+
console.log('Código de estado:', error.statusCode);
|
|
118
|
+
console.log('Tipo de error:', error.type);
|
|
119
|
+
console.log('Es error de autenticación:', error.isAuthenticationError);
|
|
120
|
+
console.log('Detalles:', error.details);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Eventos
|
|
126
|
+
|
|
127
|
+
El SDK emite eventos que puedes escuchar:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Escuchar cambios de token
|
|
131
|
+
const subscription = sdk.on('tokenChanged', (token) => {
|
|
132
|
+
console.log('Token cambiado:', token);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Escuchar errores de autenticación
|
|
136
|
+
sdk.on('authError', (error) => {
|
|
137
|
+
console.log('Error de autenticación:', error);
|
|
138
|
+
// Redirigir a login, etc.
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Cancelar suscripción cuando ya no sea necesaria
|
|
142
|
+
subscription.unsubscribe();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Caché
|
|
146
|
+
|
|
147
|
+
El SDK incluye un sistema de caché para optimizar peticiones:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Limpiar toda la caché
|
|
151
|
+
sdk.cache.clear();
|
|
152
|
+
|
|
153
|
+
// Limpiar sólo caché de settings
|
|
154
|
+
sdk.cache.clearByPrefix('/settings');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Configuración avanzada
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const sdk = new ClmSdk({
|
|
161
|
+
baseUrl: 'https://api.clm-app.com/api',
|
|
162
|
+
organization: 'mi-organizacion',
|
|
163
|
+
cache: {
|
|
164
|
+
enabled: true, // Activar/desactivar caché
|
|
165
|
+
ttl: 60000 // Tiempo de vida en ms (default: 1 minuto)
|
|
166
|
+
},
|
|
167
|
+
debug: true // Activar logs de depuración
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Desarrollo
|
|
172
|
+
|
|
173
|
+
### Instalación de dependencias
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm install
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Ejecutar tests
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
npm test # Ejecutar tests
|
|
183
|
+
npm run test:watch # Ejecutar tests en modo watch
|
|
184
|
+
npm run test:coverage # Generar reporte de cobertura
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Compilación
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npm run build # Compilar a JavaScript
|
|
191
|
+
npm run dev # Compilar en modo watch
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Lint
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm run lint
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Licencia
|
|
201
|
+
|
|
202
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Valores por defecto para la configuración
|
|
2
|
+
export const DEFAULT_CONFIG = {
|
|
3
|
+
baseUrl: "https://api.clm-app.com/api",
|
|
4
|
+
organization: "default-org",
|
|
5
|
+
cache: {
|
|
6
|
+
enabled: true,
|
|
7
|
+
ttl: 60000, // 1 minuto
|
|
8
|
+
},
|
|
9
|
+
debug: true,
|
|
10
|
+
};
|
|
11
|
+
// Función para combinar configuración del usuario con valores por defecto
|
|
12
|
+
export function mergeWithDefaultConfig(config) {
|
|
13
|
+
return {
|
|
14
|
+
...DEFAULT_CONFIG,
|
|
15
|
+
...config,
|
|
16
|
+
cache: {
|
|
17
|
+
...DEFAULT_CONFIG.cache,
|
|
18
|
+
...(config.cache || {}), // Usar un objeto vacío si config.cache es undefined
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SdkConfig } from "../types/sdk";
|
|
2
|
+
import { RequestOptions } from "../types/common";
|
|
3
|
+
import { EventEmitter } from "./event-emitter";
|
|
4
|
+
export declare class ApiClient {
|
|
5
|
+
private baseUrl;
|
|
6
|
+
private organization;
|
|
7
|
+
private token;
|
|
8
|
+
private eventEmitter;
|
|
9
|
+
private cache;
|
|
10
|
+
private debug;
|
|
11
|
+
private cacheEnabled;
|
|
12
|
+
constructor(config: SdkConfig, eventEmitter: EventEmitter);
|
|
13
|
+
setToken(token: string | null): void;
|
|
14
|
+
getToken(): string | null;
|
|
15
|
+
setOrganization(organization: string): void;
|
|
16
|
+
get<T>(endpoint: string, params?: Record<string, any>, options?: RequestOptions): Promise<T>;
|
|
17
|
+
post<T>(endpoint: string, data?: any, options?: RequestOptions): Promise<T>;
|
|
18
|
+
patch<T>(endpoint: string, data?: any, options?: RequestOptions): Promise<T>;
|
|
19
|
+
put<T>(endpoint: string, data?: any, options?: RequestOptions): Promise<T>;
|
|
20
|
+
delete<T>(endpoint: string, data?: any, // ✅ Cambiar params por data para enviarlo en el body
|
|
21
|
+
options?: RequestOptions): Promise<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Invalida la caché relacionada con un endpoint específico
|
|
24
|
+
*/
|
|
25
|
+
private invalidateCache;
|
|
26
|
+
private request;
|
|
27
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { ApiError } from "./api-error";
|
|
2
|
+
import { Cache } from "../utils/cache";
|
|
3
|
+
import { buildUrl, buildHeaders, generateCacheKey, parseResponse, } from "../utils/http";
|
|
4
|
+
export class ApiClient {
|
|
5
|
+
constructor(config, eventEmitter) {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
this.token = null;
|
|
8
|
+
this.baseUrl = config.baseUrl;
|
|
9
|
+
this.organization = config.organization;
|
|
10
|
+
this.token = config.token || null;
|
|
11
|
+
this.eventEmitter = eventEmitter;
|
|
12
|
+
this.cacheEnabled = ((_a = config.cache) === null || _a === void 0 ? void 0 : _a.enabled) !== false;
|
|
13
|
+
this.cache = new Cache((_b = config.cache) === null || _b === void 0 ? void 0 : _b.ttl);
|
|
14
|
+
this.debug = config.debug || false;
|
|
15
|
+
}
|
|
16
|
+
// Métodos para gestión del token
|
|
17
|
+
setToken(token) {
|
|
18
|
+
this.token = token;
|
|
19
|
+
this.eventEmitter.emit("tokenChanged", token);
|
|
20
|
+
}
|
|
21
|
+
getToken() {
|
|
22
|
+
return this.token;
|
|
23
|
+
}
|
|
24
|
+
setOrganization(organization) {
|
|
25
|
+
this.organization = organization;
|
|
26
|
+
}
|
|
27
|
+
// Método GET
|
|
28
|
+
async get(endpoint, params, options) {
|
|
29
|
+
return this.request("GET", endpoint, undefined, params, options);
|
|
30
|
+
}
|
|
31
|
+
// Método POST
|
|
32
|
+
async post(endpoint, data, options) {
|
|
33
|
+
return this.request("POST", endpoint, data, undefined, options);
|
|
34
|
+
}
|
|
35
|
+
// Método PATCH
|
|
36
|
+
async patch(endpoint, data, options) {
|
|
37
|
+
const result = await this.request("PATCH", endpoint, data, undefined, options);
|
|
38
|
+
this.invalidateCache(endpoint);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
// Método PUT
|
|
42
|
+
async put(endpoint, data, options) {
|
|
43
|
+
const result = await this.request("PUT", endpoint, data, undefined, options);
|
|
44
|
+
this.invalidateCache(endpoint);
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
// ✅ Método DELETE - CORREGIDO
|
|
48
|
+
async delete(endpoint, data, // ✅ Cambiar params por data para enviarlo en el body
|
|
49
|
+
options) {
|
|
50
|
+
console.log("🗑️ DELETE request:", { endpoint, data });
|
|
51
|
+
const result = await this.request("DELETE", endpoint, data, // ✅ Enviar como body (data)
|
|
52
|
+
undefined, // ✅ params es undefined
|
|
53
|
+
options);
|
|
54
|
+
this.invalidateCache(endpoint);
|
|
55
|
+
console.log("✅ DELETE response:", result);
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Invalida la caché relacionada con un endpoint específico
|
|
60
|
+
*/
|
|
61
|
+
invalidateCache(endpoint) {
|
|
62
|
+
if (this.cacheEnabled) {
|
|
63
|
+
const basePath = endpoint.split("/").slice(0, 2).join("/");
|
|
64
|
+
this.cache.clearByPrefix(`GET:${this.baseUrl}${basePath}`);
|
|
65
|
+
if (this.debug) {
|
|
66
|
+
console.log(`[SDK-Cache] Invalidated cache for: ${basePath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Método central para todas las peticiones HTTP
|
|
71
|
+
async request(method, endpoint, data, params, options = {}) {
|
|
72
|
+
const url = buildUrl(this.baseUrl, endpoint, params);
|
|
73
|
+
// =====================================================
|
|
74
|
+
// 1. Construir headers base
|
|
75
|
+
// =====================================================
|
|
76
|
+
const base = buildHeaders(this.token, {
|
|
77
|
+
"X-Organization": this.organization,
|
|
78
|
+
...(options.headers || {}),
|
|
79
|
+
});
|
|
80
|
+
const headers = new Headers(base);
|
|
81
|
+
if (!this.token) {
|
|
82
|
+
headers.delete("Authorization");
|
|
83
|
+
}
|
|
84
|
+
// =====================================================
|
|
85
|
+
// 2. Si el endpoint es /legacy/** → agregar Bearer legacy
|
|
86
|
+
// =====================================================
|
|
87
|
+
if (endpoint.startsWith("/legacy/")) {
|
|
88
|
+
const legacyToken = window.__LEGACY_TOKEN__ ||
|
|
89
|
+
sessionStorage.getItem("legacy_token") ||
|
|
90
|
+
null;
|
|
91
|
+
if (legacyToken) {
|
|
92
|
+
headers.set("Authorization", `Bearer ${legacyToken}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// =====================================================
|
|
96
|
+
// 3. Cache
|
|
97
|
+
// =====================================================
|
|
98
|
+
const useCache = this.cacheEnabled && options.useCache !== false;
|
|
99
|
+
if (useCache && method === "GET") {
|
|
100
|
+
const cacheKey = generateCacheKey(method, url, data);
|
|
101
|
+
const cachedData = this.cache.get(cacheKey);
|
|
102
|
+
if (cachedData) {
|
|
103
|
+
if (this.debug) {
|
|
104
|
+
console.log(`[SDK-Cache] Hit: ${cacheKey}`);
|
|
105
|
+
}
|
|
106
|
+
return cachedData;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
this.eventEmitter.emit("beforeRequest", {
|
|
110
|
+
url,
|
|
111
|
+
method,
|
|
112
|
+
data,
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
// =====================================================
|
|
116
|
+
// 4. Preparar options de fetch
|
|
117
|
+
// =====================================================
|
|
118
|
+
const fetchOptions = {
|
|
119
|
+
method,
|
|
120
|
+
headers,
|
|
121
|
+
credentials: "include",
|
|
122
|
+
};
|
|
123
|
+
// ✅ CRÍTICO: Agregar body para POST, PUT, PATCH y DELETE
|
|
124
|
+
if (data && method !== "GET") {
|
|
125
|
+
fetchOptions.body = JSON.stringify(data);
|
|
126
|
+
console.log(`📤 ${method} Body:`, data);
|
|
127
|
+
}
|
|
128
|
+
// =====================================================
|
|
129
|
+
// 5. Hacer la llamada real
|
|
130
|
+
// =====================================================
|
|
131
|
+
console.log(`🌐 ${method} ${url}`, fetchOptions);
|
|
132
|
+
const response = await fetch(url, fetchOptions);
|
|
133
|
+
// Manejo de errores HTTP
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
let errorData;
|
|
136
|
+
try {
|
|
137
|
+
errorData = await response.json();
|
|
138
|
+
}
|
|
139
|
+
catch (_a) {
|
|
140
|
+
errorData = { message: response.statusText };
|
|
141
|
+
}
|
|
142
|
+
console.error(`❌ ${method} ${response.status}:`, errorData);
|
|
143
|
+
if (response.status === 401) {
|
|
144
|
+
this.eventEmitter.emit("authError", {
|
|
145
|
+
statusCode: 401,
|
|
146
|
+
message: errorData.message || "Authentication required",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return errorData;
|
|
150
|
+
}
|
|
151
|
+
// Parsear respuesta exitosa
|
|
152
|
+
const responseData = await parseResponse(response);
|
|
153
|
+
// Guardar en caché si aplica
|
|
154
|
+
if (useCache && method === "GET") {
|
|
155
|
+
const cacheKey = generateCacheKey(method, url, data);
|
|
156
|
+
const cacheTime = options.cacheTime || undefined;
|
|
157
|
+
this.cache.set(cacheKey, responseData, cacheTime);
|
|
158
|
+
if (this.debug) {
|
|
159
|
+
console.log(`[SDK-Cache] Set: ${cacheKey}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
this.eventEmitter.emit("afterRequest", {
|
|
163
|
+
url,
|
|
164
|
+
method,
|
|
165
|
+
response: responseData,
|
|
166
|
+
});
|
|
167
|
+
return responseData;
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
this.eventEmitter.emit("requestError", {
|
|
171
|
+
url,
|
|
172
|
+
method,
|
|
173
|
+
error,
|
|
174
|
+
});
|
|
175
|
+
if (error instanceof ApiError) {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
throw new ApiError(error.message || "Network error", 0, {
|
|
179
|
+
originalError: error,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ErrorType } from '../types/sdk';
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
readonly isApiError = true;
|
|
4
|
+
readonly statusCode: number;
|
|
5
|
+
readonly type: ErrorType;
|
|
6
|
+
readonly details?: any;
|
|
7
|
+
constructor(message: string, statusCode: number, details?: any);
|
|
8
|
+
private determineErrorType;
|
|
9
|
+
get isNetworkError(): boolean;
|
|
10
|
+
get isAuthenticationError(): boolean;
|
|
11
|
+
get isAuthorizationError(): boolean;
|
|
12
|
+
get isNotFoundError(): boolean;
|
|
13
|
+
get isValidationError(): boolean;
|
|
14
|
+
get isServerError(): boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ErrorType } from '../types/sdk';
|
|
2
|
+
export class ApiError extends Error {
|
|
3
|
+
constructor(message, statusCode, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.isApiError = true;
|
|
6
|
+
this.name = 'ApiError';
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.details = details;
|
|
9
|
+
this.type = this.determineErrorType(statusCode);
|
|
10
|
+
// Para que instanceof funcione correctamente con clases extendidas
|
|
11
|
+
Object.setPrototypeOf(this, ApiError.prototype);
|
|
12
|
+
}
|
|
13
|
+
// Determina el tipo de error según el código HTTP
|
|
14
|
+
determineErrorType(statusCode) {
|
|
15
|
+
if (statusCode === 401)
|
|
16
|
+
return ErrorType.AUTHENTICATION;
|
|
17
|
+
if (statusCode === 403)
|
|
18
|
+
return ErrorType.AUTHORIZATION;
|
|
19
|
+
if (statusCode === 404)
|
|
20
|
+
return ErrorType.NOT_FOUND;
|
|
21
|
+
if (statusCode >= 400 && statusCode < 500)
|
|
22
|
+
return ErrorType.VALIDATION;
|
|
23
|
+
if (statusCode >= 500)
|
|
24
|
+
return ErrorType.SERVER;
|
|
25
|
+
return ErrorType.UNKNOWN;
|
|
26
|
+
}
|
|
27
|
+
// Propiedades útiles para verificación de tipo de error
|
|
28
|
+
get isNetworkError() {
|
|
29
|
+
return this.type === ErrorType.NETWORK;
|
|
30
|
+
}
|
|
31
|
+
get isAuthenticationError() {
|
|
32
|
+
return this.type === ErrorType.AUTHENTICATION;
|
|
33
|
+
}
|
|
34
|
+
get isAuthorizationError() {
|
|
35
|
+
return this.type === ErrorType.AUTHORIZATION;
|
|
36
|
+
}
|
|
37
|
+
get isNotFoundError() {
|
|
38
|
+
return this.type === ErrorType.NOT_FOUND;
|
|
39
|
+
}
|
|
40
|
+
get isValidationError() {
|
|
41
|
+
return this.type === ErrorType.VALIDATION;
|
|
42
|
+
}
|
|
43
|
+
get isServerError() {
|
|
44
|
+
return this.type === ErrorType.SERVER;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SdkEventType, SdkEvents } from '../types/sdk';
|
|
2
|
+
type Listener<T> = (data: T) => void;
|
|
3
|
+
interface Subscription {
|
|
4
|
+
unsubscribe: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare class EventEmitter {
|
|
7
|
+
private listeners;
|
|
8
|
+
on<K extends SdkEventType>(event: K, listener: Listener<SdkEvents[K]>): Subscription;
|
|
9
|
+
emit<K extends SdkEventType>(event: K, data: SdkEvents[K]): void;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class EventEmitter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.listeners = new Map();
|
|
4
|
+
}
|
|
5
|
+
// Suscribirse a un evento
|
|
6
|
+
on(event, listener) {
|
|
7
|
+
if (!this.listeners.has(event)) {
|
|
8
|
+
this.listeners.set(event, []);
|
|
9
|
+
}
|
|
10
|
+
const eventListeners = this.listeners.get(event);
|
|
11
|
+
eventListeners.push(listener);
|
|
12
|
+
// Devolver objeto para cancelar suscripción
|
|
13
|
+
return {
|
|
14
|
+
unsubscribe: () => {
|
|
15
|
+
const index = eventListeners.indexOf(listener);
|
|
16
|
+
if (index !== -1) {
|
|
17
|
+
eventListeners.splice(index, 1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// Emitir un evento con datos
|
|
23
|
+
emit(event, data) {
|
|
24
|
+
if (!this.listeners.has(event)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const eventListeners = this.listeners.get(event);
|
|
28
|
+
for (const listener of eventListeners) {
|
|
29
|
+
listener(data);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|