@nocios/crudify-components 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.
Files changed (53) hide show
  1. package/.github/workflows/test.yml +59 -0
  2. package/.nvmrc +1 -0
  3. package/README.md +398 -0
  4. package/README_DEPTH.md +1230 -0
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/coverage-final.json +85 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +506 -0
  10. package/coverage/prettify.css +1 -0
  11. package/coverage/prettify.js +2 -0
  12. package/coverage/sort-arrow-sprite.png +0 -0
  13. package/coverage/sorter.js +210 -0
  14. package/dist/CrudiaMarkdownField-C54-A_J3.d.mts +328 -0
  15. package/dist/CrudiaMarkdownField-C8HQh7s5.d.ts +328 -0
  16. package/dist/GlobalNotificationProvider-Zq18OkpI.d.mts +96 -0
  17. package/dist/GlobalNotificationProvider-Zq18OkpI.d.ts +96 -0
  18. package/dist/api-B4uXiHF0.d.mts +118 -0
  19. package/dist/api-B4uXiHF0.d.ts +118 -0
  20. package/dist/chunk-2XOTIEKS.js +1 -0
  21. package/dist/chunk-5HFI5CZ5.js +1 -0
  22. package/dist/chunk-CHDM7KGH.js +1 -0
  23. package/dist/chunk-HVTRRU4W.mjs +1 -0
  24. package/dist/chunk-JAPL7EZJ.mjs +1 -0
  25. package/dist/chunk-JNEWPO2J.mjs +1 -0
  26. package/dist/chunk-MFYHD6S5.js +1 -0
  27. package/dist/chunk-MGJZTOEM.mjs +1 -0
  28. package/dist/chunk-NBQH6QOU.mjs +1 -0
  29. package/dist/chunk-NSV6ECYO.js +1 -0
  30. package/dist/chunk-PNI3ZBZV.js +1 -0
  31. package/dist/chunk-U4RS66TB.mjs +1 -0
  32. package/dist/components.d.mts +24 -0
  33. package/dist/components.d.ts +24 -0
  34. package/dist/components.js +1 -0
  35. package/dist/components.mjs +1 -0
  36. package/dist/errorTranslation-DGdrMidg.d.ts +143 -0
  37. package/dist/errorTranslation-qwwQTvCO.d.mts +143 -0
  38. package/dist/hooks.d.mts +6 -0
  39. package/dist/hooks.d.ts +6 -0
  40. package/dist/hooks.js +1 -0
  41. package/dist/hooks.mjs +1 -0
  42. package/dist/index-BUKX3duW.d.ts +854 -0
  43. package/dist/index-Y9tTsinC.d.mts +854 -0
  44. package/dist/index.d.mts +1274 -0
  45. package/dist/index.d.ts +1274 -0
  46. package/dist/index.js +6 -0
  47. package/dist/index.mjs +6 -0
  48. package/dist/utils.d.mts +175 -0
  49. package/dist/utils.d.ts +175 -0
  50. package/dist/utils.js +1 -0
  51. package/dist/utils.mjs +1 -0
  52. package/package.json +88 -0
  53. package/vitest.config.ts +28 -0
