Documentación

Migrar desde @yoltra/core (TypeScript)

Yoltra para Python es un port semántico de @yoltra/core. El modelo de reduce / diff / suscripción es idéntico (y está verificado por conformidad entre lenguajes), así que tu modelo mental se transfiere directamente. Esta guía mapea la API y señala las divergencias intencionales de Python.

#Mapeo de la API

TypeScriptPython
createStore({...})create_store("Name", reducer={...}, ...)
store.emit(channel, type, payload)Promisestore.emit(channel, type, payload)EmitHandle
await store.emit(...)await store.emit(...) / await store.aemit(...) / handle.wait()
store.getState()store.get_state()
store.connect({ reducer, property }, handler)store.connect(reducer=..., property=..., handler=...)
store.subscribe(fn)store.subscribe(fn)
store.onEvent(channel, type, handler, phase)store.on_event(channel, type, handler, phase="committed")
store.onEffect(channel, type, handler)store.on_effect(channel, type, handler)
store.registerReducer / registerMiddleware / registerEffectstore.register_reducer / register_middleware / register_effect
store.instrument(observer)store.instrument(observer)
detectChangedProps(prev, next)detect_changed_props(prev, next)
Change { oldValue, newValue, path }Change(old_value, new_value, path)
When ({ keys | channel | channels | any })When (misma forma; un TypedDict)

Los nombres son snake_case en todo, y los campos de Change son old_value / new_value. Las claves de evento son tuplas simples — sin necesidad de as const:

python
when = {"keys": [("ui", "add"), ("ui", "rename")]}

#Azúcar de decoradores

La edición de Python agrega decoradores sobre la API imperativa de registro:

python
@store.reducer(name="todos", state={"items": []}, when={"keys": [("ui", "add")]})
def todos(prev, event): ...

@store.effect(when={"keys": [("ui", "add")]})
async def persist(event, get_state, emit): ...

@store.middleware()
def guard(state, event, emit): ...

#Divergencias intencionales

Son deliberadas, y están documentadas para que nada te sorprenda.

#Confirmación síncrona, EmitHandle para los efectos

En ambas ediciones la fase de reduce es síncrona. En TypeScript emit retorna una Promise que se resuelve después de los efectos. En Python emit retorna un EmitHandle que es a la vez esperable (await store.emit(...)) y bloqueable desde código síncrono (handle.wait()), así que el mismo store funciona en scripts, notebooks y servidores async. Elige dónde corren los efectos con effects_mode ("auto" / "inline" / "asyncio") — un control específico de Python sin equivalente en TS.

#Comparación más rica, consciente de tipos

La comparación de TypeScript trata de forma especial solo Date y RegExp. La de Python agrega manejo que no tiene análogo en TS (diseño §7.4):

  • set / frozenset, datetime / date, Decimal, tuple, bytes, dataclasses / modelos de Pydantic (por iteración de campos),
  • una verificación type(old) is not type(new), de modo que 1 vs 1.0 y True vs 1 se distinguen (el == de Python los colapsaría),
  • igualdad de NaN.

Estas extensiones están excluidas del corpus de conformidad entre lenguajes (el corpus cubre solo el subconjunto compartido serializable a JSON); todo lo que está en ese subconjunto compartido se comporta idéntico en ambos lenguajes.

#Semántica del veto en middleware

El middleware de Python veta solo con un False explícito; retornar None (un return faltante) o True deja pasar el evento. Esto es más amable que la verificación !ok de TS, que también veta con undefined.

#Las rutas son cadenas simples

No hay Path<T> / PathValue<T, P> en tiempo de compilación — Python no tiene una característica equivalente en su sistema de tipos. Las rutas son str; una ruta obsoleta es una preocupación de tiempo de ejecución con pruebas (al estilo de los lookups __ de Django). Es un no-objetivo de la v1, no una limitación del modelo.

#No portado: HMR

replaceReducers / replaceMiddleware / replaceEffects / hotReplace son un concepto de bundlers de JS sin análogo en Python y no se portan. Usa los register_* en tiempo de ejecución / las funciones de cancelación que retornan para cambios dinámicos.

#coarse_only de primera clase

La opción de rendimiento por slice es de primera clase en Python:

python
@store.reducer(name="big", state={...}, when={...}, coarse_only=True)
def big(prev, event): ...

coarse_only=True omite la comparación estructural por slice y dispara una sola señal de slice completo más los listeners gruesos — para slices donde no necesitas granularidad a nivel de ruta.

#Mecanismo de inmutabilidad

TypeScript congela el estado en el lugar de forma profunda (Object.freeze). Python no tiene un congelado en el lugar, así que get_state() retorna un proxy recursivo de solo lectura en desarrollo; el estado almacenado permanece crudo. El comportamiento (las mutaciones lanzan) coincide; el mecanismo difiere. En producción es un paso directo en ambos.

#Misma semántica, verificada

El algoritmo de comparación y el matcher glob están fijados por un corpus JSON compartido que se corre a través de ambos cores (conformance/), así que un par (prev, next) o una coincidencia pattern/path se resuelve igual en TypeScript y en Python.

Editar esta página en GitHub