pulse-framework 0.1.55__py3-none-any.whl → 0.1.56__py3-none-any.whl
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.
- pulse/__init__.py +5 -6
- pulse/app.py +144 -57
- pulse/channel.py +139 -7
- pulse/cli/cmd.py +16 -2
- pulse/codegen/codegen.py +43 -12
- pulse/component.py +104 -0
- pulse/components/for_.py +30 -4
- pulse/components/if_.py +28 -5
- pulse/components/react_router.py +61 -3
- pulse/context.py +39 -5
- pulse/cookies.py +108 -4
- pulse/decorators.py +193 -24
- pulse/env.py +56 -2
- pulse/form.py +198 -5
- pulse/helpers.py +7 -1
- pulse/hooks/core.py +135 -5
- pulse/hooks/effects.py +61 -77
- pulse/hooks/init.py +60 -1
- pulse/hooks/runtime.py +241 -0
- pulse/hooks/setup.py +77 -0
- pulse/hooks/stable.py +58 -1
- pulse/hooks/state.py +107 -20
- pulse/js/__init__.py +40 -24
- pulse/js/array.py +9 -6
- pulse/js/console.py +15 -12
- pulse/js/date.py +9 -6
- pulse/js/document.py +5 -2
- pulse/js/error.py +7 -4
- pulse/js/json.py +9 -6
- pulse/js/map.py +8 -5
- pulse/js/math.py +9 -6
- pulse/js/navigator.py +5 -2
- pulse/js/number.py +9 -6
- pulse/js/obj.py +16 -13
- pulse/js/object.py +9 -6
- pulse/js/promise.py +19 -13
- pulse/js/pulse.py +28 -25
- pulse/js/react.py +94 -55
- pulse/js/regexp.py +7 -4
- pulse/js/set.py +8 -5
- pulse/js/string.py +9 -6
- pulse/js/weakmap.py +8 -5
- pulse/js/weakset.py +8 -5
- pulse/js/window.py +6 -3
- pulse/messages.py +5 -0
- pulse/middleware.py +147 -76
- pulse/plugin.py +76 -5
- pulse/queries/client.py +186 -39
- pulse/queries/common.py +52 -3
- pulse/queries/infinite_query.py +154 -2
- pulse/queries/mutation.py +127 -7
- pulse/queries/query.py +112 -11
- pulse/react_component.py +66 -3
- pulse/reactive.py +314 -30
- pulse/reactive_extensions.py +106 -26
- pulse/render_session.py +304 -173
- pulse/request.py +46 -11
- pulse/routing.py +140 -4
- pulse/serializer.py +71 -0
- pulse/state.py +177 -9
- pulse/test_helpers.py +15 -0
- pulse/transpiler/__init__.py +0 -3
- pulse/transpiler/py_module.py +1 -7
- pulse/user_session.py +119 -18
- {pulse_framework-0.1.55.dist-info → pulse_framework-0.1.56.dist-info}/METADATA +5 -5
- pulse_framework-0.1.56.dist-info/RECORD +127 -0
- pulse/transpiler/react_component.py +0 -44
- pulse_framework-0.1.55.dist-info/RECORD +0 -127
- {pulse_framework-0.1.55.dist-info → pulse_framework-0.1.56.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.55.dist-info → pulse_framework-0.1.56.dist-info}/entry_points.txt +0 -0
pulse/reactive_extensions.py
CHANGED
|
@@ -77,11 +77,21 @@ class ReactiveDictValues(Generic[T1, T2]):
|
|
|
77
77
|
class ReactiveDict(dict[T1, T2]):
|
|
78
78
|
"""A dict-like container with per-key reactivity.
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
Reading a key registers a dependency on that key's Signal. Writing a key
|
|
81
|
+
updates only that key's Signal. Iteration, membership checks, and len are
|
|
82
|
+
reactive to structural changes.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
initial: Initial key-value pairs to populate the dict.
|
|
86
|
+
|
|
87
|
+
Example:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
data = ReactiveDict({"name": "Alice", "age": 30})
|
|
91
|
+
print(data["name"]) # "Alice" (registers dependency)
|
|
92
|
+
data["age"] = 31 # Updates age signal only
|
|
93
|
+
data.unwrap() # {"name": "Alice", "age": 31}
|
|
94
|
+
```
|
|
85
95
|
"""
|
|
86
96
|
|
|
87
97
|
__slots__ = ("_signals", "_structure") # pyright: ignore[reportUnannotatedClassAttribute]
|
|
@@ -365,7 +375,11 @@ class ReactiveDict(dict[T1, T2]):
|
|
|
365
375
|
return result
|
|
366
376
|
|
|
367
377
|
def unwrap(self) -> dict[T1, _Any]:
|
|
368
|
-
"""Return a plain dict while subscribing to contained signals.
|
|
378
|
+
"""Return a plain dict while subscribing to contained signals.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
A plain dict with all reactive containers recursively unwrapped.
|
|
382
|
+
"""
|
|
369
383
|
self._structure.read()
|
|
370
384
|
result: dict[T1, _Any] = {}
|
|
371
385
|
for key in dict.__iter__(self):
|
|
@@ -398,15 +412,24 @@ SupportsRichComparisonT = TypeVar(
|
|
|
398
412
|
|
|
399
413
|
|
|
400
414
|
class ReactiveList(list[T1]):
|
|
401
|
-
"""A list with item-level reactivity
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
415
|
+
"""A list with item-level reactivity and structural change signaling.
|
|
416
|
+
|
|
417
|
+
Index reads depend on that index's Signal. Setting an index writes to that
|
|
418
|
+
index's Signal. Structural operations (append/insert/pop/etc.) trigger a
|
|
419
|
+
structural version Signal. Iteration subscribes to all item signals and
|
|
420
|
+
structural changes. len() subscribes to structural changes.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
initial: Initial items to populate the list.
|
|
424
|
+
|
|
425
|
+
Example:
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
items = ReactiveList([1, 2, 3])
|
|
429
|
+
print(items[0]) # 1 (registers dependency on index 0)
|
|
430
|
+
items.append(4) # Triggers structural change
|
|
431
|
+
items.unwrap() # [1, 2, 3, 4]
|
|
432
|
+
```
|
|
410
433
|
"""
|
|
411
434
|
|
|
412
435
|
__slots__ = ("_signals", "_structure") # pyright: ignore[reportUnannotatedClassAttribute]
|
|
@@ -528,7 +551,11 @@ class ReactiveList(list[T1]):
|
|
|
528
551
|
return val
|
|
529
552
|
|
|
530
553
|
def unwrap(self) -> list[_Any]:
|
|
531
|
-
"""Return a plain list while subscribing to element signals.
|
|
554
|
+
"""Return a plain list while subscribing to element signals.
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
A plain list with all reactive containers recursively unwrapped.
|
|
558
|
+
"""
|
|
532
559
|
self._structure()
|
|
533
560
|
return [unwrap(self[i]) for i in range(len(self._signals))]
|
|
534
561
|
|
|
@@ -640,9 +667,21 @@ class ReactiveList(list[T1]):
|
|
|
640
667
|
class ReactiveSet(set[T1]):
|
|
641
668
|
"""A set with per-element membership reactivity.
|
|
642
669
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
670
|
+
`x in s` reads a membership Signal for element `x`. Mutations update
|
|
671
|
+
membership Signals for affected elements. Iteration subscribes to
|
|
672
|
+
membership signals for all elements.
|
|
673
|
+
|
|
674
|
+
Args:
|
|
675
|
+
initial: Initial elements to populate the set.
|
|
676
|
+
|
|
677
|
+
Example:
|
|
678
|
+
|
|
679
|
+
```python
|
|
680
|
+
tags = ReactiveSet({"python", "react"})
|
|
681
|
+
print("python" in tags) # True (registers dependency)
|
|
682
|
+
tags.add("typescript") # Updates membership signal
|
|
683
|
+
tags.unwrap() # {"python", "react", "typescript"}
|
|
684
|
+
```
|
|
646
685
|
"""
|
|
647
686
|
|
|
648
687
|
__slots__ = ("_signals",) # pyright: ignore[reportUnannotatedClassAttribute]
|
|
@@ -721,7 +760,11 @@ class ReactiveSet(set[T1]):
|
|
|
721
760
|
self.discard(v)
|
|
722
761
|
|
|
723
762
|
def unwrap(self) -> set[_Any]:
|
|
724
|
-
"""Return a plain set while subscribing to membership signals.
|
|
763
|
+
"""Return a plain set while subscribing to membership signals.
|
|
764
|
+
|
|
765
|
+
Returns:
|
|
766
|
+
A plain set with all reactive containers recursively unwrapped.
|
|
767
|
+
"""
|
|
725
768
|
result: set[_Any] = set()
|
|
726
769
|
for value in set.__iter__(self):
|
|
727
770
|
_ = value in self
|
|
@@ -1009,10 +1052,28 @@ def reactive(value: T1) -> T1: ...
|
|
|
1009
1052
|
def reactive(value: _Any) -> _Any:
|
|
1010
1053
|
"""Wrap built-in collections in their reactive counterparts if not already reactive.
|
|
1011
1054
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1055
|
+
Converts:
|
|
1056
|
+
- dict -> ReactiveDict
|
|
1057
|
+
- list -> ReactiveList
|
|
1058
|
+
- set -> ReactiveSet
|
|
1059
|
+
- dataclass instance -> reactive dataclass with Signal-backed fields
|
|
1060
|
+
|
|
1061
|
+
Leaves other values (primitives, already-reactive containers) untouched.
|
|
1062
|
+
|
|
1063
|
+
Args:
|
|
1064
|
+
value: The value to make reactive.
|
|
1065
|
+
|
|
1066
|
+
Returns:
|
|
1067
|
+
The reactive version of the value, or the original if already reactive
|
|
1068
|
+
or not a supported collection type.
|
|
1069
|
+
|
|
1070
|
+
Example:
|
|
1071
|
+
|
|
1072
|
+
```python
|
|
1073
|
+
data = reactive({"key": "value"}) # ReactiveDict
|
|
1074
|
+
items = reactive([1, 2, 3]) # ReactiveList
|
|
1075
|
+
tags = reactive({"a", "b"}) # ReactiveSet
|
|
1076
|
+
```
|
|
1016
1077
|
"""
|
|
1017
1078
|
if isinstance(value, ReactiveDict | ReactiveList | ReactiveSet):
|
|
1018
1079
|
return value
|
|
@@ -1058,9 +1119,28 @@ def reactive(value: _Any) -> _Any:
|
|
|
1058
1119
|
def unwrap(value: _Any, untrack: bool = False) -> _Any:
|
|
1059
1120
|
"""Recursively unwrap reactive containers into plain Python values.
|
|
1060
1121
|
|
|
1122
|
+
Converts:
|
|
1123
|
+
- Signal/Computed -> their read() value
|
|
1124
|
+
- ReactiveDict -> dict
|
|
1125
|
+
- ReactiveList -> list
|
|
1126
|
+
- ReactiveSet -> set
|
|
1127
|
+
- Other Mapping/Sequence types are recursively unwrapped
|
|
1128
|
+
|
|
1061
1129
|
Args:
|
|
1062
|
-
|
|
1063
|
-
|
|
1130
|
+
value: The value to unwrap.
|
|
1131
|
+
untrack: If True, don't track dependencies during unwrapping.
|
|
1132
|
+
Defaults to False.
|
|
1133
|
+
|
|
1134
|
+
Returns:
|
|
1135
|
+
A plain Python value with all reactive containers unwrapped.
|
|
1136
|
+
|
|
1137
|
+
Example:
|
|
1138
|
+
|
|
1139
|
+
```python
|
|
1140
|
+
count = Signal(5)
|
|
1141
|
+
data = ReactiveDict({"count": count})
|
|
1142
|
+
unwrap(data) # {"count": 5}
|
|
1143
|
+
```
|
|
1064
1144
|
"""
|
|
1065
1145
|
|
|
1066
1146
|
def _unwrap(v: _Any) -> _Any:
|