@@ -0,0 +1,1274 @@
1
+ import crudify__default from '@nocios/crudify-sdk';
2
+ export * from '@nocios/crudify-sdk';
3
+ export { default as crudify } from '@nocios/crudify-sdk';
4
+ import { d as CrudifyLoginConfig, l as PasswordRule, E as EvaluatedPasswordRule } from './CrudiaMarkdownField-C54-A_J3.mjs';
5
+ export { B as BoxScreenType, a as CrudiaAutoGenerate, i as CrudiaAutoGenerateProps, b as CrudiaFileField, o as CrudiaFileFieldDeletionHandlers, j as CrudiaFileFieldProps, c as CrudiaMarkdownField, k as CrudiaMarkdownFieldProps, C as CrudifyLogin, e as CrudifyLoginProps, f as CrudifyLoginTranslations, D as DEFAULT_PASSWORD_RULES, L as LoginComponent, m as POLICY_ACTIONS, n as PREFERRED_POLICY_ORDER, p as PasswordRuleType, P as Policies, h as PolicyAction, S as SessionStatus, g as UserLoginData, U as UserProfileDisplay } from './CrudiaMarkdownField-C54-A_J3.mjs';
6
+ import React, { ReactNode } from 'react';
7
+ export { A as ApiError, C as CrudifyApiResponse, d as CrudifyOperationOptions, e as CrudifyRequestOptions, a as CrudifyTransactionResponse, F as ForgotPasswordRequest, J as JwtPayload, b as LoginRequest, L as LoginResponse, R as ResetPasswordRequest, g as TransactionInput, f as TransactionOperation, T as TransactionResponseData, U as UserProfile, V as ValidateCodeRequest, c as ValidationError } from './api-B4uXiHF0.mjs';
8
+ export { F as FileItem, v as FileStatus, L as LoginResult, N as NotificationOptions, a as SessionConfig, g as SessionDebugInfo, S as SessionManager, e as SessionProvider, h as SessionProviderProps, d as SessionState, c as StorageType, b as TokenData, T as TokenStorage, n as UseAuthReturn, p as UseDataReturn, s as UseFileUploadOptions, t as UseFileUploadReturn, U as UseSessionOptions, k as UseUserDataOptions, j as UseUserDataReturn, l as UserData, m as useAuth, w as useCrudifyWithNotifications, o as useData, r as useFileUpload, u as useSession, f as useSessionContext, i as useUserData, q as useUserProfile } from './index-Y9tTsinC.mjs';
9
+ import * as react_jsx_runtime from 'react/jsx-runtime';
10
+ import { ThemeOptions } from '@mui/material';
11
+ export { A as AutoGenerateConfig, G as GlobalNotificationProvider, a as GlobalNotificationProviderProps, N as Notification, b as NotificationSeverity, d as UseAutoGenerateOptions, U as UseAutoGenerateReturn, c as useAutoGenerate, u as useGlobalNotification } from './GlobalNotificationProvider-Zq18OkpI.mjs';
12
+ export { E as ERROR_CODES, j as ERROR_SEVERITY_MAP, n as ErrorCode, o as ErrorSeverity, q as ErrorTranslationConfig, P as ParsedError, m as createErrorTranslator, d as decodeJwtSafely, a as getCookie, g as getCurrentUserEmail, f as getErrorMessage, h as handleCrudifyError, i as isTokenExpired, p as parseApiError, e as parseJavaScriptError, c as parseTransactionError, b as secureLocalStorage, s as secureSessionStorage, l as translateError, t as translateErrorCode, k as translateErrorCodes } from './errorTranslation-qwwQTvCO.mjs';
13
+ import { ApiResponse, IModule, ModuleCreateInput, ModuleEditInput, ActionListFilters, IAction, ActionCreateInput, ActionEditInput, UpdateActionsProfilesInput, UpdateActionsProfilesResponse, CalculatePermissionsInput, CalculatePermissionsResponse } from '@nocios/crudify-admin';
14
+ export { ActionCreateInput, ActionEditInput, ActionListFilters, ApiResponse, IAction, IModule, ModuleCreateInput, ModuleEditInput, UpdateActionsProfilesInput, UpdateActionsProfilesResponse } from '@nocios/crudify-admin';
15
+
16
+ interface CrudifyContextValue {
17
+ crudify: typeof crudify__default | null;
18
+ isLoading: boolean;
19
+ error: string | null;
20
+ isInitialized: boolean;
21
+ adminCredentials?: {
22
+ apiEndpointAdmin?: string;
23
+ apiKeyEndpointAdmin?: string;
24
+ };
25
+ }
26
+ interface CrudifyProviderProps {
27
+ config: CrudifyLoginConfig;
28
+ children: ReactNode;
29
+ }
30
+ declare const CrudifyProvider: React.FC<CrudifyProviderProps>;
31
+ declare const useCrudify: () => CrudifyContextValue;
32
+
33
+ /**
34
+ * Tipos de mensajes que se pueden enviar entre tabs
35
+ */
36
+ type CrossTabMessageType = "SESSION_STARTED" | "SESSION_ENDED" | "TOKEN_REFRESHED" | "SESSION_PING" | "SESSION_PONG";
37
+ /**
38
+ * Estructura del mensaje cross-tab
39
+ */
40
+ interface CrossTabMessage {
41
+ type: CrossTabMessageType;
42
+ tabId: string;
43
+ timestamp: number;
44
+ payload?: Record<string, unknown>;
45
+ }
46
+ /**
47
+ * Tipo de callback para suscriptores
48
+ */
49
+ type CrossTabListener = (message: CrossTabMessage) => void;
50
+ /**
51
+ * Manager singleton para sincronización cross-tab
52
+ */
53
+ declare class CrossTabSyncManager {
54
+ private static instance;
55
+ private broadcastChannel;
56
+ private readonly CHANNEL_NAME;
57
+ private tabId;
58
+ private listeners;
59
+ private isInitialized;
60
+ /**
61
+ * Constructor privado - usar getInstance()
62
+ */
63
+ private constructor();
64
+ /**
65
+ * Genera un ID único para esta pestaña
66
+ */
67
+ private static generateTabId;
68
+ /**
69
+ * Obtener la instancia singleton
70
+ */
71
+ static getInstance(): CrossTabSyncManager;
72
+ /**
73
+ * Resetear la instancia (útil para tests)
74
+ */
75
+ static resetInstance(): void;
76
+ /**
77
+ * Inicializar el canal de comunicación
78
+ */
79
+ private initialize;
80
+ /**
81
+ * Destruir el manager y limpiar recursos
82
+ */
83
+ destroy(): void;
84
+ /**
85
+ * Obtener el ID de esta pestaña
86
+ */
87
+ getTabId(): string;
88
+ /**
89
+ * Suscribirse a mensajes de otras pestañas
90
+ * @returns Función para desuscribirse
91
+ */
92
+ subscribe(listener: CrossTabListener): () => void;
93
+ /**
94
+ * Notificar que se inició sesión en esta pestaña
95
+ */
96
+ notifyLogin(): void;
97
+ /**
98
+ * Notificar que se cerró sesión en esta pestaña
99
+ */
100
+ notifyLogout(): void;
101
+ /**
102
+ * Notificar que se refrescaron los tokens
103
+ */
104
+ notifyTokenRefreshed(): void;
105
+ /**
106
+ * Enviar un ping para verificar otras pestañas activas
107
+ */
108
+ ping(): void;
109
+ /**
110
+ * Enviar mensaje a todas las demás pestañas
111
+ */
112
+ private broadcast;
113
+ /**
114
+ * Procesar mensaje recibido de otra pestaña
115
+ */
116
+ private handleMessage;
117
+ }
118
+ /**
119
+ * Instancia singleton exportada para uso directo
120
+ */
121
+ declare const crossTabSync: CrossTabSyncManager;
122
+
123
+ type CrudifyThemeProviderProps = {
124
+ children: ReactNode;
125
+ defaultTheme?: ThemeOptions;
126
+ disableCssBaseline?: boolean;
127
+ };
128
+ /**
129
+ * Provider de tema para aplicaciones Crudify
130
+ * Lee automáticamente la cookie "theme" y aplica el tema a Material-UI
131
+ *
132
+ * @example
133
+ * ```tsx
134
+ * <CrudifyThemeProvider>
135
+ * <App />
136
+ * </CrudifyThemeProvider>
137
+ * ```
138
+ *
139
+ * @example Con tema por defecto personalizado
140
+ * ```tsx
141
+ * <CrudifyThemeProvider defaultTheme={{ palette: { mode: 'dark' } }}>
142
+ * <App />
143
+ * </CrudifyThemeProvider>
144
+ * ```
145
+ */
146
+ declare function CrudifyThemeProvider({ children, defaultTheme, disableCssBaseline }: CrudifyThemeProviderProps): react_jsx_runtime.JSX.Element;
147
+
148
+ interface PasswordRequirementsProps {
149
+ requirements: Array<{
150
+ message: string;
151
+ valid: boolean;
152
+ }>;
153
+ show?: boolean;
154
+ }
155
+ declare const PasswordRequirements: React.FC<PasswordRequirementsProps>;
156
+
157
+ /**
158
+ * Password validation utilities
159
+ * Evaluates password against a set of configurable rules
160
+ */
161
+
162
+ /**
163
+ * Evaluates a password against a set of rules
164
+ * @param password - The password to validate
165
+ * @param rules - Array of password rules to check against
166
+ * @returns Array of evaluated rules with validation status
167
+ */
168
+ declare function evaluatePasswordRules(password: string, rules: PasswordRule[]): EvaluatedPasswordRule[];
169
+ /**
170
+ * Checks if all enforced password rules are satisfied
171
+ * @param evaluated - Array of evaluated password rules
172
+ * @returns true if all enforced rules pass, false otherwise
173
+ */
174
+ declare function allPasswordRulesPassed(evaluated: EvaluatedPasswordRule[]): boolean;
175
+
176
+ /**
177
+ * Unified Logger for Crudify Projects
178
+ *
179
+ * Environment-based logging:
180
+ * - dev/stg: All logs (error, warn, info, debug)
181
+ * - prod: Only errors (default)
182
+ *
183
+ * AUTO-DETECTION:
184
+ * The logger will automatically detect the environment from:
185
+ * 1. Explicit setEnvironment() call (highest priority)
186
+ * 2. Cookie named 'environment' (for production with Lambda@Edge)
187
+ * 3. window.__CRUDIFY_ENV__ global variable
188
+ * 4. Default to "prod" for safety
189
+ *
190
+ * Usage:
191
+ * import { logger } from './logger';
192
+ *
193
+ * // Option 1: Explicit configuration (recommended for apps)
194
+ * logger.setEnvironment('dev'); // or 'stg', 'prod'
195
+ *
196
+ * // Option 2: Auto-detection (reads cookie or global)
197
+ * // Just use the logger - it will auto-detect
198
+ *
199
+ * logger.error('Something failed', { userId: '123' });
200
+ * logger.warn('Deprecated feature used');
201
+ * logger.info('User logged in', { email: 'user@example.com' });
202
+ * logger.debug('Processing request', { payload: data });
203
+ */
204
+ type LogLevel = "error" | "warn" | "info" | "debug";
205
+ interface LogContext {
206
+ [key: string]: unknown;
207
+ }
208
+ declare class Logger {
209
+ private explicitEnv;
210
+ private prefix;
211
+ constructor();
212
+ /**
213
+ * Get current effective environment
214
+ * Priority: explicit setEnvironment() > auto-detection
215
+ */
216
+ private getEffectiveEnv;
217
+ /**
218
+ * Sanitize sensitive data from strings
219
+ */
220
+ private sanitize;
221
+ /**
222
+ * Sanitize context object
223
+ */
224
+ private sanitizeContext;
225
+ /**
226
+ * Check if a log level should be displayed based on environment
227
+ * - dev/stg: show all logs
228
+ * - prod: show only errors (unless CRUDIFY_DEBUG_MODE is enabled)
229
+ */
230
+ private shouldLog;
231
+ /**
232
+ * Format and output log entry
233
+ */
234
+ private log;
235
+ /**
236
+ * Log an error - Always logged in all environments
237
+ */
238
+ error(message: string, context?: LogContext | Error): void;
239
+ /**
240
+ * Log a warning - Only in dev/stg
241
+ */
242
+ warn(message: string, context?: LogContext): void;
243
+ /**
244
+ * Log info - Only in dev/stg
245
+ */
246
+ info(message: string, context?: LogContext): void;
247
+ /**
248
+ * Log debug information - Only in dev/stg
249
+ */
250
+ debug(message: string, context?: LogContext): void;
251
+ /**
252
+ * Get current effective environment
253
+ */
254
+ getEnvironment(): string;
255
+ /**
256
+ * Manually set environment (useful for libraries that receive env from parent app)
257
+ * This takes priority over auto-detection
258
+ */
259
+ setEnvironment(env: string): void;
260
+ /**
261
+ * Check if environment was explicitly set or auto-detected
262
+ */
263
+ isExplicitlyConfigured(): boolean;
264
+ }
265
+ declare const logger: Logger;
266
+
267
+ interface ProtectedRouteProps {
268
+ children: ReactNode;
269
+ /**
270
+ * Componente a mostrar mientras se valida la sesión
271
+ * @default <SessionLoadingScreen stage="validating-session" />
272
+ */
273
+ loadingComponent?: ReactNode;
274
+ /**
275
+ * Ruta a la que redirigir si no está autenticado
276
+ * @default "/login"
277
+ */
278
+ loginPath?: string;
279
+ }
280
+ /**
281
+ * Componente para proteger rutas que requieren autenticación
282
+ *
283
+ * Características:
284
+ * - Valida sesión de forma estricta (isAuthenticated + tokens válidos)
285
+ * - Guarda URL actual para redirect post-login
286
+ * - Previene open redirect attacks
287
+ * - Limpia storage corrupto automáticamente
288
+ *
289
+ * @example
290
+ * ```tsx
291
+ * <Route
292
+ * path="/dashboard"
293
+ * element={
294
+ * <ProtectedRoute>
295
+ * <Dashboard />
296
+ * </ProtectedRoute>
297
+ * }
298
+ * />
299
+ * ```
300
+ */
301
+ declare function ProtectedRoute({ children, loadingComponent, loginPath }: ProtectedRouteProps): react_jsx_runtime.JSX.Element;
302
+
303
+ interface AuthRouteProps {
304
+ children: ReactNode;
305
+ /**
306
+ * Ruta a la que redirigir si ya está autenticado
307
+ * Si hay parámetro ?redirect= en la URL, se usará ese en su lugar
308
+ * @default "/"
309
+ */
310
+ redirectTo?: string;
311
+ /**
312
+ * Componente a mostrar mientras se valida la sesión
313
+ * @default <SessionLoadingScreen stage="validating-session" />
314
+ */
315
+ loadingComponent?: ReactNode;
316
+ /**
317
+ * Tiempo máximo de espera (ms) para la inicialización antes de mostrar el form
318
+ * @default 3000
319
+ */
320
+ initTimeout?: number;
321
+ }
322
+ /**
323
+ * Componente para proteger rutas de autenticación (login, register, etc.)
324
+ * Redirige a home si el usuario ya está autenticado
325
+ *
326
+ * Características:
327
+ * - Si usuario autenticado → redirige a home (o ?redirect= si existe)
328
+ * - Si no autenticado → muestra el componente (login, etc.)
329
+ * - Pre-check: Si hay tokens en localStorage, espera inicialización antes de mostrar form
330
+ * - Timeout: Si la inicialización tarda mucho, muestra el form de todos modos
331
+ * - Valida parámetro redirect para prevenir open redirect
332
+ *
333
+ * @example
334
+ * ```tsx
335
+ * <Route
336
+ * path="/login"
337
+ * element={
338
+ * <AuthRoute>
339
+ * <LoginPage />
340
+ * </AuthRoute>
341
+ * }
342
+ * />
343
+ * ```
344
+ */
345
+ declare function AuthRoute({ children, redirectTo, loadingComponent, initTimeout }: AuthRouteProps): react_jsx_runtime.JSX.Element;
346
+
347
+ /**
348
+ * Comportamiento de GuestRoute cuando el usuario está autenticado
349
+ */
350
+ type GuestRouteBehavior = "redirect-if-authenticated" | "allow-all";
351
+ interface GuestRouteProps {
352
+ children: ReactNode;
353
+ /**
354
+ * Comportamiento cuando el usuario está autenticado
355
+ * - 'redirect-if-authenticated': Redirige al usuario a redirectTo (default)
356
+ * - 'allow-all': Permite acceso tanto a usuarios autenticados como no autenticados
357
+ * @default 'redirect-if-authenticated'
358
+ */
359
+ behavior?: GuestRouteBehavior;
360
+ /**
361
+ * Ruta a la que redirigir si está autenticado y behavior es 'redirect-if-authenticated'
362
+ * @default "/"
363
+ */
364
+ redirectTo?: string;
365
+ /**
366
+ * Componente a mostrar mientras se valida la sesión
367
+ * @default <SessionLoadingScreen stage="validating-session" />
368
+ */
369
+ loadingComponent?: ReactNode;
370
+ }
371
+ /**
372
+ * Componente para rutas de invitados con comportamiento configurable
373
+ *
374
+ * Casos de uso:
375
+ * - Login page: behavior='redirect-if-authenticated' → redirige si ya está logueado
376
+ * - Forgot password: behavior='allow-all' → permite acceso aunque esté logueado
377
+ * - Reset password: behavior='allow-all' → permite acceso aunque esté logueado
378
+ *
379
+ * @example
380
+ * ```tsx
381
+ * // Login - redirige si ya está autenticado
382
+ * <Route
383
+ * path="/login"
384
+ * element={
385
+ * <GuestRoute behavior="redirect-if-authenticated">
386
+ * <LoginPage />
387
+ * </GuestRoute>
388
+ * }
389
+ * />
390
+ *
391
+ * // Forgot password - permite acceso aunque esté autenticado
392
+ * <Route
393
+ * path="/login/forgotPassword"
394
+ * element={
395
+ * <GuestRoute behavior="allow-all">
396
+ * <ForgotPasswordPage />
397
+ * </GuestRoute>
398
+ * }
399
+ * />
400
+ * ```
401
+ */
402
+ declare function GuestRoute({ children, behavior, redirectTo, loadingComponent }: GuestRouteProps): react_jsx_runtime.JSX.Element;
403
+
404
+ interface SessionLoadingScreenProps {
405
+ /**
406
+ * Stage of loading to display appropriate message
407
+ * @default "loading"
408
+ */
409
+ stage?: "initializing" | "validating-session" | "loading";
410
+ /**
411
+ * Custom message to display (overrides default stage message)
412
+ */
413
+ message?: string;
414
+ }
415
+ /**
416
+ * Loading screen component for session-related loading states
417
+ *
418
+ * Features:
419
+ * - Clean, minimal design
420
+ * - Customizable message
421
+ * - Stage-based default messages
422
+ * - No external dependencies (works without i18n)
423
+ *
424
+ * @example
425
+ * ```tsx
426
+ * // Basic usage
427
+ * <SessionLoadingScreen stage="validating-session" />
428
+ *
429
+ * // With custom message
430
+ * <SessionLoadingScreen
431
+ * stage="validating-session"
432
+ * message={t("loading.validatingSession")}
433
+ * />
434
+ * ```
435
+ */
436
+ declare function SessionLoadingScreen({ stage, message, }: SessionLoadingScreenProps): react_jsx_runtime.JSX.Element;
437
+
438
+ /**
439
+ * Utilidades de seguridad para validar redirecciones
440
+ * Previene ataques de open redirect permitiendo rutas dinámicas
441
+ */
442
+ /**
443
+ * Valida que una ruta de redirección sea segura (solo rutas internas)
444
+ * Permite rutas dinámicas pero bloquea ataques de open redirect
445
+ *
446
+ * @param path - La ruta a validar
447
+ * @param defaultPath - Ruta por defecto si la validación falla
448
+ * @returns Ruta validada y segura
449
+ */
450
+ declare const validateInternalRedirect: (path: string, defaultPath?: string) => string;
451
+ /**
452
+ * Extrae y valida parámetro redirect de URL search params
453
+ *
454
+ * @param searchParams - URLSearchParams o string de query params
455
+ * @param defaultPath - Ruta por defecto
456
+ * @returns Ruta validada
457
+ */
458
+ declare const extractSafeRedirectFromUrl: (searchParams: URLSearchParams | string, defaultPath?: string) => string;
459
+
460
+ /**
461
+ * Verifica si hay tokens encriptados en localStorage
462
+ * Hace un check rápido sin desencriptar
463
+ *
464
+ * @returns true si parece haber tokens guardados en formato v2
465
+ */
466
+ declare function hasStoredTokens(): boolean;
467
+ /**
468
+ * Verifica si existe el hash de la clave de encriptación
469
+ * Si existe, indica que TokenStorage puede desencriptar los tokens
470
+ *
471
+ * @returns true si hay un hash de clave válido guardado
472
+ */
473
+ declare function hasEncryptionKeyHash(): boolean;
474
+ /**
475
+ * Determina si debemos esperar a la inicialización de la sesión
476
+ * antes de mostrar el formulario de login
477
+ *
478
+ * Retorna true si:
479
+ * - Hay tokens encriptados guardados
480
+ * - Hay un hash de clave de encriptación (para poder desencriptarlos)
481
+ *
482
+ * Esto indica alta probabilidad de sesión válida, por lo que
483
+ * deberíamos mostrar loading mientras se inicializa SessionManager
484
+ *
485
+ * @returns true si probablemente hay una sesión válida
486
+ */
487
+ declare function shouldWaitForInitialization(): boolean;
488
+ /**
489
+ * Verifica si los tokens parecen estar corruptos o incompletos
490
+ * Útil para decidir si limpiar el storage
491
+ *
492
+ * @returns true si hay datos pero parecen corruptos
493
+ */
494
+ declare function hasCorruptedTokens(): boolean;
495
+ /**
496
+ * Limpia tokens que parecen corruptos
497
+ * Solo debe llamarse si hasCorruptedTokens() retorna true
498
+ */
499
+ declare function clearCorruptedTokens(): void;
500
+
501
+ type InitializationPriority = "HIGH" | "LOW";
502
+ type InitializationStatus = "UNINITIALIZED" | "INITIALIZING" | "INITIALIZED" | "ERROR";
503
+ interface InitializationState {
504
+ status: InitializationStatus;
505
+ priority: InitializationPriority | null;
506
+ publicApiKey: string | null;
507
+ env: string | null;
508
+ error: Error | null;
509
+ initializedBy: string | null;
510
+ }
511
+ interface InitializationRequest {
512
+ priority: InitializationPriority;
513
+ publicApiKey: string;
514
+ env: "dev" | "stg" | "api" | "prod";
515
+ enableLogging?: boolean;
516
+ requestedBy: string;
517
+ }
518
+ /**
519
+ * CrudifyInitializationManager
520
+ *
521
+ * Singleton manager for controlling Crudify SDK initialization with priority system.
522
+ *
523
+ * **Priority System:**
524
+ * - HIGH: Explicit initialization via `<CrudifyInitializer>` component
525
+ * - LOW: Automatic fallback initialization in providers
526
+ *
527
+ * **Thread-Safe:**
528
+ * - Prevents duplicate initializations
529
+ * - Handles concurrent initialization requests
530
+ * - Shared promise ensures single initialization
531
+ *
532
+ * @example
533
+ * ```typescript
534
+ * // HIGH priority (CrudifyInitializer)
535
+ * await crudifyInitManager.initialize({
536
+ * priority: "HIGH",
537
+ * publicApiKey: "key",
538
+ * env: "stg",
539
+ * requestedBy: "CrudifyInitializer"
540
+ * });
541
+ *
542
+ * // LOW priority (provider auto-init)
543
+ * await crudifyInitManager.initialize({
544
+ * priority: "LOW",
545
+ * publicApiKey: "key",
546
+ * env: "stg",
547
+ * requestedBy: "SessionProvider"
548
+ * });
549
+ * ```
550
+ */
551
+ declare class CrudifyInitializationManager {
552
+ private static instance;
553
+ private state;
554
+ private initializationPromise;
555
+ private highPriorityInitializerPresent;
556
+ private waitingForHighPriority;
557
+ private readonly HIGH_PRIORITY_WAIT_TIMEOUT;
558
+ private constructor();
559
+ /**
560
+ * Get singleton instance
561
+ */
562
+ static getInstance(): CrudifyInitializationManager;
563
+ /**
564
+ * Register that a HIGH priority initializer is present
565
+ * This must be called SYNCHRONOUSLY during component mount
566
+ */
567
+ registerHighPriorityInitializer(): void;
568
+ /**
569
+ * Check if a HIGH priority initializer is present
570
+ */
571
+ isHighPriorityInitializerPresent(): boolean;
572
+ /**
573
+ * Get current initialization state (read-only)
574
+ */
575
+ getState(): Readonly<InitializationState>;
576
+ /**
577
+ * Main initialization method - handles priority and concurrency
578
+ *
579
+ * @param request Initialization request with priority and config
580
+ * @returns Promise that resolves when initialization completes
581
+ * @throws Error if initialization fails
582
+ */
583
+ initialize(request: InitializationRequest): Promise<void>;
584
+ /**
585
+ * Wait for HIGH priority initializer with timeout
586
+ */
587
+ private waitForHighPriorityOrTimeout;
588
+ /**
589
+ * Actual SDK initialization
590
+ */
591
+ private performInitialization;
592
+ /**
593
+ * Reset initialization state (for testing)
594
+ */
595
+ reset(): void;
596
+ /**
597
+ * Check if initialized (without triggering initialization)
598
+ */
599
+ isInitialized(): boolean;
600
+ /**
601
+ * Get diagnostic info (for debugging)
602
+ */
603
+ getDiagnostics(): {
604
+ waitingCount: number;
605
+ waitingComponents: string[];
606
+ hasActivePromise: boolean;
607
+ status: InitializationStatus;
608
+ priority: InitializationPriority | null;
609
+ publicApiKey: string | null;
610
+ env: string | null;
611
+ error: Error | null;
612
+ initializedBy: string | null;
613
+ };
614
+ }
615
+ /**
616
+ * Export singleton instance
617
+ *
618
+ * @example
619
+ * ```typescript
620
+ * import { crudifyInitManager } from "@nocios/crudify-components";
621
+ *
622
+ * // Initialize with HIGH priority
623
+ * await crudifyInitManager.initialize({
624
+ * priority: "HIGH",
625
+ * publicApiKey: "your-key",
626
+ * env: "stg",
627
+ * requestedBy: "CrudifyInitializer"
628
+ * });
629
+ *
630
+ * // Check if initialized
631
+ * if (crudifyInitManager.isInitialized()) {
632
+ * // Ready to use crudify SDK
633
+ * }
634
+ *
635
+ * // Get diagnostics
636
+ * console.log(crudifyInitManager.getDiagnostics());
637
+ * ```
638
+ */
639
+ declare const crudifyInitManager: CrudifyInitializationManager;
640
+
641
+ interface CrudifyInitializerConfig {
642
+ publicApiKey?: string;
643
+ env?: "dev" | "stg" | "api" | "prod";
644
+ enableLogging?: boolean;
645
+ }
646
+ interface CrudifyInitializerContextValue {
647
+ isInitialized: boolean;
648
+ isInitializing: boolean;
649
+ error: Error | null;
650
+ }
651
+ interface CrudifyInitializerProps {
652
+ config?: CrudifyInitializerConfig;
653
+ children: React.ReactNode;
654
+ fallback?: React.ReactNode;
655
+ onInitialized?: () => void;
656
+ onError?: (error: Error) => void;
657
+ }
658
+ /**
659
+ * CrudifyInitializer Component
660
+ *
661
+ * HIGH priority Crudify SDK initializer component.
662
+ * Use this at the root of your app to explicitly control initialization.
663
+ *
664
+ * **Features:**
665
+ * - HIGH priority initialization (takes precedence over auto-init)
666
+ * - Provides initialization status via context
667
+ * - Optional loading fallback while initializing
668
+ * - Callbacks for initialization complete/error
669
+ * - React 18 StrictMode compatible
670
+ *
671
+ * **Usage:**
672
+ * ```tsx
673
+ * // App.tsx
674
+ * import { CrudifyInitializer } from "@nocios/crudify-components";
675
+ *
676
+ * function App() {
677
+ * return (
678
+ * <CrudifyInitializer
679
+ * config={{
680
+ * publicApiKey: import.meta.env.VITE_CRUDIFY_PUBLIC_API_KEY,
681
+ * env: "stg",
682
+ * enableLogging: true,
683
+ * }}
684
+ * fallback={<div>Loading Crudify...</div>}
685
+ * onInitialized={() => console.log("Crudify ready!")}
686
+ * onError={(err) => console.error("Init failed:", err)}
687
+ * >
688
+ * <SessionProvider>
689
+ * <TranslationsProvider>
690
+ * <App />
691
+ * </TranslationsProvider>
692
+ * </SessionProvider>
693
+ * </CrudifyInitializer>
694
+ * );
695
+ * }
696
+ * ```
697
+ *
698
+ * **Context Access:**
699
+ * ```tsx
700
+ * import { useCrudifyInitializer } from "@nocios/crudify-components";
701
+ *
702
+ * function MyComponent() {
703
+ * const { isInitialized, isInitializing, error } = useCrudifyInitializer();
704
+ *
705
+ * if (isInitializing) return <div>Loading...</div>;
706
+ * if (error) return <div>Error: {error.message}</div>;
707
+ * if (!isInitialized) return null;
708
+ *
709
+ * return <div>Crudify is ready!</div>;
710
+ * }
711
+ * ```
712
+ */
713
+ declare const CrudifyInitializer: React.FC<CrudifyInitializerProps>;
714
+ /**
715
+ * Hook to access CrudifyInitializer context
716
+ *
717
+ * @returns Initialization status (isInitialized, isInitializing, error)
718
+ * @throws Error if used outside CrudifyInitializer
719
+ *
720
+ * @example
721
+ * ```tsx
722
+ * function MyComponent() {
723
+ * const { isInitialized, isInitializing, error } = useCrudifyInitializer();
724
+ *
725
+ * if (isInitializing) return <Spinner />;
726
+ * if (error) return <ErrorDisplay error={error} />;
727
+ *
728
+ * return <div>Ready!</div>;
729
+ * }
730
+ * ```
731
+ */
732
+ declare const useCrudifyInitializer: () => CrudifyInitializerContextValue;
733
+
734
+ /** i18next instance interface for auto-syncing translations */
735
+ interface I18nInstance {
736
+ addResourceBundle: (lang: string, ns: string, resources: Record<string, string>, deep?: boolean, overwrite?: boolean) => void;
737
+ language?: string;
738
+ changeLanguage?: (lang: string) => void;
739
+ }
740
+ interface TranslationsProviderProps {
741
+ children: React.ReactNode;
742
+ apiKey?: string;
743
+ crudifyEnv?: "dev" | "stg" | "api" | "prod";
744
+ featureKeys?: string[];
745
+ language?: string;
746
+ devTranslations?: Record<string, string>;
747
+ translationUrl?: string;
748
+ enableDebug?: boolean;
749
+ skipAutoInit?: boolean;
750
+ autoSyncI18n?: boolean;
751
+ i18nInstance?: I18nInstance;
752
+ loadingFallback?: React.ReactNode;
753
+ waitForInitialLoad?: boolean;
754
+ }
755
+ interface TranslationsContextValue {
756
+ t: (key: string, variables?: Record<string, string | number>) => string;
757
+ language: string;
758
+ availableLanguages: string[];
759
+ translations: Record<string, Record<string, string>>;
760
+ isLoading: boolean;
761
+ error: string | null;
762
+ refreshTranslations: () => Promise<void>;
763
+ }
764
+ /**
765
+ * TranslationsProvider
766
+ *
767
+ * Provides translation functionality to the application
768
+ * Handles fetching, caching, and fallback strategies
769
+ *
770
+ * Note: The app MUST initialize crudify before mounting TranslationsProvider
771
+ *
772
+ * @example Basic usage
773
+ * ```tsx
774
+ * // App must initialize crudify first
775
+ * await crudify.init(publicApiKey, 'debug')
776
+ *
777
+ * <TranslationsProvider
778
+ * apiKey="xxx"
779
+ * language="es"
780
+ * translationUrl="https://example.com/locales/es/translation.json"
781
+ * skipAutoInit={true}
782
+ * >
783
+ * <App />
784
+ * </TranslationsProvider>
785
+ * ```
786
+ *
787
+ * @example With custom loading fallback (recommended for better UX)
788
+ * ```tsx
789
+ * <TranslationsProvider
790
+ * apiKey="xxx"
791
+ * crudifyEnv="prod"
792
+ * language={i18n.language}
793
+ * autoSyncI18n={true}
794
+ * i18nInstance={i18n}
795
+ * waitForInitialLoad={true} // Default - waits on first visit
796
+ * loadingFallback={<LoadingSpinner />} // Custom loading component
797
+ * >
798
+ * <App />
799
+ * </TranslationsProvider>
800
+ * ```
801
+ *
802
+ * @example Disable waiting (legacy behavior - may show keys briefly)
803
+ * ```tsx
804
+ * <TranslationsProvider
805
+ * apiKey="xxx"
806
+ * language="es"
807
+ * waitForInitialLoad={false} // Don't wait - render immediately
808
+ * >
809
+ * <App />
810
+ * </TranslationsProvider>
811
+ * ```
812
+ */
813
+ declare const TranslationsProvider: React.FC<TranslationsProviderProps>;
814
+ /**
815
+ * Hook to use translations
816
+ *
817
+ * @example
818
+ * ```tsx
819
+ * const MyComponent = () => {
820
+ * const { t } = useTranslations();
821
+ * return <h1>{t('welcome.title')}</h1>;
822
+ * };
823
+ * ```
824
+ */
825
+ declare const useTranslations: () => TranslationsContextValue;
826
+
827
+ interface FetchTranslationsOptions {
828
+ apiKey: string;
829
+ crudifyEnv?: "dev" | "stg" | "api" | "prod";
830
+ featureKeys?: string[];
831
+ urlTranslations?: Record<string, string>;
832
+ }
833
+ interface TranslationResponse {
834
+ languages: string[];
835
+ translations: Record<string, Record<string, string>>;
836
+ timestamp: string;
837
+ }
838
+ /**
839
+ * Translation Service
840
+ * Handles fetching, caching, and fallback strategies for translations
841
+ * Uses Crudify SDK for API calls
842
+ *
843
+ * Priority order (lowest to highest):
844
+ * 1. Critical bundle (fallback)
845
+ * 2. API translations (system + subscriber merged by backend)
846
+ * 3. URL translations (buildTranslations from config.json or translationUrl)
847
+ * 4. Dev translations (props override - handled by provider)
848
+ */
849
+ declare class TranslationService {
850
+ private static instance;
851
+ private enableDebug;
852
+ private initializationPromise;
853
+ private isInitialized;
854
+ private constructor();
855
+ static getInstance(): TranslationService;
856
+ /**
857
+ * Set debug mode
858
+ */
859
+ setDebug(enable: boolean): void;
860
+ /**
861
+ * Ensure crudify is initialized before making API calls
862
+ * Thread-safe: multiple calls will wait for the same initialization
863
+ */
864
+ private ensureCrudifyInitialized;
865
+ /**
866
+ * Fetch translations with cache strategy
867
+ * 1. Try cache (if valid and not changed)
868
+ * 2. Ensure crudify is initialized
869
+ * 3. Try API fetch via crudify SDK
870
+ * 4. Try expired cache
871
+ * 5. Fall back to critical bundle
872
+ */
873
+ fetchTranslations(options: FetchTranslationsOptions): Promise<Record<string, Record<string, string>>>;
874
+ /**
875
+ * Fetch from API via crudify SDK
876
+ */
877
+ private fetchFromAPI;
878
+ /**
879
+ * Merge API translations with URL translations
880
+ * URL translations have higher priority
881
+ */
882
+ private mergeWithUrlTranslations;
883
+ /**
884
+ * Check if data has changed (compare timestamps or hash)
885
+ */
886
+ private hasDataChanged;
887
+ /**
888
+ * Simple hash function for data comparison
889
+ */
890
+ private hashData;
891
+ /**
892
+ * Save to localStorage
893
+ */
894
+ private saveToCache;
895
+ /**
896
+ * Read from localStorage
897
+ */
898
+ private getFromCache;
899
+ /**
900
+ * Refresh cache timestamp without changing data
901
+ */
902
+ private refreshCacheTimestamp;
903
+ /**
904
+ * Check if cache is expired
905
+ */
906
+ private isCacheExpired;
907
+ /**
908
+ * Check if there is valid (non-expired) cache available
909
+ * Public method for TranslationsProvider to check before rendering
910
+ */
911
+ hasValidCache(apiKey: string): boolean;
912
+ /**
913
+ * Generate cache key from apiKey
914
+ * Uses a hash to ensure privacy and consistent length
915
+ */
916
+ private getCacheKey;
917
+ /**
918
+ * Get only critical translations from bundle
919
+ */
920
+ private getCriticalTranslationsOnly;
921
+ /**
922
+ * Invalidate cache (useful for testing/debugging)
923
+ */
924
+ invalidateCache(apiKey: string): void;
925
+ /**
926
+ * Prefetch translations in background
927
+ */
928
+ prefetchTranslations(options: FetchTranslationsOptions): Promise<void>;
929
+ /**
930
+ * Clear all translation caches
931
+ */
932
+ clearAllCaches(): void;
933
+ }
934
+ declare const translationService: TranslationService;
935
+
936
+ /**
937
+ * Critical translations bundle
938
+ * These translations are bundled with the application to ensure
939
+ * basic functionality (login, errors, loading) works even if the API is unavailable
940
+ *
941
+ * Total keys: 108
942
+ * - login.*: 15 keys
943
+ * - forgotPassword.*: 11 keys
944
+ * - checkCode.*: 11 keys
945
+ * - resetPassword.*: 19 keys
946
+ * - errors.*: 39 keys
947
+ * - loading.*: 5 keys
948
+ * - common (basic): 2 keys
949
+ * - footer.*: 2 keys
950
+ * - error.app.*: 4 keys
951
+ */
952
+ declare const CRITICAL_TRANSLATIONS: {
953
+ readonly es: {
954
+ readonly "checkCode.codeLabel": "Código de Verificación";
955
+ readonly "checkCode.codePlaceholder": "Ingresa el código de 6 dígitos";
956
+ readonly "checkCode.codeRequired": "El código es obligatorio";
957
+ readonly "checkCode.emailLabel": "Correo Electrónico";
958
+ readonly "checkCode.emailPlaceholder": "Ingresa tu correo electrónico";
959
+ readonly "checkCode.emailRequired": "El correo electrónico es obligatorio";
960
+ readonly "checkCode.instructions": "Ingresa el código de 6 dígitos que enviamos a tu correo electrónico.";
961
+ readonly "checkCode.invalidEmail": "Ingresa un correo electrónico válido";
962
+ readonly "checkCode.resendCodeLink": "Reenviar código";
963
+ readonly "checkCode.title": "Verificar Código";
964
+ readonly "checkCode.verifyButton": "Verificar Código";
965
+ readonly "base.btn.back": "Volver";
966
+ readonly "base.btn.cancel": "Cancelar";
967
+ readonly "error.app.config": "Error de Configuración: {{message}}";
968
+ readonly "error.app.initialization": "Error durante la inicialización final de la aplicación.";
969
+ readonly "error.transaction": "Error en la operación";
970
+ readonly "base.errors.errorUnknown": "Ocurrió un error desconocido.";
971
+ readonly "errors.DUPLICATE": "El campo <b>{{field}}</b> debe ser único.";
972
+ readonly "errors.FOREIGN_KEY_NOT_FOUND": "El campo <b>{{field}}</b> debe referenciar un ítem existente.";
973
+ readonly "errors.INVALID_EMAIL": "El campo <b>{{field}}</b> debe ser un correo electrónico válido.";
974
+ readonly "errors.INVALID_OBJECT_ID": "El campo <b>{{field}}</b> debe ser un valor válido.";
975
+ readonly "errors.IN_USE": "El ítem está en uso y no puede ser eliminado.";
976
+ readonly "errors.MAX_LENGTH": "El campo <b>{{field}}</b> no debe exceder los <b>{{length}}</b> caracteres.";
977
+ readonly "errors.MIN_LENGTH": "El campo <b>{{field}}</b> debe tener al menos <b>{{length}}</b> caracteres.";
978
+ readonly "errors.MUST_NOT_BE_EMAIL": "El campo <b>{{field}}</b> no debe ser un correo electrónico.";
979
+ readonly "errors.NO_PERMISSION": "No tienes permiso para realizar esta acción.";
980
+ readonly "errors.NO_SPACES": "El campo <b>{{field}}</b> no debe contener espacios.";
981
+ readonly "errors.ONE_OR_MORE_OPERATIONS_FAILED": "Error en la operación";
982
+ readonly "errors.REQUIRED": "El campo <b>{{field}}</b> es obligatorio.";
983
+ readonly "errors.TOKEN_HAS_EXPIRED": "La sesión ha expirado. Por favor, inicia sesión nuevamente.";
984
+ readonly "errors.TOO_MANY_REQUESTS": "Límite de solicitudes alcanzado. Por favor intenta más tarde.";
985
+ readonly "errors.UNAUTHORIZED": "No estás autorizado para realizar esta acción.";
986
+ readonly "errors.all_password_fields_required": "Todos los campos de contraseña son obligatorios";
987
+ readonly "errors.auth.INVALID_API_KEY": "Clave de API inválida";
988
+ readonly "errors.auth.INVALID_CREDENTIALS": "Usuario y/o contraseña incorrectos";
989
+ readonly "errors.auth.NO_PERMISSION": "No tienes permisos suficientes";
990
+ readonly "errors.auth.SESSION_EXPIRED": "Tu sesión ha expirado. Por favor, inicia sesión nuevamente.";
991
+ readonly "errors.auth.TOO_MANY_REQUESTS": "Demasiados intentos. Por favor espera 15 minutos e intenta nuevamente";
992
+ readonly "errors.auth.UNAUTHORIZED": "No tienes permisos para realizar esta acción";
993
+ readonly "errors.auth.USER_NOT_ACTIVE": "Usuario no activo";
994
+ readonly "errors.auth.USER_NOT_FOUND": "Usuario no encontrado";
995
+ readonly "errors.data.BAD_REQUEST": "Solicitud inválida";
996
+ readonly "errors.data.FIELD_ERROR": "Error en los datos ingresados";
997
+ readonly "errors.data.IN_USE": "El elemento está en uso y no puede ser eliminado";
998
+ readonly "errors.data.ITEM_NOT_FOUND": "El elemento solicitado no fue encontrado";
999
+ readonly "errors.data.NOT_FOUND": "No se encontró lo solicitado";
1000
+ readonly "errors.internal_error_changing_password": "Error interno al cambiar la contraseña. Intenta nuevamente";
1001
+ readonly "errors.password_min_length": "La contraseña debe tener al menos 8 caracteres";
1002
+ readonly "errors.password_mismatch": "Las contraseñas no coinciden";
1003
+ readonly "errors.password_must_be_different": "La nueva contraseña debe ser diferente a la actual";
1004
+ readonly "errors.system.DATABASE_CONNECTION_ERROR": "Error de conexión. Verifica tu conexión a internet.";
1005
+ readonly "errors.system.INTERNAL_SERVER_ERROR": "Error interno del servidor. Intenta nuevamente.";
1006
+ readonly "errors.system.INVALID_CONFIGURATION": "Error de configuración del sistema";
1007
+ readonly "errors.system.TOO_MANY_REQUESTS": "Demasiadas solicitudes. Por favor espera un momento e intenta nuevamente";
1008
+ readonly "errors.system.UNKNOWN_OPERATION": "Operación no reconocida";
1009
+ readonly "errors.users_module_not_configured": "Módulo de usuarios no configurado";
1010
+ readonly "footer.copyright": "Nocios S.R.L. Todos los derechos reservados.";
1011
+ readonly "footer.version": "Versión";
1012
+ readonly "forgotPassword.checkEmailInstructions": "Revisa tu bandeja de entrada para el código de verificación";
1013
+ readonly "forgotPassword.codeAlreadyExistsMessage": "Ya se envió un código y aún es válido";
1014
+ readonly "forgotPassword.emailLabel": "Correo Electrónico";
1015
+ readonly "forgotPassword.emailPlaceholder": "Ingresa tu correo electrónico";
1016
+ readonly "forgotPassword.emailRequired": "El correo electrónico es obligatorio";
1017
+ readonly "forgotPassword.emailSentMessage": "Código enviado exitosamente";
1018
+ readonly "forgotPassword.enterCodeLink": "Ingresar Código";
1019
+ readonly "forgotPassword.instructions": "Ingresa tu correo electrónico y te enviaremos un código para restablecer tu contraseña.";
1020
+ readonly "forgotPassword.invalidEmail": "Ingresa un correo electrónico válido";
1021
+ readonly "forgotPassword.sendCodeButton": "Enviar Código";
1022
+ readonly "forgotPassword.title": "Recuperar Contraseña";
1023
+ readonly "base.loading.configInitial": "Cargando configuración inicial...";
1024
+ readonly "base.loading.pleaseWait": "Cargando, por favor espera...";
1025
+ readonly "base.loading.starting": "Iniciando aplicación...";
1026
+ readonly "base.loading.themeSetup": "Generando tema...";
1027
+ readonly "base.loading.validatingSession": "Validando sesión...";
1028
+ readonly "login.alreadyHaveCodeLink": "¿Ya tienes un código?";
1029
+ readonly "login.forgotPasswordLink": "¿Olvidaste tu contraseña?";
1030
+ readonly "login.initializationError": "Error de inicialización";
1031
+ readonly "login.initializing": "Inicializando...";
1032
+ readonly "login.loginButton": "Iniciar Sesión";
1033
+ readonly "login.logoAlt": "Logotipo";
1034
+ readonly "login.noAccountPrompt": "¿No tienes una cuenta?";
1035
+ readonly "login.notInitialized": "Sistema no inicializado";
1036
+ readonly "login.passwordLabel": "Contraseña";
1037
+ readonly "login.passwordPlaceholder": "Ingresa tu contraseña";
1038
+ readonly "login.passwordRequired": "Contraseña es obligatoria";
1039
+ readonly "login.signUpLink": "Regístrate";
1040
+ readonly "login.usernameOrEmailLabel": "Correo Electrónico";
1041
+ readonly "login.usernameOrEmailPlaceholder": "Ingresa tu correo electrónico";
1042
+ readonly "login.usernameRequired": "Correo electrónico es obligatorio";
1043
+ readonly "resetPassword.codeExpiredOrInvalid": "El código ha expirado o es inválido";
1044
+ readonly "resetPassword.confirmPasswordLabel": "Confirmar Contraseña";
1045
+ readonly "resetPassword.confirmPasswordPlaceholder": "Confirma tu nueva contraseña";
1046
+ readonly "resetPassword.confirmPasswordRequired": "Confirma tu contraseña";
1047
+ readonly "resetPassword.goToLoginButton": "Ir al Login";
1048
+ readonly "resetPassword.instructions": "Ingresa tu nueva contraseña.";
1049
+ readonly "resetPassword.invalidCode": "El código de verificación es inválido. Por favor, verifica el enlace o solicita uno nuevo.";
1050
+ readonly "resetPassword.missingParameters": "Faltan parámetros obligatorios";
1051
+ readonly "resetPassword.newPasswordLabel": "Nueva Contraseña";
1052
+ readonly "resetPassword.newPasswordPlaceholder": "Ingresa tu nueva contraseña";
1053
+ readonly "resetPassword.newPasswordRequired": "La nueva contraseña es obligatoria";
1054
+ readonly "resetPassword.passwordTooShort": "La contraseña debe tener al menos 8 caracteres";
1055
+ readonly "resetPassword.passwordsDoNotMatch": "Las contraseñas no coinciden";
1056
+ readonly "resetPassword.requestNewCodeButton": "Solicitar Nuevo Código";
1057
+ readonly "resetPassword.resetPasswordButton": "Restablecer Contraseña";
1058
+ readonly "resetPassword.successInstructions": "Ahora puedes iniciar sesión con tu nueva contraseña";
1059
+ readonly "resetPassword.successMessage": "Contraseña restablecida exitosamente";
1060
+ readonly "resetPassword.title": "Nueva Contraseña";
1061
+ readonly "resetPassword.validatingCode": "Validando código...";
1062
+ readonly "resetPassword.passwordRequirementsNotMet": "La contraseña no cumple con todos los requisitos";
1063
+ readonly "passwordRequirements.minLength": "Mínimo 8 caracteres";
1064
+ readonly "passwordRequirements.uppercase": "Una letra mayúscula";
1065
+ readonly "passwordRequirements.lowercase": "Una letra minúscula";
1066
+ readonly "passwordRequirements.number": "Un número";
1067
+ readonly "passwordRequirements.special": "Un carácter especial (!@#$%...)";
1068
+ readonly "base.btn.showPassword": "Mostrar contraseña";
1069
+ readonly "base.btn.hidePassword": "Ocultar contraseña";
1070
+ readonly "publicPolicies.fields.conditions.customEdit": "Edición personalizada";
1071
+ readonly "base.file.clickToPreview": "Clic para ver imagen";
1072
+ readonly "base.file.clickToDownload": "Clic para descargar";
1073
+ readonly "base.file.privateFile": "Archivo privado";
1074
+ readonly "base.file.noPreview": "Sin vista previa";
1075
+ readonly "base.file.dropHere": "Suelta el archivo aquí";
1076
+ readonly "base.file.dragOrClick": "Arrastra archivos aquí o haz clic para seleccionar";
1077
+ readonly "base.file.uploading": "Subiendo archivos...";
1078
+ readonly "base.file.restore": "Restaurar";
1079
+ readonly "base.file.delete": "Eliminar";
1080
+ readonly "base.file.pendingDeletion": "Pendiente de eliminación";
1081
+ readonly "base.file.allowedTypes": "Tipos permitidos: {{types}}";
1082
+ readonly "base.file.maxSize": "Máximo {{size}} por archivo";
1083
+ readonly "base.file.maxFiles": "Máximo {{count}} archivos";
1084
+ readonly "base.file.validation.minFilesRequired": "Se requiere al menos un archivo";
1085
+ readonly "base.file.validation.minFilesRequiredPlural": "Se requieren al menos {{count}} archivos";
1086
+ readonly "base.file.validation.maxFilesExceeded": "Máximo {{count}} archivos permitidos";
1087
+ readonly "base.file.validation.filesWithErrors": "Algunos archivos tienen errores";
1088
+ readonly "base.file.confirmDelete.title": "Eliminar archivo";
1089
+ readonly "base.file.confirmDelete.message": "¿Estás seguro de que deseas eliminar este archivo? Esta acción no se puede deshacer.";
1090
+ readonly "base.file.confirmDelete.confirm": "Eliminar";
1091
+ readonly "base.file.confirmDelete.cancel": "Cancelar";
1092
+ readonly "base.file.deleting": "Eliminando...";
1093
+ };
1094
+ readonly en: {
1095
+ readonly "checkCode.codeLabel": "Verification Code";
1096
+ readonly "checkCode.codePlaceholder": "Enter 6-digit code";
1097
+ readonly "checkCode.codeRequired": "Code is required";
1098
+ readonly "checkCode.emailLabel": "Email Address";
1099
+ readonly "checkCode.emailPlaceholder": "Enter your email address";
1100
+ readonly "checkCode.emailRequired": "Email address is required";
1101
+ readonly "checkCode.instructions": "Enter the 6-digit code we sent to your email address.";
1102
+ readonly "checkCode.invalidEmail": "Enter a valid email address";
1103
+ readonly "checkCode.resendCodeLink": "Resend code";
1104
+ readonly "checkCode.title": "Verify Code";
1105
+ readonly "checkCode.verifyButton": "Verify Code";
1106
+ readonly "base.btn.back": "Back";
1107
+ readonly "base.btn.cancel": "Cancel";
1108
+ readonly "error.app.config": "Configuration Error: {{message}}";
1109
+ readonly "error.app.initialization": "Error during final application initialization.";
1110
+ readonly "error.transaction": "Operation error";
1111
+ readonly "base.errors.errorUnknown": "An unknown error occurred.";
1112
+ readonly "errors.DUPLICATE": "The field <b>{{field}}</b> must be unique.";
1113
+ readonly "errors.FOREIGN_KEY_NOT_FOUND": "The field <b>{{field}}</b> must reference an existing item.";
1114
+ readonly "errors.INVALID_EMAIL": "The field <b>{{field}}</b> must be a valid email address.";
1115
+ readonly "errors.INVALID_OBJECT_ID": "The field <b>{{field}}</b> must be a valid value.";
1116
+ readonly "errors.IN_USE": "The item is currently in use and cannot be deleted.";
1117
+ readonly "errors.MAX_LENGTH": "The field <b>{{field}}</b> must not exceed <b>{{maxLength}}</b> characters.";
1118
+ readonly "errors.MIN_LENGTH": "The field <b>{{field}}</b> must have at least <b>{{minLength}}</b> characters.";
1119
+ readonly "errors.MUST_NOT_BE_EMAIL": "The field <b>{{field}}</b> must not be an email address.";
1120
+ readonly "errors.NO_PERMISSION": "You do not have permission to perform this action.";
1121
+ readonly "errors.NO_SPACES": "The field <b>{{field}}</b> must not contain spaces.";
1122
+ readonly "errors.ONE_OR_MORE_OPERATIONS_FAILED": "Operation error";
1123
+ readonly "errors.REQUIRED": "The field <b>{{field}}</b> is required.";
1124
+ readonly "errors.TOKEN_HAS_EXPIRED": "Your session has expired, please log in again.";
1125
+ readonly "errors.TOO_MANY_REQUESTS": "Request limit reached. Please try again later.";
1126
+ readonly "errors.UNAUTHORIZED": "You are not authorized to perform this action.";
1127
+ readonly "errors.all_password_fields_required": "All password fields are required";
1128
+ readonly "errors.auth.INVALID_API_KEY": "Invalid API key";
1129
+ readonly "errors.auth.INVALID_CREDENTIALS": "Incorrect username and/or password";
1130
+ readonly "errors.auth.NO_PERMISSION": "Insufficient permissions";
1131
+ readonly "errors.auth.SESSION_EXPIRED": "Your session has expired. Please log in again.";
1132
+ readonly "errors.auth.TOO_MANY_REQUESTS": "Too many attempts. Please wait 15 minutes and try again";
1133
+ readonly "errors.auth.UNAUTHORIZED": "You don't have permission to perform this action";
1134
+ readonly "errors.auth.USER_NOT_ACTIVE": "User account is not active";
1135
+ readonly "errors.auth.USER_NOT_FOUND": "User not found";
1136
+ readonly "errors.data.BAD_REQUEST": "Invalid request";
1137
+ readonly "errors.data.FIELD_ERROR": "Error in the provided data";
1138
+ readonly "errors.data.IN_USE": "The item is in use and cannot be deleted";
1139
+ readonly "errors.data.ITEM_NOT_FOUND": "The requested item was not found";
1140
+ readonly "errors.data.NOT_FOUND": "The requested resource was not found";
1141
+ readonly "errors.internal_error_changing_password": "Internal error changing password. Please try again";
1142
+ readonly "errors.password_min_length": "Password must be at least 8 characters";
1143
+ readonly "errors.password_mismatch": "Passwords do not match";
1144
+ readonly "errors.password_must_be_different": "New password must be different from current password";
1145
+ readonly "errors.system.DATABASE_CONNECTION_ERROR": "Connection error. Please check your internet connection.";
1146
+ readonly "errors.system.INTERNAL_SERVER_ERROR": "Internal server error. Please try again.";
1147
+ readonly "errors.system.INVALID_CONFIGURATION": "System configuration error";
1148
+ readonly "errors.system.TOO_MANY_REQUESTS": "Too many requests. Please wait a moment and try again";
1149
+ readonly "errors.system.UNKNOWN_OPERATION": "Unrecognized operation";
1150
+ readonly "errors.users_module_not_configured": "Users module not configured";
1151
+ readonly "footer.copyright": "Nocios S.R.L. All Rights Reserved.";
1152
+ readonly "footer.version": "Version";
1153
+ readonly "forgotPassword.checkEmailInstructions": "Check your inbox for the verification code";
1154
+ readonly "forgotPassword.codeAlreadyExistsMessage": "A code was already sent and is still valid";
1155
+ readonly "forgotPassword.emailLabel": "Email Address";
1156
+ readonly "forgotPassword.emailPlaceholder": "Enter your email address";
1157
+ readonly "forgotPassword.emailRequired": "Email address is required";
1158
+ readonly "forgotPassword.emailSentMessage": "Code sent successfully";
1159
+ readonly "forgotPassword.enterCodeLink": "Enter Code";
1160
+ readonly "forgotPassword.instructions": "Enter your email address and we'll send you a code to reset your password.";
1161
+ readonly "forgotPassword.invalidEmail": "Enter a valid email address";
1162
+ readonly "forgotPassword.sendCodeButton": "Send Code";
1163
+ readonly "forgotPassword.title": "Reset Password";
1164
+ readonly "base.loading.configInitial": "Loading initial configuration...";
1165
+ readonly "base.loading.pleaseWait": "Loading, please wait...";
1166
+ readonly "base.loading.starting": "Starting application...";
1167
+ readonly "base.loading.themeSetup": "Generating theme...";
1168
+ readonly "base.loading.validatingSession": "Validating session...";
1169
+ readonly "login.alreadyHaveCodeLink": "Already have a code?";
1170
+ readonly "login.forgotPasswordLink": "Forgot Password?";
1171
+ readonly "login.initializationError": "Initialization error";
1172
+ readonly "login.initializing": "Initializing...";
1173
+ readonly "login.loginButton": "Log In";
1174
+ readonly "login.logoAlt": "Logo";
1175
+ readonly "login.noAccountPrompt": "Don't have an account?";
1176
+ readonly "login.notInitialized": "System not initialized";
1177
+ readonly "login.passwordLabel": "Password";
1178
+ readonly "login.passwordPlaceholder": "Enter your password";
1179
+ readonly "login.passwordRequired": "Password is required";
1180
+ readonly "login.signUpLink": "Sign up";
1181
+ readonly "login.usernameOrEmailLabel": "Username or Email";
1182
+ readonly "login.usernameOrEmailPlaceholder": "Enter your username or email";
1183
+ readonly "login.usernameRequired": "Username or email is required";
1184
+ readonly "resetPassword.codeExpiredOrInvalid": "Code expired or invalid";
1185
+ readonly "resetPassword.confirmPasswordLabel": "Confirm Password";
1186
+ readonly "resetPassword.confirmPasswordPlaceholder": "Confirm your new password";
1187
+ readonly "resetPassword.confirmPasswordRequired": "Confirm your password";
1188
+ readonly "resetPassword.goToLoginButton": "Go to Login";
1189
+ readonly "resetPassword.instructions": "Enter your new password.";
1190
+ readonly "resetPassword.invalidCode": "The verification code is invalid. Please check the link or request a new one.";
1191
+ readonly "resetPassword.missingParameters": "Missing required parameters";
1192
+ readonly "resetPassword.newPasswordLabel": "New Password";
1193
+ readonly "resetPassword.newPasswordPlaceholder": "Enter your new password";
1194
+ readonly "resetPassword.newPasswordRequired": "New password is required";
1195
+ readonly "resetPassword.passwordTooShort": "Password must be at least 8 characters";
1196
+ readonly "resetPassword.passwordsDoNotMatch": "Passwords do not match";
1197
+ readonly "resetPassword.requestNewCodeButton": "Request New Code";
1198
+ readonly "resetPassword.resetPasswordButton": "Reset Password";
1199
+ readonly "resetPassword.successInstructions": "You can now log in with your new password";
1200
+ readonly "resetPassword.successMessage": "Password reset successfully";
1201
+ readonly "resetPassword.title": "New Password";
1202
+ readonly "resetPassword.validatingCode": "Validating code...";
1203
+ readonly "resetPassword.passwordRequirementsNotMet": "Password does not meet all requirements";
1204
+ readonly "passwordRequirements.minLength": "Minimum 8 characters";
1205
+ readonly "passwordRequirements.uppercase": "One uppercase letter";
1206
+ readonly "passwordRequirements.lowercase": "One lowercase letter";
1207
+ readonly "passwordRequirements.number": "One number";
1208
+ readonly "passwordRequirements.special": "One special character (!@#$%...)";
1209
+ readonly "base.btn.showPassword": "Show password";
1210
+ readonly "base.btn.hidePassword": "Hide password";
1211
+ readonly "publicPolicies.fields.conditions.customEdit": "Custom Edit";
1212
+ readonly "base.file.clickToPreview": "Click to preview";
1213
+ readonly "base.file.clickToDownload": "Click to download";
1214
+ readonly "base.file.privateFile": "Private file";
1215
+ readonly "base.file.noPreview": "No preview";
1216
+ readonly "base.file.dropHere": "Drop file here";
1217
+ readonly "base.file.dragOrClick": "Drag files here or click to select";
1218
+ readonly "base.file.uploading": "Uploading files...";
1219
+ readonly "base.file.restore": "Restore";
1220
+ readonly "base.file.delete": "Delete";
1221
+ readonly "base.file.pendingDeletion": "Pending deletion";
1222
+ readonly "base.file.allowedTypes": "Allowed types: {{types}}";
1223
+ readonly "base.file.maxSize": "Max {{size}} per file";
1224
+ readonly "base.file.maxFiles": "Max {{count}} files";
1225
+ readonly "base.file.validation.minFilesRequired": "At least one file is required";
1226
+ readonly "base.file.validation.minFilesRequiredPlural": "At least {{count}} files are required";
1227
+ readonly "base.file.validation.maxFilesExceeded": "Maximum {{count}} files allowed";
1228
+ readonly "base.file.validation.filesWithErrors": "Some files have errors";
1229
+ readonly "base.file.confirmDelete.title": "Delete file";
1230
+ readonly "base.file.confirmDelete.message": "Are you sure you want to delete this file? This action cannot be undone.";
1231
+ readonly "base.file.confirmDelete.confirm": "Delete";
1232
+ readonly "base.file.deleting": "Deleting...";
1233
+ readonly "base.file.confirmDelete.cancel": "Cancel";
1234
+ };
1235
+ };
1236
+ type CriticalTranslationKey = keyof typeof CRITICAL_TRANSLATIONS.es;
1237
+ type SupportedLanguage = keyof typeof CRITICAL_TRANSLATIONS;
1238
+ /**
1239
+ * Get list of supported languages
1240
+ */
1241
+ declare const getCriticalLanguages: () => SupportedLanguage[];
1242
+ /**
1243
+ * Get critical translations for a specific language
1244
+ * Falls back to Spanish if language not found
1245
+ */
1246
+ declare const getCriticalTranslations: (language: string) => Record<string, string>;
1247
+
1248
+ /**
1249
+ * crudifyAdmin wrapper with auto-initialization.
1250
+ * Automatically injects JWT token in each request and handles token refresh on 401 errors.
1251
+ */
1252
+ declare const crudifyAdmin: {
1253
+ listModules: () => Promise<ApiResponse<IModule[]>>;
1254
+ getModule: (moduleKey: string) => Promise<ApiResponse<IModule>>;
1255
+ createModule: (moduleData: ModuleCreateInput) => Promise<ApiResponse<IModule>>;
1256
+ editModule: (moduleKey: string, moduleData: ModuleEditInput) => Promise<ApiResponse<IModule>>;
1257
+ deleteModule: (moduleKey: string) => Promise<ApiResponse>;
1258
+ activateModule: (moduleKey: string) => Promise<ApiResponse<IModule>>;
1259
+ deactivateModule: (moduleKey: string) => Promise<ApiResponse<IModule>>;
1260
+ getModuleVersions: (moduleKey: string) => Promise<ApiResponse<any[]>>;
1261
+ listActions: (filters?: ActionListFilters) => Promise<ApiResponse<IAction[]>>;
1262
+ getAction: (actionKey: string) => Promise<ApiResponse<IAction>>;
1263
+ createAction: (actionData: ActionCreateInput) => Promise<ApiResponse<IAction>>;
1264
+ editAction: (actionKey: string, actionData: ActionEditInput) => Promise<ApiResponse<IAction>>;
1265
+ deleteAction: (actionKey: string) => Promise<ApiResponse>;
1266
+ activateAction: (actionKey: string) => Promise<ApiResponse<IAction>>;
1267
+ deactivateAction: (actionKey: string) => Promise<ApiResponse<IAction>>;
1268
+ getActionVersions: (actionKey: string) => Promise<ApiResponse<any[]>>;
1269
+ getActionsByProfile: (profileId: string) => Promise<ApiResponse<IAction[]>>;
1270
+ updateActionsProfiles: (data: UpdateActionsProfilesInput) => Promise<ApiResponse<UpdateActionsProfilesResponse>>;
1271
+ calculatePermissions: (data: CalculatePermissionsInput) => Promise<ApiResponse<CalculatePermissionsResponse>>;
1272
+ };
1273
+
1274
+ export { AuthRoute, type AuthRouteProps, CRITICAL_TRANSLATIONS, type CriticalTranslationKey, type CrossTabListener, type CrossTabMessage, type CrossTabMessageType, CrossTabSyncManager, CrudifyInitializationManager, CrudifyInitializer, type CrudifyInitializerConfig, type CrudifyInitializerProps, CrudifyLoginConfig, CrudifyProvider, CrudifyThemeProvider, type CrudifyThemeProviderProps, EvaluatedPasswordRule, type FetchTranslationsOptions, GuestRoute, type GuestRouteBehavior, type GuestRouteProps, type InitializationPriority, type InitializationRequest, type InitializationStatus, type LogContext, type LogLevel, PasswordRequirements, type PasswordRequirementsProps, PasswordRule, ProtectedRoute, type ProtectedRouteProps, SessionLoadingScreen, type SessionLoadingScreenProps, type SupportedLanguage, type TranslationResponse, TranslationService, type TranslationsContextValue, TranslationsProvider, type TranslationsProviderProps, allPasswordRulesPassed, clearCorruptedTokens, crossTabSync, crudifyAdmin, crudifyInitManager, evaluatePasswordRules, extractSafeRedirectFromUrl, getCriticalLanguages, getCriticalTranslations, hasCorruptedTokens, hasEncryptionKeyHash, hasStoredTokens, logger, shouldWaitForInitialization, translationService, useCrudify, useCrudifyInitializer, useTranslations, validateInternalRedirect };