@yoltra/react 0.6.0 → 0.7.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/README.es.md CHANGED
@@ -3,7 +3,7 @@
3
3
  # @yoltra/react
4
4
 
5
5
  > 👉 🇲🇽 Versión en Español  |
6
- >  [ 🇺🇸 English Version](./README.md) 
6
+ >  [ 🇺🇸 English Versión](./README.md) 
7
7
 
8
8
  ![npm downloads](https://badgen.net/npm/dm/@yoltra/react)
9
9
  ![License](https://badgen.net/npm/license/@yoltra/react)
@@ -11,14 +11,14 @@
11
11
  **Hooks de React para [yoltra](../../README.md) con
12
12
  suscripciones de grano fino por ruta.**
13
13
 
14
- Suscribete a `"items.0.title"` o `"items.*.done"` -- el componente se re-renderiza solo cuando
15
- esa ruta exacta cambia. Sin selectores, sin memoizacion, sin optimizacion manual.
14
+ Suscríbete a `"items.0.title"` o `"items.*.done"`. El componente se re-renderiza solo cuando
15
+ esa ruta exacta cambia. Sin selectores, sin memoización, sin optimización manual.
16
16
 
17
- [Ver la comparacion de flamegraph (Redux vs yoltra).](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
17
+ [Ver la comparación de flamegraph (Redux vs yoltra).](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
18
18
 
19
19
  ---
20
20
 
21
- ## Instalacion
21
+ ## Instalación
22
22
 
23
23
  ```bash
24
24
  npm install @yoltra/core @yoltra/react
@@ -28,11 +28,11 @@ npm install @yoltra/core @yoltra/react
28
28
 
29
29
  ---
30
30
 
31
- ## Configuracion con `createYoltra` (recomendado)
31
+ ## Configuración con `createYoltra` (recomendado)
32
32
 
33
- `createYoltra` crea el store **y** todos los hooks tipados en una sola llamada — sin archivo de
34
- context aparte, sin cableado de `createHooks`, sin provider obligatorio. Todos los parametros de
35
- tipo se infieren de tu reducer, asi que los componentes no necesitan generics explicitos.
33
+ `createYoltra` crea el store **y** todos los hooks tipados en una sola llamada, sin archivo de
34
+ context aparte, sin cableado de `createHooks`, sin provider obligatorio. Todos los parámetros de
35
+ tipo se infieren de tu reducer, así que los componentes no necesitan generics explícitos.
36
36
 
37
37
  ### 1. Crea el store y los hooks
38
38
 
@@ -74,10 +74,10 @@ export const { store, useAtomicProp, useEmit, StoreProvider } = createYoltra({
74
74
  });
75
75
  ```
76
76
 
77
- ### 2. Usa los hooks — sin provider
77
+ ### 2. Usa los hooks, sin provider
78
78
 
79
- Los hooks usan por defecto el store de arriba, asi que puedes renderizar componentes directamente.
80
- Suscribete con una spec **`{ reducer, property }`**: el `property` con puntos nombra la ruta exacta
79
+ Los hooks usan por defecto el store de arriba, así que puedes renderizar componentes directamente.
80
+ Suscríbete con una spec **`{ reducer, property }`**: el `property` con puntos nombra la ruta exacta
81
81
  a leer.
82
82
 
83
83
  ```tsx
@@ -85,7 +85,7 @@ a leer.
85
85
  import { useAtomicProp, useEmit } from "./yoltra";
86
86
 
87
87
  export function Counter() {
88
- // Forma objeto — se re-renderiza solo cuando counter.value cambia. Sin selectores, sin memo.
88
+ // Forma objeto: se re-renderiza solo cuando counter.value cambia. Sin selectores, sin memo.
89
89
  const value = useAtomicProp({ reducer: "counter", property: "value" });
90
90
  const emit = useEmit();
91
91
 
@@ -101,13 +101,13 @@ export function Counter() {
101
101
  ```
102
102
 
103
103
  Un `<StoreProvider>` solo se necesita para acotar una instancia **diferente** del store a un
104
- subarbol (p. ej. un store nuevo por test) — `createYoltra` devuelve uno justo para eso.
104
+ subarbol (p. ej. un store nuevo por test). `createYoltra` devuelve uno justo para eso.
105
105
 
106
106
  ---
107
107
 
108
108
  ## Avanzado: cableado manual con `createHooks`
109
109
 
110
- Cuando necesites un mismo conjunto de hooks compartido entre varias instancias de store a traves
110
+ Cuando necesites un mismo conjunto de hooks compartido entre varias instancias de store a través
111
111
  de tu propio context de React, vinculalos tu mismo con `createHooks(context)`. `createYoltra` es
112
112
  este mismo cableado colapsado en una sola llamada.
113
113
 
@@ -135,7 +135,7 @@ export const {
135
135
  } = createHooks(AppStoreContext);
136
136
  ```
137
137
 
138
- Provee el store con `<AppStoreContext.Provider value={store}>` en tu raiz.
138
+ Provee el store con `<AppStoreContext.Provider value={store}>` en tu raíz.
139
139
 
140
140
  ---
141
141
 
@@ -143,21 +143,21 @@ Provee el store con `<AppStoreContext.Provider value={store}>` en tu raiz.
143
143
 
144
144
  ### `useAtomicProp({ reducer, property }, map?, isEqual?)`
145
145
 
146
- Selector de ruta unica con grano fino. Se re-renderiza solo cuando la hoja especificada cambia. El
147
- `property` con puntos nombra la ruta exacta — incluyendo rutas dinamicas
146
+ Selector de ruta única con grano fino. Se re-renderiza solo cuando la hoja especificada cambia. El
147
+ `property` con puntos nombra la ruta exacta, incluyendo rutas dinámicas
148
148
  (`` `items.${id}.title` ``) y con comodines.
149
149
 
150
150
  ```tsx
151
- // Forma objeto (recomendada) — suscribete a la ruta exacta
151
+ // Forma objeto (recomendada): suscribete a la ruta exacta
152
152
  const title = useAtomicProp({ reducer: "todos", property: "items.0.title" });
153
153
 
154
- // Ruta dinamica — interpola la clave
154
+ // Ruta dinamica: interpola la clave
155
155
  const byId = useAtomicProp({ reducer: "todos", property: `items.${id}.title` });
156
156
 
157
- // Con mapper — derivar un valor de la ruta
157
+ // Con mapper: derivar un valor de la ruta
158
158
  const count = useAtomicProp({ reducer: "todos", property: "items" }, (items) => items.length);
159
159
 
160
- // Patron wildcard — se re-renderiza cuando cualquier item cambia
160
+ // Patron wildcard: se re-renderiza cuando cualquier item cambia
161
161
  const allTitles = useAtomicProp(
162
162
  { reducer: "todos", property: "items.**" },
163
163
  (state) => state.items.map((t) => t.title),
@@ -165,20 +165,20 @@ const allTitles = useAtomicProp(
165
165
  );
166
166
  ```
167
167
 
168
- > Tambien existe una sobrecarga con accessor tipado — `useAtomicProp("todos", (s) => s.items[0].title)`
169
- > — para rutas estaticas; autocompleta la forma del estado e infiere el tipo de retorno.
168
+ > También existe una sobrecarga con accessor tipado, `useAtomicProp("todos", (s) => s.items[0].title)`,
169
+ > para rutas estáticas; autocompleta la forma del estado e infiere el tipo de retorno.
170
170
 
171
171
  **Patrones soportados:**
172
172
 
173
- - `"items.0.title"` -- ruta exacta (incluyendo indices numericos de array)
174
- - `"items.*.title"` -- `*` coincide con un segmento
175
- - `"items.**"` -- `**` coincide con cero o mas segmentos
173
+ - `"items.0.title"`: ruta exacta (incluyendo índices numéricos de array)
174
+ - `"items.*.title"`: `*` coincide con un segmento
175
+ - `"items.**"`: `**` coincide con cero o más segmentos
176
176
 
177
177
  ---
178
178
 
179
179
  ### `useAtomicProps(specs, selector, isEqual?)`
180
180
 
181
- Selector de multiples rutas. Se suscribe a varias rutas y recalcula cuando alguna cambia.
181
+ Selector de múltiples rutas. Se suscribe a varias rutas y recalcula cuando alguna cambia.
182
182
 
183
183
  ```tsx
184
184
  const filtered = useAtomicProps(
@@ -195,16 +195,15 @@ const filtered = useAtomicProps(
195
195
 
196
196
  ### `useEvent(channel, type, handler, phase?)`
197
197
 
198
- Suscribete a eventos del store desde un componente. No afecta el flujo de eventos --
199
- fire-and-forget.
198
+ Suscríbete a eventos del store desde un componente. No afecta el flujo de eventos. Es fire-and-forget.
200
199
 
201
200
  ```tsx
202
- // Eventos confirmados (por defecto) — eventos que pasaron el middleware
201
+ // Eventos confirmados (por defecto): eventos que pasaron el middleware
203
202
  useEvent("ui", "save", (event) => {
204
203
  showToast("Saved!");
205
204
  });
206
205
 
207
- // Eventos no confirmados — eventos rechazados por el middleware
206
+ // Eventos no confirmados: eventos rechazados por el middleware
208
207
  useEvent(
209
208
  "ui",
210
209
  "delete",
@@ -214,7 +213,7 @@ useEvent(
214
213
  "uncommitted",
215
214
  );
216
215
 
217
- // Todos los eventos — distinguir por fase
216
+ // Todos los eventos: distinguir por fase
218
217
  useEvent(
219
218
  "ui",
220
219
  "action",
@@ -227,15 +226,15 @@ useEvent(
227
226
 
228
227
  **Fases:**
229
228
 
230
- - `'committed'` (por defecto) -- eventos que pasaron el middleware y llegaron a los reducers
231
- - `'uncommitted'` -- eventos rechazados por el middleware
232
- - `'all'` -- ambos, con parametro `phase` para distinguir
229
+ - `'committed'` (por defecto): eventos que pasaron el middleware y llegaron a los reducers
230
+ - `'uncommitted'`: eventos rechazados por el middleware
231
+ - `'all'`: ambos, con parámetro `phase` para distinguir
233
232
 
234
233
  ---
235
234
 
236
235
  ### `useEmit()`
237
236
 
238
- Retorna la funcion `emit` tipada del store (referencia estable).
237
+ Retorna la función `emit` tipada del store (referencia estable).
239
238
 
240
239
  ```tsx
241
240
  const emit = useEmit();
@@ -246,7 +245,7 @@ await emit("counter", "increment", 1);
246
245
 
247
246
  ### `useSelector(selector, isEqual?)`
248
247
 
249
- Selector de grano grueso via `useSyncExternalStore`. Se re-renderiza cuando el valor
248
+ Selector de grano grueso vía `useSyncExternalStore`. Se re-renderiza cuando el valor
250
249
  seleccionado cambia.
251
250
 
252
251
  ```tsx
@@ -270,7 +269,7 @@ const value = store.getState().counter.value;
270
269
  ```
271
270
 
272
271
  `getState()` es una lectura, no una suscripción. Llamado durante el render, el componente se
273
- renderiza una vez con ese valor y nunca más — nada le avisó de que el valor cambió. Parece que
272
+ renderiza una vez con ese valor y nunca más, porque nada le avisó de que el valor cambió. Parece que
274
273
  funciona hasta que el estado cambia y la pantalla no. Lee con `useAtomicProp` o `useSelector` lo
275
274
  que vayas a renderizar, y deja `getState()` para callbacks y efectos, que es para lo que es.
276
275
 
@@ -280,8 +279,8 @@ que vayas a renderizar, y deja `getState()` para callbacks y efectos, que es par
280
279
 
281
280
  ### `useSuspenseAtomicProp(spec, options)`
282
281
 
283
- Version compatible con Suspense de `useAtomicProp`. Lanza una promesa mientras carga, capturada
284
- por el boundary `<Suspense>` mas cercano.
282
+ Versión compatible con Suspense de `useAtomicProp`. Lanza una promesa mientras carga, capturada
283
+ por el boundary `<Suspense>` más cercano.
285
284
 
286
285
  ```tsx
287
286
  function UserName({ userId }: { userId: string }) {
@@ -303,7 +302,7 @@ function UserName({ userId }: { userId: string }) {
303
302
 
304
303
  ### `useSuspenseAtomicProps(specs, options)`
305
304
 
306
- Selector Suspense de multiples rutas.
305
+ Selector Suspense de múltiples rutas.
307
306
 
308
307
  ```tsx
309
308
  const stats = useSuspenseAtomicProps(
@@ -318,11 +317,11 @@ const stats = useSuspenseAtomicProps(
318
317
  ### Importalos de tu conjunto de hooks, no del barrel
319
318
 
320
319
  `createYoltra` y `createHooks` devuelven estos dos junto con el resto, ligados al mismo contexto.
321
- Deliberadamente **no** se exportan desde el barrel del paquete: una copia a nivel de paquete seria
322
- identica en forma y aun asi lanzaria `useStore must be used inside <StoreProvider>` en tiempo de
323
- ejecucion cuando el contexto que lee nunca se lleno — un error que los tipos no podian atrapar.
320
+ Deliberadamente **no** se exportan desde el barrel del paquete: una copia a nivel de paquete sería
321
+ idéntica en forma y aun así lanzaría `useStore must be used inside <StoreProvider>` en tiempo de
322
+ ejecución cuando el contexto que lee nunca se lleno, un error que los tipos no podian atrapar.
324
323
  Importarlos desde cualquier sitio que no sea el resultado de tu propio `createYoltra`/`createHooks`
325
- es ahora un error de compilacion, que es el mismo aviso llegando en el momento correcto.
324
+ es ahora un error de compilación, que es el mismo aviso llegando en el momento correcto.
326
325
 
327
326
  ```tsx
328
327
  // store.ts
@@ -332,8 +331,8 @@ export const { store, useAtomicProp, useSuspenseAtomicProp } = createYoltra({ ..
332
331
  import { useSuspenseAtomicProp } from "./store"; // ✅ conoce el store
333
332
  ```
334
333
 
335
- Los valores en cache tienen alcance por store, asi que dos stores que compartan nombre de reducer
336
- y ruta mantienen entradas separadas; las utilidades de invalidacion de abajo reciben una ruta y la
334
+ Los valores en cache tienen alcance por store, así que dos stores que compartan nombre de reducer
335
+ y ruta mantienen entradas separadas; las utilidades de invalidación de abajo reciben una ruta y la
337
336
  limpian en todos los stores que la hayan cacheado.
338
337
 
339
338
  ### Utilidades de cache
@@ -359,7 +358,7 @@ clearSuspenseCache();
359
358
 
360
359
  ## `shallowEqual`
361
360
 
362
- Comparador de igualdad superficial de objetos. Usalo como argumento `isEqual` cuando tu valor
361
+ Comparador de igualdad superficial de objetos. Úsalo como argumento `isEqual` cuando tu valor
363
362
  derivado es un objeto plano:
364
363
 
365
364
  ```tsx
@@ -372,7 +371,7 @@ const todos = useAtomicProp(
372
371
 
373
372
  ---
374
373
 
375
- ## Rendimiento: Antes y Despues
374
+ ## Rendimiento: Antes y Después
376
375
 
377
376
  ### Antes (grano grueso)
378
377
 
@@ -384,7 +383,7 @@ function TodoList() {
384
383
  }
385
384
  ```
386
385
 
387
- ### Despues (grano fino con yoltra)
386
+ ### Después (grano fino con yoltra)
388
387
 
389
388
  ```tsx
390
389
  // Cada TodoItem se re-renderiza SOLO cuando sus propios datos cambian
@@ -401,14 +400,14 @@ function TodoItem({ index }: { index: number }) {
401
400
  }
402
401
  ```
403
402
 
404
- [Ver la comparacion completa de flamegraph.](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
403
+ [Ver la comparación completa de flamegraph.](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
405
404
 
406
405
  ---
407
406
 
408
407
  ## Compatibilidad con React 18+
409
408
 
410
409
  - **Concurrent Mode:** Totalmente compatible. Todos los hooks usan `useSyncExternalStore`.
411
- - **Strict Mode:** La deduplicacion de eventos previene el doble procesamiento.
410
+ - **Strict Mode:** La deduplicación de eventos previene el doble procesamiento.
412
411
  - **Suspense:** `useSuspenseAtomicProp` y `useSuspenseAtomicProps` lanzan promesas para
413
412
  boundaries `<Suspense>`.
414
413
 
@@ -416,37 +415,37 @@ function TodoItem({ index }: { index: number }) {
416
415
 
417
416
  ## Ejemplos
418
417
 
419
- - **[App de Tareas con Profiler](../../examples/v0/yoltra-in-react)** -- CRUD completo con
420
- comparacion de flamegraph · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/in-react)
421
- - **[Logo Cinetico (3000 particulas)](../../examples/v0/yoltra-kinetic-logo)** -- Suscripciones
418
+ - **[App de Tareas con Profiler](../../examples/v0/yoltra-in-react)**: CRUD completo con
419
+ comparación de flamegraph · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/in-react)
420
+ - **[Logo Cinético (3000 particulas)](../../examples/v0/yoltra-kinetic-logo)**: Suscripciones
422
421
  independientes por circulo SVG · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/kinetic-logo)
423
- - **[Next.js (Pages Router)](../../examples/v0/yoltra-in-nextjs)** -- estado de cliente + cambio de tema · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/in-nextjs)
422
+ - **[Next.js (Pages Router)](../../examples/v0/yoltra-in-nextjs)**: estado de cliente + cambio de tema · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/in-nextjs)
424
423
 
425
424
  ---
426
425
 
427
- ## Documentacion
426
+ ## Documentación
428
427
 
429
- - **[README raiz de yoltra](../../README.md)** --
430
- Descripcion general y configuracion rapida
431
- - **[API de @yoltra/core](../core/README.md)**
432
- -- Store, middleware, efectos, matchers `When`
433
- - **[Guia de Inicio Rapido](https://github.com/yoltra/yoltra/blob/main/docs/en/QUICK_START_GUIDE.md)**
434
- -- Cinco pasos hacia una app funcional
435
- - **[Comparacion de Bibliotecas](https://github.com/yoltra/yoltra/blob/main/docs/en/design/state-management-library-comparison.md)**
436
- -- Comparacion arquitectonica
428
+ - **[README raíz de yoltra](../../README.md)**:
429
+ Descripción general y configuración rápida
430
+ - **[API de @yoltra/core](../core/README.md)**:
431
+ Store, middleware, efectos, matchers `When`
432
+ - **[Guia de Inicio Rápido](https://github.com/yoltra/yoltra/blob/main/docs/en/QUICK_START_GUIDE.md)**:
433
+ Cinco pasos hacia una app funcional
434
+ - **[Comparación de Bibliotecas](https://github.com/yoltra/yoltra/blob/main/docs/en/design/state-management-library-comparison.md)**:
435
+ Comparación arquitectónica
437
436
 
438
437
  ---
439
438
 
440
439
  ## Contribuir
441
440
 
442
- - [Raiz del Monorepo](../../)
443
- - [Guia de Contribucion](../../CONTRIBUTING.md)
441
+ - [Raíz del Monorepo](../../)
442
+ - [Guia de Contribución](../../CONTRIBUTING.md)
444
443
 
445
444
  ---
446
445
 
447
446
  ## Estado
448
447
 
449
- **Release Candidate** -- Las APIs son estables, usadas en produccion, cambios menores posibles
448
+ **Release Candidate**. Las APIs son estables, usadas en producción, cambios menores posibles
450
449
  antes de v1.0.0.
451
450
 
452
451
  ---
@@ -454,7 +453,7 @@ antes de v1.0.0.
454
453
  ## Colecciones normalizadas
455
454
 
456
455
  `useEntityIds`, `useEntity` y `useEntityField` se emparejan con `createEntityAdapter` de
457
- `@yoltra/core`. Son envoltorios delgados sobre `useAtomicProp`; el valor esta en que la ruta viene
456
+ `@yoltra/core`. Son envoltorios delgados sobre `useAtomicProp`; el valor está en que la ruta viene
458
457
  del adapter en vez de escribirse a mano en un componente, donde nada la verifica.
459
458
 
460
459
  ```tsx
@@ -472,4 +471,4 @@ function Row({ id }: { id: string }) {
472
471
 
473
472
  ## Licencia
474
473
 
475
- **MIT** -- Libre para usar en proyectos comerciales y de codigo abierto.
474
+ **MIT**. Libre para usar en proyectos comerciales y de código abierto.
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  **React hooks for [yoltra](../../README.md) with
12
12
  fine-grained path subscriptions.**
13
13
 
14
- Subscribe to `"items.0.title"` or `"items.*.done"` — the component re-renders only when that
14
+ Subscribe to `"items.0.title"` or `"items.*.done"`. The component re-renders only when that
15
15
  exact path changes. No selectors, no memoization, no manual optimization.
16
16
 
17
17
  [See the flamegraph comparison (Redux vs yoltra).](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
@@ -30,7 +30,7 @@ npm install @yoltra/core @yoltra/react
30
30
 
31
31
  ## Setup with `createYoltra` (recommended)
32
32
 
33
- `createYoltra` creates the store **and** every fully-typed hook in one call — no separate context
33
+ `createYoltra` creates the store **and** every fully-typed hook in one call, with no separate context
34
34
  file, no `createHooks` wiring, no required provider. All type parameters are inferred from your
35
35
  reducer, so components need no explicit generics.
36
36
 
@@ -74,7 +74,7 @@ export const { store, useAtomicProp, useEmit, StoreProvider } = createYoltra({
74
74
  });
75
75
  ```
76
76
 
77
- ### 2. Use the hooks — no provider required
77
+ ### 2. Use the hooks, no provider required
78
78
 
79
79
  The hooks default to the store above, so you can render components directly. Subscribe with a
80
80
  **`{ reducer, property }`** spec: the dotted `property` names the exact path to read.
@@ -84,7 +84,7 @@ The hooks default to the store above, so you can render components directly. Sub
84
84
  import { useAtomicProp, useEmit } from "./yoltra";
85
85
 
86
86
  export function Counter() {
87
- // Object form — re-renders only when counter.value changes. No selectors, no memo.
87
+ // Object form: re-renders only when counter.value changes. No selectors, no memo.
88
88
  const value = useAtomicProp({ reducer: "counter", property: "value" });
89
89
  const emit = useEmit();
90
90
 
@@ -100,7 +100,7 @@ export function Counter() {
100
100
  ```
101
101
 
102
102
  A `<StoreProvider>` is only needed to scope a **different** store instance to a subtree (e.g. a
103
- fresh store per test) — `createYoltra` returns one for exactly that.
103
+ fresh store per test). `createYoltra` returns one for exactly that.
104
104
 
105
105
  ---
106
106
 
@@ -143,19 +143,19 @@ Provide the store with `<AppStoreContext.Provider value={store}>` at your root.
143
143
  ### `useAtomicProp({ reducer, property }, map?, isEqual?)`
144
144
 
145
145
  Fine-grained single-path selector. Re-renders only when the specified leaf changes. The dotted
146
- `property` names the exact path — including dynamic (`` `items.${id}.title` ``) and wildcard paths.
146
+ `property` names the exact path, including dynamic (`` `items.${id}.title` ``) and wildcard paths.
147
147
 
148
148
  ```tsx
149
- // Object form (recommended) — subscribe to the exact path
149
+ // Object form (recommended): subscribe to the exact path
150
150
  const title = useAtomicProp({ reducer: "todos", property: "items.0.title" });
151
151
 
152
- // Dynamic path — interpolate the key
152
+ // Dynamic path: interpolate the key
153
153
  const byId = useAtomicProp({ reducer: "todos", property: `items.${id}.title` });
154
154
 
155
- // With mapper — derive a value from the path
155
+ // With mapper: derive a value from the path
156
156
  const count = useAtomicProp({ reducer: "todos", property: "items" }, (items) => items.length);
157
157
 
158
- // Wildcard pattern — re-renders when any item changes
158
+ // Wildcard pattern: re-renders when any item changes
159
159
  const allTitles = useAtomicProp(
160
160
  { reducer: "todos", property: "items.**" },
161
161
  (state) => state.items.map((t) => t.title),
@@ -163,14 +163,14 @@ const allTitles = useAtomicProp(
163
163
  );
164
164
  ```
165
165
 
166
- > A typed-accessor overload — `useAtomicProp("todos", (s) => s.items[0].title)` — is also available
166
+ > A typed-accessor overload, `useAtomicProp("todos", (s) => s.items[0].title)`, is also available
167
167
  > for static paths; it autocompletes the state shape and infers the return type.
168
168
 
169
169
  **Supported patterns:**
170
170
 
171
- - `"items.0.title"` — exact path (including numeric array indices)
172
- - `"items.*.title"` — `*` matches one segment
173
- - `"items.**"` — `**` matches zero or more segments
171
+ - `"items.0.title"`: exact path (including numeric array indices)
172
+ - `"items.*.title"`: `*` matches one segment
173
+ - `"items.**"`: `**` matches zero or more segments
174
174
 
175
175
  ---
176
176
 
@@ -193,15 +193,15 @@ const filtered = useAtomicProps(
193
193
 
194
194
  ### `useEvent(channel, type, handler, phase?)`
195
195
 
196
- Subscribe to store events from a component. Does not affect event flow — fire-and-forget.
196
+ Subscribe to store events from a component. Does not affect event flow. Fire-and-forget.
197
197
 
198
198
  ```tsx
199
- // Committed events (default) — events that passed middleware
199
+ // Committed events (default): events that passed middleware
200
200
  useEvent("ui", "save", (event) => {
201
201
  showToast("Saved!");
202
202
  });
203
203
 
204
- // Uncommitted events — events rejected by middleware
204
+ // Uncommitted events: events rejected by middleware
205
205
  useEvent(
206
206
  "ui",
207
207
  "delete",
@@ -211,7 +211,7 @@ useEvent(
211
211
  "uncommitted",
212
212
  );
213
213
 
214
- // All events — distinguish by phase
214
+ // All events: distinguish by phase
215
215
  useEvent(
216
216
  "ui",
217
217
  "action",
@@ -224,9 +224,9 @@ useEvent(
224
224
 
225
225
  **Phases:**
226
226
 
227
- - `'committed'` (default) — events that passed middleware and reached reducers
228
- - `'uncommitted'` — events rejected by middleware
229
- - `'all'` — both, with `phase` parameter to distinguish
227
+ - `'committed'` (default): events that passed middleware and reached reducers
228
+ - `'uncommitted'`: events rejected by middleware
229
+ - `'all'`: both, with `phase` parameter to distinguish
230
230
 
231
231
  ---
232
232
 
@@ -266,7 +266,7 @@ const value = store.getState().counter.value;
266
266
  ```
267
267
 
268
268
  `getState()` is a read, not a subscription. Called while rendering, the component renders once
269
- with that value and never again — nothing told it the value moved. It looks like it works right
269
+ with that value and never again, because nothing told it the value moved. It looks like it works right
270
270
  up until the state changes and the screen does not. Read what you render with `useAtomicProp` or
271
271
  `useSelector`, and keep `getState()` for callbacks and effects, which is what it is for.
272
272
 
@@ -316,7 +316,7 @@ const stats = useSuspenseAtomicProps(
316
316
  `createYoltra` and `createHooks` return these two alongside the rest, bound to the same context.
317
317
  They are deliberately **not** exported from the package barrel: a package-level copy would be
318
318
  identical in shape and still throw `useStore must be used inside <StoreProvider>` at runtime
319
- whenever the context it reads was never filled — a mistake the types could not catch. Importing
319
+ whenever the context it reads was never filled, a mistake the types could not catch. Importing
320
320
  them from anywhere but your own `createYoltra`/`createHooks` result is now a compile error,
321
321
  which is the same warning arriving at the right time.
322
322
 
@@ -430,24 +430,24 @@ function Row({ id }: { id: string }) {
430
430
 
431
431
  ## Examples
432
432
 
433
- - **[Todo App with Profiler](../../examples/v0/yoltra-in-react)** — Full CRUD with flamegraph
433
+ - **[Todo App with Profiler](../../examples/v0/yoltra-in-react)**: Full CRUD with flamegraph
434
434
  comparison · [▶ Open the live demo](https://yoltra.dev/en/demos/in-react)
435
- - **[Kinetic Logo (3000 particles)](../../examples/v0/yoltra-kinetic-logo)** — Independent
435
+ - **[Kinetic Logo (3000 particles)](../../examples/v0/yoltra-kinetic-logo)**: Independent
436
436
  subscriptions per circle · [▶ Open the live demo](https://yoltra.dev/en/demos/kinetic-logo)
437
- - **[Next.js (Pages Router)](../../examples/v0/yoltra-in-nextjs)** — client-side state + theme switcher · [▶ Open the live demo](https://yoltra.dev/en/demos/in-nextjs)
437
+ - **[Next.js (Pages Router)](../../examples/v0/yoltra-in-nextjs)**: client-side state + theme switcher · [▶ Open the live demo](https://yoltra.dev/en/demos/in-nextjs)
438
438
 
439
439
  ---
440
440
 
441
441
  ## Documentation
442
442
 
443
- - **[yoltra Root README](../../README.md)** — Overview and
443
+ - **[yoltra Root README](../../README.md)**: Overview and
444
444
  quick start
445
- - **[@yoltra/core API](../core/README.md)** —
445
+ - **[@yoltra/core API](../core/README.md)**:
446
446
  Store, middleware, effects, `When` matchers
447
- - **[Quick Start Guide](https://github.com/yoltra/yoltra/blob/main/docs/en/QUICK_START_GUIDE.md)**
448
- — Five steps to a working app
449
- - **[Library Comparison](https://github.com/yoltra/yoltra/blob/main/docs/en/design/state-management-library-comparison.md)**
450
- — Architectural comparison
447
+ - **[Quick Start Guide](https://github.com/yoltra/yoltra/blob/main/docs/en/QUICK_START_GUIDE.md)**:
448
+ Five steps to a working app
449
+ - **[Library Comparison](https://github.com/yoltra/yoltra/blob/main/docs/en/design/state-management-library-comparison.md)**:
450
+ Architectural comparison
451
451
 
452
452
  ---
453
453
 
@@ -460,11 +460,11 @@ function Row({ id }: { id: string }) {
460
460
 
461
461
  ## Status
462
462
 
463
- **Release Candidate** — APIs are stable, used in production, minor changes possible before
463
+ **Release Candidate**. APIs are stable, used in production, minor changes possible before
464
464
  v1.0.0.
465
465
 
466
466
  ---
467
467
 
468
468
  ## License
469
469
 
470
- **MIT** — Free to use in commercial and open-source projects.
470
+ **MIT**. Free to use in commercial and open-source projects.
package/dist/index.cjs CHANGED
@@ -1,16 +1,11 @@
1
1
  /*!
2
- * @yoltra/react v0.6.0
2
+ * @yoltra/react v0.7.0
3
3
  * (c) 2026 Manu Ramirez <@pixerael>
4
- * License: MIT
5
- * Homepage: https://yoltra.dev
6
- *
7
- * This source code is licensed under the MIT license found in the
8
- * LICENSE file in the root directory of this source tree
9
- */
10
- "use strict";var Ee=Object.defineProperty;var Re=(t,e,n)=>e in t?Ee(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var ce=(t,e,n)=>Re(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react"),Ae=require("@yoltra/core"),K=u.createContext(null);var W={exports:{}},D={};var ue;function Pe(){if(ue)return D;ue=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(o,s,c){var a=null;if(c!==void 0&&(a=""+c),s.key!==void 0&&(a=""+s.key),"key"in s){c={};for(var h in s)h!=="key"&&(c[h]=s[h])}else c=s;return s=c.ref,{$$typeof:t,type:o,key:a,ref:s!==void 0?s:null,props:c}}return D.Fragment=e,D.jsx=n,D.jsxs=n,D}var F={};var ae;function ge(){return ae||(ae=1,process.env.NODE_ENV!=="production"&&(function(){function t(r){if(r==null)return null;if(typeof r=="function")return r.$$typeof===V?null:r.displayName||r.name||null;if(typeof r=="string")return r;switch(r){case y:return"Fragment";case k:return"Profiler";case b:return"StrictMode";case $:return"Suspense";case I:return"SuspenseList";case O:return"Activity"}if(typeof r=="object")switch(typeof r.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),r.$$typeof){case v:return"Portal";case P:return(r.displayName||"Context")+".Provider";case _:return(r._context.displayName||"Context")+".Consumer";case j:var i=r.render;return r=r.displayName,r||(r=i.displayName||i.name||"",r=r!==""?"ForwardRef("+r+")":"ForwardRef"),r;case M:return i=r.displayName||null,i!==null?i:t(r.type)||"Memo";case A:i=r._payload,r=r._init;try{return t(r(i))}catch{}}return null}function e(r){return""+r}function n(r){try{e(r);var i=!1}catch{i=!0}if(i){i=console;var p=i.error,E=typeof Symbol=="function"&&Symbol.toStringTag&&r[Symbol.toStringTag]||r.constructor.name||"Object";return p.call(i,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",E),e(r)}}function o(r){if(r===y)return"<>";if(typeof r=="object"&&r!==null&&r.$$typeof===A)return"<...>";try{var i=t(r);return i?"<"+i+">":"<...>"}catch{return"<...>"}}function s(){var r=w.A;return r===null?null:r.getOwner()}function c(){return Error("react-stack-top-frame")}function a(r){if(B.call(r,"key")){var i=Object.getOwnPropertyDescriptor(r,"key").get;if(i&&i.isReactWarning)return!1}return r.key!==void 0}function h(r,i){function p(){te||(te=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",i))}p.isReactWarning=!0,Object.defineProperty(r,"key",{get:p,configurable:!0})}function S(){var r=t(this.type);return re[r]||(re[r]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),r=this.props.ref,r!==void 0?r:null}function g(r,i,p,E,C,x,X,H){return p=x.ref,r={$$typeof:f,type:r,key:i,props:x,_owner:C},(p!==void 0?p:null)!==null?Object.defineProperty(r,"ref",{enumerable:!1,get:S}):Object.defineProperty(r,"ref",{enumerable:!1,value:null}),r._store={},Object.defineProperty(r._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(r,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(r,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:X}),Object.defineProperty(r,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:H}),Object.freeze&&(Object.freeze(r.props),Object.freeze(r)),r}function d(r,i,p,E,C,x,X,H){var R=i.children;if(R!==void 0)if(E)if(J(R)){for(E=0;E<R.length;E++)l(R[E]);Object.freeze&&Object.freeze(R)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else l(R);if(B.call(i,"key")){R=t(r);var Y=Object.keys(i).filter(function(be){return be!=="key"});E=0<Y.length?"{key: someKey, "+Y.join(": ..., ")+": ...}":"{key: someKey}",se[R+E]||(Y=0<Y.length?"{"+Y.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
4
+ * License: MIT */
5
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react"),be=require("@yoltra/core"),K=c.createContext(null);var W={exports:{}},D={};var ue;function Ee(){if(ue)return D;ue=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function o(n,s,u){var a=null;if(u!==void 0&&(a=""+u),s.key!==void 0&&(a=""+s.key),"key"in s){u={};for(var m in s)m!=="key"&&(u[m]=s[m])}else u=s;return s=u.ref,{$$typeof:t,type:n,key:a,ref:s!==void 0?s:null,props:u}}return D.Fragment=e,D.jsx=o,D.jsxs=o,D}var F={};var ce;function Re(){return ce||(ce=1,process.env.NODE_ENV!=="production"&&(function(){function t(r){if(r==null)return null;if(typeof r=="function")return r.$$typeof===U?null:r.displayName||r.name||null;if(typeof r=="string")return r;switch(r){case h:return"Fragment";case k:return"Profiler";case P:return"StrictMode";case $:return"Suspense";case I:return"SuspenseList";case O:return"Activity"}if(typeof r=="object")switch(typeof r.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),r.$$typeof){case v:return"Portal";case A:return(r.displayName||"Context")+".Provider";case _:return(r._context.displayName||"Context")+".Consumer";case j:var i=r.render;return r=r.displayName,r||(r=i.displayName||i.name||"",r=r!==""?"ForwardRef("+r+")":"ForwardRef"),r;case M:return i=r.displayName||null,i!==null?i:t(r.type)||"Memo";case R:i=r._payload,r=r._init;try{return t(r(i))}catch{}}return null}function e(r){return""+r}function o(r){try{e(r);var i=!1}catch{i=!0}if(i){i=console;var p=i.error,b=typeof Symbol=="function"&&Symbol.toStringTag&&r[Symbol.toStringTag]||r.constructor.name||"Object";return p.call(i,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",b),e(r)}}function n(r){if(r===h)return"<>";if(typeof r=="object"&&r!==null&&r.$$typeof===R)return"<...>";try{var i=t(r);return i?"<"+i+">":"<...>"}catch{return"<...>"}}function s(){var r=w.A;return r===null?null:r.getOwner()}function u(){return Error("react-stack-top-frame")}function a(r){if(V.call(r,"key")){var i=Object.getOwnPropertyDescriptor(r,"key").get;if(i&&i.isReactWarning)return!1}return r.key!==void 0}function m(r,i){function p(){te||(te=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",i))}p.isReactWarning=!0,Object.defineProperty(r,"key",{get:p,configurable:!0})}function y(){var r=t(this.type);return re[r]||(re[r]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),r=this.props.ref,r!==void 0?r:null}function g(r,i,p,b,C,x,X,H){return p=x.ref,r={$$typeof:f,type:r,key:i,props:x,_owner:C},(p!==void 0?p:null)!==null?Object.defineProperty(r,"ref",{enumerable:!1,get:y}):Object.defineProperty(r,"ref",{enumerable:!1,value:null}),r._store={},Object.defineProperty(r._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(r,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(r,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:X}),Object.defineProperty(r,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:H}),Object.freeze&&(Object.freeze(r.props),Object.freeze(r)),r}function d(r,i,p,b,C,x,X,H){var E=i.children;if(E!==void 0)if(b)if(B(E)){for(b=0;b<E.length;b++)l(E[b]);Object.freeze&&Object.freeze(E)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else l(E);if(V.call(i,"key")){E=t(r);var Y=Object.keys(i).filter(function(ve){return ve!=="key"});b=0<Y.length?"{key: someKey, "+Y.join(": ..., ")+": ...}":"{key: someKey}",se[E+b]||(Y=0<Y.length?"{"+Y.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
11
6
  let props = %s;
12
7
  <%s {...props} />
13
8
  React keys must be passed directly to JSX without using spread:
14
9
  let props = %s;
15
- <%s key={someKey} {...props} />`,E,R,Y,R),se[R+E]=!0)}if(R=null,p!==void 0&&(n(p),R=""+p),a(i)&&(n(i.key),R=""+i.key),"key"in i){p={};for(var Z in i)Z!=="key"&&(p[Z]=i[Z])}else p=i;return R&&h(p,typeof r=="function"?r.displayName||r.name||"Unknown":r),g(r,R,x,C,s(),p,X,H)}function l(r){typeof r=="object"&&r!==null&&r.$$typeof===f&&r._store&&(r._store.validated=1)}var m=u,f=Symbol.for("react.transitional.element"),v=Symbol.for("react.portal"),y=Symbol.for("react.fragment"),b=Symbol.for("react.strict_mode"),k=Symbol.for("react.profiler"),_=Symbol.for("react.consumer"),P=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),$=Symbol.for("react.suspense"),I=Symbol.for("react.suspense_list"),M=Symbol.for("react.memo"),A=Symbol.for("react.lazy"),O=Symbol.for("react.activity"),V=Symbol.for("react.client.reference"),w=m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,B=Object.prototype.hasOwnProperty,J=Array.isArray,q=console.createTask?console.createTask:function(){return null};m={react_stack_bottom_frame:function(r){return r()}};var te,re={},ne=m.react_stack_bottom_frame.bind(m,c)(),oe=q(o(c)),se={};F.Fragment=y,F.jsx=function(r,i,p,E,C){var x=1e4>w.recentlyCreatedOwnerStacks++;return d(r,i,p,!1,E,C,x?Error("react-stack-top-frame"):ne,x?q(o(r)):oe)},F.jsxs=function(r,i,p,E,C){var x=1e4>w.recentlyCreatedOwnerStacks++;return d(r,i,p,!0,E,C,x?Error("react-stack-top-frame"):ne,x?q(o(r)):oe)}})()),F}var ie;function _e(){return ie||(ie=1,process.env.NODE_ENV==="production"?W.exports=Pe():W.exports=ge()),W.exports}var de=_e();const we=({store:t,children:e})=>de.jsx(K.Provider,{value:t,children:e}),le=new Set;function xe(t,e){process.env.NODE_ENV!=="production"&&(le.has(t)||(le.add(t),console.warn(e)))}function G(t){return t.includes("*")}function T(t){return t.replace(/^\./,"")}function Te(t){return T(t).split(".").filter(Boolean)}function ke(t){const e=n=>`${n.length}:${n}`;return t.map(n=>{const o=Array.isArray(n.property)?n.property:[n.property];return e(n.reducer)+e(String(o.length))+o.map(e).join("")}).join("")}const me=Symbol("yoltra.pathSegments");function he(t){const e=()=>{};return new Proxy(e,{get(n,o){if(o===me)return t;if(typeof o!="symbol")return he([...t,String(o)])},apply(){throw new Error(`[yoltra] A typed path accessor called a method (near "${t.join(".")}"). The accessor must be a plain member chain like \`p => p.items[0].title\` — it cannot call functions such as \`.map()\` or \`.toString()\`. Compute derived values in the component or a selector, or use the \`{ reducer, property }\` string form.`)}})}function je(t){const e=t(he([])),n=e!=null?e[me]:void 0,s=(Array.isArray(n)?n:[]).join(".");return s===""&&xe("yoltra.toDottedPath.empty","[yoltra] A typed path accessor recorded no property access, so it subscribed to the slice root. That fires only when the slice's whole value is replaced — which for a slice holding an object means never, since its changes are reported at their leaves. The accessor must be a plain member chain like `p => p.items[0].title` and cannot return a computed value or a default. For a dynamic subscription, or for a slice whose state is a single value, use the `{ reducer, property }` string form instead."),s}function L(t,e){if(!e)return t;let n=t;for(const o of Te(e)){if(n==null)return;n=n[o]}return n}function U(t,e,n){const o=u.useRef(t);o.current=t;const s=u.useRef(e);s.current=e;const c=u.useMemo(()=>({has:!1,value:void 0}),n);return u.useCallback(()=>{const a=o.current();return(!c.has||!s.current(c.value,a))&&(c.has=!0,c.value=a),c.value},[c])}function ye(t,e){if(Object.is(t,e))return!0;if(!t||!e)return!1;const n=Object.keys(t),o=Object.keys(e);if(n.length!==o.length)return!1;for(const s of n)if(!Object.is(t[s],e[s]))return!1;return!0}function z(){const t=u.useContext(K);if(!t)throw new Error("useStore must be used inside <StoreProvider>");return t}function Oe(){return z().emit}function Ce(t,e=Object.is){const n=z(),o=u.useMemo(()=>c=>n.subscribe(c),[n]),s=U(()=>t(n.getState()),e,[n]);return u.useSyncExternalStore(o,s,s)}function Ne(t,e,n=Object.is){const o=z(),s=u.useMemo(()=>{const S=T(t.property);return{reducer:t.reducer,property:S}},[t.reducer,t.property]),c=u.useMemo(()=>S=>o.connect({reducer:s.reducer,property:s.property},()=>S()),[o,s]),a=G(s.property),h=U(()=>{const g=o.getState()[s.reducer];return a?g:L(g,s.property)},n,[o,s]);return u.useSyncExternalStore(c,h,h)}function Q(t,e,n=Object.is){return Ne(t,e,n)}function Me(t,e,n,o="committed"){const s=z(),c=u.useRef(n);c.current=n,u.useEffect(()=>s.onEvent(t,e,(a,h,S,g)=>{c.current(a,h,S,g)},o),[s,t,e,o])}function $e(t){return t==null||t<=0?null:Date.now()+t}function Ie(t){return t===null?null:t===void 0||t<=0?0:Date.now()+t}const Ye=2e3;class De{constructor(){ce(this,"store",new Map)}touch(e){this.store.has(e)&&this.store.delete(e)}evict(){for(;this.store.size>Ye;){const e=this.store.keys().next();if(e.done===!0)return;const n=this.store.get(e.value);if((n==null?void 0:n.status)==="pending"){this.store.delete(e.value),this.store.set(e.value,n);continue}this.store.delete(e.value)}}get size(){return this.store.size}read(e,n,o,s){const c=Date.now(),a=this.store.get(e);if(a&&a.status==="ready"&&(a.expiresAt==null||a.expiresAt>c))return this.touch(e),this.store.set(e,a),a.value;if(a&&a.status==="pending")throw a.promise;if(a&&a.status==="error"){if(a.delivered!==!0)throw this.store.set(e,{...a,delivered:!0}),a.error;if(a.expiresAt===null||a.expiresAt>c)throw a.error;this.store.delete(e)}const h=Promise.resolve().then(n).then(S=>{this.store.set(e,{status:"ready",value:S,expiresAt:$e(o)})}).catch(S=>{this.store.set(e,{status:"error",error:S,expiresAt:Ie(s)})});throw this.store.set(e,{status:"pending",promise:h,expiresAt:null}),this.evict(),h}invalidate(e){this.store.delete(e)}invalidatePathKey(e){for(const n of this.store.keys())pe(n)===e&&this.store.delete(n)}invalidateReducer(e){const n=`${e}::`;for(const o of this.store.keys())pe(o).split("||").some(s=>s.startsWith(n))&&this.store.delete(o)}clear(){this.store.clear()}}const N=new De,fe=new WeakMap;let Fe=0;function Se(t){let e=fe.get(t);return e===void 0&&(e=`s${++Fe}`,fe.set(t,e)),e}function pe(t){return t.slice(t.indexOf("::")+2)}function ee(t,e,n){const o=Array.isArray(e)?e.map(T).sort().join("|"):T(e);return n?`${t}::${o}::${n}`:`${t}::${o}`}function ze(t,e,n){const o=t(),s=e.reducer,c=T(e.property),a=`${Se(o)}::${ee(s,c,n.key)}`,h=u.useMemo(()=>l=>o.connect({reducer:s,property:c},()=>{N.invalidate(a),l()}),[o,s,c,a]),S=u.useRef(n);S.current=n;const g=u.useMemo(()=>{const l=G(c);return()=>{var b;const f=o.getState()[s],v=l?f:L(f,c),y=S.current;return N.read(a,()=>y.load(v,f),(b=y.staleTime)!=null?b:0,y.errorTtlMs)}},[o,s,c,a]),d=u.useMemo(()=>{const l=G(c);return()=>{const f=o.getState()[s];return l?f:L(f,c)}},[o,s,c]);return u.useSyncExternalStore(h,g,d)}function We(t,e,n){const o=t(),s=u.useMemo(()=>e.map(d=>({reducer:d.reducer,property:Array.isArray(d.property)?d.property.map(l=>T(l)):T(d.property)})),[JSON.stringify(e)]),c=u.useMemo(()=>{const d=s.map(l=>ee(l.reducer,l.property)).sort().join("||");return`${Se(o)}::${n.key?`${d}::${n.key}`:d}`},[o,s,n.key]),a=u.useMemo(()=>d=>{const l=()=>{N.invalidate(c),d()},m=s.map(f=>o.connect(f,l));return()=>{for(const f of m)f()}},[o,s,c]),h=u.useRef(n);h.current=n;const S=u.useMemo(()=>()=>{var m;const d=o.getState(),l=h.current;return N.read(c,()=>l.load(d),(m=l.staleTime)!=null?m:0,l.errorTtlMs)},[o,c]),g=()=>{const d=h.current.load(o.getState());return d instanceof Promise?void 0:d};return u.useSyncExternalStore(a,S,g)}function Ge(t,e,n){N.invalidatePathKey(ee(t,e,n))}function Le(t){N.invalidateReducer(t)}function Ue(){N.clear()}function Ve(t){return{useSuspenseAtomicProp:(o,s)=>ze(t,o,s),useSuspenseAtomicProps:(o,s)=>We(t,o,s)}}function ve(t){function e(){const l=u.useContext(t);if(!l)throw new Error("[yoltra] No store in context. Wrap your tree in <StoreProvider store={...}>, or use the hooks returned by createYoltra (which default to their own store).");return l}function n(){return e().emit}function o(l,m=Object.is){const f=e(),v=u.useMemo(()=>b=>f.subscribe(b),[f]),y=U(()=>l(f.getState()),m,[f]);return u.useSyncExternalStore(v,y,y)}const c=(l,m,f)=>{const v=e(),y=typeof l=="string",b=y?l:l.reducer,k=y?je(m):l.property,_=y?void 0:m,P=u.useMemo(()=>({reducer:b,property:T(k)}),[b,k]),j=u.useMemo(()=>M=>v.connect({reducer:P.reducer,property:P.property},()=>M()),[v,P]),$=G(P.property),I=U(()=>{const A=v.getState()[P.reducer],O=$?A:L(A,P.property);return _?_(O):O},f!=null?f:Object.is,[v,P]);return u.useSyncExternalStore(j,I,I)},h=(l,m,f=Object.is)=>{const v=e(),y=u.useRef(0),b=u.useRef(void 0),k=u.useRef(-1),_=u.useRef(!1),P=u.useRef(m);P.current=m;const j=u.useRef(f);j.current=f;const $=u.useMemo(()=>l.map(A=>({reducer:A.reducer,property:Array.isArray(A.property)?A.property.map(O=>T(O)):T(A.property)})),[ke(l)]),I=u.useMemo(()=>A=>{const O=()=>{y.current++,A()},V=$.flatMap(w=>(Array.isArray(w.property)?w.property:[w.property]).map(J=>v.connect({reducer:w.reducer,property:J},O)));return()=>{for(const w of V)w()}},[v,$]),M=u.useCallback(()=>{if(k.current!==y.current||!_.current){const A=P.current(v.getState());(!_.current||!j.current(b.current,A))&&(b.current=A,_.current=!0),k.current=y.current}return b.current},[v]);return u.useSyncExternalStore(I,M,M)},S=(l,m,f,v="committed")=>{const y=e(),b=u.useRef(f);b.current=f,u.useEffect(()=>y.onEvent(l,m,(k,_,P,j)=>{b.current(k,_,P,j)},v),[y,l,m,v])},{useSuspenseAtomicProp:g,useSuspenseAtomicProps:d}=Ve(e);return{useStore:e,useEmit:n,useSelector:o,useAtomicProp:c,useAtomicProps:h,useEvent:S,useSuspenseAtomicProp:g,useSuspenseAtomicProps:d,shallowEqual:ye}}function Be(t){const e=Ae.createStore(t),n=u.createContext(e),o=ve(n);return{store:e,StoreContext:n,StoreProvider:({store:c,children:a})=>de.jsx(n.Provider,{value:c!=null?c:e,children:a}),...o}}function Je(t,e){return Q({reducer:t,property:e.idsPath})}function qe(t,e,n){return Q({reducer:t,property:e.pathTo(n)})}function Xe(t,e,n,o){return Q({reducer:t,property:e.pathTo(n,o)})}exports.StoreContext=K;exports.StoreProvider=we;exports.clearSuspenseCache=Ue;exports.createHooks=ve;exports.createYoltra=Be;exports.invalidateAtomicProp=Ge;exports.invalidateAtomicPropsByReducer=Le;exports.shallowEqual=ye;exports.suspenseCache=N;exports.useEmit=Oe;exports.useEntity=qe;exports.useEntityField=Xe;exports.useEntityIds=Je;exports.useEvent=Me;exports.useSelector=Ce;exports.useStore=z;
10
+ <%s key={someKey} {...props} />`,b,E,Y,E),se[E+b]=!0)}if(E=null,p!==void 0&&(o(p),E=""+p),a(i)&&(o(i.key),E=""+i.key),"key"in i){p={};for(var Z in i)Z!=="key"&&(p[Z]=i[Z])}else p=i;return E&&m(p,typeof r=="function"?r.displayName||r.name||"Unknown":r),g(r,E,x,C,s(),p,X,H)}function l(r){typeof r=="object"&&r!==null&&r.$$typeof===f&&r._store&&(r._store.validated=1)}var S=c,f=Symbol.for("react.transitional.element"),v=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),P=Symbol.for("react.strict_mode"),k=Symbol.for("react.profiler"),_=Symbol.for("react.consumer"),A=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),$=Symbol.for("react.suspense"),I=Symbol.for("react.suspense_list"),M=Symbol.for("react.memo"),R=Symbol.for("react.lazy"),O=Symbol.for("react.activity"),U=Symbol.for("react.client.reference"),w=S.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,V=Object.prototype.hasOwnProperty,B=Array.isArray,J=console.createTask?console.createTask:function(){return null};S={react_stack_bottom_frame:function(r){return r()}};var te,re={},oe=S.react_stack_bottom_frame.bind(S,u)(),ne=J(n(u)),se={};F.Fragment=h,F.jsx=function(r,i,p,b,C){var x=1e4>w.recentlyCreatedOwnerStacks++;return d(r,i,p,!1,b,C,x?Error("react-stack-top-frame"):oe,x?J(n(r)):ne)},F.jsxs=function(r,i,p,b,C){var x=1e4>w.recentlyCreatedOwnerStacks++;return d(r,i,p,!0,b,C,x?Error("react-stack-top-frame"):oe,x?J(n(r)):ne)}})()),F}var ae;function Ae(){return ae||(ae=1,process.env.NODE_ENV==="production"?W.exports=Ee():W.exports=Re()),W.exports}var pe=Ae();const Pe=({store:t,children:e})=>pe.jsx(K.Provider,{value:t,children:e}),ie=new Set;function ge(t,e){process.env.NODE_ENV!=="production"&&(ie.has(t)||(ie.add(t),console.warn(e)))}function q(t){return t.includes("*")}function T(t){return t.replace(/^\./,"")}function _e(t){return T(t).split(".").filter(Boolean)}function we(t){const e=o=>`${o.length}:${o}`;return t.map(o=>{const n=Array.isArray(o.property)?o.property:[o.property];return e(o.reducer)+e(String(n.length))+n.map(e).join("")}).join("")}const de=Symbol("yoltra.pathSegments");function me(t){const e=()=>{};return new Proxy(e,{get(o,n){if(n===de)return t;if(typeof n!="symbol")return me([...t,String(n)])},apply(){throw new Error(`[yoltra] A typed path accessor called a method (near "${t.join(".")}"). The accessor must be a plain member chain like \`p => p.items[0].title\` — it cannot call functions such as \`.map()\` or \`.toString()\`. Compute derived values in the component or a selector, or use the \`{ reducer, property }\` string form.`)}})}function xe(t){const e=t(me([])),o=e?.[de],s=(Array.isArray(o)?o:[]).join(".");return s===""&&ge("yoltra.toDottedPath.empty","[yoltra] A typed path accessor recorded no property access, so it subscribed to the slice root. That fires only when the slice's whole value is replaced — which for a slice holding an object means never, since its changes are reported at their leaves. The accessor must be a plain member chain like `p => p.items[0].title` and cannot return a computed value or a default. For a dynamic subscription, or for a slice whose state is a single value, use the `{ reducer, property }` string form instead."),s}function G(t,e){if(!e)return t;let o=t;for(const n of _e(e)){if(o==null)return;o=o[n]}return o}function L(t,e,o){const n=c.useRef(t);n.current=t;const s=c.useRef(e);s.current=e;const u=c.useMemo(()=>({has:!1,value:void 0}),o);return c.useCallback(()=>{const a=n.current();return(!u.has||!s.current(u.value,a))&&(u.has=!0,u.value=a),u.value},[u])}function he(t,e){if(Object.is(t,e))return!0;if(!t||!e)return!1;const o=Object.keys(t),n=Object.keys(e);if(o.length!==n.length)return!1;for(const s of o)if(!Object.is(t[s],e[s]))return!1;return!0}function z(){const t=c.useContext(K);if(!t)throw new Error("useStore must be used inside <StoreProvider>");return t}function Te(){return z().emit}function ke(t,e=Object.is){const o=z(),n=c.useMemo(()=>u=>o.subscribe(u),[o]),s=L(()=>t(o.getState()),e,[o]);return c.useSyncExternalStore(n,s,s)}function je(t,e,o=Object.is){const n=z(),s=c.useMemo(()=>{const y=T(t.property);return{reducer:t.reducer,property:y}},[t.reducer,t.property]),u=c.useMemo(()=>y=>n.connect({reducer:s.reducer,property:s.property},()=>y()),[n,s]),a=q(s.property),m=L(()=>{const g=n.getState()[s.reducer];return a?g:G(g,s.property)},o,[n,s]);return c.useSyncExternalStore(u,m,m)}function Q(t,e,o=Object.is){return je(t,e,o)}function Oe(t,e,o,n="committed"){const s=z(),u=c.useRef(o);u.current=o,c.useEffect(()=>s.onEvent(t,e,(a,m,y,g)=>{u.current(a,m,y,g)},n),[s,t,e,n])}function Ce(t){return t==null||t<=0?null:Date.now()+t}function Ne(t){return t===null?null:t===void 0||t<=0?0:Date.now()+t}const Me=2e3;class $e{store=new Map;touch(e){this.store.has(e)&&this.store.delete(e)}evict(){for(;this.store.size>Me;){const e=this.store.keys().next();if(e.done===!0)return;const o=this.store.get(e.value);if(o?.status==="pending"){this.store.delete(e.value),this.store.set(e.value,o);continue}this.store.delete(e.value)}}get size(){return this.store.size}read(e,o,n,s){const u=Date.now(),a=this.store.get(e);if(a&&a.status==="ready"&&(a.expiresAt==null||a.expiresAt>u))return this.touch(e),this.store.set(e,a),a.value;if(a&&a.status==="pending")throw a.promise;if(a&&a.status==="error"){if(a.delivered!==!0)throw this.store.set(e,{...a,delivered:!0}),a.error;if(a.expiresAt===null||a.expiresAt>u)throw a.error;this.store.delete(e)}const m=Promise.resolve().then(o).then(y=>{this.store.set(e,{status:"ready",value:y,expiresAt:Ce(n)})}).catch(y=>{this.store.set(e,{status:"error",error:y,expiresAt:Ne(s)})});throw this.store.set(e,{status:"pending",promise:m,expiresAt:null}),this.evict(),m}invalidate(e){this.store.delete(e)}invalidatePathKey(e){for(const o of this.store.keys())fe(o)===e&&this.store.delete(o)}invalidateReducer(e){const o=`${e}::`;for(const n of this.store.keys())fe(n).split("||").some(s=>s.startsWith(o))&&this.store.delete(n)}clear(){this.store.clear()}}const N=new $e,le=new WeakMap;let Ie=0;function ye(t){let e=le.get(t);return e===void 0&&(e=`s${++Ie}`,le.set(t,e)),e}function fe(t){return t.slice(t.indexOf("::")+2)}function ee(t,e,o){const n=Array.isArray(e)?e.map(T).sort().join("|"):T(e);return o?`${t}::${n}::${o}`:`${t}::${n}`}function Ye(t,e,o){const n=t(),s=e.reducer,u=T(e.property),a=`${ye(n)}::${ee(s,u,o.key)}`,m=c.useMemo(()=>l=>n.connect({reducer:s,property:u},()=>{N.invalidate(a),l()}),[n,s,u,a]),y=c.useRef(o);y.current=o;const g=c.useMemo(()=>{const l=q(u);return()=>{const f=n.getState()[s],v=l?f:G(f,u),h=y.current;return N.read(a,()=>h.load(v,f),h.staleTime??0,h.errorTtlMs)}},[n,s,u,a]),d=c.useMemo(()=>{const l=q(u);return()=>{const f=n.getState()[s];return l?f:G(f,u)}},[n,s,u]);return c.useSyncExternalStore(m,g,d)}function De(t,e,o){const n=t(),s=c.useMemo(()=>e.map(d=>({reducer:d.reducer,property:Array.isArray(d.property)?d.property.map(l=>T(l)):T(d.property)})),[JSON.stringify(e)]),u=c.useMemo(()=>{const d=s.map(l=>ee(l.reducer,l.property)).sort().join("||");return`${ye(n)}::${o.key?`${d}::${o.key}`:d}`},[n,s,o.key]),a=c.useMemo(()=>d=>{const l=()=>{N.invalidate(u),d()},S=s.map(f=>n.connect(f,l));return()=>{for(const f of S)f()}},[n,s,u]),m=c.useRef(o);m.current=o;const y=c.useMemo(()=>()=>{const d=n.getState(),l=m.current;return N.read(u,()=>l.load(d),l.staleTime??0,l.errorTtlMs)},[n,u]),g=()=>{const d=m.current.load(n.getState());return d instanceof Promise?void 0:d};return c.useSyncExternalStore(a,y,g)}function Fe(t,e,o){N.invalidatePathKey(ee(t,e,o))}function ze(t){N.invalidateReducer(t)}function We(){N.clear()}function qe(t){return{useSuspenseAtomicProp:(n,s)=>Ye(t,n,s),useSuspenseAtomicProps:(n,s)=>De(t,n,s)}}function Se(t){function e(){const l=c.useContext(t);if(!l)throw new Error("[yoltra] No store in context. Wrap your tree in <StoreProvider store={...}>, or use the hooks returned by createYoltra (which default to their own store).");return l}function o(){return e().emit}function n(l,S=Object.is){const f=e(),v=c.useMemo(()=>P=>f.subscribe(P),[f]),h=L(()=>l(f.getState()),S,[f]);return c.useSyncExternalStore(v,h,h)}const u=(l,S,f)=>{const v=e(),h=typeof l=="string",P=h?l:l.reducer,k=h?xe(S):l.property,_=h?void 0:S,A=c.useMemo(()=>({reducer:P,property:T(k)}),[P,k]),j=c.useMemo(()=>M=>v.connect({reducer:A.reducer,property:A.property},()=>M()),[v,A]),$=q(A.property),I=L(()=>{const R=v.getState()[A.reducer],O=$?R:G(R,A.property);return _?_(O):O},f??Object.is,[v,A]);return c.useSyncExternalStore(j,I,I)},m=(l,S,f=Object.is)=>{const v=e(),h=c.useRef(0),P=c.useRef(void 0),k=c.useRef(-1),_=c.useRef(!1),A=c.useRef(S);A.current=S;const j=c.useRef(f);j.current=f;const $=c.useMemo(()=>l.map(R=>({reducer:R.reducer,property:Array.isArray(R.property)?R.property.map(O=>T(O)):T(R.property)})),[we(l)]),I=c.useMemo(()=>R=>{const O=()=>{h.current++,R()},U=$.flatMap(w=>(Array.isArray(w.property)?w.property:[w.property]).map(B=>v.connect({reducer:w.reducer,property:B},O)));return()=>{for(const w of U)w()}},[v,$]),M=c.useCallback(()=>{if(k.current!==h.current||!_.current){const R=A.current(v.getState());(!_.current||!j.current(P.current,R))&&(P.current=R,_.current=!0),k.current=h.current}return P.current},[v]);return c.useSyncExternalStore(I,M,M)},y=(l,S,f,v="committed")=>{const h=e(),P=c.useRef(f);P.current=f,c.useEffect(()=>h.onEvent(l,S,(k,_,A,j)=>{P.current(k,_,A,j)},v),[h,l,S,v])},{useSuspenseAtomicProp:g,useSuspenseAtomicProps:d}=qe(e);return{useStore:e,useEmit:o,useSelector:n,useAtomicProp:u,useAtomicProps:m,useEvent:y,useSuspenseAtomicProp:g,useSuspenseAtomicProps:d,shallowEqual:he}}function Ge(t){const e=be.createStore(t),o=c.createContext(e),n=Se(o);return{store:e,StoreContext:o,StoreProvider:({store:u,children:a})=>pe.jsx(o.Provider,{value:u??e,children:a}),...n}}function Le(t,e){return Q({reducer:t,property:e.idsPath})}function Ue(t,e,o){return Q({reducer:t,property:e.pathTo(o)})}function Ve(t,e,o,n){return Q({reducer:t,property:e.pathTo(o,n)})}exports.StoreContext=K;exports.StoreProvider=Pe;exports.clearSuspenseCache=We;exports.createHooks=Se;exports.createYoltra=Ge;exports.invalidateAtomicProp=Fe;exports.invalidateAtomicPropsByReducer=ze;exports.shallowEqual=he;exports.suspenseCache=N;exports.useEmit=Te;exports.useEntity=Ue;exports.useEntityField=Ve;exports.useEntityIds=Le;exports.useEvent=Oe;exports.useSelector=ke;exports.useStore=z;
16
11
  //# sourceMappingURL=index.cjs.map