@yoltra/core 0.1.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/LICENSE +21 -0
- package/README.es.md +473 -0
- package/README.md +468 -0
- package/dist/types/eventBus/EventBus.d.ts +127 -0
- package/dist/types/eventBus/LooseEventBus.d.ts +219 -0
- package/dist/types/eventBus/index.d.ts +2 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/reducer/Reducer.d.ts +81 -0
- package/dist/types/store/Store.d.ts +834 -0
- package/dist/types/types.d.ts +939 -0
- package/dist/types/utils/detectChangedProps.d.ts +67 -0
- package/dist/types/utils/immutability.d.ts +47 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/yoltra.cjs.js +8 -0
- package/dist/yoltra.esm.js +1557 -0
- package/dist/yoltra.umd.js +8 -0
- package/package.json +83 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Yoltra β Copyright (c) 2026 Manu Ramirez <@pixerael>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.es.md
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# @yoltra/core
|
|
4
|
+
|
|
5
|
+
> π π²π½ VersiΓ³n en EspaΓ±ol |
|
|
6
|
+
> [ πΊπΈ English Version](https://github.com/yoltra/yoltra/blob/main/packages/core/README.md)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
**Contenedor de estado orientado a eventos, agnostico de framework, con suscripciones de grano
|
|
12
|
+
fino por ruta.**
|
|
13
|
+
|
|
14
|
+
`@yoltra/core` es la base de [yoltra](https://github.com/yoltra/yoltra/blob/main/README.md).
|
|
15
|
+
Proporciona el store, el pipeline de eventos, middleware, efectos y el sistema de suscripciones
|
|
16
|
+
`connect()`. Cero dependencias de framework.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Instalacion
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @yoltra/core
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## El Pipeline de Eventos
|
|
29
|
+
|
|
30
|
+
Cada llamada a `emit()` fluye a traves de un pipeline determinista:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
emit(channel, type, payload)
|
|
34
|
+
β
|
|
35
|
+
ββ 1. Dedup βββ Omitir si la huella es identica dentro de la ventana de tiempo
|
|
36
|
+
β
|
|
37
|
+
ββ 2. Middleware βββ Hooks pre-reducer (pueden rechazar β evento "no confirmado")
|
|
38
|
+
β
|
|
39
|
+
ββ 3. Reducers βββ Actualizaciones de estado sincronas, deteccion de cambios de grano fino por ruta
|
|
40
|
+
β
|
|
41
|
+
ββ 4. Suscriptores de eventos βββ Notificaciones de eventos confirmados/no confirmados
|
|
42
|
+
β
|
|
43
|
+
ββ 5. Efectos βββ Efectos secundarios async (post-reducer, indexados para busqueda O(1))
|
|
44
|
+
β
|
|
45
|
+
ββ 6. Suscriptores gruesos βββ Listeners externos del store (useSyncExternalStore, etc.)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Cada etapa es interceptable. El middleware puede cancelar eventos, creando eventos "no
|
|
49
|
+
confirmados" a los que la UI aun puede reaccionar. Los efectos se ejecutan despues de los
|
|
50
|
+
reducers y ven el estado final.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Conceptos Fundamentales
|
|
55
|
+
|
|
56
|
+
### Eventos basados en canales
|
|
57
|
+
|
|
58
|
+
Los eventos son tuplas `(channel, type, payload)`. Los canales proporcionan namespacing natural
|
|
59
|
+
que escala en bases de codigo grandes:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
await store.emit("auth", "login", credentials);
|
|
63
|
+
await store.emit("analytics", "track", { event: "page_view" });
|
|
64
|
+
await store.emit("ui", "toast", { message: "Saved!" });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Suscripciones de grano fino via `connect()`
|
|
68
|
+
|
|
69
|
+
Suscribete a rutas de estado exactas usando notacion de puntos. Soporta wildcards `*` (un
|
|
70
|
+
segmento) y `**` (cero o mas segmentos):
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Ruta exacta β se dispara cuando items[0].title cambia
|
|
74
|
+
store.connect({ reducer: "todos", property: "items.0.title" }, (change) =>
|
|
75
|
+
console.log("title:", change.oldValue, "β", change.newValue),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Wildcard de un segmento β se dispara cuando el titulo de CUALQUIER item cambia
|
|
79
|
+
store.connect({ reducer: "todos", property: "items.*.title" }, (change) =>
|
|
80
|
+
console.log("some title changed at", change.path),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Wildcard profundo β se dispara cuando algo bajo items cambia
|
|
84
|
+
store.connect({ reducer: "todos", property: "items.**" }, (change) =>
|
|
85
|
+
console.log("items tree changed at", change.path),
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Inmutabilidad
|
|
90
|
+
|
|
91
|
+
El estado se congela profundamente antes de confirmarse. Las mutaciones lanzan error en modo
|
|
92
|
+
estricto:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const state = store.getState();
|
|
96
|
+
state.counter.value = 999; // TypeError: Cannot assign to read-only property
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Targeting de Eventos con Matchers `When`
|
|
102
|
+
|
|
103
|
+
Los reducers, efectos y middleware usan un matcher `When` unificado para declarar a cuales
|
|
104
|
+
eventos responden:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { createStore, eventKeys } from "@yoltra/core";
|
|
108
|
+
|
|
109
|
+
type AppEM = {
|
|
110
|
+
ui: { increment: number; decrement: number; reset: void };
|
|
111
|
+
admin: { setCounter: number };
|
|
112
|
+
system: { init: void; shutdown: void };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Coincidir con claves de evento especificas (recomendado β preserva la correlacion de tipos)
|
|
116
|
+
const counterReducer = {
|
|
117
|
+
state: { value: 0 },
|
|
118
|
+
when: {
|
|
119
|
+
keys: eventKeys<AppEM>()([
|
|
120
|
+
["ui", "increment"],
|
|
121
|
+
["ui", "decrement"],
|
|
122
|
+
]),
|
|
123
|
+
},
|
|
124
|
+
reducer: (state, event) => {
|
|
125
|
+
if (event.type === "increment") return { value: state.value + event.payload };
|
|
126
|
+
if (event.type === "decrement") return { value: state.value - event.payload };
|
|
127
|
+
return state;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Coincidir con todos los eventos de un canal
|
|
132
|
+
const uiLogger = {
|
|
133
|
+
when: { channel: "ui" },
|
|
134
|
+
effect: (event) => console.log("UI event:", event.type),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Coincidir con eventos de multiples canales
|
|
138
|
+
const auditTrail = {
|
|
139
|
+
when: { channels: ["ui", "admin"] },
|
|
140
|
+
effect: (event) => logToAuditTrail(event),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Coincidir con TODOS los eventos
|
|
144
|
+
const globalLogger = {
|
|
145
|
+
when: { any: true },
|
|
146
|
+
middleware: (state, event) => {
|
|
147
|
+
console.log(`[${event.channel}] ${event.type}`);
|
|
148
|
+
return true;
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Middleware
|
|
156
|
+
|
|
157
|
+
El middleware se ejecuta **antes** de los reducers y puede cancelar la propagacion de eventos.
|
|
158
|
+
Soporta tanto funciones directas (legacy) como objetos `MiddlewareSpec` con targeting:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import type { MiddlewareSpec } from "@yoltra/core";
|
|
162
|
+
|
|
163
|
+
// Middleware con target β solo se ejecuta para eventos del canal admin
|
|
164
|
+
const adminGuard: MiddlewareSpec<AppState, AppEM> = {
|
|
165
|
+
when: { channel: "admin" },
|
|
166
|
+
middleware: (state, event) => {
|
|
167
|
+
if (!state.auth.isAdmin) return false; // Rechazar β crea evento "no confirmado"
|
|
168
|
+
return true;
|
|
169
|
+
},
|
|
170
|
+
meta: { type: "middleware", name: "adminGuard" },
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Middleware global β se ejecuta para todos los eventos
|
|
174
|
+
const logger = async (state, event, emit) => {
|
|
175
|
+
console.log("Event:", event.channel, event.type);
|
|
176
|
+
return true;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const store = createStore({
|
|
180
|
+
name: "App",
|
|
181
|
+
reducer: {
|
|
182
|
+
/* ... */
|
|
183
|
+
},
|
|
184
|
+
middleware: [adminGuard, logger],
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Middleware dinamico
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const off = store.registerMiddleware(async (state, event) => {
|
|
192
|
+
return event.type !== "forbidden";
|
|
193
|
+
});
|
|
194
|
+
off(); // Remover despues
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Efectos
|
|
200
|
+
|
|
201
|
+
Los efectos se ejecutan **despues** de los reducers y ven el estado final. Estan indexados por
|
|
202
|
+
evento para busqueda O(1):
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// Via spec del store
|
|
206
|
+
const store = createStore({
|
|
207
|
+
name: "App",
|
|
208
|
+
reducer: {
|
|
209
|
+
/* ... */
|
|
210
|
+
},
|
|
211
|
+
effects: [
|
|
212
|
+
{
|
|
213
|
+
when: {
|
|
214
|
+
keys: eventKeys<AppEM>()([
|
|
215
|
+
["todos", "add"],
|
|
216
|
+
["todos", "delete"],
|
|
217
|
+
]),
|
|
218
|
+
},
|
|
219
|
+
effect: async (event, getState, emit) => {
|
|
220
|
+
await saveToServer(getState());
|
|
221
|
+
},
|
|
222
|
+
meta: { type: "effect", name: "syncToServer" },
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Registro dinamico
|
|
228
|
+
const off = store.registerEffect({
|
|
229
|
+
when: { channel: "analytics" },
|
|
230
|
+
effect: async (event) => sendToAnalytics(event),
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Helper de conveniencia para un solo evento
|
|
234
|
+
const off2 = store.onEffect("ui", "save", async (payload, getState, emit) => {
|
|
235
|
+
await saveToCloud(payload);
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Suscripciones a Eventos
|
|
242
|
+
|
|
243
|
+
Suscribete a eventos (no al estado) desde la capa de vista. Util para notificaciones,
|
|
244
|
+
animaciones y reaccionar a eventos rechazados:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// Eventos confirmados (por defecto) β eventos que pasaron el middleware
|
|
248
|
+
const off = store.onEvent("ui", "save", (event, getState, emit, phase) => {
|
|
249
|
+
console.log("Save committed:", event.payload);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Eventos no confirmados β eventos rechazados por el middleware
|
|
253
|
+
store.onEvent(
|
|
254
|
+
"ui",
|
|
255
|
+
"delete",
|
|
256
|
+
(event, getState, emit, phase) => {
|
|
257
|
+
console.log("Delete was rejected");
|
|
258
|
+
},
|
|
259
|
+
"uncommitted",
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// Todos los eventos β tanto confirmados como no confirmados
|
|
263
|
+
store.onEvent(
|
|
264
|
+
"ui",
|
|
265
|
+
"action",
|
|
266
|
+
(event, getState, emit, phase) => {
|
|
267
|
+
console.log(`Action ${phase}:`, event.type);
|
|
268
|
+
},
|
|
269
|
+
"all",
|
|
270
|
+
);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Deduplicacion de Eventos
|
|
276
|
+
|
|
277
|
+
yoltra deduplica automaticamente eventos identicos dentro de una ventana de tiempo configurable.
|
|
278
|
+
Esto previene el doble procesamiento en React Strict Mode:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
const store = createStore({
|
|
282
|
+
name: "App",
|
|
283
|
+
reducer: {
|
|
284
|
+
/* ... */
|
|
285
|
+
},
|
|
286
|
+
dedupWindowMs: 100, // default: 50ms dev, 100ms prod
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Reducers Dinamicos
|
|
293
|
+
|
|
294
|
+
Agrega o elimina slices de reducer en tiempo de ejecucion:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const dispose = store.registerReducer("filters", {
|
|
298
|
+
state: { q: "" },
|
|
299
|
+
when: { keys: eventKeys<AppEM>()([["ui", "setQuery"]]) },
|
|
300
|
+
reducer: (state, event) => (event.type === "setQuery" ? { q: event.payload } : state),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Despues: remover el slice y su estado
|
|
304
|
+
dispose();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Hot Module Replacement
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
if (import.meta.hot) {
|
|
313
|
+
import.meta.hot.accept("./reducers", (mod) => {
|
|
314
|
+
store.replaceReducers(mod.reducers, { preserveState: true });
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
import.meta.hot.accept("./middleware", (mod) => {
|
|
318
|
+
store.replaceMiddleware(mod.middleware);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
import.meta.hot.accept("./effects", (mod) => {
|
|
322
|
+
store.replaceEffects(mod.effects);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// O reemplazar todo de una vez
|
|
326
|
+
store.hotReplace({
|
|
327
|
+
reducer: newReducers,
|
|
328
|
+
middleware: newMiddleware,
|
|
329
|
+
effects: newEffects,
|
|
330
|
+
preserveState: true,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Mejores Practicas
|
|
338
|
+
|
|
339
|
+
### Siempre hacer await de `emit()`
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
await emit("todo", "add", todo);
|
|
343
|
+
const state = store.getState(); // Garantiza que refleja la nueva tarea
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Mantener los reducers rapidos
|
|
347
|
+
|
|
348
|
+
Los reducers son sincronos y bloquean la cola de eventos. Mueve el trabajo costoso a los
|
|
349
|
+
efectos:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// Reducer: solo establecer un flag de carga
|
|
353
|
+
reducer: ((state, event) => ({ ...state, loading: true }),
|
|
354
|
+
// Efecto: hacer el trabajo pesado
|
|
355
|
+
store.onEffect("data", "compute", async (payload, getState, emit) => {
|
|
356
|
+
const result = await computeAsync();
|
|
357
|
+
await emit("data", "computeComplete", result);
|
|
358
|
+
}));
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Manejar errores de efectos
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
store.registerEffect({
|
|
365
|
+
when: { channel: "data" },
|
|
366
|
+
effect: async (event, getState, emit) => {
|
|
367
|
+
try {
|
|
368
|
+
const data = await fetch(url);
|
|
369
|
+
await emit("data", "loadSuccess", data);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
await emit("data", "loadFailure", { error: error.message });
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Resumen de API
|
|
380
|
+
|
|
381
|
+
### Creacion del Store
|
|
382
|
+
|
|
383
|
+
| API | Descripcion |
|
|
384
|
+
| ----------------------------------------------- | ----------------------------------------------------- |
|
|
385
|
+
| `createStore(spec)` | Crear un store (tipos inferidos de los reducers) |
|
|
386
|
+
| `createStore<S, EM>(spec)` | Crear un store con tipos de estado/eventos explicitos |
|
|
387
|
+
| `store.emit(channel, type, payload)` | Emitir un evento (retorna una promesa) |
|
|
388
|
+
| `store.getState()` | Obtener snapshot del estado actual (solo lectura) |
|
|
389
|
+
| `store.subscribe(listener)` | Suscripcion gruesa (cualquier cambio de estado) |
|
|
390
|
+
| `store.connect(spec, handler)` | Suscripcion de grano fino por ruta con wildcards |
|
|
391
|
+
| `store.onEvent(channel, type, handler, phase?)` | Suscripcion a eventos (committed/uncommitted/all) |
|
|
392
|
+
| `store.onEffect(channel, type, handler)` | Shorthand de efecto para un solo evento |
|
|
393
|
+
| `store.dispose()` | Limpiar timers y recursos |
|
|
394
|
+
|
|
395
|
+
### Registro Dinamico
|
|
396
|
+
|
|
397
|
+
| API | Descripcion |
|
|
398
|
+
| ----------------------------------- | ----------------------------------------- |
|
|
399
|
+
| `store.registerReducer(name, spec)` | Agregar un slice en tiempo de ejecucion |
|
|
400
|
+
| `store.registerMiddleware(fn)` | Agregar middleware en tiempo de ejecucion |
|
|
401
|
+
| `store.registerEffect(spec)` | Agregar un efecto en tiempo de ejecucion |
|
|
402
|
+
|
|
403
|
+
### HMR
|
|
404
|
+
|
|
405
|
+
| API | Descripcion |
|
|
406
|
+
| --------------------------------------- | ------------------------------------------- |
|
|
407
|
+
| `store.replaceReducers(reducers, opts)` | Reemplazar todos los reducers |
|
|
408
|
+
| `store.replaceMiddleware(middleware)` | Reemplazar todos los middleware |
|
|
409
|
+
| `store.replaceEffects(effects)` | Reemplazar todos los efectos |
|
|
410
|
+
| `store.hotReplace(partial)` | Reemplazar cualquier subconjunto de una vez |
|
|
411
|
+
|
|
412
|
+
### Helpers
|
|
413
|
+
|
|
414
|
+
| API | Descripcion |
|
|
415
|
+
| ------------------------ | ---------------------------------------------------------------- |
|
|
416
|
+
| `eventKeys<EM>()([...])` | Arrays de claves de evento con seguridad de tipos sin `as const` |
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## Rendimiento
|
|
421
|
+
|
|
422
|
+
| Metrica | Valor |
|
|
423
|
+
| --------------------- | ----------------------------------------- |
|
|
424
|
+
| **Tamano del bundle** | ~8KB (minificado + gzipped) |
|
|
425
|
+
| **Tree-shakeable** | Si (modulos ES) |
|
|
426
|
+
| **Dependencias** | Cero |
|
|
427
|
+
| **TypeScript** | Definiciones de tipos completas incluidas |
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Documentacion
|
|
432
|
+
|
|
433
|
+
- **[README raiz de yoltra](https://github.com/yoltra/yoltra/blob/main/README.md)** --
|
|
434
|
+
Descripcion general y configuracion rapida
|
|
435
|
+
- **[@yoltra/react](https://github.com/yoltra/yoltra/blob/main/packages/react/README.md)** --
|
|
436
|
+
Hooks de React y Suspense
|
|
437
|
+
- **[Guia de Inicio Rapido](https://github.com/yoltra/yoltra/blob/main/docs/en/QUICK_START_GUIDE.md)**
|
|
438
|
+
-- Cinco pasos hacia una app funcional
|
|
439
|
+
- **[Arquitectura de Cola de Eventos](https://github.com/yoltra/yoltra/blob/main/docs/en/design/event-queue-architecture.md)**
|
|
440
|
+
-- Inmersion tecnica profunda
|
|
441
|
+
- **[Comparacion de Bibliotecas](https://github.com/yoltra/yoltra/blob/main/docs/en/design/state-management-library-comparison.md)**
|
|
442
|
+
-- Comparacion arquitectonica
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Ejemplos
|
|
447
|
+
|
|
448
|
+
- **[App de Tareas](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react)** --
|
|
449
|
+
CRUD completo con perfilado de rendimiento
|
|
450
|
+
- **[Logo Cinetico](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-kinetic-logo)**
|
|
451
|
+
-- 3000 cΓrculos con simulaciΓ³n fΓsica.
|
|
452
|
+
- **[Integracion con Next.js](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-nextjs)**
|
|
453
|
+
-- SSR + App Router + cambio de tema
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Contribuir
|
|
458
|
+
|
|
459
|
+
- [Raiz del Monorepo](https://github.com/yoltra/yoltra/blob/main/README.md)
|
|
460
|
+
- [Guia de Contribucion](https://github.com/yoltra/yoltra/blob/main/CONTRIBUTING.md)
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## Estado
|
|
465
|
+
|
|
466
|
+
**Release Candidate** -- Las APIs son estables, usadas en produccion, cambios menores posibles
|
|
467
|
+
antes de v1.0.0.
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## Licencia
|
|
472
|
+
|
|
473
|
+
**MIT** -- Libre para usar en proyectos comerciales y de codigo abierto.
|