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
| TypeScript | Python |
|---|---|
createStore({...}) | create_store("Name", reducer={...}, ...) |
store.emit(channel, type, payload) → Promise | store.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 / registerEffect | store.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:
when = {"keys": [("ui", "add"), ("ui", "rename")]}#Azúcar de decoradores
La edición de Python agrega decoradores sobre la API imperativa de registro:
@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 que1vs1.0yTruevs1se 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:
@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.