@dypai-ai/client-sdk 0.0.1
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/README.md +5 -0
- package/README.md.backup +5 -0
- package/dist/index.d.ts +958 -0
- package/dist/index.esm.js +1 -0
- package/dist/index.js +1 -0
- package/package.json +70 -0
package/README.md
ADDED
package/README.md.backup
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos para el paquete @dypai/client-sdk
|
|
3
|
+
*/
|
|
4
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
5
|
+
interface ApiOptions {
|
|
6
|
+
token: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
body?: any;
|
|
9
|
+
params?: Record<string, any>;
|
|
10
|
+
showToasts?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface ApiContextType {
|
|
13
|
+
token: string;
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
interface SupportedStorage {
|
|
18
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
19
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
20
|
+
removeItem(key: string): void | Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
interface AuthClientOptions {
|
|
23
|
+
storage?: SupportedStorage;
|
|
24
|
+
storageKey?: string;
|
|
25
|
+
autoRefreshToken?: boolean;
|
|
26
|
+
persistSession?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Error estandarizado de Dypai.
|
|
30
|
+
* Permite manejar errores de forma consistente (ej: if (error.status === 401))
|
|
31
|
+
*/
|
|
32
|
+
declare class DypaiError extends Error {
|
|
33
|
+
status: number;
|
|
34
|
+
code?: string | undefined;
|
|
35
|
+
details?: any | undefined;
|
|
36
|
+
constructor(message: string, status?: number, code?: string | undefined, details?: any | undefined);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Respuesta estandarizada del SDK (Estilo Supabase)
|
|
40
|
+
* @typeParam T - Tipo de los datos en caso de éxito
|
|
41
|
+
*/
|
|
42
|
+
type DypaiResponse<T> = {
|
|
43
|
+
data: T;
|
|
44
|
+
error: null;
|
|
45
|
+
} | {
|
|
46
|
+
data: null;
|
|
47
|
+
error: DypaiError;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Configuración global para el cliente API.
|
|
51
|
+
*/
|
|
52
|
+
interface GlobalClientOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Implementación personalizada de fetch.
|
|
55
|
+
* Útil para Node.js, tests, o para mockear peticiones.
|
|
56
|
+
*/
|
|
57
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
58
|
+
/**
|
|
59
|
+
* Headers globales que se enviarán en todas las peticiones.
|
|
60
|
+
*/
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Define la estructura de un endpoint tipado.
|
|
65
|
+
* Usado para inferir tipos de request/response automáticamente.
|
|
66
|
+
*/
|
|
67
|
+
interface EndpointDefinition {
|
|
68
|
+
/** Tipo de respuesta del endpoint */
|
|
69
|
+
response?: unknown;
|
|
70
|
+
/** Tipo del body para POST/PUT/PATCH */
|
|
71
|
+
body?: unknown;
|
|
72
|
+
/** Tipo de los query params */
|
|
73
|
+
params?: Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Mapa de endpoints tipados.
|
|
77
|
+
* El usuario define su API así:
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* interface MyApi extends EndpointMap {
|
|
82
|
+
* 'get-pacientes': { response: Paciente[], params: { active?: boolean } };
|
|
83
|
+
* 'create-cita': { body: CitaInput, response: Cita };
|
|
84
|
+
* 'update-paciente': { body: Partial<Paciente>, response: Paciente, params: { id: string } };
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
interface EndpointMap {
|
|
89
|
+
[endpoint: string]: EndpointDefinition;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extrae el tipo de respuesta de un endpoint
|
|
93
|
+
*/
|
|
94
|
+
type EndpointResponse<TMap extends EndpointMap, TEndpoint extends keyof TMap> = TMap[TEndpoint] extends {
|
|
95
|
+
response: infer R;
|
|
96
|
+
} ? R : any;
|
|
97
|
+
/**
|
|
98
|
+
* Extrae el tipo del body de un endpoint
|
|
99
|
+
*/
|
|
100
|
+
type EndpointBody<TMap extends EndpointMap, TEndpoint extends keyof TMap> = TMap[TEndpoint] extends {
|
|
101
|
+
body: infer B;
|
|
102
|
+
} ? B : any;
|
|
103
|
+
/**
|
|
104
|
+
* Extrae el tipo de los params de un endpoint
|
|
105
|
+
*/
|
|
106
|
+
type EndpointParams<TMap extends EndpointMap, TEndpoint extends keyof TMap> = TMap[TEndpoint] extends {
|
|
107
|
+
params: infer P;
|
|
108
|
+
} ? P : Record<string, any>;
|
|
109
|
+
/**
|
|
110
|
+
* Opciones para llamadas GET tipadas
|
|
111
|
+
*/
|
|
112
|
+
interface TypedGetOptions<TParams> {
|
|
113
|
+
params?: TParams;
|
|
114
|
+
showToasts?: boolean;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Opciones para llamadas POST/PUT/PATCH tipadas
|
|
118
|
+
*/
|
|
119
|
+
interface TypedMutationOptions<TParams> {
|
|
120
|
+
params?: TParams;
|
|
121
|
+
showToasts?: boolean;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Cliente API tipado con inferencia de tipos para endpoints.
|
|
125
|
+
* Permite autocompletado para endpoints conocidos, pero acepta strings arbitrarios (fallback a any).
|
|
126
|
+
*/
|
|
127
|
+
interface TypedApiClient<TMap extends EndpointMap = EndpointMap> {
|
|
128
|
+
/**
|
|
129
|
+
* GET request tipado
|
|
130
|
+
*/
|
|
131
|
+
get<TEndpoint extends (keyof TMap & string) | (string & {})>(endpoint: TEndpoint, options?: TypedGetOptions<TEndpoint extends keyof TMap ? EndpointParams<TMap, TEndpoint> : Record<string, any>>): Promise<TEndpoint extends keyof TMap ? EndpointResponse<TMap, TEndpoint> : any>;
|
|
132
|
+
/**
|
|
133
|
+
* POST request tipado
|
|
134
|
+
*/
|
|
135
|
+
post<TEndpoint extends (keyof TMap & string) | (string & {})>(endpoint: TEndpoint, body: TEndpoint extends keyof TMap ? EndpointBody<TMap, TEndpoint> : any, options?: TypedMutationOptions<TEndpoint extends keyof TMap ? EndpointParams<TMap, TEndpoint> : Record<string, any>>): Promise<TEndpoint extends keyof TMap ? EndpointResponse<TMap, TEndpoint> : any>;
|
|
136
|
+
/**
|
|
137
|
+
* PUT request tipado
|
|
138
|
+
*/
|
|
139
|
+
put<TEndpoint extends (keyof TMap & string) | (string & {})>(endpoint: TEndpoint, body: TEndpoint extends keyof TMap ? EndpointBody<TMap, TEndpoint> : any, options?: TypedMutationOptions<TEndpoint extends keyof TMap ? EndpointParams<TMap, TEndpoint> : Record<string, any>>): Promise<TEndpoint extends keyof TMap ? EndpointResponse<TMap, TEndpoint> : any>;
|
|
140
|
+
/**
|
|
141
|
+
* PATCH request tipado
|
|
142
|
+
*/
|
|
143
|
+
patch<TEndpoint extends (keyof TMap & string) | (string & {})>(endpoint: TEndpoint, body?: TEndpoint extends keyof TMap ? Partial<EndpointBody<TMap, TEndpoint>> : any, options?: TypedMutationOptions<TEndpoint extends keyof TMap ? EndpointParams<TMap, TEndpoint> : Record<string, any>>): Promise<TEndpoint extends keyof TMap ? EndpointResponse<TMap, TEndpoint> : any>;
|
|
144
|
+
/**
|
|
145
|
+
* DELETE request tipado
|
|
146
|
+
*/
|
|
147
|
+
delete<TEndpoint extends (keyof TMap & string) | (string & {})>(endpoint: TEndpoint, options?: TypedGetOptions<TEndpoint extends keyof TMap ? EndpointParams<TMap, TEndpoint> : Record<string, any>>): Promise<TEndpoint extends keyof TMap ? EndpointResponse<TMap, TEndpoint> : any>;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Tipo para el cliente API sin tipado (legacy/fallback)
|
|
151
|
+
*/
|
|
152
|
+
interface ApiClient {
|
|
153
|
+
get: (endpoint: string, options?: {
|
|
154
|
+
params?: Record<string, any>;
|
|
155
|
+
[key: string]: any;
|
|
156
|
+
}) => Promise<any>;
|
|
157
|
+
post: (endpoint: string, body: any, options?: {
|
|
158
|
+
params?: Record<string, any>;
|
|
159
|
+
[key: string]: any;
|
|
160
|
+
}) => Promise<any>;
|
|
161
|
+
put: (endpoint: string, body: any, options?: {
|
|
162
|
+
params?: Record<string, any>;
|
|
163
|
+
[key: string]: any;
|
|
164
|
+
}) => Promise<any>;
|
|
165
|
+
patch: (endpoint: string, body?: any, options?: {
|
|
166
|
+
params?: Record<string, any>;
|
|
167
|
+
[key: string]: any;
|
|
168
|
+
}) => Promise<any>;
|
|
169
|
+
delete: (endpoint: string, options?: {
|
|
170
|
+
params?: Record<string, any>;
|
|
171
|
+
[key: string]: any;
|
|
172
|
+
}) => Promise<any>;
|
|
173
|
+
}
|
|
174
|
+
interface AppConfig {
|
|
175
|
+
DYPAI_API_URL: string;
|
|
176
|
+
DYPAI_API_KEY?: string;
|
|
177
|
+
VITE_DYPAI_DEV_USERNAME?: string;
|
|
178
|
+
VITE_DYPAI_DEV_PASSWORD?: string;
|
|
179
|
+
[key: string]: any;
|
|
180
|
+
}
|
|
181
|
+
interface UserData {
|
|
182
|
+
token: string;
|
|
183
|
+
userId: string;
|
|
184
|
+
username: string;
|
|
185
|
+
email: string;
|
|
186
|
+
}
|
|
187
|
+
interface AppData {
|
|
188
|
+
config: AppConfig;
|
|
189
|
+
userData: UserData;
|
|
190
|
+
}
|
|
191
|
+
interface ToastFunction {
|
|
192
|
+
(props: {
|
|
193
|
+
title?: string;
|
|
194
|
+
description?: string;
|
|
195
|
+
variant?: 'default' | 'success' | 'error' | 'warning' | 'info';
|
|
196
|
+
}): void;
|
|
197
|
+
}
|
|
198
|
+
interface ApiServiceConfig {
|
|
199
|
+
toast?: ToastFunction;
|
|
200
|
+
showToasts?: boolean;
|
|
201
|
+
onUnauthorized?: () => void;
|
|
202
|
+
onTokenExpired?: () => Promise<string | null>;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
declare class DataModule<TDatabase = any> {
|
|
206
|
+
private api;
|
|
207
|
+
constructor(api: ApiClient);
|
|
208
|
+
/**
|
|
209
|
+
* Inicia una operación sobre una tabla/colección específica.
|
|
210
|
+
* En el backend esto mapea a /api/v0/{table}
|
|
211
|
+
* @param table Nombre de la tabla o workflow
|
|
212
|
+
*/
|
|
213
|
+
from<TTableName extends keyof TDatabase & string>(table: TTableName): QueryBuilder;
|
|
214
|
+
}
|
|
215
|
+
declare class QueryBuilder {
|
|
216
|
+
private table;
|
|
217
|
+
private api;
|
|
218
|
+
constructor(table: string, api: ApiClient);
|
|
219
|
+
select(filters?: Record<string, any>): Promise<any>;
|
|
220
|
+
insert(data: Record<string, any>): Promise<any>;
|
|
221
|
+
update(id: string | number, data: Record<string, any>): Promise<any>;
|
|
222
|
+
delete(id: string | number): Promise<any>;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
interface AppRole {
|
|
226
|
+
name: string;
|
|
227
|
+
weight: number;
|
|
228
|
+
description?: string;
|
|
229
|
+
}
|
|
230
|
+
interface User {
|
|
231
|
+
id: string;
|
|
232
|
+
email: string;
|
|
233
|
+
name?: string;
|
|
234
|
+
username?: string;
|
|
235
|
+
role?: string;
|
|
236
|
+
_role_details?: AppRole;
|
|
237
|
+
_app_context?: {
|
|
238
|
+
app_id: string;
|
|
239
|
+
};
|
|
240
|
+
created_at?: string;
|
|
241
|
+
updated_at?: string;
|
|
242
|
+
app_metadata?: any;
|
|
243
|
+
user_metadata?: any;
|
|
244
|
+
[key: string]: any;
|
|
245
|
+
}
|
|
246
|
+
interface AppUserResponse {
|
|
247
|
+
user: User;
|
|
248
|
+
role: AppRole;
|
|
249
|
+
app_id: string;
|
|
250
|
+
}
|
|
251
|
+
interface AuthResponse {
|
|
252
|
+
user: User;
|
|
253
|
+
token: string;
|
|
254
|
+
refreshToken?: string;
|
|
255
|
+
expiresIn?: number;
|
|
256
|
+
expiresAt?: number;
|
|
257
|
+
}
|
|
258
|
+
interface LoginCredentials {
|
|
259
|
+
email?: string;
|
|
260
|
+
phone?: string;
|
|
261
|
+
identifier?: string;
|
|
262
|
+
password: string;
|
|
263
|
+
}
|
|
264
|
+
interface RegisterData {
|
|
265
|
+
email?: string;
|
|
266
|
+
phone?: string;
|
|
267
|
+
password: string;
|
|
268
|
+
user_data?: Record<string, any>;
|
|
269
|
+
[key: string]: any;
|
|
270
|
+
}
|
|
271
|
+
interface OAuthOptions {
|
|
272
|
+
redirectTo?: string;
|
|
273
|
+
scopes?: string[];
|
|
274
|
+
}
|
|
275
|
+
interface JoinResponse {
|
|
276
|
+
message: string;
|
|
277
|
+
joined: boolean;
|
|
278
|
+
}
|
|
279
|
+
interface PasswordRecoveryRequest {
|
|
280
|
+
email: string;
|
|
281
|
+
}
|
|
282
|
+
interface PasswordResetRequest {
|
|
283
|
+
password: string;
|
|
284
|
+
}
|
|
285
|
+
interface ResetPasswordWithTokenRequest {
|
|
286
|
+
token: string;
|
|
287
|
+
password: string;
|
|
288
|
+
}
|
|
289
|
+
interface PasswordRecoveryResponse {
|
|
290
|
+
message: string;
|
|
291
|
+
}
|
|
292
|
+
interface PasswordResetResponse {
|
|
293
|
+
message: string;
|
|
294
|
+
}
|
|
295
|
+
interface OTPRequest {
|
|
296
|
+
email?: string;
|
|
297
|
+
phone?: string;
|
|
298
|
+
create_user?: boolean;
|
|
299
|
+
data?: Record<string, any>;
|
|
300
|
+
}
|
|
301
|
+
interface VerifyOTPRequest {
|
|
302
|
+
email?: string;
|
|
303
|
+
phone?: string;
|
|
304
|
+
token: string;
|
|
305
|
+
type?: 'sms' | 'signup' | 'magiclink' | 'recovery' | 'invite';
|
|
306
|
+
}
|
|
307
|
+
interface UpdateUserAttributes {
|
|
308
|
+
email?: string;
|
|
309
|
+
password?: string;
|
|
310
|
+
data?: Record<string, any>;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Sesión de autenticación (similar a Supabase)
|
|
314
|
+
*/
|
|
315
|
+
interface Session {
|
|
316
|
+
access_token: string;
|
|
317
|
+
refresh_token?: string;
|
|
318
|
+
expires_in?: number;
|
|
319
|
+
expires_at?: number;
|
|
320
|
+
token_type?: string;
|
|
321
|
+
user: User;
|
|
322
|
+
}
|
|
323
|
+
type AuthChangeEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'USER_UPDATED' | 'TOKEN_REFRESHED' | 'INITIAL_SESSION' | 'PASSWORD_RECOVERY';
|
|
324
|
+
/**
|
|
325
|
+
* Respuesta de getSession (similar a Supabase)
|
|
326
|
+
*/
|
|
327
|
+
interface SessionResponse {
|
|
328
|
+
data: {
|
|
329
|
+
session: Session | null;
|
|
330
|
+
};
|
|
331
|
+
error: Error | null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
type AuthListener = (event: AuthChangeEvent, session: Session | null) => void;
|
|
335
|
+
declare class AuthModule {
|
|
336
|
+
private config;
|
|
337
|
+
private _user;
|
|
338
|
+
private _token;
|
|
339
|
+
private _refreshToken;
|
|
340
|
+
private _expiresAt;
|
|
341
|
+
private _listeners;
|
|
342
|
+
private _refreshTimeout;
|
|
343
|
+
private readonly STORAGE_KEY;
|
|
344
|
+
private storage;
|
|
345
|
+
private _sessionRecovered;
|
|
346
|
+
constructor(config: {
|
|
347
|
+
baseUrl?: string;
|
|
348
|
+
apiKey?: string;
|
|
349
|
+
} & AuthClientOptions, _apiClient?: ApiClient | null);
|
|
350
|
+
private _handleVisibilityChange;
|
|
351
|
+
private deriveStorageKey;
|
|
352
|
+
get user(): User | null;
|
|
353
|
+
get token(): string | null;
|
|
354
|
+
onAuthStateChange(listener: AuthListener): {
|
|
355
|
+
data: {
|
|
356
|
+
subscription: {
|
|
357
|
+
unsubscribe: () => void;
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Login con email y contraseña
|
|
363
|
+
*/
|
|
364
|
+
signInWithPassword(credentials: LoginCredentials): Promise<DypaiResponse<AuthResponse>>;
|
|
365
|
+
/** Alias para signInWithPassword */
|
|
366
|
+
login(credentials: LoginCredentials): Promise<DypaiResponse<AuthResponse>>;
|
|
367
|
+
/**
|
|
368
|
+
* Registro de nuevo usuario
|
|
369
|
+
*/
|
|
370
|
+
signUp(data: RegisterData): Promise<DypaiResponse<AuthResponse>>;
|
|
371
|
+
/** Alias para signUp */
|
|
372
|
+
register(data: RegisterData): Promise<DypaiResponse<AuthResponse>>;
|
|
373
|
+
/**
|
|
374
|
+
* Obtiene la sesión actual
|
|
375
|
+
*/
|
|
376
|
+
getSession(): Promise<DypaiResponse<Session | null>>;
|
|
377
|
+
/**
|
|
378
|
+
* Obtiene los datos del usuario actual desde el servidor
|
|
379
|
+
*/
|
|
380
|
+
getUser(): Promise<DypaiResponse<User>>;
|
|
381
|
+
/** Alias para getUser */
|
|
382
|
+
me(): Promise<DypaiResponse<User>>;
|
|
383
|
+
/**
|
|
384
|
+
* Inicia sesión con OAuth
|
|
385
|
+
*/
|
|
386
|
+
signInWithOAuth(provider: 'google' | 'github', options?: OAuthOptions): Promise<DypaiResponse<void>>;
|
|
387
|
+
/**
|
|
388
|
+
* Cierra la sesión
|
|
389
|
+
*/
|
|
390
|
+
signOut(): Promise<DypaiResponse<void>>;
|
|
391
|
+
/** Alias para signOut */
|
|
392
|
+
logout(): Promise<DypaiResponse<void>>;
|
|
393
|
+
/**
|
|
394
|
+
* Solicita recuperación de contraseña
|
|
395
|
+
*/
|
|
396
|
+
resetPasswordForEmail(email: string): Promise<DypaiResponse<PasswordRecoveryResponse>>;
|
|
397
|
+
/** Alias para resetPasswordForEmail */
|
|
398
|
+
recoverPassword(data: PasswordRecoveryRequest): Promise<DypaiResponse<PasswordRecoveryResponse>>;
|
|
399
|
+
/**
|
|
400
|
+
* Refresca la sesión actual utilizando el refresh token.
|
|
401
|
+
*/
|
|
402
|
+
refreshSession(): Promise<DypaiResponse<AuthResponse>>;
|
|
403
|
+
/**
|
|
404
|
+
* Envía un código OTP (One Time Password) al email o teléfono indicado.
|
|
405
|
+
* Utilizado para login sin contraseña (Magic Link o SMS OTP).
|
|
406
|
+
*/
|
|
407
|
+
signInWithOtp(credentials: OTPRequest): Promise<DypaiResponse<any>>;
|
|
408
|
+
/**
|
|
409
|
+
* Verifica un código OTP enviado por email o teléfono.
|
|
410
|
+
* Si es válido, inicia la sesión del usuario.
|
|
411
|
+
*/
|
|
412
|
+
verifyOtp(params: VerifyOTPRequest): Promise<DypaiResponse<AuthResponse>>;
|
|
413
|
+
/**
|
|
414
|
+
* Actualiza los datos del usuario actual (email, contraseña o metadatos).
|
|
415
|
+
* Requiere que el usuario esté autenticado.
|
|
416
|
+
*/
|
|
417
|
+
updateUser(attributes: UpdateUserAttributes): Promise<DypaiResponse<User>>;
|
|
418
|
+
/**
|
|
419
|
+
* Programa un refresco de sesión automático antes de que el token caduque.
|
|
420
|
+
*/
|
|
421
|
+
private _scheduleRefresh;
|
|
422
|
+
private _normalizeUser;
|
|
423
|
+
private _handleAuthResponse;
|
|
424
|
+
private _updateUser;
|
|
425
|
+
private _clearSession;
|
|
426
|
+
private _recoverSession;
|
|
427
|
+
private _getSessionObject;
|
|
428
|
+
private _notifyListeners;
|
|
429
|
+
handleSessionExpired(): void;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
interface CreateUserRequest {
|
|
433
|
+
email: string;
|
|
434
|
+
password: string;
|
|
435
|
+
email_confirm?: boolean;
|
|
436
|
+
user_metadata?: Record<string, any>;
|
|
437
|
+
app_metadata?: Record<string, any>;
|
|
438
|
+
}
|
|
439
|
+
interface UpdateUserRequest {
|
|
440
|
+
email?: string;
|
|
441
|
+
password?: string;
|
|
442
|
+
email_confirm?: boolean;
|
|
443
|
+
user_metadata?: Record<string, any>;
|
|
444
|
+
app_metadata?: Record<string, any>;
|
|
445
|
+
}
|
|
446
|
+
interface UserListResponse {
|
|
447
|
+
users: User[];
|
|
448
|
+
aud: string;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Módulo para gestión administrativa de usuarios
|
|
453
|
+
*/
|
|
454
|
+
declare class UsersModule {
|
|
455
|
+
private api;
|
|
456
|
+
constructor(api: ApiClient);
|
|
457
|
+
/**
|
|
458
|
+
* Obtiene la lista de usuarios de la aplicación.
|
|
459
|
+
* Requiere rol de admin.
|
|
460
|
+
*/
|
|
461
|
+
list(params?: {
|
|
462
|
+
page?: number;
|
|
463
|
+
per_page?: number;
|
|
464
|
+
}): Promise<DypaiResponse<UserListResponse>>;
|
|
465
|
+
/**
|
|
466
|
+
* Crea un nuevo usuario en la aplicación.
|
|
467
|
+
*/
|
|
468
|
+
create(data: CreateUserRequest): Promise<DypaiResponse<User>>;
|
|
469
|
+
/**
|
|
470
|
+
* Actualiza un usuario existente.
|
|
471
|
+
*/
|
|
472
|
+
update(userId: string, data: UpdateUserRequest): Promise<DypaiResponse<User>>;
|
|
473
|
+
/**
|
|
474
|
+
* Elimina un usuario.
|
|
475
|
+
*/
|
|
476
|
+
delete(userId: string): Promise<DypaiResponse<{
|
|
477
|
+
success: boolean;
|
|
478
|
+
}>>;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Tipos para el módulo de Storage
|
|
483
|
+
*/
|
|
484
|
+
interface UploadOptions {
|
|
485
|
+
path?: string;
|
|
486
|
+
onProgress?: (percent: number) => void;
|
|
487
|
+
params?: Record<string, any>;
|
|
488
|
+
uploadEndpoint?: string;
|
|
489
|
+
confirmEndpoint?: string;
|
|
490
|
+
contentType?: string;
|
|
491
|
+
cacheControl?: string;
|
|
492
|
+
metadata?: Record<string, string>;
|
|
493
|
+
}
|
|
494
|
+
interface DownloadOptions {
|
|
495
|
+
fileName?: string;
|
|
496
|
+
method?: 'GET' | 'POST';
|
|
497
|
+
params?: Record<string, any>;
|
|
498
|
+
downloadEndpoint?: string;
|
|
499
|
+
}
|
|
500
|
+
interface SignedUrlOptions {
|
|
501
|
+
expiresIn?: number;
|
|
502
|
+
params?: Record<string, any>;
|
|
503
|
+
endpoint?: string;
|
|
504
|
+
}
|
|
505
|
+
interface ListOptions {
|
|
506
|
+
prefix?: string;
|
|
507
|
+
limit?: number;
|
|
508
|
+
offset?: number;
|
|
509
|
+
sortBy?: 'name' | 'created_at' | 'updated_at' | 'last_accessed_at';
|
|
510
|
+
order?: 'asc' | 'desc';
|
|
511
|
+
search?: string;
|
|
512
|
+
params?: Record<string, any>;
|
|
513
|
+
endpoint?: string;
|
|
514
|
+
}
|
|
515
|
+
interface UploadResponse {
|
|
516
|
+
files: Array<{
|
|
517
|
+
file_name: string;
|
|
518
|
+
file_path: string;
|
|
519
|
+
size_bytes: number;
|
|
520
|
+
content_type: string;
|
|
521
|
+
public_url?: string;
|
|
522
|
+
id?: string;
|
|
523
|
+
}>;
|
|
524
|
+
bucket: string;
|
|
525
|
+
}
|
|
526
|
+
interface SignedUrlResponse {
|
|
527
|
+
signedUrl: string;
|
|
528
|
+
expiresIn: number;
|
|
529
|
+
path: string;
|
|
530
|
+
}
|
|
531
|
+
interface StorageObject {
|
|
532
|
+
id: string;
|
|
533
|
+
name: string;
|
|
534
|
+
path: string;
|
|
535
|
+
size: number;
|
|
536
|
+
mime_type?: string;
|
|
537
|
+
created_at: string;
|
|
538
|
+
updated_at: string;
|
|
539
|
+
last_accessed_at?: string;
|
|
540
|
+
metadata?: Record<string, any>;
|
|
541
|
+
}
|
|
542
|
+
interface ListResponse {
|
|
543
|
+
data: StorageObject[];
|
|
544
|
+
total?: number;
|
|
545
|
+
hasMore?: boolean;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Módulo de Storage (Almacenamiento)
|
|
550
|
+
*
|
|
551
|
+
* Proporciona una API tipo Supabase para gestionar archivos.
|
|
552
|
+
* Internamente usa Smart Storage con workflows del Engine.
|
|
553
|
+
*/
|
|
554
|
+
declare class StorageModule {
|
|
555
|
+
private api;
|
|
556
|
+
constructor(api: ApiClient);
|
|
557
|
+
/**
|
|
558
|
+
* Selecciona un bucket para operaciones
|
|
559
|
+
* @param bucketName Nombre del bucket
|
|
560
|
+
* @returns Builder para operaciones en el bucket
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```typescript
|
|
564
|
+
* const { data, error } = await dypai.storage
|
|
565
|
+
* .from('imagenes')
|
|
566
|
+
* .upload('avatar.jpg', file);
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
from(bucketName: string): StorageBucketBuilder;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Builder para operaciones en un bucket específico
|
|
573
|
+
*/
|
|
574
|
+
declare class StorageBucketBuilder {
|
|
575
|
+
private bucketName;
|
|
576
|
+
private api;
|
|
577
|
+
constructor(bucketName: string, api: ApiClient);
|
|
578
|
+
/**
|
|
579
|
+
* Sube un archivo al bucket
|
|
580
|
+
*
|
|
581
|
+
* @param path Path donde guardar el archivo (ej: 'carpeta/archivo.jpg')
|
|
582
|
+
* @param file Archivo a subir
|
|
583
|
+
* @param options Opciones adicionales
|
|
584
|
+
*
|
|
585
|
+
* @example
|
|
586
|
+
* ```typescript
|
|
587
|
+
* const file = document.getElementById('input').files[0];
|
|
588
|
+
* const { data, error } = await dypai.storage
|
|
589
|
+
* .from('documentos')
|
|
590
|
+
* .upload('facturas/2024/inv-001.pdf', file, {
|
|
591
|
+
* onProgress: (pct) => console.log(`${pct}%`)
|
|
592
|
+
* });
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
upload(path: string, file: File, options?: UploadOptions): Promise<DypaiResponse<UploadResponse>>;
|
|
596
|
+
/**
|
|
597
|
+
* Descarga un archivo del bucket
|
|
598
|
+
*
|
|
599
|
+
* @param path Path del archivo a descargar
|
|
600
|
+
* @param options Opciones adicionales
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```typescript
|
|
604
|
+
* await dypai.storage
|
|
605
|
+
* .from('documentos')
|
|
606
|
+
* .download('facturas/2024/inv-001.pdf', {
|
|
607
|
+
* fileName: 'factura.pdf'
|
|
608
|
+
* });
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
download(path: string, options?: DownloadOptions): Promise<DypaiResponse<void>>;
|
|
612
|
+
/**
|
|
613
|
+
* Obtiene una URL firmada para acceso temporal
|
|
614
|
+
*
|
|
615
|
+
* @param path Path del archivo
|
|
616
|
+
* @param options Opciones adicionales
|
|
617
|
+
*
|
|
618
|
+
* @example
|
|
619
|
+
* ```typescript
|
|
620
|
+
* const { data, error } = await dypai.storage
|
|
621
|
+
* .from('documentos')
|
|
622
|
+
* .createSignedUrl('facturas/inv-001.pdf', 15);
|
|
623
|
+
*
|
|
624
|
+
* if (data) {
|
|
625
|
+
* console.log('URL:', data.signedUrl);
|
|
626
|
+
* }
|
|
627
|
+
* ```
|
|
628
|
+
*/
|
|
629
|
+
createSignedUrl(path: string, expiresIn?: number, options?: Omit<SignedUrlOptions, 'expiresIn'>): Promise<DypaiResponse<SignedUrlResponse>>;
|
|
630
|
+
/**
|
|
631
|
+
* Obtiene la URL pública de un archivo (solo para buckets públicos)
|
|
632
|
+
*
|
|
633
|
+
* @param path Path del archivo
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```typescript
|
|
637
|
+
* const { data } = await dypai.storage
|
|
638
|
+
* .from('public-images')
|
|
639
|
+
* .getPublicUrl('logo.png');
|
|
640
|
+
*
|
|
641
|
+
* console.log('URL pública:', data.publicUrl);
|
|
642
|
+
* ```
|
|
643
|
+
*/
|
|
644
|
+
getPublicUrl(path: string): {
|
|
645
|
+
data: {
|
|
646
|
+
publicUrl: string;
|
|
647
|
+
};
|
|
648
|
+
};
|
|
649
|
+
/**
|
|
650
|
+
* Lista archivos en el bucket
|
|
651
|
+
*
|
|
652
|
+
* @param path Path/carpeta a listar (opcional, lista raíz si no se especifica)
|
|
653
|
+
* @param options Opciones adicionales
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* ```typescript
|
|
657
|
+
* const { data, error } = await dypai.storage
|
|
658
|
+
* .from('documentos')
|
|
659
|
+
* .list('facturas/2024', {
|
|
660
|
+
* limit: 20,
|
|
661
|
+
* sortBy: 'created_at',
|
|
662
|
+
* order: 'desc'
|
|
663
|
+
* });
|
|
664
|
+
*
|
|
665
|
+
* if (data) {
|
|
666
|
+
* data.data.forEach(file => {
|
|
667
|
+
* console.log(file.name, file.size);
|
|
668
|
+
* });
|
|
669
|
+
* }
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
list(path?: string, options?: ListOptions): Promise<DypaiResponse<ListResponse>>;
|
|
673
|
+
/**
|
|
674
|
+
* Elimina un archivo del bucket
|
|
675
|
+
*
|
|
676
|
+
* @param path Path del archivo a eliminar
|
|
677
|
+
* @param options Opciones adicionales
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```typescript
|
|
681
|
+
* const { error } = await dypai.storage
|
|
682
|
+
* .from('documentos')
|
|
683
|
+
* .remove('facturas/2024/inv-001.pdf');
|
|
684
|
+
*
|
|
685
|
+
* if (!error) {
|
|
686
|
+
* console.log('Archivo eliminado');
|
|
687
|
+
* }
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
remove(path: string | string[], options?: {
|
|
691
|
+
endpoint?: string;
|
|
692
|
+
params?: Record<string, any>;
|
|
693
|
+
}): Promise<DypaiResponse<{
|
|
694
|
+
success: boolean;
|
|
695
|
+
}>>;
|
|
696
|
+
/**
|
|
697
|
+
* Mueve/renombra un archivo
|
|
698
|
+
*
|
|
699
|
+
* @param fromPath Path origen
|
|
700
|
+
* @param toPath Path destino
|
|
701
|
+
* @param options Opciones adicionales
|
|
702
|
+
*
|
|
703
|
+
* @example
|
|
704
|
+
* ```typescript
|
|
705
|
+
* await dypai.storage
|
|
706
|
+
* .from('documentos')
|
|
707
|
+
* .move('temp/archivo.pdf', 'final/archivo.pdf');
|
|
708
|
+
* ```
|
|
709
|
+
*/
|
|
710
|
+
move(fromPath: string, toPath: string, options?: {
|
|
711
|
+
endpoint?: string;
|
|
712
|
+
params?: Record<string, any>;
|
|
713
|
+
}): Promise<DypaiResponse<{
|
|
714
|
+
success: boolean;
|
|
715
|
+
}>>;
|
|
716
|
+
/**
|
|
717
|
+
* Copia un archivo
|
|
718
|
+
*
|
|
719
|
+
* @param fromPath Path origen
|
|
720
|
+
* @param toPath Path destino
|
|
721
|
+
* @param options Opciones adicionales
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* ```typescript
|
|
725
|
+
* await dypai.storage
|
|
726
|
+
* .from('documentos')
|
|
727
|
+
* .copy('original.pdf', 'copia.pdf');
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
copy(fromPath: string, toPath: string, options?: {
|
|
731
|
+
endpoint?: string;
|
|
732
|
+
params?: Record<string, any>;
|
|
733
|
+
}): Promise<DypaiResponse<{
|
|
734
|
+
success: boolean;
|
|
735
|
+
}>>;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
interface DypaiConfig {
|
|
739
|
+
/** URL base de la API (ej: 'http://localhost:8000') - Debe apuntar al Gateway unificado */
|
|
740
|
+
baseUrl: string;
|
|
741
|
+
/** API Key pública (Anon Key) */
|
|
742
|
+
apiKey: string;
|
|
743
|
+
/** Opciones de autenticación */
|
|
744
|
+
auth?: AuthClientOptions;
|
|
745
|
+
/** Opciones globales (fetch, headers) */
|
|
746
|
+
global?: GlobalClientOptions;
|
|
747
|
+
/** Opcional: ID para aislar localStorage entre múltiples clientes */
|
|
748
|
+
storageKey?: string;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Cliente unificado de Dypai.
|
|
752
|
+
*
|
|
753
|
+
* @typeParam TDatabase - Estructura de la base de datos (opcional)
|
|
754
|
+
* @typeParam TApi - Mapa de endpoints tipados (opcional)
|
|
755
|
+
*/
|
|
756
|
+
declare class DypaiClient<TDatabase = any, TApi extends EndpointMap = EndpointMap> {
|
|
757
|
+
/** Módulo de autenticación */
|
|
758
|
+
auth: AuthModule;
|
|
759
|
+
/** Módulo de base de datos (CRUD) */
|
|
760
|
+
db: DataModule<TDatabase>;
|
|
761
|
+
/** Módulo de gestión de usuarios (admin) */
|
|
762
|
+
users: UsersModule;
|
|
763
|
+
/** Módulo de almacenamiento (Storage) */
|
|
764
|
+
storage: StorageModule;
|
|
765
|
+
/** Cliente API tipado para endpoints personalizados */
|
|
766
|
+
api: TApi extends EndpointMap ? TypedApiClient<TApi> : ApiClient;
|
|
767
|
+
constructor(config: DypaiConfig);
|
|
768
|
+
/**
|
|
769
|
+
* Alias para acceder a datos (estilo Supabase)
|
|
770
|
+
*/
|
|
771
|
+
from<TTableName extends keyof TDatabase & string>(table: TTableName): QueryBuilder;
|
|
772
|
+
/**
|
|
773
|
+
* Obtiene el usuario actual
|
|
774
|
+
*/
|
|
775
|
+
me(): Promise<DypaiResponse<User>>;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Crea una instancia del cliente Dypai.
|
|
779
|
+
* Requiere URL y API Key (modo standalone estilo Supabase).
|
|
780
|
+
*
|
|
781
|
+
* @typeParam TDatabase - Estructura de la base de datos (opcional)
|
|
782
|
+
* @typeParam TApi - Mapa de endpoints tipados (opcional)
|
|
783
|
+
*/
|
|
784
|
+
declare function createClient<TDatabase = any, TApi extends EndpointMap = EndpointMap>(url: string, apiKey: string, options?: Omit<DypaiConfig, 'baseUrl' | 'apiKey'>): DypaiClient<TDatabase, TApi>;
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* @dypai/client-sdk - Cliente HTTP centralizado
|
|
788
|
+
*
|
|
789
|
+
* Gestiona llamadas al Gateway unificado (Auth + Engine).
|
|
790
|
+
*/
|
|
791
|
+
|
|
792
|
+
type TokenProvider = () => string | null;
|
|
793
|
+
declare function setTokenProvider(provider: TokenProvider): void;
|
|
794
|
+
declare function configureApiService(config: ApiServiceConfig): void;
|
|
795
|
+
declare function callApi(token: string, method: HttpMethod, endpoint: string, body?: any, showToastsOverride?: boolean, params?: Record<string, any>, apiKey?: string, baseUrl?: string): Promise<any>;
|
|
796
|
+
declare function createApiClient(context: ApiContextType | (() => ApiContextType)): ApiClient;
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Configuración global para todos los servicios DYPAI
|
|
800
|
+
*
|
|
801
|
+
* Este archivo maneja la configuración centralizada para:
|
|
802
|
+
* - ApiService (API autenticada)
|
|
803
|
+
*/
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Configura todos los servicios DYPAI de una vez (recomendado)
|
|
807
|
+
* Esta es la función principal que deberías usar al inicio de tu aplicación
|
|
808
|
+
*
|
|
809
|
+
* @param config Configuración global para todos los servicios
|
|
810
|
+
* @example
|
|
811
|
+
* ```typescript
|
|
812
|
+
* import { configureDypaiServices } from '@dypai/client-sdk';
|
|
813
|
+
*
|
|
814
|
+
* configureDypaiServices({
|
|
815
|
+
* showToasts: true,
|
|
816
|
+
* toast: myCustomToast
|
|
817
|
+
* });
|
|
818
|
+
* ```
|
|
819
|
+
*/
|
|
820
|
+
declare function configureDypaiServices(config: ApiServiceConfig): void;
|
|
821
|
+
/**
|
|
822
|
+
* Obtiene la configuración global actual
|
|
823
|
+
* @returns Configuración global compartida
|
|
824
|
+
*/
|
|
825
|
+
declare function getGlobalConfig(): ApiServiceConfig;
|
|
826
|
+
/**
|
|
827
|
+
* Resetea toda la configuración global (útil para testing)
|
|
828
|
+
*/
|
|
829
|
+
declare function resetGlobalConfig(): void;
|
|
830
|
+
/**
|
|
831
|
+
* Fuerza la recarga de la configuración detectando cambios en variables de entorno
|
|
832
|
+
* Útil cuando se cambian variables de entorno en tiempo de ejecución
|
|
833
|
+
*
|
|
834
|
+
* @example
|
|
835
|
+
* ```typescript
|
|
836
|
+
* import { reloadDypaiConfig } from '@dypai/client-sdk';
|
|
837
|
+
*
|
|
838
|
+
* // Después de cambiar variables de entorno
|
|
839
|
+
* await reloadDypaiConfig();
|
|
840
|
+
* ```
|
|
841
|
+
*/
|
|
842
|
+
declare function reloadDypaiConfig(): Promise<void>;
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Hook básico para notificaciones toast en el paquete @dypai/client-sdk
|
|
846
|
+
*
|
|
847
|
+
* Este es un hook minimalista que puede ser reemplazado por la implementación
|
|
848
|
+
* de la aplicación host mediante la configuración del ApiService.
|
|
849
|
+
*/
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Configura una función toast personalizada para usar en el paquete
|
|
853
|
+
* @param toastFn Función toast de la aplicación host
|
|
854
|
+
*/
|
|
855
|
+
declare function setToastFunction(toastFn: ToastFunction): void;
|
|
856
|
+
/**
|
|
857
|
+
* Hook que retorna la función toast configurada o el fallback
|
|
858
|
+
*/
|
|
859
|
+
declare function useToast(): {
|
|
860
|
+
toast: ToastFunction;
|
|
861
|
+
toastSuccess: (title: string, description?: string) => void;
|
|
862
|
+
toastError: (title: string, description?: string) => void;
|
|
863
|
+
toastWarning: (title: string, description?: string) => void;
|
|
864
|
+
toastInfo: (title: string, description?: string) => void;
|
|
865
|
+
};
|
|
866
|
+
/**
|
|
867
|
+
* Función toast independiente que puede ser usada sin hook
|
|
868
|
+
*/
|
|
869
|
+
declare const toast: (props: Parameters<ToastFunction>[0]) => void;
|
|
870
|
+
declare const toastSuccess: (title: string, description?: string) => void;
|
|
871
|
+
declare const toastError: (title: string, description?: string) => void;
|
|
872
|
+
declare const toastWarning: (title: string, description?: string) => void;
|
|
873
|
+
declare const toastInfo: (title: string, description?: string) => void;
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* @dypai-ai/client-sdk
|
|
877
|
+
*
|
|
878
|
+
* Paquete centralizado para manejo de llamadas API con autenticación DYPAI
|
|
879
|
+
*
|
|
880
|
+
* ⚠️ IMPORTANTE: Debes crear el cliente primero (igual que Supabase). No puedes usar el paquete sin crear una instancia del cliente.
|
|
881
|
+
*
|
|
882
|
+
* @example Crear el cliente (OBLIGATORIO - Paso 1)
|
|
883
|
+
* ```typescript
|
|
884
|
+
* // src/lib/dypai.ts
|
|
885
|
+
* import { createClient } from '@dypai/client-sdk';
|
|
886
|
+
*
|
|
887
|
+
* // Crear el cliente con tu URL y API Key
|
|
888
|
+
* export const dypai = createClient(
|
|
889
|
+
* 'https://api.dypai.es', // URL de la API
|
|
890
|
+
* 'tu-anon-key-publica' // API Key pública
|
|
891
|
+
* );
|
|
892
|
+
*
|
|
893
|
+
* // Con variables de entorno (Vite)
|
|
894
|
+
* export const dypai = createClient(
|
|
895
|
+
* import.meta.env.VITE_DYPAI_API_URL,
|
|
896
|
+
* import.meta.env.VITE_DYPAI_API_KEY
|
|
897
|
+
* );
|
|
898
|
+
* ```
|
|
899
|
+
*
|
|
900
|
+
* @example Uso básico con el cliente (Paso 2)
|
|
901
|
+
* ```typescript
|
|
902
|
+
* // En cualquier componente o archivo
|
|
903
|
+
* import { dypai } from '../lib/dypai'; // Tu cliente configurado
|
|
904
|
+
*
|
|
905
|
+
* // Autenticación
|
|
906
|
+
* const { data, error } = await dypai.auth.signInWithPassword({ email: 'user@test.com', password: '123' });
|
|
907
|
+
*
|
|
908
|
+
* // Base de Datos (CRUD) con autocompletado
|
|
909
|
+
* const { data: productos, error: dbError } = await dypai.from('productos').select({ limit: 10 });
|
|
910
|
+
*
|
|
911
|
+
* // API personalizada (endpoints)
|
|
912
|
+
* const facturas = await dypai.api.get('mis_facturas');
|
|
913
|
+
*
|
|
914
|
+
* // Storage (Almacenamiento) - Estilo Supabase
|
|
915
|
+
* const { data, error } = await dypai.storage
|
|
916
|
+
* .from('documentos')
|
|
917
|
+
* .upload('facturas/2024/inv-001.pdf', file);
|
|
918
|
+
*
|
|
919
|
+
* const { data: url } = await dypai.storage
|
|
920
|
+
* .from('documentos')
|
|
921
|
+
* .getSignedUrl('facturas/2024/inv-001.pdf', { expiresIn: 15 });
|
|
922
|
+
* ```
|
|
923
|
+
*
|
|
924
|
+
* @example Uso avanzado con tipado de Base de Datos y API
|
|
925
|
+
* ```typescript
|
|
926
|
+
* // Define tus interfaces
|
|
927
|
+
* interface Database {
|
|
928
|
+
* productos: { id: string; nombre: string; precio: number };
|
|
929
|
+
* }
|
|
930
|
+
*
|
|
931
|
+
* export const dypai = createClient<Database>(url, key);
|
|
932
|
+
*
|
|
933
|
+
* // 'productos' tiene autocompletado y el resultado está tipado
|
|
934
|
+
* const { data } = await dypai.from('productos').select();
|
|
935
|
+
* ```
|
|
936
|
+
*
|
|
937
|
+
* @example Uso con servicios estáticos (Legacy - No recomendado para nuevas apps)
|
|
938
|
+
* ```typescript
|
|
939
|
+
* // ⚠️ Solo si usas configuración global
|
|
940
|
+
* // import { ApiService, FileService } from '@dypai/client-sdk'; // DEPRECATED
|
|
941
|
+
* // Usa createClient() en su lugar
|
|
942
|
+
* ```
|
|
943
|
+
*
|
|
944
|
+
*/
|
|
945
|
+
/**
|
|
946
|
+
* Nuevo Cliente Unificado DYPAI
|
|
947
|
+
* Forma recomendada de acceder a toda la funcionalidad (Auth, Data, etc.)
|
|
948
|
+
*/
|
|
949
|
+
|
|
950
|
+
declare const PACKAGE_INFO: {
|
|
951
|
+
readonly name: "@dypai-ai/client-sdk";
|
|
952
|
+
readonly version: "0.0.1";
|
|
953
|
+
readonly description: "Cliente JavaScript para Dypai Engine";
|
|
954
|
+
readonly features: readonly ["API REST autenticada", "Smart Storage (Upload/Download delegado)", "Gestión de usuarios (Admin)"];
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
export { DypaiClient, PACKAGE_INFO, callApi, configureApiService, configureDypaiServices, createApiClient, createClient, getGlobalConfig, reloadDypaiConfig, resetGlobalConfig, setToastFunction, setTokenProvider, toast, toastError, toastInfo, toastSuccess, toastWarning, useToast };
|
|
958
|
+
export type { ApiClient, ApiContextType, ApiOptions, ApiServiceConfig, AppConfig, AppData, AppRole, AppUserResponse, AuthChangeEvent, AuthResponse, CreateUserRequest, DownloadOptions, DypaiConfig, EndpointBody, EndpointDefinition, EndpointMap, EndpointParams, EndpointResponse, HttpMethod, JoinResponse, ListOptions, ListResponse, LoginCredentials, OAuthOptions, PasswordRecoveryRequest, PasswordRecoveryResponse, PasswordResetRequest, PasswordResetResponse, RegisterData, ResetPasswordWithTokenRequest, Session, SessionResponse, SignedUrlOptions, SignedUrlResponse, StorageObject, ToastFunction, TypedApiClient, TypedGetOptions, TypedMutationOptions, UpdateUserRequest, UploadOptions, UploadResponse, User, UserData, UserListResponse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class t extends Error{constructor(t,e=500,s,n){super(t),this.status=e,this.code=s,this.details=n,this.name="DypaiError"}}const e={getItem:t=>"undefined"==typeof window?null:window.localStorage.getItem(t),setItem:(t,e)=>{if("undefined"!=typeof window)try{window.localStorage.setItem(t,e)}catch(t){console.error("[DYPAI SDK] ❌ Error crítico guardando en localStorage (¿Quota/Permisos?):",t)}},removeItem:t=>{if("undefined"!=typeof window)try{window.localStorage.removeItem(t)}catch(t){}}};async function s(e){try{return{data:await e,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e.message||"Error de autenticación",e.status||400)}}}class n{constructor(t,s=null){this.config=t,this.t=null,this.i=null,this.o=null,this.h=null,this.u=[],this.l=null,this.storage=t.storage||e;const n=t.storageKey||this.deriveStorageKey(t.apiKey);console.log(`[DYPAI SDK] 🛠️ Inicializando AuthModule (apiKey: ${t.apiKey?.substring(0,8)}..., storageKey: ${n})`),this.STORAGE_KEY=`dypai-${n}-auth-session`,this.p=this.S().then(()=>{this.i&&this.getUser().catch(()=>{})}),"undefined"!=typeof window&&(window.addEventListener("visibilitychange",this.m.bind(this)),window.addEventListener("focus",this.m.bind(this)))}async m(){"undefined"!=typeof document&&"visible"===document.visibilityState&&(console.log("[DYPAI SDK] 👁️ Ventana visible. Verificando estado de sesión..."),await this.S())}deriveStorageKey(t){return t?t.substring(0,8):"default"}get user(){return this.t}get token(){return this.i}onAuthStateChange(t){return console.log("[DYPAI SDK] 👂 Nuevo suscriptor añadido a onAuthStateChange"),this.u.push(t),this.p.then(()=>{const e=this._(),s=e?"SIGNED_IN":"INITIAL_SESSION";console.log(`[DYPAI SDK] 📣 Notificando estado inicial a nuevo suscriptor: ${s} (Sesión activa: ${!!e})`),t(s,e)}).catch(e=>{console.error("[DYPAI SDK] ❌ Error esperando recuperación de sesión para suscriptor:",e),t("INITIAL_SESSION",null)}),{data:{subscription:{unsubscribe:()=>{this.u=this.u.filter(e=>e!==t)}}}}}async signInWithPassword(e){return s((async()=>{const s=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/token?grant_type=password`,n={email:e.email||e.identifier||"",password:e.password},i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(n)});if(!i.ok){const e=await i.json();throw new t(e.msg||e.error_description||"Login failed",i.status)}const a=await i.json(),o={token:a.access_token,refreshToken:a.refresh_token,expiresIn:a.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(a.expires_in||3600),user:this.D(a)};return this.v(o),o})())}async login(t){return this.signInWithPassword(t)}async signUp(e){return s((async()=>{const s=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/signup`,n={email:e.email,password:e.password,data:e.user_data||{}},i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(n)});if(!i.ok){const e=await i.json();throw new t(e.msg||e.error_description||"Registration failed",i.status)}const a=await i.json(),o={token:a.access_token,refreshToken:a.refresh_token,expiresIn:a.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(a.expires_in||3600),user:this.D(a)};return o.token&&this.v(o),o})())}async register(t){return this.signUp(t)}async getSession(){try{if(!this.i||!this.t)return{data:null,error:null};const t=Math.floor(Date.now()/1e3);return(this.h||0)-t<30&&this.o&&(console.log("[DYPAI SDK] ⏳ getSession: Token próximo a expirar. Refrescando..."),await this.refreshSession().catch(t=>console.warn("Error refreshing session in getSession:",t))),{data:{access_token:this.i,refresh_token:this.o||void 0,token_type:"bearer",user:this.t},error:null}}catch(e){return{data:null,error:new t(e.message,500)}}}async getUser(){return s((async()=>{if(!this.i)throw new t("No hay sesión activa",401);const e=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/user`,s=await fetch(e,{headers:{Authorization:`Bearer ${this.i}`,...this.config.apiKey&&{apikey:this.config.apiKey}}});if(!s.ok)throw new t("Session invalid",s.status);const n=await s.json(),i=this.D(n);return this.$(i),i})())}async me(){return this.getUser()}async signInWithOAuth(t,e={}){return s((async()=>{const{redirectTo:s=window.location.href}=e,n=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/authorize?provider=${t}&redirect_to=${encodeURIComponent(s)}`;window.location.href=n})())}async signOut(){return s((async()=>{try{if(this.i){const t=this.config.baseUrl||"http://localhost:8000";await fetch(`${t}/auth/v1/logout`,{method:"POST",headers:{Authorization:`Bearer ${this.i}`,...this.config.apiKey&&{apikey:this.config.apiKey}}})}}finally{this.I()}})())}async logout(){return this.signOut()}async resetPasswordForEmail(e){return s((async()=>{const s=this.config.baseUrl||"http://localhost:8000",n=await fetch(`${s}/auth/v1/recover`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:e})});if(!n.ok)throw new t("Recovery failed",n.status);return await n.json()})())}async recoverPassword(t){return this.resetPasswordForEmail(t.email)}async refreshSession(){return console.log("[DYPAI SDK] 🔄 Intentando refrescar sesión con Refresh Token..."),s((async()=>{if(!this.o)throw new t("No hay refresh token disponible",401);const e=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/token?grant_type=refresh_token`,s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify({refresh_token:this.o})});if(!s.ok){const e=await s.json();throw 400!==s.status&&401!==s.status||this.I(),new t(e.msg||e.error_description||"Refresh session failed",s.status)}const n=await s.json(),i={token:n.access_token,refreshToken:n.refresh_token,expiresIn:n.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(n.expires_in||3600),user:this.D(n)};return this.v(i),i})())}async signInWithOtp(e){return s((async()=>{const s=this.config.baseUrl||"http://localhost:8000",n=await fetch(`${s}/auth/v1/otp`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(e)});if(!n.ok){const e=await n.json();throw new t(e.detail||"OTP request failed",n.status)}return await n.json()})())}async verifyOtp(e){return s((async()=>{const s=this.config.baseUrl||"http://localhost:8000",n=await fetch(`${s}/auth/v1/verify`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(e)});if(!n.ok){const e=await n.json();throw new t(e.detail||"OTP verification failed",n.status)}const i=await n.json(),a={token:i.access_token,refreshToken:i.refresh_token,expiresIn:i.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(i.expires_in||3600),user:this.D(i)};return a.token&&this.v(a),a})())}async updateUser(e){return s((async()=>{if(!this.i)throw new t("No hay sesión activa",401);const s=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/user`,n=await fetch(s,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.i}`,...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(e)});if(!n.ok){const e=await n.json();throw new t(e.detail||"Update user failed",n.status)}const i=await n.json(),a=this.D(i);return this.$(a),a})())}T(t){this.l&&(clearTimeout(this.l),this.l=null);const e=t-Math.floor(Date.now()/1e3)-60;e>0?(console.log(`[DYPAI SDK] ⏱️ Refresco de sesión programado en ${e} segundos.`),this.l=setTimeout(()=>{this.refreshSession().catch(t=>{console.error("Error in auto-refresh:",t)})},1e3*e)):this.o&&this.refreshSession().catch(()=>{})}D(t){if(!t)return console.warn("[DYPAI SDK] ⚠️ Intentando normalizar un usuario inexistente (data es null/undefined)"),{};const e=t.user||t,s=e.app_metadata||{},n=e.user_metadata||{};return{id:e.id,email:e.email,phone:e.phone,role:s.role||e.role,created_at:e.created_at,updated_at:e.updated_at,confirmed_at:e.confirmed_at,last_sign_in_at:e.last_sign_in_at,app_metadata:s,user_metadata:n,k:{name:s.role||"authenticated",weight:0},P:{app_id:"default"}}}v(t){t.token&&(this.i=t.token,this.o=t.refreshToken||null,this.h=t.expiresAt||null,this.$(t.user,t.token,t.refreshToken,t.expiresAt))}async $(t,e,s,n){this.t=t,e&&(this.i=e),void 0!==s&&(this.o=s||null),void 0!==n&&(this.h=n||null);try{if(this.i&&this.t){const t={access_token:this.i,refresh_token:this.o,expires_at:this.h,user:this.t};await this.storage.setItem(this.STORAGE_KEY,JSON.stringify(t))}else console.warn("[DYPAI SDK] ⚠️ _updateUser: No se guardó sesión porque falta token o user.",{hasToken:!!this.i,hasUser:!!this.t})}catch(t){console.error("[DYPAI SDK] ❌ Error guardando sesión en storage:",t)}this.h&&this.T(this.h);const i=e?"SIGNED_IN":"USER_UPDATED";this.A(i)}async I(){console.log("[DYPAI SDK] 🧹 Limpiando sesión del estado y storage."),this.i=null,this.o=null,this.t=null,this.h=null,this.l&&(clearTimeout(this.l),this.l=null);try{await this.storage.removeItem(this.STORAGE_KEY)}catch(t){console.error("[DYPAI SDK] ❌ Error eliminando sesión de storage:",t)}this.A()}async S(){console.log("[DYPAI SDK] 🔍 Iniciando recuperación de sesión...");try{const t=await this.storage.getItem(this.STORAGE_KEY);if(t){console.log("[DYPAI SDK] ✅ Sesión consolidada encontrada. Restaurando datos...");const e=JSON.parse(t);this.i=e.access_token,this.o=e.refresh_token,this.h=e.expires_at,this.t=e.user}else{console.log("[DYPAI SDK] ℹ️ No hay sesión consolidada. Buscando llaves antiguas para migración...");const t=this.STORAGE_KEY.replace("dypai-","").replace("-auth-session",""),e=await this.storage.getItem(`dypai-auth-token-${t}`),s=await this.storage.getItem(`dypai-auth-user-${t}`);if(!e||!s)return void console.log("[DYPAI SDK] ℹ️ No se encontró ninguna sesión (storage vacío).");{console.log("[DYPAI SDK] 🚚 Migración: Datos antiguos detectados. Consolidando...");const n=JSON.parse(s);this.i=e,this.t=n;const i=await this.storage.getItem(`dypai-auth-refresh-token-${t}`),a=await this.storage.getItem(`dypai-auth-expires-at-${t}`);this.o=i,this.h=a?parseInt(a,10):null,await this.$(n,this.i||void 0,this.o||void 0,this.h||void 0);const o=[`dypai-auth-token-${t}`,`dypai-auth-refresh-token-${t}`,`dypai-auth-user-${t}`,`dypai-auth-expires-at-${t}`];for(const t of o)try{await this.storage.removeItem(t)}catch(t){}console.log("[DYPAI SDK] ✨ Migración completada con éxito.")}}const e=Math.floor(Date.now()/1e3);console.log(`[DYPAI SDK] 🕒 Token expira en: ${this.h}. Ahora es: ${e}. Diferencia: ${(this.h||0)-e}s`),this.h&&this.h<=e&&this.o?(console.log("[DYPAI SDK] ⚠️ El Access Token ha caducado. Intentando refrescar sesión inmediatamente..."),await this.refreshSession().catch(t=>{console.error("[DYPAI SDK] ❌ El refresco inicial falló (posible error de red):",t)})):this.h&&(console.log("[DYPAI SDK] ✨ Sesión válida. Programando refresco futuro."),this.T(this.h)),this.A("SIGNED_IN")}catch(t){console.error("[DYPAI SDK] ❌ Error crítico durante la recuperación de sesión:",t)}}_(){return this.i&&this.t?{access_token:this.i,refresh_token:this.o||void 0,expires_at:this.h||void 0,token_type:"bearer",user:this.t}:null}A(t="USER_UPDATED"){const e=this._();console.log(`[DYPAI SDK] 📢 Notificando a ${this.u.length} suscriptores: Evento=${t} (Sesión activa: ${!!e})`),this.u.forEach(s=>s(t,e))}handleSessionExpired(){this.I()}}class i{constructor(t){this.api=t}from(t){return new a(t,this.api)}}class a{constructor(t,e){this.table=t,this.api=e}async select(t={}){return this.api.get(this.table,{params:t})}async insert(t){return this.api.post(this.table,t)}async update(t,e){return this.api.patch(`${this.table}/${t}`,e)}async delete(t){return this.api.delete(`${this.table}/${t}`)}}async function o(e){try{return{data:await e,error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e.message||"Error desconocido",e.status||500)}}}class r{constructor(t){this.api=t}async list(t={}){return o(this.api.get("admin/users",{params:t}))}async create(t){return o(this.api.post("admin/users",{body:t}))}async update(t,e){return o(this.api.put(`admin/users/${t}`,{body:e}))}async delete(t){return o(this.api.delete(`admin/users/${t}`))}}let c=null;function h(t){c=t}const u=t=>{const{title:e,description:s,variant:n="default"}=t,i=`${"error"===n?"❌":"success"===n?"✅":"warning"===n?"⚠️":"info"===n?"ℹ️":"📢"} ${e}${s?`: ${s}`:""}`;"error"===n?console.error(i):"warning"===n?console.warn(i):console.log(i)};function l(){const t=c||u;return{toast:t,toastSuccess:(e,s)=>t({title:e,description:s,variant:"success"}),toastError:(e,s)=>t({title:e,description:s,variant:"error"}),toastWarning:(e,s)=>t({title:e,description:s,variant:"warning"}),toastInfo:(e,s)=>t({title:e,description:s,variant:"info"})}}const d=t=>(c||u)(t),p=(t,e)=>d({title:t,description:e,variant:"success"}),f=(t,e)=>d({title:t,description:e,variant:"error"}),y=(t,e)=>d({title:t,description:e,variant:"warning"}),w=(t,e)=>d({title:t,description:e,variant:"info"});let S={},g=null;function m(t){g=t}function _(t){S={...S,...t}}const D=new Map;function v(){let t=S;try{const{getGlobalConfig:e}=require("../config/global-config");t={...e(),...S}}catch(t){}return t}async function $(e,s,n,i,a,o,r,c){const h=v(),u=c||null;if(!u&&!n.startsWith("http"))throw new Error("Base URL no definida. Usa createClient(url, key).");if("string"!=typeof n||!n.trim())throw new Error("Endpoint debe ser un string válido");let l;if(n.startsWith("http"))l=n;else{const t=u.replace(/\/+$/,"");l=n.startsWith("/")?t+n:t+"/api/v0/"+n}if(o&&Object.keys(o).length>0){const t=new URLSearchParams,e=(s,n)=>{null!=n&&(Array.isArray(n)?n.forEach((t,n)=>e(`${s}[${n}]`,t)):"object"==typeof n?Object.entries(n).forEach(([t,n])=>e(`${s}[${t}]`,n)):t.append(s,String(n)))};Object.entries(o).forEach(([t,s])=>e(t,s));const s=t.toString();s&&(l+=`?${s}`)}const p="GET"===s?`${s}:${l}:${JSON.stringify(i)}`:null;if(p&&D.has(p))return D.get(p);const f=i instanceof FormData,y={...h.headers||{},...f?{}:{"Content-Type":"application/json"},...e&&{Authorization:`Bearer ${e}`},...r&&{"x-api-key":r}},w={method:s,headers:y,credentials:"include"};i&&"GET"!==s&&"DELETE"!==s&&(w.body=f?i:JSON.stringify(i));const S=h.fetch||("undefined"!=typeof window?window.fetch.bind(window):fetch);if(!S)throw new Error("Fetch no disponible.");const g=(async()=>{const e=v().toast||d,u=(void 0!==a?a:!1!==h.showToasts)&&e;try{const d=await S(l,w);if(!d.ok){let l,p="Error en la petición";try{const t=await d.text();try{const e=JSON.parse(t);l=e,p=e.message||e.msg||e.error_description||e.error||p}catch{t.length<200&&(p=t)}}catch{}if(401===d.status){if(h.onTokenExpired){console.log("[DYPAI SDK] 🔄 Token caducado detectado. Intentando refrescar...");const t=await h.onTokenExpired();if(t)return console.log("[DYPAI SDK] ✅ Token refrescado con éxito. Reintentando petición..."),$(t,s,n,i,a,o,r,c)}h.onUnauthorized&&h.onUnauthorized()}throw u&&e({title:"Error",description:p,variant:"error"}),new t(p,d.status,void 0,l)}!u||"POST"!==s&&"PUT"!==s&&"PATCH"!==s&&"DELETE"!==s||e({title:"Éxito",description:"Operación completada",variant:"success"});const p=d.headers.get("content-type")||"";return p.includes("application/pdf")||p.includes("image/")||p.includes("audio/")||p.includes("video/")||p.includes("application/octet-stream")||p.includes("application/zip")||p.includes("application/vnd.openxmlformats-officedocument")?await d.blob():p.includes("application/json")?await d.json():await d.text()}finally{p&&D.delete(p)}})();return p&&D.set(p,g),g}function I(t,e){return async(s,n,i)=>{const a=t();let o,r={};n&&"object"==typeof n&&("token"in n||"params"in n||"apiKey"in n)?(r=n,o=r.body):(o=n,r=i||{});const c=r.token||a.token||(g?g():"")||"",h=r.apiKey||a.apiKey;return $(c,e,s,o,r.showToasts,r.params,h,a.baseUrl)}}function T(t){const e=()=>"function"==typeof t?t():t;return{get:k(e,"GET"),post:k(e,"POST"),put:k(e,"PUT"),patch:k(e,"PATCH"),delete:k(e,"DELETE")}}function k(t,e){return async(s,n,i)=>{const a=t(),o=await async function(t,e){let s,n={};t&&"object"==typeof t&&("token"in t||"params"in t||"apiKey"in t)?(n=t,s=n.body):(s=t,n=e||{});let i=n.token;return!i&&g&&(i=g()||""),i=i||"",{token:i,apiKey:n.apiKey,body:s,params:n.params,showToasts:n.showToasts}}(n,i),r=o.token||a.token||"",c=o.apiKey||a.apiKey;return $(r,e,s,o.body,o.showToasts,o.params,c,a.baseUrl)}}class P{constructor(t){this.api=t}from(t){return new b(t,this.api)}}class b{constructor(t,e){this.bucketName=t,this.api=e}async upload(e,s,n={}){return A(async()=>{const i=n.uploadEndpoint||`storage_upload_${this.bucketName}`,a={file_path:e,...n.params},o=await async function(e,s,n,i){const a=await e.post(s,{file_path:n.name,content_type:n.type||"application/octet-stream",size_bytes:n.size,confirm:!1,...i?.params||{}});if(!a||!a.upload_url)throw new t("El workflow no devolvió una URL de subida válida (¿falta get_upload_url?)",400);const{upload_url:o,method:r="PUT",headers:c={},file_path:h}=a;i?.onProgress&&i.onProgress(10);const u=await fetch(o,{method:r,headers:{"Content-Type":n.type||"application/octet-stream",...c},body:n});if(!u.ok)throw new t("Error en la subida directa a la nube",u.status);i?.onProgress&&i.onProgress(90);const l=i?.confirmEndpoint||s,d=await e.post(l,{...i?.params,bucket:a.bucket||i?.params?.bucket,file_path:h,filename:n.name,content_type:n.type||"application/octet-stream",size_bytes:n.size,confirm:!0});return i?.onProgress&&i.onProgress(100),d}(this.api,i,s,{confirmEndpoint:n.confirmEndpoint,params:a,onProgress:n.onProgress});return o})}async download(t,e={}){return A(async()=>{const s=e.downloadEndpoint||`storage_download_${this.bucketName}`,n={bucket:this.bucketName,file_path:t,...e.params};await async function(t,e,s,n){const i=n?.method,a=await("GET"===i?t.get(e,{params:n?.params}):t.post(e,s,{params:n?.params}));if(a instanceof Blob){const t=window.URL.createObjectURL(a),e=document.createElement("a");e.href=t,e.download=n?.fileName||"archivo-descargado",document.body.appendChild(e),e.click(),window.URL.revokeObjectURL(t),document.body.removeChild(e)}else if(a&&"object"==typeof a&&("url"in a||"signed_url"in a||"signedUrl"in a)){const t=a.url||a.signed_url||a.signedUrl;"string"==typeof t&&window.open(t,"_blank")}}(this.api,s,n,{fileName:e.fileName,method:e.method||"POST"})})}async createSignedUrl(t,e=15,s={}){return A(async()=>{const n=s.endpoint||`storage_signed_url_${this.bucketName}`,i={bucket:this.bucketName,file_path:t,expires_minutes:e,...s.params},a=await this.api.post(n,i);return{signedUrl:a.signed_url||a.signedUrl||a.url,expiresIn:a.expires_in||a.expiresIn||e,path:t}})}getPublicUrl(t){const e=t.replace(/^\/+/,"");return{data:{publicUrl:`${this.api.baseUrl||""}/storage/v1/render/public/${this.bucketName}/${e}`}}}async list(t="",e={}){return A(async()=>{const s=e.endpoint||`storage_list_${this.bucketName}`,n={bucket:this.bucketName,prefix:t||e.prefix||"",limit:e.limit,offset:e.offset,sort_by:e.sortBy,order:e.order,search:e.search,...e.params},i=await this.api.post(s,n);return{data:i.data||i.files||[],total:i.total,hasMore:i.has_more||i.hasMore||!1}})}async remove(t,e={}){return A(async()=>{const s=e.endpoint||`storage_delete_${this.bucketName}`,n=Array.isArray(t)?t:[t],i={bucket:this.bucketName,paths:n,...e.params};return await this.api.post(s,i),{success:!0}})}async move(t,e,s={}){return A(async()=>{const n=s.endpoint||`storage_move_${this.bucketName}`,i={bucket:this.bucketName,from_path:t,to_path:e,...s.params};return await this.api.post(n,i),{success:!0}})}async copy(t,e,s={}){return A(async()=>{const n=s.endpoint||`storage_copy_${this.bucketName}`,i={bucket:this.bucketName,from_path:t,to_path:e,...s.params};return await this.api.post(n,i),{success:!0}})}}async function A(e){try{return{data:await e(),error:null}}catch(e){return{data:null,error:e instanceof t?e:new t(e.message||"Error de storage",e.status||500)}}}class E{constructor(t){const{baseUrl:e,apiKey:s}=t,a=t.auth?.storageKey||t.storageKey;t.global&&function(t){S={...S,...t}}(t.global),this.auth=new n({baseUrl:e,apiKey:s,storageKey:a,storage:t.auth?.storage,autoRefreshToken:t.auth?.autoRefreshToken,persistSession:t.auth?.persistSession},null);const o={get:I(c=()=>({token:this.auth.token,apiKey:s,baseUrl:e}),"GET"),post:I(c,"POST"),put:I(c,"PUT"),patch:I(c,"PATCH"),delete:I(c,"DELETE")};var c;this.auth.api=o,this.db=new i(o),this.users=new r(o),this.storage=new P(o),this.api=T(()=>({token:this.auth.token||"",apiKey:s,baseUrl:e})),m(()=>this.auth.token),_({onTokenExpired:async()=>{const t=await this.auth.refreshSession();return t.data?.token||null},onUnauthorized:()=>this.auth.handleSessionExpired()})}from(t){return this.db.from(t)}async me(){return this.auth.getUser()}}function K(t,e,s){if(!t||!e)throw new Error("createClient() requiere URL y API Key");return new E({baseUrl:t,apiKey:e,...s})}let O={};function U(t){O={...O,toast:d,...t},x()}function N(){return{...O}}function x(){try{const{configureApiService:t}=require("../services/ApiService");t(O)}catch(t){}}function Y(){O={},x()}async function C(){x()}const R={name:"@dypai-ai/client-sdk",version:"0.0.1",description:"Cliente JavaScript para Dypai Engine",features:["API REST autenticada","Smart Storage (Upload/Download delegado)","Gestión de usuarios (Admin)"]};export{E as DypaiClient,R as PACKAGE_INFO,$ as callApi,_ as configureApiService,U as configureDypaiServices,T as createApiClient,K as createClient,N as getGlobalConfig,C as reloadDypaiConfig,Y as resetGlobalConfig,h as setToastFunction,m as setTokenProvider,d as toast,f as toastError,w as toastInfo,p as toastSuccess,y as toastWarning,l as useToast};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class DypaiError extends Error{constructor(t,e=500,s,i){super(t),this.status=e,this.code=s,this.details=i,this.name="DypaiError"}}const localStorageAdapter={getItem:t=>"undefined"==typeof window?null:window.localStorage.getItem(t),setItem:(t,e)=>{if("undefined"!=typeof window)try{window.localStorage.setItem(t,e)}catch(t){console.error("[DYPAI SDK] ❌ Error crítico guardando en localStorage (¿Quota/Permisos?):",t)}},removeItem:t=>{if("undefined"!=typeof window)try{window.localStorage.removeItem(t)}catch(t){}}};async function wrapAuthResponse(t){try{return{data:await t,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t.message||"Error de autenticación",t.status||400)}}}class AuthModule{constructor(t,e=null){this.config=t,this.t=null,this.i=null,this.o=null,this.h=null,this.l=[],this.u=null,this.storage=t.storage||localStorageAdapter;const s=t.storageKey||this.deriveStorageKey(t.apiKey);console.log(`[DYPAI SDK] 🛠️ Inicializando AuthModule (apiKey: ${t.apiKey?.substring(0,8)}..., storageKey: ${s})`),this.STORAGE_KEY=`dypai-${s}-auth-session`,this.p=this.S().then(()=>{this.i&&this.getUser().catch(()=>{})}),"undefined"!=typeof window&&(window.addEventListener("visibilitychange",this.D.bind(this)),window.addEventListener("focus",this.D.bind(this)))}async D(){"undefined"!=typeof document&&"visible"===document.visibilityState&&(console.log("[DYPAI SDK] 👁️ Ventana visible. Verificando estado de sesión..."),await this.S())}deriveStorageKey(t){return t?t.substring(0,8):"default"}get user(){return this.t}get token(){return this.i}onAuthStateChange(t){return console.log("[DYPAI SDK] 👂 Nuevo suscriptor añadido a onAuthStateChange"),this.l.push(t),this.p.then(()=>{const e=this.m(),s=e?"SIGNED_IN":"INITIAL_SESSION";console.log(`[DYPAI SDK] 📣 Notificando estado inicial a nuevo suscriptor: ${s} (Sesión activa: ${!!e})`),t(s,e)}).catch(e=>{console.error("[DYPAI SDK] ❌ Error esperando recuperación de sesión para suscriptor:",e),t("INITIAL_SESSION",null)}),{data:{subscription:{unsubscribe:()=>{this.l=this.l.filter(e=>e!==t)}}}}}async signInWithPassword(t){return wrapAuthResponse((async()=>{const e=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/token?grant_type=password`,s={email:t.email||t.identifier||"",password:t.password},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(s)});if(!i.ok){const t=await i.json();throw new DypaiError(t.msg||t.error_description||"Login failed",i.status)}const n=await i.json(),o={token:n.access_token,refreshToken:n.refresh_token,expiresIn:n.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(n.expires_in||3600),user:this.v(n)};return this.A(o),o})())}async login(t){return this.signInWithPassword(t)}async signUp(t){return wrapAuthResponse((async()=>{const e=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/signup`,s={email:t.email,password:t.password,data:t.user_data||{}},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(s)});if(!i.ok){const t=await i.json();throw new DypaiError(t.msg||t.error_description||"Registration failed",i.status)}const n=await i.json(),o={token:n.access_token,refreshToken:n.refresh_token,expiresIn:n.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(n.expires_in||3600),user:this.v(n)};return o.token&&this.A(o),o})())}async register(t){return this.signUp(t)}async getSession(){try{if(!this.i||!this.t)return{data:null,error:null};const t=Math.floor(Date.now()/1e3);return(this.h||0)-t<30&&this.o&&(console.log("[DYPAI SDK] ⏳ getSession: Token próximo a expirar. Refrescando..."),await this.refreshSession().catch(t=>console.warn("Error refreshing session in getSession:",t))),{data:{access_token:this.i,refresh_token:this.o||void 0,token_type:"bearer",user:this.t},error:null}}catch(t){return{data:null,error:new DypaiError(t.message,500)}}}async getUser(){return wrapAuthResponse((async()=>{if(!this.i)throw new DypaiError("No hay sesión activa",401);const t=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/user`,e=await fetch(t,{headers:{Authorization:`Bearer ${this.i}`,...this.config.apiKey&&{apikey:this.config.apiKey}}});if(!e.ok)throw new DypaiError("Session invalid",e.status);const s=await e.json(),i=this.v(s);return this.T(i),i})())}async me(){return this.getUser()}async signInWithOAuth(t,e={}){return wrapAuthResponse((async()=>{const{redirectTo:s=window.location.href}=e,i=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/authorize?provider=${t}&redirect_to=${encodeURIComponent(s)}`;window.location.href=i})())}async signOut(){return wrapAuthResponse((async()=>{try{if(this.i){const t=this.config.baseUrl||"http://localhost:8000";await fetch(`${t}/auth/v1/logout`,{method:"POST",headers:{Authorization:`Bearer ${this.i}`,...this.config.apiKey&&{apikey:this.config.apiKey}}})}}finally{this._()}})())}async logout(){return this.signOut()}async resetPasswordForEmail(t){return wrapAuthResponse((async()=>{const e=this.config.baseUrl||"http://localhost:8000",s=await fetch(`${e}/auth/v1/recover`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t})});if(!s.ok)throw new DypaiError("Recovery failed",s.status);return await s.json()})())}async recoverPassword(t){return this.resetPasswordForEmail(t.email)}async refreshSession(){return console.log("[DYPAI SDK] 🔄 Intentando refrescar sesión con Refresh Token..."),wrapAuthResponse((async()=>{if(!this.o)throw new DypaiError("No hay refresh token disponible",401);const t=`${this.config.baseUrl||"http://localhost:8000"}/api/v0/auth/token?grant_type=refresh_token`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify({refresh_token:this.o})});if(!e.ok){const t=await e.json();throw 400!==e.status&&401!==e.status||this._(),new DypaiError(t.msg||t.error_description||"Refresh session failed",e.status)}const s=await e.json(),i={token:s.access_token,refreshToken:s.refresh_token,expiresIn:s.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(s.expires_in||3600),user:this.v(s)};return this.A(i),i})())}async signInWithOtp(t){return wrapAuthResponse((async()=>{const e=this.config.baseUrl||"http://localhost:8000",s=await fetch(`${e}/auth/v1/otp`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(t)});if(!s.ok){const t=await s.json();throw new DypaiError(t.detail||"OTP request failed",s.status)}return await s.json()})())}async verifyOtp(t){return wrapAuthResponse((async()=>{const e=this.config.baseUrl||"http://localhost:8000",s=await fetch(`${e}/auth/v1/verify`,{method:"POST",headers:{"Content-Type":"application/json",...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(t)});if(!s.ok){const t=await s.json();throw new DypaiError(t.detail||"OTP verification failed",s.status)}const i=await s.json(),n={token:i.access_token,refreshToken:i.refresh_token,expiresIn:i.expires_in,expiresAt:Math.floor(Date.now()/1e3)+(i.expires_in||3600),user:this.v(i)};return n.token&&this.A(n),n})())}async updateUser(t){return wrapAuthResponse((async()=>{if(!this.i)throw new DypaiError("No hay sesión activa",401);const e=`${this.config.baseUrl||"http://localhost:8000"}/auth/v1/user`,s=await fetch(e,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.i}`,...this.config.apiKey&&{apikey:this.config.apiKey}},body:JSON.stringify(t)});if(!s.ok){const t=await s.json();throw new DypaiError(t.detail||"Update user failed",s.status)}const i=await s.json(),n=this.v(i);return this.T(n),n})())}k(t){this.u&&(clearTimeout(this.u),this.u=null);const e=t-Math.floor(Date.now()/1e3)-60;e>0?(console.log(`[DYPAI SDK] ⏱️ Refresco de sesión programado en ${e} segundos.`),this.u=setTimeout(()=>{this.refreshSession().catch(t=>{console.error("Error in auto-refresh:",t)})},1e3*e)):this.o&&this.refreshSession().catch(()=>{})}v(t){if(!t)return console.warn("[DYPAI SDK] ⚠️ Intentando normalizar un usuario inexistente (data es null/undefined)"),{};const e=t.user||t,s=e.app_metadata||{},i=e.user_metadata||{};return{id:e.id,email:e.email,phone:e.phone,role:s.role||e.role,created_at:e.created_at,updated_at:e.updated_at,confirmed_at:e.confirmed_at,last_sign_in_at:e.last_sign_in_at,app_metadata:s,user_metadata:i,P:{name:s.role||"authenticated",weight:0},$:{app_id:"default"}}}A(t){t.token&&(this.i=t.token,this.o=t.refreshToken||null,this.h=t.expiresAt||null,this.T(t.user,t.token,t.refreshToken,t.expiresAt))}async T(t,e,s,i){this.t=t,e&&(this.i=e),void 0!==s&&(this.o=s||null),void 0!==i&&(this.h=i||null);try{if(this.i&&this.t){const t={access_token:this.i,refresh_token:this.o,expires_at:this.h,user:this.t};await this.storage.setItem(this.STORAGE_KEY,JSON.stringify(t))}else console.warn("[DYPAI SDK] ⚠️ _updateUser: No se guardó sesión porque falta token o user.",{hasToken:!!this.i,hasUser:!!this.t})}catch(t){console.error("[DYPAI SDK] ❌ Error guardando sesión en storage:",t)}this.h&&this.k(this.h);const n=e?"SIGNED_IN":"USER_UPDATED";this.I(n)}async _(){console.log("[DYPAI SDK] 🧹 Limpiando sesión del estado y storage."),this.i=null,this.o=null,this.t=null,this.h=null,this.u&&(clearTimeout(this.u),this.u=null);try{await this.storage.removeItem(this.STORAGE_KEY)}catch(t){console.error("[DYPAI SDK] ❌ Error eliminando sesión de storage:",t)}this.I()}async S(){console.log("[DYPAI SDK] 🔍 Iniciando recuperación de sesión...");try{const t=await this.storage.getItem(this.STORAGE_KEY);if(t){console.log("[DYPAI SDK] ✅ Sesión consolidada encontrada. Restaurando datos...");const e=JSON.parse(t);this.i=e.access_token,this.o=e.refresh_token,this.h=e.expires_at,this.t=e.user}else{console.log("[DYPAI SDK] ℹ️ No hay sesión consolidada. Buscando llaves antiguas para migración...");const t=this.STORAGE_KEY.replace("dypai-","").replace("-auth-session",""),e=await this.storage.getItem(`dypai-auth-token-${t}`),s=await this.storage.getItem(`dypai-auth-user-${t}`);if(!e||!s)return void console.log("[DYPAI SDK] ℹ️ No se encontró ninguna sesión (storage vacío).");{console.log("[DYPAI SDK] 🚚 Migración: Datos antiguos detectados. Consolidando...");const i=JSON.parse(s);this.i=e,this.t=i;const n=await this.storage.getItem(`dypai-auth-refresh-token-${t}`),o=await this.storage.getItem(`dypai-auth-expires-at-${t}`);this.o=n,this.h=o?parseInt(o,10):null,await this.T(i,this.i||void 0,this.o||void 0,this.h||void 0);const r=[`dypai-auth-token-${t}`,`dypai-auth-refresh-token-${t}`,`dypai-auth-user-${t}`,`dypai-auth-expires-at-${t}`];for(const t of r)try{await this.storage.removeItem(t)}catch(t){}console.log("[DYPAI SDK] ✨ Migración completada con éxito.")}}const e=Math.floor(Date.now()/1e3);console.log(`[DYPAI SDK] 🕒 Token expira en: ${this.h}. Ahora es: ${e}. Diferencia: ${(this.h||0)-e}s`),this.h&&this.h<=e&&this.o?(console.log("[DYPAI SDK] ⚠️ El Access Token ha caducado. Intentando refrescar sesión inmediatamente..."),await this.refreshSession().catch(t=>{console.error("[DYPAI SDK] ❌ El refresco inicial falló (posible error de red):",t)})):this.h&&(console.log("[DYPAI SDK] ✨ Sesión válida. Programando refresco futuro."),this.k(this.h)),this.I("SIGNED_IN")}catch(t){console.error("[DYPAI SDK] ❌ Error crítico durante la recuperación de sesión:",t)}}m(){return this.i&&this.t?{access_token:this.i,refresh_token:this.o||void 0,expires_at:this.h||void 0,token_type:"bearer",user:this.t}:null}I(t="USER_UPDATED"){const e=this.m();console.log(`[DYPAI SDK] 📢 Notificando a ${this.l.length} suscriptores: Evento=${t} (Sesión activa: ${!!e})`),this.l.forEach(s=>s(t,e))}handleSessionExpired(){this._()}}class DataModule{constructor(t){this.api=t}from(t){return new QueryBuilder(t,this.api)}}class QueryBuilder{constructor(t,e){this.table=t,this.api=e}async select(t={}){return this.api.get(this.table,{params:t})}async insert(t){return this.api.post(this.table,t)}async update(t,e){return this.api.patch(`${this.table}/${t}`,e)}async delete(t){return this.api.delete(`${this.table}/${t}`)}}async function wrapResponse(t){try{return{data:await t,error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t.message||"Error desconocido",t.status||500)}}}class UsersModule{constructor(t){this.api=t}async list(t={}){return wrapResponse(this.api.get("admin/users",{params:t}))}async create(t){return wrapResponse(this.api.post("admin/users",{body:t}))}async update(t,e){return wrapResponse(this.api.put(`admin/users/${t}`,{body:e}))}async delete(t){return wrapResponse(this.api.delete(`admin/users/${t}`))}}let customToastFunction=null;const fallbackToast=t=>{const{title:e,description:s,variant:i="default"}=t,n=`${"error"===i?"❌":"success"===i?"✅":"warning"===i?"⚠️":"info"===i?"ℹ️":"📢"} ${e}${s?`: ${s}`:""}`;"error"===i?console.error(n):"warning"===i?console.warn(n):console.log(n)},toast=t=>(customToastFunction||fallbackToast)(t);let serviceConfig={},globalTokenProvider=null;function setTokenProvider(t){globalTokenProvider=t}function configureApiService(t){serviceConfig={...serviceConfig,...t}}const pendingRequests=new Map;function getCompleteConfig(){let t=serviceConfig;try{const{getGlobalConfig:e}=require("../config/global-config");t={...e(),...serviceConfig}}catch(t){}return t}async function callApi(t,e,s,i,n,o,r,a){const c=getCompleteConfig(),h=a||null;if(!h&&!s.startsWith("http"))throw new Error("Base URL no definida. Usa createClient(url, key).");if("string"!=typeof s||!s.trim())throw new Error("Endpoint debe ser un string válido");let l;if(s.startsWith("http"))l=s;else{const t=h.replace(/\/+$/,"");l=s.startsWith("/")?t+s:t+"/api/v0/"+s}if(o&&Object.keys(o).length>0){const t=new URLSearchParams,e=(s,i)=>{null!=i&&(Array.isArray(i)?i.forEach((t,i)=>e(`${s}[${i}]`,t)):"object"==typeof i?Object.entries(i).forEach(([t,i])=>e(`${s}[${t}]`,i)):t.append(s,String(i)))};Object.entries(o).forEach(([t,s])=>e(t,s));const s=t.toString();s&&(l+=`?${s}`)}const u="GET"===e?`${e}:${l}:${JSON.stringify(i)}`:null;if(u&&pendingRequests.has(u))return pendingRequests.get(u);const p=i instanceof FormData,d={...c.headers||{},...p?{}:{"Content-Type":"application/json"},...t&&{Authorization:`Bearer ${t}`},...r&&{"x-api-key":r}},y={method:e,headers:d,credentials:"include"};i&&"GET"!==e&&"DELETE"!==e&&(y.body=p?i:JSON.stringify(i));const f=c.fetch||("undefined"!=typeof window?window.fetch.bind(window):fetch);if(!f)throw new Error("Fetch no disponible.");const w=(async()=>{const t=getCompleteConfig().toast||toast,h=(void 0!==n?n:!1!==c.showToasts)&&t;try{const u=await f(l,y);if(!u.ok){let l,p="Error en la petición";try{const t=await u.text();try{const e=JSON.parse(t);l=e,p=e.message||e.msg||e.error_description||e.error||p}catch{t.length<200&&(p=t)}}catch{}if(401===u.status){if(c.onTokenExpired){console.log("[DYPAI SDK] 🔄 Token caducado detectado. Intentando refrescar...");const t=await c.onTokenExpired();if(t)return console.log("[DYPAI SDK] ✅ Token refrescado con éxito. Reintentando petición..."),callApi(t,e,s,i,n,o,r,a)}c.onUnauthorized&&c.onUnauthorized()}throw h&&t({title:"Error",description:p,variant:"error"}),new DypaiError(p,u.status,void 0,l)}!h||"POST"!==e&&"PUT"!==e&&"PATCH"!==e&&"DELETE"!==e||t({title:"Éxito",description:"Operación completada",variant:"success"});const p=u.headers.get("content-type")||"";return p.includes("application/pdf")||p.includes("image/")||p.includes("audio/")||p.includes("video/")||p.includes("application/octet-stream")||p.includes("application/zip")||p.includes("application/vnd.openxmlformats-officedocument")?await u.blob():p.includes("application/json")?await u.json():await u.text()}finally{u&&pendingRequests.delete(u)}})();return u&&pendingRequests.set(u,w),w}function createMethod(t,e){return async(s,i,n)=>{const o=t();let r,a={};i&&"object"==typeof i&&("token"in i||"params"in i||"apiKey"in i)?(a=i,r=a.body):(r=i,a=n||{});const c=a.token||o.token||(globalTokenProvider?globalTokenProvider():"")||"",h=a.apiKey||o.apiKey;return callApi(c,e,s,r,a.showToasts,a.params,h,o.baseUrl)}}function createApiClient(t){const e=()=>"function"==typeof t?t():t;return{get:createMethodFromCtx(e,"GET"),post:createMethodFromCtx(e,"POST"),put:createMethodFromCtx(e,"PUT"),patch:createMethodFromCtx(e,"PATCH"),delete:createMethodFromCtx(e,"DELETE")}}function createMethodFromCtx(t,e){return async(s,i,n)=>{const o=t(),r=await async function(t,e){let s,i={};t&&"object"==typeof t&&("token"in t||"params"in t||"apiKey"in t)?(i=t,s=i.body):(s=t,i=e||{});let n=i.token;return!n&&globalTokenProvider&&(n=globalTokenProvider()||""),n=n||"",{token:n,apiKey:i.apiKey,body:s,params:i.params,showToasts:i.showToasts}}(i,n),a=r.token||o.token||"",c=r.apiKey||o.apiKey;return callApi(a,e,s,r.body,r.showToasts,r.params,c,o.baseUrl)}}class StorageModule{constructor(t){this.api=t}from(t){return new StorageBucketBuilder(t,this.api)}}class StorageBucketBuilder{constructor(t,e){this.bucketName=t,this.api=e}async upload(t,e,s={}){return wrapStorageResponse(async()=>{const i=s.uploadEndpoint||`storage_upload_${this.bucketName}`,n={file_path:t,...s.params},o=await async function(t,e,s,i){const n=await t.post(e,{file_path:s.name,content_type:s.type||"application/octet-stream",size_bytes:s.size,confirm:!1,...i?.params||{}});if(!n||!n.upload_url)throw new DypaiError("El workflow no devolvió una URL de subida válida (¿falta get_upload_url?)",400);const{upload_url:o,method:r="PUT",headers:a={},file_path:c}=n;i?.onProgress&&i.onProgress(10);const h=await fetch(o,{method:r,headers:{"Content-Type":s.type||"application/octet-stream",...a},body:s});if(!h.ok)throw new DypaiError("Error en la subida directa a la nube",h.status);i?.onProgress&&i.onProgress(90);const l=i?.confirmEndpoint||e,u=await t.post(l,{...i?.params,bucket:n.bucket||i?.params?.bucket,file_path:c,filename:s.name,content_type:s.type||"application/octet-stream",size_bytes:s.size,confirm:!0});return i?.onProgress&&i.onProgress(100),u}(this.api,i,e,{confirmEndpoint:s.confirmEndpoint,params:n,onProgress:s.onProgress});return o})}async download(t,e={}){return wrapStorageResponse(async()=>{const s=e.downloadEndpoint||`storage_download_${this.bucketName}`,i={bucket:this.bucketName,file_path:t,...e.params};await async function(t,e,s,i){const n=i?.method,o=await("GET"===n?t.get(e,{params:i?.params}):t.post(e,s,{params:i?.params}));if(o instanceof Blob){const t=window.URL.createObjectURL(o),e=document.createElement("a");e.href=t,e.download=i?.fileName||"archivo-descargado",document.body.appendChild(e),e.click(),window.URL.revokeObjectURL(t),document.body.removeChild(e)}else if(o&&"object"==typeof o&&("url"in o||"signed_url"in o||"signedUrl"in o)){const t=o.url||o.signed_url||o.signedUrl;"string"==typeof t&&window.open(t,"_blank")}}(this.api,s,i,{fileName:e.fileName,method:e.method||"POST"})})}async createSignedUrl(t,e=15,s={}){return wrapStorageResponse(async()=>{const i=s.endpoint||`storage_signed_url_${this.bucketName}`,n={bucket:this.bucketName,file_path:t,expires_minutes:e,...s.params},o=await this.api.post(i,n);return{signedUrl:o.signed_url||o.signedUrl||o.url,expiresIn:o.expires_in||o.expiresIn||e,path:t}})}getPublicUrl(t){const e=t.replace(/^\/+/,"");return{data:{publicUrl:`${this.api.baseUrl||""}/storage/v1/render/public/${this.bucketName}/${e}`}}}async list(t="",e={}){return wrapStorageResponse(async()=>{const s=e.endpoint||`storage_list_${this.bucketName}`,i={bucket:this.bucketName,prefix:t||e.prefix||"",limit:e.limit,offset:e.offset,sort_by:e.sortBy,order:e.order,search:e.search,...e.params},n=await this.api.post(s,i);return{data:n.data||n.files||[],total:n.total,hasMore:n.has_more||n.hasMore||!1}})}async remove(t,e={}){return wrapStorageResponse(async()=>{const s=e.endpoint||`storage_delete_${this.bucketName}`,i=Array.isArray(t)?t:[t],n={bucket:this.bucketName,paths:i,...e.params};return await this.api.post(s,n),{success:!0}})}async move(t,e,s={}){return wrapStorageResponse(async()=>{const i=s.endpoint||`storage_move_${this.bucketName}`,n={bucket:this.bucketName,from_path:t,to_path:e,...s.params};return await this.api.post(i,n),{success:!0}})}async copy(t,e,s={}){return wrapStorageResponse(async()=>{const i=s.endpoint||`storage_copy_${this.bucketName}`,n={bucket:this.bucketName,from_path:t,to_path:e,...s.params};return await this.api.post(i,n),{success:!0}})}}async function wrapStorageResponse(t){try{return{data:await t(),error:null}}catch(t){return{data:null,error:t instanceof DypaiError?t:new DypaiError(t.message||"Error de storage",t.status||500)}}}class DypaiClient{constructor(t){const{baseUrl:e,apiKey:s}=t,i=t.auth?.storageKey||t.storageKey;t.global&&function(t){serviceConfig={...serviceConfig,...t}}(t.global),this.auth=new AuthModule({baseUrl:e,apiKey:s,storageKey:i,storage:t.auth?.storage,autoRefreshToken:t.auth?.autoRefreshToken,persistSession:t.auth?.persistSession},null);const n={get:createMethod(o=()=>({token:this.auth.token,apiKey:s,baseUrl:e}),"GET"),post:createMethod(o,"POST"),put:createMethod(o,"PUT"),patch:createMethod(o,"PATCH"),delete:createMethod(o,"DELETE")};var o;this.auth.api=n,this.db=new DataModule(n),this.users=new UsersModule(n),this.storage=new StorageModule(n),this.api=createApiClient(()=>({token:this.auth.token||"",apiKey:s,baseUrl:e})),setTokenProvider(()=>this.auth.token),configureApiService({onTokenExpired:async()=>{const t=await this.auth.refreshSession();return t.data?.token||null},onUnauthorized:()=>this.auth.handleSessionExpired()})}from(t){return this.db.from(t)}async me(){return this.auth.getUser()}}let globalConfig={};function applyConfigToAllServices(){try{const{configureApiService:t}=require("../services/ApiService");t(globalConfig)}catch(t){}}exports.DypaiClient=DypaiClient,exports.PACKAGE_INFO={name:"@dypai-ai/client-sdk",version:"0.0.1",description:"Cliente JavaScript para Dypai Engine",features:["API REST autenticada","Smart Storage (Upload/Download delegado)","Gestión de usuarios (Admin)"]},exports.callApi=callApi,exports.configureApiService=configureApiService,exports.configureDypaiServices=function(t){globalConfig={...globalConfig,toast:toast,...t},applyConfigToAllServices()},exports.createApiClient=createApiClient,exports.createClient=function(t,e,s){if(!t||!e)throw new Error("createClient() requiere URL y API Key");return new DypaiClient({baseUrl:t,apiKey:e,...s})},exports.getGlobalConfig=function(){return{...globalConfig}},exports.reloadDypaiConfig=async function(){applyConfigToAllServices()},exports.resetGlobalConfig=function(){globalConfig={},applyConfigToAllServices()},exports.setToastFunction=function(t){customToastFunction=t},exports.setTokenProvider=setTokenProvider,exports.toast=toast,exports.toastError=(t,e)=>toast({title:t,description:e,variant:"error"}),exports.toastInfo=(t,e)=>toast({title:t,description:e,variant:"info"}),exports.toastSuccess=(t,e)=>toast({title:t,description:e,variant:"success"}),exports.toastWarning=(t,e)=>toast({title:t,description:e,variant:"warning"}),exports.useToast=function(){const t=customToastFunction||fallbackToast;return{toast:t,toastSuccess:(e,s)=>t({title:e,description:s,variant:"success"}),toastError:(e,s)=>t({title:e,description:s,variant:"error"}),toastWarning:(e,s)=>t({title:e,description:s,variant:"warning"}),toastInfo:(e,s)=>t({title:e,description:s,variant:"info"})}};
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dypai-ai/client-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Cliente JavaScript para Dypai Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.esm.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "rollup -c",
|
|
15
|
+
"build:watch": "rollup -c -w",
|
|
16
|
+
"dev": "NODE_ENV=development rollup -c -w",
|
|
17
|
+
"dev:watch": "NODE_ENV=development rollup -c -w",
|
|
18
|
+
"dev:test": "vite",
|
|
19
|
+
"type-check": "tsc --noEmit",
|
|
20
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
21
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
22
|
+
"test": "jest",
|
|
23
|
+
"test:watch": "jest --watch",
|
|
24
|
+
"clean": "rimraf dist",
|
|
25
|
+
"postbuild_DISABLED": "node scripts/obfuscate-dts.cjs",
|
|
26
|
+
"prepublishOnly": "npm run clean && npm run build && npm run type-check && node -e \"if(require('fs').existsSync('README.md.backup')) require('fs').unlinkSync('README.md.backup')\" && node -e \"require('fs').copyFileSync('README.md', 'README.md.backup')\" && node -e \"require('fs').writeFileSync('README.md', '# @dypai-ai/client-sdk\\n\\nCliente JavaScript para Dypai Engine.\\n\\nContacta al equipo de desarrollo para más información.')\"",
|
|
27
|
+
"postpublish": "node -e \"if(require('fs').existsSync('README.md.backup')) { require('fs').copyFileSync('README.md.backup', 'README.md'); require('fs').unlinkSync('README.md.backup'); }\"",
|
|
28
|
+
"publish:public": "npm publish --access public"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"api",
|
|
32
|
+
"http",
|
|
33
|
+
"client",
|
|
34
|
+
"service"
|
|
35
|
+
],
|
|
36
|
+
"author": "DYPAI Team",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": ">=16.8.0",
|
|
40
|
+
"react-dom": ">=16.8.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@rollup/plugin-commonjs": "^25.0.0",
|
|
44
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
45
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
46
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
47
|
+
"@types/jest": "^29.0.0",
|
|
48
|
+
"@types/react": "^18.0.0",
|
|
49
|
+
"@types/react-dom": "^18.0.0",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
51
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
52
|
+
"dotenv": "^17.2.2",
|
|
53
|
+
"eslint": "^8.0.0",
|
|
54
|
+
"eslint-plugin-react": "^7.0.0",
|
|
55
|
+
"eslint-plugin-react-hooks": "^4.0.0",
|
|
56
|
+
"jest": "^29.0.0",
|
|
57
|
+
"rimraf": "^5.0.0",
|
|
58
|
+
"rollup": "^4.0.0",
|
|
59
|
+
"rollup-plugin-dts": "^6.0.0",
|
|
60
|
+
"tslib": "^2.8.1",
|
|
61
|
+
"typescript": "^5.0.0",
|
|
62
|
+
"vite": "^7.1.5"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=16.0.0"
|
|
69
|
+
}
|
|
70
|
+
}
|