@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.
- package/.github/workflows/test.yml +59 -0
- package/.nvmrc +1 -0
- package/README.md +398 -0
- package/README_DEPTH.md +1230 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/coverage-final.json +85 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +506 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/CrudiaMarkdownField-C54-A_J3.d.mts +328 -0
- package/dist/CrudiaMarkdownField-C8HQh7s5.d.ts +328 -0
- package/dist/GlobalNotificationProvider-Zq18OkpI.d.mts +96 -0
- package/dist/GlobalNotificationProvider-Zq18OkpI.d.ts +96 -0
- package/dist/api-B4uXiHF0.d.mts +118 -0
- package/dist/api-B4uXiHF0.d.ts +118 -0
- package/dist/chunk-2XOTIEKS.js +1 -0
- package/dist/chunk-5HFI5CZ5.js +1 -0
- package/dist/chunk-CHDM7KGH.js +1 -0
- package/dist/chunk-HVTRRU4W.mjs +1 -0
- package/dist/chunk-JAPL7EZJ.mjs +1 -0
- package/dist/chunk-JNEWPO2J.mjs +1 -0
- package/dist/chunk-MFYHD6S5.js +1 -0
- package/dist/chunk-MGJZTOEM.mjs +1 -0
- package/dist/chunk-NBQH6QOU.mjs +1 -0
- package/dist/chunk-NSV6ECYO.js +1 -0
- package/dist/chunk-PNI3ZBZV.js +1 -0
- package/dist/chunk-U4RS66TB.mjs +1 -0
- package/dist/components.d.mts +24 -0
- package/dist/components.d.ts +24 -0
- package/dist/components.js +1 -0
- package/dist/components.mjs +1 -0
- package/dist/errorTranslation-DGdrMidg.d.ts +143 -0
- package/dist/errorTranslation-qwwQTvCO.d.mts +143 -0
- package/dist/hooks.d.mts +6 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.js +1 -0
- package/dist/hooks.mjs +1 -0
- package/dist/index-BUKX3duW.d.ts +854 -0
- package/dist/index-Y9tTsinC.d.mts +854 -0
- package/dist/index.d.mts +1274 -0
- package/dist/index.d.ts +1274 -0
- package/dist/index.js +6 -0
- package/dist/index.mjs +6 -0
- package/dist/utils.d.mts +175 -0
- package/dist/utils.d.ts +175 -0
- package/dist/utils.js +1 -0
- package/dist/utils.mjs +1 -0
- package/package.json +88 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Test & Quality Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
timeout-minutes: 15
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Use Node.js 24.12.0
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: "24.12.0"
|
|
21
|
+
|
|
22
|
+
- name: Cache node modules
|
|
23
|
+
uses: actions/cache@v4
|
|
24
|
+
with:
|
|
25
|
+
path: ~/.npm
|
|
26
|
+
key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}
|
|
27
|
+
restore-keys: |
|
|
28
|
+
${{ runner.os }}-node-
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: npm install
|
|
32
|
+
|
|
33
|
+
- name: Run linter
|
|
34
|
+
run: npm run lint --if-present
|
|
35
|
+
continue-on-error: true
|
|
36
|
+
|
|
37
|
+
- name: Run type check
|
|
38
|
+
run: npm run type-check --if-present
|
|
39
|
+
continue-on-error: true
|
|
40
|
+
|
|
41
|
+
- name: Run tests with coverage
|
|
42
|
+
run: npm test -- --run
|
|
43
|
+
timeout-minutes: 5
|
|
44
|
+
|
|
45
|
+
- name: Upload coverage to Codecov
|
|
46
|
+
if: success()
|
|
47
|
+
uses: codecov/codecov-action@v4
|
|
48
|
+
with:
|
|
49
|
+
file: ./coverage/coverage-final.json
|
|
50
|
+
flags: unittests
|
|
51
|
+
name: codecov-umbrella
|
|
52
|
+
continue-on-error: true
|
|
53
|
+
|
|
54
|
+
- name: Build package
|
|
55
|
+
run: npm run build
|
|
56
|
+
|
|
57
|
+
- name: Check bundle size
|
|
58
|
+
run: npm run build:analyze --if-present
|
|
59
|
+
continue-on-error: true
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.12.0
|
package/README.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# @nocios/crudify-components
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/%40nocios%2Fcrudify-ui)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://typescriptlang.org/)
|
|
6
|
+
[](https://reactjs.org/)
|
|
7
|
+
|
|
8
|
+
**UI component library for the Crudify ecosystem - Modern React components with authentication, session management, and CRUD operations.**
|
|
9
|
+
|
|
10
|
+
Biblioteca completa de componentes UI y hooks de React para aplicaciones Crudify. Proporciona autenticación moderna, manejo de sesiones y una API unificada.
|
|
11
|
+
|
|
12
|
+
## 🚀 Características
|
|
13
|
+
|
|
14
|
+
- **🔐 Autenticación Moderna**: Sistema completo con Refresh Token Pattern
|
|
15
|
+
- **⚡ Manejo de Sesiones**: Persistencia automática y sincronización cross-tab
|
|
16
|
+
- **🎨 Componentes UI**: Componentes React listos con Material-UI
|
|
17
|
+
- **🪝 React Hooks**: Hooks especializados para CRUD y autenticación
|
|
18
|
+
- **🔄 API Unificada**: Re-exporta completamente @nocios/crudify-sdk
|
|
19
|
+
- **🌍 Internacionalización**: Soporte para múltiples idiomas
|
|
20
|
+
- **📱 TypeScript**: Completamente tipado
|
|
21
|
+
|
|
22
|
+
## 📖 Documentation
|
|
23
|
+
|
|
24
|
+
- 📋 **[Complete Documentation](docs/overview.md)** - Comprehensive component and API reference
|
|
25
|
+
- 🔒 **[Security Guide](docs/security.md)** - Security features and best practices
|
|
26
|
+
- 🏗️ **[Architecture](docs/architecture.md)** - Library design and patterns
|
|
27
|
+
- 🔧 **[Migration Guide](docs/migration.md)** - Upgrade instructions between versions
|
|
28
|
+
- 💡 **[Examples](docs/examples.md)** - Real-world usage examples
|
|
29
|
+
|
|
30
|
+
## 📦 Instalación
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @nocios/crudify-components
|
|
34
|
+
|
|
35
|
+
# Peer dependencies
|
|
36
|
+
npm install @mui/material @mui/icons-material react react-dom
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 🏗️ Configuración
|
|
40
|
+
|
|
41
|
+
### ⚙️ Sistema de Configuración
|
|
42
|
+
|
|
43
|
+
La librería utiliza un sistema de configuración por prioridad para obtener credenciales y configuración:
|
|
44
|
+
|
|
45
|
+
1. **Props directos** (máxima prioridad) - Override manual
|
|
46
|
+
2. **Variables de entorno** (desarrollo local) - Se leen desde `.env`
|
|
47
|
+
3. **Cookies** (producción) - Las establece Lambda automáticamente
|
|
48
|
+
4. **Error** - Si no hay configuración válida
|
|
49
|
+
|
|
50
|
+
### 🏗️ Arquitectura
|
|
51
|
+
|
|
52
|
+
### 📋 Configuración Centralizada
|
|
53
|
+
|
|
54
|
+
**IMPORTANTE**: La configuración ahora se pasa al `SessionProvider` en lugar de individual componentes como `CrudifyLogin`. Esta arquitectura centralizada garantiza que todos los componentes tengan acceso a la misma configuración.
|
|
55
|
+
|
|
56
|
+
````tsx
|
|
57
|
+
// ✅ CORRECTO - Nueva arquitectura
|
|
58
|
+
<SessionProvider config={{ publicApiKey: "...", env: "dev" }}>
|
|
59
|
+
<CrudifyLogin />
|
|
60
|
+
</SessionProvider>
|
|
61
|
+
|
|
62
|
+
**Requisito**: `CrudifyLogin` debe estar dentro de un `SessionProvider` para funcionar correctamente.
|
|
63
|
+
|
|
64
|
+
## 🔧 Configuración para Desarrollo Local
|
|
65
|
+
|
|
66
|
+
#### 1. Crear archivo `.env` en tu proyecto
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# .env (para desarrollo local)
|
|
70
|
+
VITE_TEST_PUBLIC_API_KEY=CRUD_tu_api_key_aqui
|
|
71
|
+
VITE_TEST_ENV=dev
|
|
72
|
+
VITE_TEST_LOGIN_ACTIONS=createUser,forgotPassword
|
|
73
|
+
````
|
|
74
|
+
|
|
75
|
+
#### 2. Configurar Providers
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { SessionProvider, crudify } from "@nocios/crudify-components";
|
|
79
|
+
|
|
80
|
+
function App() {
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
// Inicializar crudify (opcional)
|
|
83
|
+
const initCrudify = async () => {
|
|
84
|
+
const publicApiKey = import.meta.env.VITE_TEST_PUBLIC_API_KEY;
|
|
85
|
+
const environment = import.meta.env.VITE_TEST_ENV || "dev";
|
|
86
|
+
|
|
87
|
+
if (publicApiKey) {
|
|
88
|
+
crudify.config(environment);
|
|
89
|
+
await crudify.init(publicApiKey, "debug");
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
initCrudify();
|
|
94
|
+
}, []);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<SessionProvider
|
|
98
|
+
options={{
|
|
99
|
+
autoRestore: true,
|
|
100
|
+
enableLogging: process.env.NODE_ENV === "development",
|
|
101
|
+
}}
|
|
102
|
+
config={{
|
|
103
|
+
publicApiKey: import.meta.env.VITE_TEST_PUBLIC_API_KEY,
|
|
104
|
+
env: import.meta.env.VITE_TEST_ENV,
|
|
105
|
+
loginActions: import.meta.env.VITE_TEST_LOGIN_ACTIONS?.split(","),
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
<YourApp />
|
|
109
|
+
</SessionProvider>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### 3. Usar CrudifyLogin con configuración automática
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { CrudifyLogin } from "@nocios/crudify-components";
|
|
118
|
+
|
|
119
|
+
function LoginPage() {
|
|
120
|
+
return (
|
|
121
|
+
<CrudifyLogin
|
|
122
|
+
onLoginSuccess={(userData) => {
|
|
123
|
+
console.log("Login exitoso:", userData);
|
|
124
|
+
// Navegar al dashboard
|
|
125
|
+
}}
|
|
126
|
+
onError={(error) => {
|
|
127
|
+
console.error("Error en login:", error);
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 🌐 Configuración para Producción
|
|
135
|
+
|
|
136
|
+
En producción (staging/prod), **NO** uses variables de entorno. El Lambda de AWS se encarga de establecer las cookies automáticamente:
|
|
137
|
+
|
|
138
|
+
- `publicApiKey` → Cookie
|
|
139
|
+
- `environment` → Cookie
|
|
140
|
+
- `loginActions` → Cookie
|
|
141
|
+
|
|
142
|
+
La librería las detectará automáticamente:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
// En producción, la configuración se lee de cookies automáticamente
|
|
146
|
+
<SessionProvider
|
|
147
|
+
config={{
|
|
148
|
+
appName: "Mi App",
|
|
149
|
+
colors: { primaryColor: "#1976d2" },
|
|
150
|
+
// publicApiKey, env y loginActions se leen de cookies automáticamente
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
<CrudifyLogin />
|
|
154
|
+
</SessionProvider>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 🪝 Hooks Principales
|
|
158
|
+
|
|
159
|
+
### useAuth - Autenticación
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { useAuth } from "@nocios/crudify-components";
|
|
163
|
+
|
|
164
|
+
function LoginComponent() {
|
|
165
|
+
const { login, logout, isAuthenticated, isLoading } = useAuth();
|
|
166
|
+
|
|
167
|
+
const handleLogin = async () => {
|
|
168
|
+
const result = await login("user@example.com", "password");
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return isAuthenticated ? (
|
|
172
|
+
<button onClick={logout}>Cerrar Sesión</button>
|
|
173
|
+
) : (
|
|
174
|
+
<button onClick={handleLogin} disabled={isLoading}>
|
|
175
|
+
Iniciar Sesión
|
|
176
|
+
</button>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### useUserData - Datos del Usuario
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
import { useUserData } from "@nocios/crudify-components";
|
|
185
|
+
|
|
186
|
+
function UserProfile() {
|
|
187
|
+
const { userData, sessionData, isLoading } = useUserData();
|
|
188
|
+
|
|
189
|
+
if (isLoading) return <div>Cargando...</div>;
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div>
|
|
193
|
+
<h2>{userData?.fullName}</h2>
|
|
194
|
+
<p>Email: {sessionData?.email}</p>
|
|
195
|
+
<img src={userData?.avatar} alt="Avatar" />
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### useData - Operaciones CRUD
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
import { useData } from "@nocios/crudify-components";
|
|
205
|
+
|
|
206
|
+
function ProductManager() {
|
|
207
|
+
const { create, read, update, remove } = useData();
|
|
208
|
+
|
|
209
|
+
const createProduct = async () => {
|
|
210
|
+
await create("products", { name: "Nuevo Producto", price: 99.99 });
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const getProducts = async () => {
|
|
214
|
+
const products = await read("products", { limit: 10 });
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return <button onClick={createProduct}>Crear Producto</button>;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 🎨 Componentes UI
|
|
222
|
+
|
|
223
|
+
### CrudifyLogin - Login Completo
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { CrudifyLogin, SessionProvider } from "@nocios/crudify-components";
|
|
227
|
+
|
|
228
|
+
function AuthPage() {
|
|
229
|
+
return (
|
|
230
|
+
<SessionProvider
|
|
231
|
+
config={{
|
|
232
|
+
appName: "Mi App",
|
|
233
|
+
logo: "/logo.png",
|
|
234
|
+
colors: { primaryColor: "#1976d2" },
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
<CrudifyLogin
|
|
238
|
+
onLoginSuccess={(userData) => {
|
|
239
|
+
console.log("Login exitoso:", userData);
|
|
240
|
+
}}
|
|
241
|
+
language="es"
|
|
242
|
+
/>
|
|
243
|
+
</SessionProvider>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### ProtectedRoute - Protección de Rutas
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { ProtectedRoute } from "@nocios/crudify-components";
|
|
252
|
+
|
|
253
|
+
function App() {
|
|
254
|
+
return (
|
|
255
|
+
<ProtectedRoute fallback={<LoginPage />}>
|
|
256
|
+
<Dashboard />
|
|
257
|
+
</ProtectedRoute>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Policies - Gestión de Políticas Públicas
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
import { Policies, POLICY_ACTIONS, PolicyAction } from "@nocios/crudify-components";
|
|
266
|
+
|
|
267
|
+
function ModuleConfiguration() {
|
|
268
|
+
const [policies, setPolicies] = useState([]);
|
|
269
|
+
const availableFields = ["name", "email", "description", "status"];
|
|
270
|
+
|
|
271
|
+
return <Policies policies={policies} onChange={setPolicies} availableFields={availableFields} errors={null} isSubmitting={false} />;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## 🔄 API de Crudify SDK
|
|
276
|
+
|
|
277
|
+
Re-exporta completamente `@nocios/crudify-sdk`:
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { crudify } from "@nocios/crudify-components";
|
|
281
|
+
|
|
282
|
+
// Usar directamente la API de crudify
|
|
283
|
+
const result = await crudify.create("users", userData);
|
|
284
|
+
const users = await crudify.read("users");
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## 🛠️ Utilidades
|
|
288
|
+
|
|
289
|
+
### JWT Utils
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
import { decodeJwtSafely, getCurrentUserEmail } from "@nocios/crudify-components";
|
|
293
|
+
|
|
294
|
+
const payload = decodeJwtSafely(token);
|
|
295
|
+
const email = getCurrentUserEmail();
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Error Handler
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import { handleCrudifyError, getErrorMessage } from "@nocios/crudify-components";
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
// operación
|
|
305
|
+
} catch (error) {
|
|
306
|
+
const parsedError = handleCrudifyError(error);
|
|
307
|
+
const userMessage = getErrorMessage(parsedError.code);
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## 🚀 Ejemplo Completo
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import React from "react";
|
|
315
|
+
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
|
316
|
+
import { SessionProvider, ProtectedRoute, CrudifyLogin, useSessionContext } from "@nocios/crudify-components";
|
|
317
|
+
|
|
318
|
+
function Dashboard() {
|
|
319
|
+
const { sessionData, logout } = useSessionContext();
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<div>
|
|
323
|
+
<h1>Dashboard</h1>
|
|
324
|
+
<p>Bienvenido, {sessionData?.email}</p>
|
|
325
|
+
<button onClick={logout}>Cerrar Sesión</button>
|
|
326
|
+
</div>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function LoginPage() {
|
|
331
|
+
return (
|
|
332
|
+
<div style={{ maxWidth: "400px", margin: "0 auto" }}>
|
|
333
|
+
<CrudifyLogin onLoginSuccess={(userData) => console.log("Login:", userData)} />
|
|
334
|
+
</div>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function App() {
|
|
339
|
+
return (
|
|
340
|
+
<SessionProvider>
|
|
341
|
+
<BrowserRouter>
|
|
342
|
+
<Routes>
|
|
343
|
+
<Route path="/login" element={<LoginPage />} />
|
|
344
|
+
<Route
|
|
345
|
+
path="/dashboard"
|
|
346
|
+
element={
|
|
347
|
+
<ProtectedRoute fallback={<LoginPage />}>
|
|
348
|
+
<Dashboard />
|
|
349
|
+
</ProtectedRoute>
|
|
350
|
+
}
|
|
351
|
+
/>
|
|
352
|
+
</Routes>
|
|
353
|
+
</BrowserRouter>
|
|
354
|
+
</SessionProvider>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## 📱 TypeScript
|
|
360
|
+
|
|
361
|
+
Tipos completos incluidos:
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
import type { CrudifyLoginProps, UseAuthReturn, UserProfile, TokenData } from "@nocios/crudify-components";
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## 🌐 Internacionalización
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
// Idiomas soportados: 'en' | 'es'
|
|
371
|
+
<CrudifyLogin language="es" />
|
|
372
|
+
|
|
373
|
+
// Traducciones personalizadas
|
|
374
|
+
<CrudifyLogin
|
|
375
|
+
translations={{
|
|
376
|
+
login: { title: "Iniciar Sesión" }
|
|
377
|
+
}}
|
|
378
|
+
/>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## 🔒 Seguridad
|
|
382
|
+
|
|
383
|
+
- ✅ Encriptación de tokens
|
|
384
|
+
- ✅ Refresh Token Pattern
|
|
385
|
+
- ✅ Validación automática
|
|
386
|
+
- ✅ Limpieza automática de tokens expirados
|
|
387
|
+
|
|
388
|
+
## 📚 Documentación Completa
|
|
389
|
+
|
|
390
|
+
Para documentación detallada, ejemplos avanzados y guías completas, consulta [README_DEPTH.md](./README_DEPTH.md).
|
|
391
|
+
|
|
392
|
+
## 📄 Licencia
|
|
393
|
+
|
|
394
|
+
MIT © [Nocios](https://github.com/nocios)
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
**¿Necesitas ayuda?** Consulta [README_DEPTH.md](./README_DEPTH.md) para documentación completa.
|